e6646eff1180e8cf1ba1796eea53ea7c00035591
[oweals/minetest.git] / src / clientobject.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "clientobject.h"
21 #include "debug.h"
22 #include "porting.h"
23 #include "constants.h"
24 #include "utility.h"
25
26 ClientActiveObject::ClientActiveObject(u16 id):
27         ActiveObject(id)
28 {
29 }
30
31 ClientActiveObject::~ClientActiveObject()
32 {
33         removeFromScene();
34 }
35
36 ClientActiveObject* ClientActiveObject::create(u8 type)
37 {
38         if(type == ACTIVEOBJECT_TYPE_INVALID)
39         {
40                 dstream<<"ClientActiveObject::create(): passed "
41                                 <<"ACTIVEOBJECT_TYPE_INVALID"<<std::endl;
42                 return NULL;
43         }
44         else if(type == ACTIVEOBJECT_TYPE_TEST)
45         {
46                 dstream<<"ClientActiveObject::create(): passed "
47                                 <<"ACTIVEOBJECT_TYPE_TEST"<<std::endl;
48                 return new TestCAO(0);
49         }
50         else if(type == ACTIVEOBJECT_TYPE_LUA)
51         {
52                 dstream<<"ClientActiveObject::create(): passed "
53                                 <<"ACTIVEOBJECT_TYPE_LUA"<<std::endl;
54                 return new LuaCAO(0);
55         }
56         else
57         {
58                 dstream<<"ClientActiveObject::create(): passed "
59                                 <<"unknown type="<<type<<std::endl;
60                 return NULL;
61         }
62 }
63
64 /*
65         TestCAO
66 */
67
68 TestCAO::TestCAO(u16 id):
69         ClientActiveObject(id),
70         m_node(NULL),
71         m_position(v3f(0,10*BS,0))
72 {
73 }
74
75 TestCAO::~TestCAO()
76 {
77 }
78
79 void TestCAO::addToScene(scene::ISceneManager *smgr)
80 {
81         if(m_node != NULL)
82                 return;
83         
84         video::IVideoDriver* driver = smgr->getVideoDriver();
85         
86         scene::SMesh *mesh = new scene::SMesh();
87         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
88         video::SColor c(255,255,255,255);
89         video::S3DVertex vertices[4] =
90         {
91                 video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
92                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
93                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
94                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),
95         };
96         u16 indices[] = {0,1,2,2,3,0};
97         buf->append(vertices, 4, indices, 6);
98         // Set material
99         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
100         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
101         buf->getMaterial().setTexture
102                         (0, driver->getTexture(porting::getDataPath("rat.png").c_str()));
103         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
104         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
105         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
106         // Add to mesh
107         mesh->addMeshBuffer(buf);
108         buf->drop();
109         m_node = smgr->addMeshSceneNode(mesh, NULL);
110         mesh->drop();
111         updateNodePos();
112 }
113
114 void TestCAO::removeFromScene()
115 {
116         if(m_node == NULL)
117                 return;
118
119         m_node->remove();
120         m_node = NULL;
121 }
122
123 void TestCAO::updateLight(u8 light_at_pos)
124 {
125 }
126
127 v3s16 TestCAO::getLightPosition()
128 {
129         return floatToInt(m_position, BS);
130 }
131
132 void TestCAO::updateNodePos()
133 {
134         if(m_node == NULL)
135                 return;
136
137         m_node->setPosition(m_position);
138         //m_node->setRotation(v3f(0, 45, 0));
139 }
140
141 void TestCAO::step(float dtime)
142 {
143         if(m_node)
144         {
145                 v3f rot = m_node->getRotation();
146                 //dstream<<"dtime="<<dtime<<", rot.Y="<<rot.Y<<std::endl;
147                 rot.Y += dtime * 180;
148                 m_node->setRotation(rot);
149         }
150 }
151
152 void TestCAO::processMessage(const std::string &data)
153 {
154         dstream<<"TestCAO: Got data: "<<data<<std::endl;
155         std::istringstream is(data, std::ios::binary);
156         u16 cmd;
157         is>>cmd;
158         if(cmd == 0)
159         {
160                 v3f newpos;
161                 is>>newpos.X;
162                 is>>newpos.Y;
163                 is>>newpos.Z;
164                 m_position = newpos;
165                 updateNodePos();
166         }
167 }
168
169 /*
170         LuaCAO
171 */
172
173 extern "C"{
174 #include "lstring.h"
175 }
176
177 /*
178         object_set_position(self, x, y, z)
179 */
180 static int lf_object_set_position(lua_State *L)
181 {
182         // 4: z
183         lua_Number z = lua_tonumber(L, -1);
184         lua_pop(L, 1);
185         // 3: y
186         lua_Number y = lua_tonumber(L, -1);
187         lua_pop(L, 1);
188         // 2: x
189         lua_Number x = lua_tonumber(L, -1);
190         lua_pop(L, 1);
191         // 1: self
192         LuaCAO *self = (LuaCAO*)lua_touserdata(L, -1);
193         lua_pop(L, 1);
194         
195         assert(self);
196         
197         self->setPosition(v3f(x*BS,y*BS,z*BS));
198         
199         return 0; // Number of return values
200 }
201
202 /*
203         object_set_rotation(self, x, y, z)
204 */
205 static int lf_object_set_rotation(lua_State *L)
206 {
207         // 4: z
208         lua_Number z = lua_tonumber(L, -1);
209         lua_pop(L, 1);
210         // 3: y
211         lua_Number y = lua_tonumber(L, -1);
212         lua_pop(L, 1);
213         // 2: x
214         lua_Number x = lua_tonumber(L, -1);
215         lua_pop(L, 1);
216         // 1: self
217         LuaCAO *self = (LuaCAO*)lua_touserdata(L, -1);
218         lua_pop(L, 1);
219         
220         assert(self);
221         
222         self->setRotation(v3f(x,y,z));
223         
224         return 0; // Number of return values
225 }
226
227 /*
228         object_add_to_mesh(self, image, corners, backface_culling)
229         corners is an array like this:
230         {{x,y,z},{x,y,z},{x,y,z},{x,y,z}}
231 */
232 static int lf_object_add_to_mesh(lua_State *L)
233 {
234         // 4: backface_culling
235         bool backface_culling = lua_toboolean(L, -1);
236         lua_pop(L, 1);
237         // 3: corners
238         if(lua_istable(L, -1) == false)
239         {
240                 dstream<<"ERROR: object_add_to_mesh(): parameter 3 not a table"
241                                 <<std::endl;
242                 return 0;
243         }
244         v3f corners[4];
245         // Loop table
246         for(int i=0; i<4; i++)
247         {
248                 // Get child table
249                 lua_pushinteger(L, i+1);
250                 lua_gettable(L, -2);
251                 if(lua_istable(L, -1) == false)
252                 {
253                         dstream<<"ERROR: object_add_to_mesh(): parameter 3 not a"
254                                         " table of tables"<<std::endl;
255                         return 0;
256                 }
257
258                 // Get x, y and z from the child table
259                 
260                 lua_pushinteger(L, 1);
261                 lua_gettable(L, -2);
262                 corners[i].X = lua_tonumber(L, -1) * BS;
263                 lua_pop(L, 1);
264
265                 lua_pushinteger(L, 2);
266                 lua_gettable(L, -2);
267                 corners[i].Y = lua_tonumber(L, -1) * BS;
268                 lua_pop(L, 1);
269
270                 lua_pushinteger(L, 3);
271                 lua_gettable(L, -2);
272                 corners[i].Z = lua_tonumber(L, -1) * BS;
273                 lua_pop(L, 1);
274
275                 // Pop child table
276                 lua_pop(L, 1);
277         }
278         lua_pop(L, 1);
279         // 2: image
280         const char *image = lua_tostring(L, -1);
281         lua_pop(L, 1);
282         // 1: self
283         LuaCAO *self = (LuaCAO*)lua_touserdata(L, -1);
284         lua_pop(L, 1);
285
286         assert(self);
287         
288         self->addToMesh(image, corners, backface_culling);
289         
290         return 0; // Number of return values
291 }
292
293 /*
294         object_clear_mesh(self)
295 */
296 static int lf_object_clear_mesh(lua_State *L)
297 {
298         // 1: self
299         LuaCAO *self = (LuaCAO*)lua_touserdata(L, -1);
300         lua_pop(L, 1);
301         
302         assert(self);
303
304         self->clearMesh();
305
306         return 0;
307 }
308
309 LuaCAO::LuaCAO(u16 id):
310         ClientActiveObject(id),
311         L(NULL),
312         m_smgr(NULL),
313         m_node(NULL),
314         m_mesh(NULL),
315         m_position(v3f(0,10*BS,0))
316 {
317         dstream<<"LuaCAO::LuaCAO(): id="<<id<<std::endl;
318         L = lua_open();
319         assert(L);
320         
321         // Load libraries
322         luaopen_base(L);
323         luaopen_table(L);
324         luaopen_string(L);
325         luaopen_math(L);
326
327         // Disable some stuff
328         const char *to_disable[] = {
329                 "arg",
330                 "debug",
331                 "dofile",
332                 "io",
333                 "loadfile",
334                 "os",
335                 "package",
336                 "require",
337                 NULL
338         };
339         const char **td = to_disable;
340         do{
341                 lua_pushnil(L);
342                 lua_setglobal(L, *td);
343         }while(*(++td));
344         
345         // Add globals
346         //lua_pushlightuserdata(L, this);
347         //lua_setglobal(L, "self");
348         
349         // Register functions
350         lua_register(L, "object_set_position", lf_object_set_position);
351         lua_register(L, "object_set_rotation", lf_object_set_rotation);
352         lua_register(L, "object_add_to_mesh", lf_object_add_to_mesh);
353         lua_register(L, "object_clear_mesh", lf_object_clear_mesh);
354 }
355
356 LuaCAO::~LuaCAO()
357 {
358         lua_close(L);
359 }
360
361 void LuaCAO::step(float dtime)
362 {
363         /*
364                 Call step(self, dtime) from lua
365         */
366         
367         const char *funcname = "step";
368         lua_getglobal(L, funcname);
369         if(!lua_isfunction(L,-1))
370         {
371                 lua_pop(L,1);
372                 dstream<<"WARNING: LuaCAO: Function not found: "
373                                 <<funcname<<std::endl;
374                 return;
375         }
376         
377         // Parameters:
378         // 1: self
379         lua_pushlightuserdata(L, this);
380         // 2: dtime
381         lua_pushnumber(L, dtime);
382         
383         // Call (2 parameters, 0 result)
384         if(lua_pcall(L, 2, 0, 0))
385         {
386                 dstream<<"WARNING: LuaCAO: Error running function "
387                                 <<funcname<<": "
388                                 <<lua_tostring(L,-1)<<std::endl;
389                 return;
390         }
391 }
392
393 void LuaCAO::processMessage(const std::string &data)
394 {
395         /*
396                 Call process_message(self, data) from lua
397         */
398         
399         const char *funcname = "process_message";
400         lua_getglobal(L, funcname);
401         if(!lua_isfunction(L,-1))
402         {
403                 lua_pop(L,1);
404                 dstream<<"WARNING: LuaCAO: Function not found: "
405                                 <<funcname<<std::endl;
406                 return;
407         }
408         
409         // Parameters:
410         // 1: self
411         lua_pushlightuserdata(L, this);
412         // 2: data
413         lua_pushlstring(L, data.c_str(), data.size());
414         
415         // Call (2 parameters, 0 results)
416         if(lua_pcall(L, 2, 1, 0))
417         {
418                 dstream<<"WARNING: LuaCAO: Error running function "
419                                 <<funcname<<": "
420                                 <<lua_tostring(L,-1)<<std::endl;
421                 return;
422         }
423 }
424
425 void LuaCAO::initialize(const std::string &data)
426 {
427         dstream<<"LuaCAO::initialize(): id="<<getId()<<std::endl;
428
429         std::istringstream is(data, std::ios::binary);
430         std::string script = deSerializeLongString(is);
431         std::string other = deSerializeLongString(is);
432
433         /*dstream<<"=== script (size="<<script.size()<<")"<<std::endl
434                         <<script<<std::endl
435                         <<"==="<<std::endl;*/
436         dstream<<"LuaCAO::initialize(): script size="<<script.size()<<std::endl;
437         
438         /*dstream<<"other.size()="<<other.size()<<std::endl;
439         dstream<<"other=\""<<other<<"\""<<std::endl;*/
440         
441         // Load the script to lua
442         loadScript(script);
443
444         /*
445                 Call initialize(self, data) in the script
446         */
447         
448         const char *funcname = "initialize";
449         lua_getglobal(L, funcname);
450         if(!lua_isfunction(L,-1))
451         {
452                 lua_pop(L,1);
453                 dstream<<"WARNING: LuaCAO: Function not found: "
454                                 <<funcname<<std::endl;
455                 return;
456         }
457         
458         // Parameters:
459         // 1: self
460         lua_pushlightuserdata(L, this);
461         // 2: data (other)
462         lua_pushlstring(L, other.c_str(), other.size());
463         
464         // Call (2 parameters, 0 result)
465         if(lua_pcall(L, 2, 0, 0))
466         {
467                 dstream<<"WARNING: LuaCAO: Error running function "
468                                 <<funcname<<": "
469                                 <<lua_tostring(L,-1)<<std::endl;
470                 return;
471         }
472
473 }
474
475 void LuaCAO::loadScript(const std::string script)
476 {
477         int ret;
478         ret = luaL_loadstring(L, script.c_str());
479         if(ret)
480         {
481                 const char *message = lua_tostring(L, -1);
482                 lua_pop(L, 1);
483                 dstream<<"LuaCAO::loadScript(): lua_loadstring failed: "
484                                 <<message<<std::endl;
485                 assert(0);
486                 return;
487         }
488         ret = lua_pcall(L, 0, 0, 0);
489         if(ret)
490         {
491                 const char *message = lua_tostring(L, -1);
492                 lua_pop(L, 1);
493                 dstream<<"LuaCAO::loadScript(): lua_pcall failed: "
494                                 <<message<<std::endl;
495                 assert(0);
496                 return;
497         }
498 }
499
500 void LuaCAO::addToScene(scene::ISceneManager *smgr)
501 {
502         if(m_smgr != NULL)
503         {
504                 dstream<<"WARNING: LuaCAO::addToScene() called more than once"
505                                 <<std::endl;
506                 return;
507         }
508
509         if(m_node != NULL)
510         {
511                 dstream<<"WARNING: LuaCAO::addToScene(): m_node != NULL"
512                                 <<std::endl;
513                 return;
514         }
515         
516         m_smgr = smgr;
517         
518         if(m_mesh == NULL)
519         {
520                 m_mesh = new scene::SMesh();
521                 m_node = smgr->addMeshSceneNode(m_mesh, NULL);
522                 
523                 /*v3f corners[4] = {
524                         v3f(-BS/2,-BS/4,0),
525                         v3f(BS/2,-BS/4,0),
526                         v3f(BS/2,BS/4,0),
527                         v3f(-BS/2,BS/4,0),
528                 };
529                 addToMesh("rat.png", corners, false);*/
530         }
531         else
532         {
533                 dstream<<"WARNING: LuaCAO::addToScene(): m_mesh != NULL"
534                                 <<std::endl;
535                 return;
536         }
537         
538         updateNodePos();
539 }
540
541 void LuaCAO::addToMesh(const char *image, v3f *corners, bool backface_culling)
542 {
543         dstream<<"INFO: addToMesh called"<<std::endl;
544
545         video::IVideoDriver* driver = m_smgr->getVideoDriver();
546         
547         assert(m_mesh);
548                 
549         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
550         video::SColor c(255,255,255,255);
551         video::S3DVertex vertices[4] =
552         {
553                 video::S3DVertex(corners[0], v3f(0,0,0), c, v2f(0,1)),
554                 video::S3DVertex(corners[1], v3f(0,0,0), c, v2f(1,1)),
555                 video::S3DVertex(corners[2], v3f(0,0,0), c, v2f(1,0)),
556                 video::S3DVertex(corners[3], v3f(0,0,0), c, v2f(0,0)),
557                 /*video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
558                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
559                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
560                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),*/
561         };
562         u16 indices[] = {0,1,2,2,3,0};
563         buf->append(vertices, 4, indices, 6);
564         // Set material
565         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
566         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, backface_culling);
567         buf->getMaterial().setTexture
568                         (0, driver->getTexture(porting::getDataPath(image).c_str()));
569         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
570         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
571         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
572         // Add to mesh
573         m_mesh->addMeshBuffer(buf);
574         buf->drop();
575         // Reset mesh
576         if(m_node)
577                 m_node->setMesh(m_mesh);
578 }
579
580 void LuaCAO::clearMesh()
581 {
582         if(m_node)
583         {
584                 m_node->setMesh(NULL);
585         }
586         
587         if(m_mesh)
588         {
589                 m_mesh->drop();
590                 m_mesh = NULL;
591         }
592 }
593
594 void LuaCAO::removeFromScene()
595 {
596         if(m_node == NULL)
597                 return;
598         
599         if(m_node)
600         {
601                 m_node->remove();
602                 m_node = NULL;
603         }
604         if(m_mesh)
605         {
606                 m_mesh->drop();
607                 m_mesh = NULL;
608         }
609
610         m_smgr = NULL;
611 }
612
613 void LuaCAO::updateLight(u8 light_at_pos)
614 {
615 }
616
617 v3s16 LuaCAO::getLightPosition()
618 {
619         return floatToInt(m_position, BS);
620 }
621
622 void LuaCAO::updateNodePos()
623 {
624         if(m_node == NULL)
625                 return;
626
627         m_node->setPosition(m_position);
628         m_node->setRotation(-m_rotation);
629 }
630
631 void LuaCAO::setPosition(v3f pos)
632 {
633         m_position = pos;
634         updateNodePos();
635 }
636
637 v3f LuaCAO::getPosition()
638 {
639         return m_position;
640 }
641
642 void LuaCAO::setRotation(v3f rot)
643 {
644         m_rotation = rot;
645         updateNodePos();
646 }
647
648 v3f LuaCAO::getRotation()
649 {
650         return m_rotation;
651 }
652
653