]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CMeshSceneNode.cpp
Merging r5975 through r6036 from trunk to ogl-es branch.
[irrlicht.git] / source / Irrlicht / CMeshSceneNode.cpp
1 // Copyright (C) 2002-2012 Nikolaus Gebhardt\r
2 // This file is part of the "Irrlicht Engine".\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h\r
4 \r
5 #include "CMeshSceneNode.h"\r
6 #include "IVideoDriver.h"\r
7 #include "ISceneManager.h"\r
8 #include "S3DVertex.h"\r
9 #include "ICameraSceneNode.h"\r
10 #include "IMeshCache.h"\r
11 #include "IAnimatedMesh.h"\r
12 #include "IMaterialRenderer.h"\r
13 #include "IFileSystem.h"\r
14 #ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_\r
15 #include "CShadowVolumeSceneNode.h"\r
16 #else\r
17 #include "IShadowVolumeSceneNode.h"\r
18 #endif // _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_\r
19 \r
20 namespace irr\r
21 {\r
22 namespace scene\r
23 {\r
24 \r
25 \r
26 \r
27 //! constructor\r
28 CMeshSceneNode::CMeshSceneNode(IMesh* mesh, ISceneNode* parent, ISceneManager* mgr, s32 id,\r
29                         const core::vector3df& position, const core::vector3df& rotation,\r
30                         const core::vector3df& scale)\r
31 : IMeshSceneNode(parent, mgr, id, position, rotation, scale), Mesh(0), Shadow(0),\r
32         PassCount(0), ReadOnlyMaterials(false)\r
33 {\r
34         #ifdef _DEBUG\r
35         setDebugName("CMeshSceneNode");\r
36         #endif\r
37 \r
38         setMesh(mesh);\r
39 }\r
40 \r
41 \r
42 //! destructor\r
43 CMeshSceneNode::~CMeshSceneNode()\r
44 {\r
45         if (Shadow)\r
46                 Shadow->drop();\r
47         if (Mesh)\r
48                 Mesh->drop();\r
49 }\r
50 \r
51 \r
52 //! frame\r
53 void CMeshSceneNode::OnRegisterSceneNode()\r
54 {\r
55         if (IsVisible && Mesh)\r
56         {\r
57                 // because this node supports rendering of mixed mode meshes consisting of\r
58                 // transparent and solid material at the same time, we need to go through all\r
59                 // materials, check of what type they are and register this node for the right\r
60                 // render pass according to that.\r
61 \r
62                 video::IVideoDriver* driver = SceneManager->getVideoDriver();\r
63 \r
64                 PassCount = 0;\r
65                 int transparentCount = 0;\r
66                 int solidCount = 0;\r
67 \r
68                 // count transparent and solid materials in this scene node\r
69                 const u32 numMaterials = ReadOnlyMaterials ? Mesh->getMeshBufferCount() : Materials.size();\r
70                 for (u32 i=0; i<numMaterials; ++i)\r
71                 {\r
72                         const video::SMaterial& material = ReadOnlyMaterials ? Mesh->getMeshBuffer(i)->getMaterial() : Materials[i];\r
73 \r
74                         if ( driver->needsTransparentRenderPass(material) )\r
75                                 ++transparentCount;\r
76                         else\r
77                                 ++solidCount;\r
78 \r
79                         if (solidCount && transparentCount)\r
80                                 break;\r
81                 }\r
82 \r
83                 // register according to material types counted\r
84 \r
85                 if (solidCount)\r
86                         SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);\r
87 \r
88                 if (transparentCount)\r
89                         SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);\r
90 \r
91                 ISceneNode::OnRegisterSceneNode();\r
92         }\r
93 }\r
94 \r
95 \r
96 //! renders the node.\r
97 void CMeshSceneNode::render()\r
98 {\r
99         video::IVideoDriver* driver = SceneManager->getVideoDriver();\r
100 \r
101         if (!Mesh || !driver)\r
102                 return;\r
103 \r
104         const bool isTransparentPass =\r
105                 SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;\r
106 \r
107         ++PassCount;\r
108 \r
109         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);\r
110         Box = Mesh->getBoundingBox();\r
111 \r
112         if (Shadow && PassCount==1)\r
113                 Shadow->updateShadowVolumes();\r
114 \r
115         // for debug purposes only:\r
116 \r
117         bool renderMeshes = true;\r
118         video::SMaterial mat;\r
119         if (DebugDataVisible && PassCount==1)\r
120         {\r
121                 // overwrite half transparency\r
122                 if (DebugDataVisible & scene::EDS_HALF_TRANSPARENCY)\r
123                 {\r
124                         for (u32 g=0; g<Mesh->getMeshBufferCount(); ++g)\r
125                         {\r
126                                 mat = Materials[g];\r
127                                 mat.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;\r
128                                 driver->setMaterial(mat);\r
129                                 driver->drawMeshBuffer(Mesh->getMeshBuffer(g));\r
130                         }\r
131                         renderMeshes = false;\r
132                 }\r
133         }\r
134 \r
135         // render original meshes\r
136         if (renderMeshes)\r
137         {\r
138                 for (u32 i=0; i<Mesh->getMeshBufferCount(); ++i)\r
139                 {\r
140                         scene::IMeshBuffer* mb = Mesh->getMeshBuffer(i);\r
141                         if (mb)\r
142                         {\r
143                                 const video::SMaterial& material = ReadOnlyMaterials ? mb->getMaterial() : Materials[i];\r
144 \r
145                                 const bool transparent = driver->needsTransparentRenderPass(material);\r
146 \r
147                                 // only render transparent buffer if this is the transparent render pass\r
148                                 // and solid only in solid pass\r
149                                 if (transparent == isTransparentPass)\r
150                                 {\r
151                                         driver->setMaterial(material);\r
152                                         driver->drawMeshBuffer(mb);\r
153                                 }\r
154                         }\r
155                 }\r
156         }\r
157 \r
158         // for debug purposes only:\r
159         if (DebugDataVisible && PassCount==1)\r
160         {\r
161                 video::SMaterial m;\r
162                 m.Lighting = false;\r
163                 m.AntiAliasing=0;\r
164                 driver->setMaterial(m);\r
165 \r
166                 if (DebugDataVisible & scene::EDS_BBOX)\r
167                 {\r
168                         driver->draw3DBox(Box, video::SColor(255,255,255,255));\r
169                 }\r
170                 if (DebugDataVisible & scene::EDS_BBOX_BUFFERS)\r
171                 {\r
172                         for (u32 g=0; g<Mesh->getMeshBufferCount(); ++g)\r
173                         {\r
174                                 driver->draw3DBox(\r
175                                         Mesh->getMeshBuffer(g)->getBoundingBox(),\r
176                                         video::SColor(255,190,128,128));\r
177                         }\r
178                 }\r
179 \r
180                 if (DebugDataVisible & scene::EDS_NORMALS)\r
181                 {\r
182                         // draw normals\r
183                         const f32 debugNormalLength = SceneManager->getParameters()->getAttributeAsFloat(DEBUG_NORMAL_LENGTH);\r
184                         const video::SColor debugNormalColor = SceneManager->getParameters()->getAttributeAsColor(DEBUG_NORMAL_COLOR);\r
185                         const u32 count = Mesh->getMeshBufferCount();\r
186 \r
187                         for (u32 i=0; i != count; ++i)\r
188                         {\r
189                                 driver->drawMeshBufferNormals(Mesh->getMeshBuffer(i), debugNormalLength, debugNormalColor);\r
190                         }\r
191                 }\r
192 \r
193                 // show mesh\r
194                 if (DebugDataVisible & scene::EDS_MESH_WIRE_OVERLAY)\r
195                 {\r
196                         m.Wireframe = true;\r
197                         driver->setMaterial(m);\r
198 \r
199                         for (u32 g=0; g<Mesh->getMeshBufferCount(); ++g)\r
200                         {\r
201                                 driver->drawMeshBuffer(Mesh->getMeshBuffer(g));\r
202                         }\r
203                 }\r
204         }\r
205 }\r
206 \r
207 \r
208 //! Removes a child from this scene node.\r
209 //! Implemented here, to be able to remove the shadow properly, if there is one,\r
210 //! or to remove attached childs.\r
211 bool CMeshSceneNode::removeChild(ISceneNode* child)\r
212 {\r
213         if (child && Shadow == child)\r
214         {\r
215                 Shadow->drop();\r
216                 Shadow = 0;\r
217         }\r
218 \r
219         return ISceneNode::removeChild(child);\r
220 }\r
221 \r
222 \r
223 //! returns the axis aligned bounding box of this node\r
224 const core::aabbox3d<f32>& CMeshSceneNode::getBoundingBox() const\r
225 {\r
226         return Mesh ? Mesh->getBoundingBox() : Box;\r
227 }\r
228 \r
229 \r
230 //! returns the material based on the zero based index i. To get the amount\r
231 //! of materials used by this scene node, use getMaterialCount().\r
232 //! This function is needed for inserting the node into the scene hierarchy on a\r
233 //! optimal position for minimizing renderstate changes, but can also be used\r
234 //! to directly modify the material of a scene node.\r
235 video::SMaterial& CMeshSceneNode::getMaterial(u32 i)\r
236 {\r
237         if (Mesh && ReadOnlyMaterials && i<Mesh->getMeshBufferCount())\r
238         {\r
239                 ReadOnlyMaterial = Mesh->getMeshBuffer(i)->getMaterial();\r
240                 return ReadOnlyMaterial;\r
241         }\r
242 \r
243         if (i >= Materials.size())\r
244                 return ISceneNode::getMaterial(i);\r
245 \r
246         return Materials[i];\r
247 }\r
248 \r
249 \r
250 //! returns amount of materials used by this scene node.\r
251 u32 CMeshSceneNode::getMaterialCount() const\r
252 {\r
253         if (Mesh && ReadOnlyMaterials)\r
254                 return Mesh->getMeshBufferCount();\r
255 \r
256         return Materials.size();\r
257 }\r
258 \r
259 \r
260 //! Sets a new mesh\r
261 void CMeshSceneNode::setMesh(IMesh* mesh)\r
262 {\r
263         if (mesh)\r
264         {\r
265                 mesh->grab();\r
266                 if (Mesh)\r
267                         Mesh->drop();\r
268 \r
269                 Mesh = mesh;\r
270                 copyMaterials();\r
271         }\r
272 }\r
273 \r
274 \r
275 //! Creates shadow volume scene node as child of this node\r
276 //! and returns a pointer to it.\r
277 IShadowVolumeSceneNode* CMeshSceneNode::addShadowVolumeSceneNode(\r
278                 const IMesh* shadowMesh, s32 id, bool zfailmethod, f32 infinity)\r
279 {\r
280 #ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_\r
281         if (!SceneManager->getVideoDriver()->queryFeature(video::EVDF_STENCIL_BUFFER))\r
282                 return 0;\r
283 \r
284         if (!shadowMesh)\r
285                 shadowMesh = Mesh; // if null is given, use the mesh of node\r
286 \r
287         if (Shadow)\r
288                 Shadow->drop();\r
289 \r
290         Shadow = new CShadowVolumeSceneNode(shadowMesh, this, SceneManager, id,  zfailmethod, infinity);\r
291         return Shadow;\r
292 #else\r
293         return 0;\r
294 #endif\r
295 }\r
296 \r
297 \r
298 void CMeshSceneNode::copyMaterials()\r
299 {\r
300         Materials.clear();\r
301 \r
302         if (Mesh)\r
303         {\r
304                 video::SMaterial mat;\r
305 \r
306                 for (u32 i=0; i<Mesh->getMeshBufferCount(); ++i)\r
307                 {\r
308                         IMeshBuffer* mb = Mesh->getMeshBuffer(i);\r
309                         if (mb)\r
310                                 mat = mb->getMaterial();\r
311 \r
312                         Materials.push_back(mat);\r
313                 }\r
314         }\r
315 }\r
316 \r
317 \r
318 //! Writes attributes of the scene node.\r
319 void CMeshSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const\r
320 {\r
321         IMeshSceneNode::serializeAttributes(out, options);\r
322 \r
323         if (options && (options->Flags&io::EARWF_USE_RELATIVE_PATHS) && options->Filename)\r
324         {\r
325                 const io::path path = SceneManager->getFileSystem()->getRelativeFilename(\r
326                                 SceneManager->getFileSystem()->getAbsolutePath(SceneManager->getMeshCache()->getMeshName(Mesh).getPath()),\r
327                                 options->Filename);\r
328                 out->addString("Mesh", path.c_str());\r
329         }\r
330         else\r
331                 out->addString("Mesh", SceneManager->getMeshCache()->getMeshName(Mesh).getPath().c_str());\r
332         out->addBool("ReadOnlyMaterials", ReadOnlyMaterials);\r
333 }\r
334 \r
335 \r
336 //! Reads attributes of the scene node.\r
337 void CMeshSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)\r
338 {\r
339         io::path oldMeshStr = SceneManager->getMeshCache()->getMeshName(Mesh);\r
340         io::path newMeshStr = in->getAttributeAsString("Mesh");\r
341         ReadOnlyMaterials = in->getAttributeAsBool("ReadOnlyMaterials");\r
342 \r
343         if (newMeshStr != "" && oldMeshStr != newMeshStr)\r
344         {\r
345                 IMesh* newMesh = 0;\r
346                 IAnimatedMesh* newAnimatedMesh = SceneManager->getMesh(newMeshStr.c_str());\r
347 \r
348                 if (newAnimatedMesh)\r
349                         newMesh = newAnimatedMesh->getMesh(0);\r
350 \r
351                 if (newMesh)\r
352                         setMesh(newMesh);\r
353         }\r
354 \r
355         // optional attribute to assign the hint to the whole mesh\r
356         if (in->existsAttribute("HardwareMappingHint") &&\r
357                 in->existsAttribute("HardwareMappingBufferType"))\r
358         {\r
359                 scene::E_HARDWARE_MAPPING mapping = scene::EHM_NEVER;\r
360                 scene::E_BUFFER_TYPE bufferType = scene::EBT_NONE;\r
361 \r
362                 core::stringc smapping = in->getAttributeAsString("HardwareMappingHint");\r
363                 if (smapping.equals_ignore_case("static"))\r
364                         mapping = scene::EHM_STATIC;\r
365                 else if (smapping.equals_ignore_case("dynamic"))\r
366                         mapping = scene::EHM_DYNAMIC;\r
367                 else if (smapping.equals_ignore_case("stream"))\r
368                         mapping = scene::EHM_STREAM;\r
369 \r
370                 core::stringc sbufferType = in->getAttributeAsString("HardwareMappingBufferType");\r
371                 if (sbufferType.equals_ignore_case("vertex"))\r
372                         bufferType = scene::EBT_VERTEX;\r
373                 else if (sbufferType.equals_ignore_case("index"))\r
374                         bufferType = scene::EBT_INDEX;\r
375                 else if (sbufferType.equals_ignore_case("vertexindex"))\r
376                         bufferType = scene::EBT_VERTEX_AND_INDEX;\r
377 \r
378                 IMesh* mesh = getMesh();\r
379                 if (mesh)\r
380                         mesh->setHardwareMappingHint(mapping, bufferType);\r
381         }\r
382 \r
383         IMeshSceneNode::deserializeAttributes(in, options);\r
384 }\r
385 \r
386 \r
387 //! Sets if the scene node should not copy the materials of the mesh but use them in a read only style.\r
388 /* In this way it is possible to change the materials a mesh causing all mesh scene nodes\r
389 referencing this mesh to change too. */\r
390 void CMeshSceneNode::setReadOnlyMaterials(bool readonly)\r
391 {\r
392         ReadOnlyMaterials = readonly;\r
393 }\r
394 \r
395 \r
396 //! Returns if the scene node should not copy the materials of the mesh but use them in a read only style\r
397 bool CMeshSceneNode::isReadOnlyMaterials() const\r
398 {\r
399         return ReadOnlyMaterials;\r
400 }\r
401 \r
402 \r
403 //! Creates a clone of this scene node and its children.\r
404 ISceneNode* CMeshSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager)\r
405 {\r
406         if (!newParent)\r
407                 newParent = Parent;\r
408         if (!newManager)\r
409                 newManager = SceneManager;\r
410 \r
411         CMeshSceneNode* nb = new CMeshSceneNode(Mesh, newParent,\r
412                 newManager, ID, RelativeTranslation, RelativeRotation, RelativeScale);\r
413 \r
414         nb->cloneMembers(this, newManager);\r
415         nb->ReadOnlyMaterials = ReadOnlyMaterials;\r
416         nb->Materials = Materials;\r
417         nb->Shadow = Shadow;\r
418         if ( nb->Shadow )\r
419                 nb->Shadow->grab();\r
420 \r
421         if (newParent)\r
422                 nb->drop();\r
423         return nb;\r
424 }\r
425 \r
426 \r
427 } // end namespace scene\r
428 } // end namespace irr\r
429 \r