~baltix/+junk/irrlicht-test

« back to all changes in this revision

Viewing changes to source/Irrlicht/CAnimatedMeshSceneNode.cpp

  • Committer: Mantas Kriaučiūnas
  • Date: 2011-07-18 13:06:25 UTC
  • Revision ID: mantas@akl.lt-20110718130625-c5pvifp61e7kj1ol
Included whole irrlicht SVN libraries to work around launchpad recipe issue with quilt, see https://answers.launchpad.net/launchpad/+question/165193

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (C) 2002-2011 Nikolaus Gebhardt
 
2
// This file is part of the "Irrlicht Engine".
 
3
// For conditions of distribution and use, see copyright notice in irrlicht.h
 
4
 
 
5
#include "CAnimatedMeshSceneNode.h"
 
6
#include "IVideoDriver.h"
 
7
#include "ISceneManager.h"
 
8
#include "S3DVertex.h"
 
9
#include "os.h"
 
10
#include "CShadowVolumeSceneNode.h"
 
11
#include "IAnimatedMeshMD3.h"
 
12
#include "CSkinnedMesh.h"
 
13
#include "IDummyTransformationSceneNode.h"
 
14
#include "IBoneSceneNode.h"
 
15
#include "IMaterialRenderer.h"
 
16
#include "IMesh.h"
 
17
#include "IMeshCache.h"
 
18
#include "IAnimatedMesh.h"
 
19
#include "quaternion.h"
 
20
 
 
21
 
 
22
namespace irr
 
23
{
 
24
namespace scene
 
25
{
 
26
 
 
27
 
 
28
//! constructor
 
29
CAnimatedMeshSceneNode::CAnimatedMeshSceneNode(IAnimatedMesh* mesh,
 
30
                ISceneNode* parent, ISceneManager* mgr, s32 id,
 
31
                const core::vector3df& position,
 
32
                const core::vector3df& rotation,
 
33
                const core::vector3df& scale)
 
34
: IAnimatedMeshSceneNode(parent, mgr, id, position, rotation, scale), Mesh(0),
 
35
        StartFrame(0), EndFrame(0), FramesPerSecond(0.025f),
 
36
        CurrentFrameNr(0.f), LastTimeMs(0),
 
37
        TransitionTime(0), Transiting(0.f), TransitingBlend(0.f),
 
38
        JointMode(EJUOR_NONE), JointsUsed(false),
 
39
        Looping(true), ReadOnlyMaterials(false), RenderFromIdentity(false),
 
40
        LoopCallBack(0), PassCount(0), Shadow(0), MD3Special(0)
 
41
{
 
42
        #ifdef _DEBUG
 
43
        setDebugName("CAnimatedMeshSceneNode");
 
44
        #endif
 
45
 
 
46
        setMesh(mesh);
 
47
}
 
48
 
 
49
 
 
50
//! destructor
 
51
CAnimatedMeshSceneNode::~CAnimatedMeshSceneNode()
 
52
{
 
53
        if (MD3Special)
 
54
                MD3Special->drop();
 
55
 
 
56
        if (Mesh)
 
57
                Mesh->drop();
 
58
 
 
59
        if (Shadow)
 
60
                Shadow->drop();
 
61
 
 
62
        if (LoopCallBack)
 
63
                LoopCallBack->drop();
 
64
}
 
65
 
 
66
 
 
67
//! Sets the current frame. From now on the animation is played from this frame.
 
68
void CAnimatedMeshSceneNode::setCurrentFrame(f32 frame)
 
69
{
 
70
        // if you pass an out of range value, we just clamp it
 
71
        CurrentFrameNr = core::clamp ( frame, (f32)StartFrame, (f32)EndFrame );
 
72
 
 
73
        beginTransition(); //transit to this frame if enabled
 
74
}
 
75
 
 
76
 
 
77
//! Returns the currently displayed frame number.
 
78
f32 CAnimatedMeshSceneNode::getFrameNr() const
 
79
{
 
80
        return CurrentFrameNr;
 
81
}
 
82
 
 
83
 
 
84
void CAnimatedMeshSceneNode::buildFrameNr(u32 timeMs)
 
85
{
 
86
        if (Transiting!=0.f)
 
87
        {
 
88
                TransitingBlend += (f32)(timeMs) * Transiting;
 
89
                if (TransitingBlend > 1.f)
 
90
                {
 
91
                        Transiting=0.f;
 
92
                        TransitingBlend=0.f;
 
93
                }
 
94
        }
 
95
 
 
96
        if ((StartFrame==EndFrame))
 
97
        {
 
98
                CurrentFrameNr = (f32)StartFrame; //Support for non animated meshes
 
99
        }
 
100
        else if (Looping)
 
101
        {
 
102
                // play animation looped
 
103
                CurrentFrameNr += timeMs * FramesPerSecond;
 
104
 
 
105
                // We have no interpolation between EndFrame and StartFrame,
 
106
                // the last frame must be identical to first one with our current solution.
 
107
                if (FramesPerSecond > 0.f) //forwards...
 
108
                {
 
109
                        if (CurrentFrameNr > EndFrame)
 
110
                                CurrentFrameNr = StartFrame + fmod(CurrentFrameNr - StartFrame, (f32)(EndFrame-StartFrame));
 
111
                }
 
112
                else //backwards...
 
113
                {
 
114
                        if (CurrentFrameNr < StartFrame)
 
115
                                CurrentFrameNr = EndFrame - fmod(EndFrame - CurrentFrameNr, (f32)(EndFrame-StartFrame));
 
116
                }
 
117
        }
 
118
        else
 
119
        {
 
120
                // play animation non looped
 
121
 
 
122
                CurrentFrameNr += timeMs * FramesPerSecond;
 
123
                if (FramesPerSecond > 0.f) //forwards...
 
124
                {
 
125
                        if (CurrentFrameNr > (f32)EndFrame)
 
126
                        {
 
127
                                CurrentFrameNr = (f32)EndFrame;
 
128
                                if (LoopCallBack)
 
129
                                        LoopCallBack->OnAnimationEnd(this);
 
130
                        }
 
131
                }
 
132
                else //backwards...
 
133
                {
 
134
                        if (CurrentFrameNr < (f32)StartFrame)
 
135
                        {
 
136
                                CurrentFrameNr = (f32)StartFrame;
 
137
                                if (LoopCallBack)
 
138
                                        LoopCallBack->OnAnimationEnd(this);
 
139
                        }
 
140
                }
 
141
        }
 
142
}
 
143
 
 
144
 
 
145
void CAnimatedMeshSceneNode::OnRegisterSceneNode()
 
146
{
 
147
        if (IsVisible)
 
148
        {
 
149
                // because this node supports rendering of mixed mode meshes consisting of
 
150
                // transparent and solid material at the same time, we need to go through all
 
151
                // materials, check of what type they are and register this node for the right
 
152
                // render pass according to that.
 
153
 
 
154
                video::IVideoDriver* driver = SceneManager->getVideoDriver();
 
155
 
 
156
                PassCount = 0;
 
157
                int transparentCount = 0;
 
158
                int solidCount = 0;
 
159
 
 
160
                // count transparent and solid materials in this scene node
 
161
                for (u32 i=0; i<Materials.size(); ++i)
 
162
                {
 
163
                        video::IMaterialRenderer* rnd =
 
164
                                driver->getMaterialRenderer(Materials[i].MaterialType);
 
165
 
 
166
                        if (rnd && rnd->isTransparent())
 
167
                                ++transparentCount;
 
168
                        else
 
169
                                ++solidCount;
 
170
 
 
171
                        if (solidCount && transparentCount)
 
172
                                break;
 
173
                }
 
174
 
 
175
                // register according to material types counted
 
176
 
 
177
                if (solidCount)
 
178
                        SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
 
179
 
 
180
                if (transparentCount)
 
181
                        SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
 
182
 
 
183
                ISceneNode::OnRegisterSceneNode();
 
184
        }
 
185
}
 
186
 
 
187
IMesh * CAnimatedMeshSceneNode::getMeshForCurrentFrame()
 
188
{
 
189
        if(Mesh->getMeshType() != EAMT_SKINNED)
 
190
        {
 
191
                s32 frameNr = (s32) getFrameNr();
 
192
                s32 frameBlend = (s32) (core::fract ( getFrameNr() ) * 1000.f);
 
193
                return Mesh->getMesh(frameNr, frameBlend, StartFrame, EndFrame);
 
194
        }
 
195
        else
 
196
        {
 
197
#ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_
 
198
                return 0;
 
199
#else
 
200
 
 
201
                // As multiple scene nodes may be sharing the same skinned mesh, we have to
 
202
                // re-animate it every frame to ensure that this node gets the mesh that it needs.
 
203
 
 
204
                CSkinnedMesh* skinnedMesh = reinterpret_cast<CSkinnedMesh*>(Mesh);
 
205
 
 
206
                if (JointMode == EJUOR_CONTROL)//write to mesh
 
207
                        skinnedMesh->transferJointsToMesh(JointChildSceneNodes);
 
208
                else
 
209
                        skinnedMesh->animateMesh(getFrameNr(), 1.0f);
 
210
 
 
211
                // Update the skinned mesh for the current joint transforms.
 
212
                skinnedMesh->skinMesh();
 
213
 
 
214
                if (JointMode == EJUOR_READ)//read from mesh
 
215
                {
 
216
                        skinnedMesh->recoverJointsFromMesh(JointChildSceneNodes);
 
217
 
 
218
                        //---slow---
 
219
                        for (u32 n=0;n<JointChildSceneNodes.size();++n)
 
220
                                if (JointChildSceneNodes[n]->getParent()==this)
 
221
                                {
 
222
                                        JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); //temp, should be an option
 
223
                                }
 
224
                }
 
225
 
 
226
                if(JointMode == EJUOR_CONTROL)
 
227
                {
 
228
                        // For meshes other than EJUOR_CONTROL, this is done by calling animateMesh()
 
229
                        skinnedMesh->updateBoundingBox();
 
230
                }
 
231
 
 
232
                return skinnedMesh;
 
233
#endif
 
234
        }
 
235
}
 
236
 
 
237
 
 
238
//! OnAnimate() is called just before rendering the whole scene.
 
239
void CAnimatedMeshSceneNode::OnAnimate(u32 timeMs)
 
240
{
 
241
        buildFrameNr(timeMs-LastTimeMs);
 
242
 
 
243
        if (Mesh)
 
244
        {
 
245
                scene::IMesh * mesh = getMeshForCurrentFrame();
 
246
 
 
247
                if (mesh)
 
248
                        Box = mesh->getBoundingBox();
 
249
        }
 
250
        LastTimeMs = timeMs;
 
251
 
 
252
        IAnimatedMeshSceneNode::OnAnimate ( timeMs );
 
253
}
 
254
 
 
255
 
 
256
//! renders the node.
 
257
void CAnimatedMeshSceneNode::render()
 
258
{
 
259
        video::IVideoDriver* driver = SceneManager->getVideoDriver();
 
260
 
 
261
        if (!Mesh || !driver)
 
262
                return;
 
263
 
 
264
 
 
265
        bool isTransparentPass =
 
266
                SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;
 
267
 
 
268
        ++PassCount;
 
269
 
 
270
        scene::IMesh* m = getMeshForCurrentFrame();
 
271
 
 
272
        if(m)
 
273
        {
 
274
                Box = m->getBoundingBox();
 
275
        }
 
276
        else
 
277
        {
 
278
                #ifdef _DEBUG
 
279
                        os::Printer::log("Animated Mesh returned no mesh to render.", Mesh->getDebugName(), ELL_WARNING);
 
280
                #endif
 
281
        }
 
282
 
 
283
        driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
 
284
 
 
285
 
 
286
        if (Shadow && PassCount==1)
 
287
                Shadow->updateShadowVolumes();
 
288
 
 
289
        // for debug purposes only:
 
290
 
 
291
        bool renderMeshes = true;
 
292
        video::SMaterial mat;
 
293
        if (DebugDataVisible && PassCount==1)
 
294
        {
 
295
                // overwrite half transparency
 
296
                if (DebugDataVisible & scene::EDS_HALF_TRANSPARENCY)
 
297
                {
 
298
 
 
299
                        for (u32 i=0; i<m->getMeshBufferCount(); ++i)
 
300
                        {
 
301
                                scene::IMeshBuffer* mb = m->getMeshBuffer(i);
 
302
                                mat = ReadOnlyMaterials ? mb->getMaterial() : Materials[i];
 
303
                                mat.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
 
304
                                if (RenderFromIdentity)
 
305
                                        driver->setTransform(video::ETS_WORLD, core::IdentityMatrix );
 
306
                                else if (Mesh->getMeshType() == EAMT_SKINNED)
 
307
                                        driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation);
 
308
 
 
309
                                driver->setMaterial(mat);
 
310
                                driver->drawMeshBuffer(mb);
 
311
                        }
 
312
                        renderMeshes = false;
 
313
                }
 
314
        }
 
315
 
 
316
        // render original meshes
 
317
        if (renderMeshes)
 
318
        {
 
319
                for (u32 i=0; i<m->getMeshBufferCount(); ++i)
 
320
                {
 
321
                        video::IMaterialRenderer* rnd = driver->getMaterialRenderer(Materials[i].MaterialType);
 
322
                        bool transparent = (rnd && rnd->isTransparent());
 
323
 
 
324
                        // only render transparent buffer if this is the transparent render pass
 
325
                        // and solid only in solid pass
 
326
                        if (transparent == isTransparentPass)
 
327
                        {
 
328
                                scene::IMeshBuffer* mb = m->getMeshBuffer(i);
 
329
                                const video::SMaterial& material = ReadOnlyMaterials ? mb->getMaterial() : Materials[i];
 
330
                                if (RenderFromIdentity)
 
331
                                        driver->setTransform(video::ETS_WORLD, core::IdentityMatrix );
 
332
                                else if (Mesh->getMeshType() == EAMT_SKINNED)
 
333
                                        driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation);
 
334
 
 
335
                                driver->setMaterial(material);
 
336
                                driver->drawMeshBuffer(mb);
 
337
                        }
 
338
                }
 
339
        }
 
340
 
 
341
        driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
 
342
 
 
343
        // for debug purposes only:
 
344
        if (DebugDataVisible && PassCount==1)
 
345
        {
 
346
                video::SMaterial debug_mat;
 
347
                debug_mat.Lighting = false;
 
348
                debug_mat.AntiAliasing=0;
 
349
                driver->setMaterial(debug_mat);
 
350
                // show normals
 
351
                if (DebugDataVisible & scene::EDS_NORMALS)
 
352
                {
 
353
                        core::vector3df normalizedNormal;
 
354
                        const f32 DebugNormalLength = SceneManager->getParameters()->getAttributeAsFloat(DEBUG_NORMAL_LENGTH);
 
355
                        const video::SColor DebugNormalColor = SceneManager->getParameters()->getAttributeAsColor(DEBUG_NORMAL_COLOR);
 
356
 
 
357
                        // draw normals
 
358
                        for (u32 g=0; g < m->getMeshBufferCount(); ++g)
 
359
                        {
 
360
                                const scene::IMeshBuffer* mb = m->getMeshBuffer(g);
 
361
                                const u32 vSize = video::getVertexPitchFromType(mb->getVertexType());
 
362
                                const video::S3DVertex* v = ( const video::S3DVertex*)mb->getVertices();
 
363
                                const bool normalize = mb->getMaterial().NormalizeNormals;
 
364
 
 
365
                                for (u32 i=0; i != mb->getVertexCount(); ++i)
 
366
                                {
 
367
                                        normalizedNormal = v->Normal;
 
368
                                        if (normalize)
 
369
                                                normalizedNormal.normalize();
 
370
 
 
371
                                        driver->draw3DLine(v->Pos, v->Pos + (normalizedNormal * DebugNormalLength), DebugNormalColor);
 
372
 
 
373
                                        v = (const video::S3DVertex*) ( (u8*) v+vSize );
 
374
                                }
 
375
                        }
 
376
                        driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
 
377
                }
 
378
 
 
379
                debug_mat.ZBuffer = video::ECFN_NEVER;
 
380
                debug_mat.Lighting = false;
 
381
                driver->setMaterial(debug_mat);
 
382
 
 
383
                if (DebugDataVisible & scene::EDS_BBOX)
 
384
                        driver->draw3DBox(Box, video::SColor(255,255,255,255));
 
385
 
 
386
                // show bounding box
 
387
                if (DebugDataVisible & scene::EDS_BBOX_BUFFERS)
 
388
                {
 
389
 
 
390
                        for (u32 g=0; g< m->getMeshBufferCount(); ++g)
 
391
                        {
 
392
                                const IMeshBuffer* mb = m->getMeshBuffer(g);
 
393
 
 
394
                                if (Mesh->getMeshType() == EAMT_SKINNED)
 
395
                                        driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation);
 
396
                                driver->draw3DBox( mb->getBoundingBox(),
 
397
                                                video::SColor(255,190,128,128) );
 
398
                        }
 
399
                }
 
400
 
 
401
                // show skeleton
 
402
                if (DebugDataVisible & scene::EDS_SKELETON)
 
403
                {
 
404
                        if (Mesh->getMeshType() == EAMT_SKINNED)
 
405
                        {
 
406
                                // draw skeleton
 
407
 
 
408
                                for (u32 g=0; g < ((ISkinnedMesh*)Mesh)->getAllJoints().size(); ++g)
 
409
                                {
 
410
                                        ISkinnedMesh::SJoint *joint=((ISkinnedMesh*)Mesh)->getAllJoints()[g];
 
411
 
 
412
                                        for (u32 n=0;n<joint->Children.size();++n)
 
413
                                        {
 
414
                                                driver->draw3DLine(joint->GlobalAnimatedMatrix.getTranslation(),
 
415
                                                                joint->Children[n]->GlobalAnimatedMatrix.getTranslation(),
 
416
                                                                video::SColor(255,51,66,255));
 
417
                                        }
 
418
                                }
 
419
                        }
 
420
 
 
421
                        // show tag for quake3 models
 
422
                        if (Mesh->getMeshType() == EAMT_MD3)
 
423
                        {
 
424
                                IAnimatedMesh * arrow =
 
425
                                        SceneManager->addArrowMesh (
 
426
                                                        "__tag_show",
 
427
                                                        0xFF0000FF, 0xFF000088,
 
428
                                                        4, 8, 5.f, 4.f, 0.5f,
 
429
                                                        1.f);
 
430
                                if (!arrow)
 
431
                                {
 
432
                                        arrow = SceneManager->getMesh ( "__tag_show" );
 
433
                                }
 
434
                                IMesh *arrowMesh = arrow->getMesh(0);
 
435
 
 
436
                                core::matrix4 matr;
 
437
 
 
438
                                SMD3QuaternionTagList *taglist = ((IAnimatedMeshMD3*)Mesh)->getTagList(
 
439
                                                (s32)getFrameNr(), 255,
 
440
                                                getStartFrame(), getEndFrame());
 
441
                                if (taglist)
 
442
                                {
 
443
                                        for ( u32 ts = 0; ts != taglist->size(); ++ts )
 
444
                                        {
 
445
                                                (*taglist)[ts].setto(matr);
 
446
 
 
447
                                                driver->setTransform(video::ETS_WORLD, matr );
 
448
 
 
449
                                                for ( u32 a = 0; a != arrowMesh->getMeshBufferCount(); ++a )
 
450
                                                        driver->drawMeshBuffer(arrowMesh->getMeshBuffer(a));
 
451
                                        }
 
452
                                }
 
453
                        }
 
454
                }
 
455
 
 
456
                // show mesh
 
457
                if (DebugDataVisible & scene::EDS_MESH_WIRE_OVERLAY)
 
458
                {
 
459
                        debug_mat.Lighting = false;
 
460
                        debug_mat.Wireframe = true;
 
461
                        debug_mat.ZBuffer = video::ECFN_NEVER;
 
462
                        driver->setMaterial(debug_mat);
 
463
 
 
464
                        for (u32 g=0; g<m->getMeshBufferCount(); ++g)
 
465
                        {
 
466
                                const IMeshBuffer* mb = m->getMeshBuffer(g);
 
467
                                if (RenderFromIdentity)
 
468
                                        driver->setTransform(video::ETS_WORLD, core::IdentityMatrix );
 
469
                                else if (Mesh->getMeshType() == EAMT_SKINNED)
 
470
                                        driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer*)mb)->Transformation);
 
471
                                driver->drawMeshBuffer(mb);
 
472
                        }
 
473
                }
 
474
        }
 
475
}
 
476
 
 
477
 
 
478
//! Returns the current start frame number.
 
479
s32 CAnimatedMeshSceneNode::getStartFrame() const
 
480
{
 
481
        return StartFrame;
 
482
}
 
483
 
 
484
 
 
485
//! Returns the current start frame number.
 
486
s32 CAnimatedMeshSceneNode::getEndFrame() const
 
487
{
 
488
        return EndFrame;
 
489
}
 
490
 
 
491
 
 
492
//! sets the frames between the animation is looped.
 
493
//! the default is 0 - MaximalFrameCount of the mesh.
 
494
bool CAnimatedMeshSceneNode::setFrameLoop(s32 begin, s32 end)
 
495
{
 
496
        const s32 maxFrameCount = Mesh->getFrameCount() - 1;
 
497
        if (end < begin)
 
498
        {
 
499
                StartFrame = core::s32_clamp(end, 0, maxFrameCount);
 
500
                EndFrame = core::s32_clamp(begin, StartFrame, maxFrameCount);
 
501
        }
 
502
        else
 
503
        {
 
504
                StartFrame = core::s32_clamp(begin, 0, maxFrameCount);
 
505
                EndFrame = core::s32_clamp(end, StartFrame, maxFrameCount);
 
506
        }
 
507
        if (FramesPerSecond < 0)
 
508
                setCurrentFrame((f32)EndFrame);
 
509
        else
 
510
                setCurrentFrame((f32)StartFrame);
 
511
 
 
512
        return true;
 
513
}
 
514
 
 
515
 
 
516
//! sets the speed with witch the animation is played
 
517
void CAnimatedMeshSceneNode::setAnimationSpeed(f32 framesPerSecond)
 
518
{
 
519
        FramesPerSecond = framesPerSecond * 0.001f;
 
520
}
 
521
 
 
522
 
 
523
f32 CAnimatedMeshSceneNode::getAnimationSpeed() const
 
524
{
 
525
        return FramesPerSecond * 1000.f;
 
526
}
 
527
 
 
528
 
 
529
//! returns the axis aligned bounding box of this node
 
530
const core::aabbox3d<f32>& CAnimatedMeshSceneNode::getBoundingBox() const
 
531
{
 
532
        return Box;
 
533
}
 
534
 
 
535
 
 
536
//! returns the material based on the zero based index i. To get the amount
 
537
//! of materials used by this scene node, use getMaterialCount().
 
538
//! This function is needed for inserting the node into the scene hirachy on a
 
539
//! optimal position for minimizing renderstate changes, but can also be used
 
540
//! to directly modify the material of a scene node.
 
541
video::SMaterial& CAnimatedMeshSceneNode::getMaterial(u32 i)
 
542
{
 
543
        if (i >= Materials.size())
 
544
                return ISceneNode::getMaterial(i);
 
545
 
 
546
        return Materials[i];
 
547
}
 
548
 
 
549
 
 
550
 
 
551
//! returns amount of materials used by this scene node.
 
552
u32 CAnimatedMeshSceneNode::getMaterialCount() const
 
553
{
 
554
        return Materials.size();
 
555
}
 
556
 
 
557
 
 
558
//! Creates shadow volume scene node as child of this node
 
559
//! and returns a pointer to it.
 
560
IShadowVolumeSceneNode* CAnimatedMeshSceneNode::addShadowVolumeSceneNode(
 
561
                const IMesh* shadowMesh, s32 id, bool zfailmethod, f32 infinity)
 
562
{
 
563
        if (!SceneManager->getVideoDriver()->queryFeature(video::EVDF_STENCIL_BUFFER))
 
564
                return 0;
 
565
 
 
566
        if (!shadowMesh)
 
567
                shadowMesh = Mesh; // if null is given, use the mesh of node
 
568
 
 
569
        if (Shadow)
 
570
                Shadow->drop();
 
571
 
 
572
        Shadow = new CShadowVolumeSceneNode(shadowMesh, this, SceneManager, id,  zfailmethod, infinity);
 
573
        return Shadow;
 
574
}
 
575
 
 
576
//! Returns a pointer to a child node, which has the same transformation as
 
577
//! the corresponding joint, if the mesh in this scene node is a skinned mesh.
 
578
IBoneSceneNode* CAnimatedMeshSceneNode::getJointNode(const c8* jointName)
 
579
{
 
580
#ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_
 
581
        os::Printer::log("Compiled without _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_", ELL_WARNING);
 
582
        return 0;
 
583
#else
 
584
 
 
585
        if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)
 
586
        {
 
587
                os::Printer::log("No mesh, or mesh not of skinned mesh type", ELL_WARNING);
 
588
                return 0;
 
589
        }
 
590
 
 
591
        checkJoints();
 
592
 
 
593
        ISkinnedMesh *skinnedMesh=(ISkinnedMesh*)Mesh;
 
594
 
 
595
        const s32 number = skinnedMesh->getJointNumber(jointName);
 
596
 
 
597
        if (number == -1)
 
598
        {
 
599
                os::Printer::log("Joint with specified name not found in skinned mesh", jointName, ELL_DEBUG);
 
600
                return 0;
 
601
        }
 
602
 
 
603
        if ((s32)JointChildSceneNodes.size() <= number)
 
604
        {
 
605
                os::Printer::log("Joint was found in mesh, but is not loaded into node", jointName, ELL_WARNING);
 
606
                return 0;
 
607
        }
 
608
 
 
609
        return JointChildSceneNodes[number];
 
610
#endif
 
611
}
 
612
 
 
613
 
 
614
 
 
615
//! Returns a pointer to a child node, which has the same transformation as
 
616
//! the corresponding joint, if the mesh in this scene node is a skinned mesh.
 
617
IBoneSceneNode* CAnimatedMeshSceneNode::getJointNode(u32 jointID)
 
618
{
 
619
#ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_
 
620
        os::Printer::log("Compiled without _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_", ELL_WARNING);
 
621
        return 0;
 
622
#else
 
623
 
 
624
        if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)
 
625
        {
 
626
                os::Printer::log("No mesh, or mesh not of skinned mesh type", ELL_WARNING);
 
627
                return 0;
 
628
        }
 
629
 
 
630
        checkJoints();
 
631
 
 
632
        if (JointChildSceneNodes.size() <= jointID)
 
633
        {
 
634
                os::Printer::log("Joint not loaded into node", ELL_WARNING);
 
635
                return 0;
 
636
        }
 
637
 
 
638
        return JointChildSceneNodes[jointID];
 
639
#endif
 
640
}
 
641
 
 
642
//! Gets joint count.
 
643
u32 CAnimatedMeshSceneNode::getJointCount() const
 
644
{
 
645
#ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_
 
646
        return 0;
 
647
#else
 
648
 
 
649
        if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)
 
650
                return 0;
 
651
 
 
652
        ISkinnedMesh *skinnedMesh=(ISkinnedMesh*)Mesh;
 
653
 
 
654
        return skinnedMesh->getJointCount();
 
655
#endif
 
656
}
 
657
 
 
658
 
 
659
//! Returns a pointer to a child node, which has the same transformation as
 
660
//! the corresponding joint, if the mesh in this scene node is a ms3d mesh.
 
661
ISceneNode* CAnimatedMeshSceneNode::getMS3DJointNode(const c8* jointName)
 
662
{
 
663
        return  getJointNode(jointName);
 
664
}
 
665
 
 
666
 
 
667
//! Returns a pointer to a child node, which has the same transformation as
 
668
//! the corresponding joint, if the mesh in this scene node is a .x mesh.
 
669
ISceneNode* CAnimatedMeshSceneNode::getXJointNode(const c8* jointName)
 
670
{
 
671
        return  getJointNode(jointName);
 
672
}
 
673
 
 
674
//! Removes a child from this scene node.
 
675
//! Implemented here, to be able to remove the shadow properly, if there is one,
 
676
//! or to remove attached childs.
 
677
bool CAnimatedMeshSceneNode::removeChild(ISceneNode* child)
 
678
{
 
679
        if (child && Shadow == child)
 
680
        {
 
681
                Shadow->drop();
 
682
                Shadow = 0;
 
683
        }
 
684
 
 
685
        if (ISceneNode::removeChild(child))
 
686
        {
 
687
                if (JointsUsed) //stop weird bugs caused while changing parents as the joints are being created
 
688
                {
 
689
                        for (u32 i=0; i<JointChildSceneNodes.size(); ++i)
 
690
                        {
 
691
                                if (JointChildSceneNodes[i] == child)
 
692
                                {
 
693
                                        JointChildSceneNodes[i] = 0; //remove link to child
 
694
                                        break;
 
695
                                }
 
696
                        }
 
697
                }
 
698
                return true;
 
699
        }
 
700
 
 
701
        return false;
 
702
}
 
703
 
 
704
 
 
705
//! Starts a MD2 animation.
 
706
bool CAnimatedMeshSceneNode::setMD2Animation(EMD2_ANIMATION_TYPE anim)
 
707
{
 
708
        if (!Mesh || Mesh->getMeshType() != EAMT_MD2)
 
709
                return false;
 
710
 
 
711
        IAnimatedMeshMD2* md = (IAnimatedMeshMD2*)Mesh;
 
712
 
 
713
        s32 begin, end, speed;
 
714
        md->getFrameLoop(anim, begin, end, speed);
 
715
 
 
716
        setAnimationSpeed( f32(speed) );
 
717
        setFrameLoop(begin, end);
 
718
        return true;
 
719
}
 
720
 
 
721
 
 
722
//! Starts a special MD2 animation.
 
723
bool CAnimatedMeshSceneNode::setMD2Animation(const c8* animationName)
 
724
{
 
725
        if (!Mesh || Mesh->getMeshType() != EAMT_MD2)
 
726
                return false;
 
727
 
 
728
        IAnimatedMeshMD2* md = (IAnimatedMeshMD2*)Mesh;
 
729
 
 
730
        s32 begin, end, speed;
 
731
        if (!md->getFrameLoop(animationName, begin, end, speed))
 
732
                return false;
 
733
 
 
734
        setAnimationSpeed( (f32)speed );
 
735
        setFrameLoop(begin, end);
 
736
        return true;
 
737
}
 
738
 
 
739
 
 
740
//! Sets looping mode which is on by default. If set to false,
 
741
//! animations will not be looped.
 
742
void CAnimatedMeshSceneNode::setLoopMode(bool playAnimationLooped)
 
743
{
 
744
        Looping = playAnimationLooped;
 
745
}
 
746
 
 
747
 
 
748
//! Sets a callback interface which will be called if an animation
 
749
//! playback has ended. Set this to 0 to disable the callback again.
 
750
void CAnimatedMeshSceneNode::setAnimationEndCallback(IAnimationEndCallBack* callback)
 
751
{
 
752
        if (callback == LoopCallBack)
 
753
                return;
 
754
 
 
755
        if (LoopCallBack)
 
756
                LoopCallBack->drop();
 
757
 
 
758
        LoopCallBack = callback;
 
759
 
 
760
        if (LoopCallBack)
 
761
                LoopCallBack->grab();
 
762
}
 
763
 
 
764
 
 
765
//! Sets if the scene node should not copy the materials of the mesh but use them in a read only style.
 
766
void CAnimatedMeshSceneNode::setReadOnlyMaterials(bool readonly)
 
767
{
 
768
        ReadOnlyMaterials = readonly;
 
769
}
 
770
 
 
771
 
 
772
//! Returns if the scene node should not copy the materials of the mesh but use them in a read only style
 
773
bool CAnimatedMeshSceneNode::isReadOnlyMaterials() const
 
774
{
 
775
        return ReadOnlyMaterials;
 
776
}
 
777
 
 
778
 
 
779
//! Writes attributes of the scene node.
 
780
void CAnimatedMeshSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
 
781
{
 
782
        IAnimatedMeshSceneNode::serializeAttributes(out, options);
 
783
 
 
784
        if (options && (options->Flags&io::EARWF_USE_RELATIVE_PATHS) && options->Filename)
 
785
        {
 
786
                const io::path path = SceneManager->getFileSystem()->getRelativeFilename(
 
787
                                SceneManager->getFileSystem()->getAbsolutePath(SceneManager->getMeshCache()->getMeshName(Mesh).getPath()),
 
788
                                options->Filename);
 
789
                out->addString("Mesh", path.c_str());
 
790
        }
 
791
        else
 
792
                out->addString("Mesh", SceneManager->getMeshCache()->getMeshName(Mesh).getPath().c_str());
 
793
        out->addBool("Looping", Looping);
 
794
        out->addBool("ReadOnlyMaterials", ReadOnlyMaterials);
 
795
        out->addFloat("FramesPerSecond", FramesPerSecond);
 
796
        out->addInt("StartFrame", StartFrame);
 
797
        out->addInt("EndFrame", EndFrame);
 
798
}
 
799
 
 
800
 
 
801
//! Reads attributes of the scene node.
 
802
void CAnimatedMeshSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
 
803
{
 
804
        IAnimatedMeshSceneNode::deserializeAttributes(in, options);
 
805
 
 
806
        io::path oldMeshStr = SceneManager->getMeshCache()->getMeshName(Mesh);
 
807
        io::path newMeshStr = in->getAttributeAsString("Mesh");
 
808
 
 
809
        Looping = in->getAttributeAsBool("Looping");
 
810
        ReadOnlyMaterials = in->getAttributeAsBool("ReadOnlyMaterials");
 
811
        FramesPerSecond = in->getAttributeAsFloat("FramesPerSecond");
 
812
        StartFrame = in->getAttributeAsInt("StartFrame");
 
813
        EndFrame = in->getAttributeAsInt("EndFrame");
 
814
 
 
815
        if (newMeshStr != "" && oldMeshStr != newMeshStr)
 
816
        {
 
817
                IAnimatedMesh* newAnimatedMesh = SceneManager->getMesh(newMeshStr.c_str());
 
818
 
 
819
                if (newAnimatedMesh)
 
820
                        setMesh(newAnimatedMesh);
 
821
        }
 
822
 
 
823
        // TODO: read animation names instead of frame begin and ends
 
824
}
 
825
 
 
826
 
 
827
//! Sets a new mesh
 
828
void CAnimatedMeshSceneNode::setMesh(IAnimatedMesh* mesh)
 
829
{
 
830
        if (!mesh)
 
831
                return; // won't set null mesh
 
832
 
 
833
        if (Mesh != mesh)
 
834
        {
 
835
                if (Mesh)
 
836
                        Mesh->drop();
 
837
 
 
838
                Mesh = mesh;
 
839
 
 
840
                // grab the mesh (it's non-null!)
 
841
                Mesh->grab();
 
842
        }
 
843
 
 
844
        // get materials and bounding box
 
845
        Box = Mesh->getBoundingBox();
 
846
 
 
847
        IMesh* m = Mesh->getMesh(0,0);
 
848
        if (m)
 
849
        {
 
850
                Materials.clear();
 
851
                Materials.reallocate(m->getMeshBufferCount());
 
852
 
 
853
                for (u32 i=0; i<m->getMeshBufferCount(); ++i)
 
854
                {
 
855
                        IMeshBuffer* mb = m->getMeshBuffer(i);
 
856
                        if (mb)
 
857
                                Materials.push_back(mb->getMaterial());
 
858
                        else
 
859
                                Materials.push_back(video::SMaterial());
 
860
                }
 
861
        }
 
862
 
 
863
        // clean up joint nodes
 
864
        if (JointsUsed)
 
865
        {
 
866
                JointsUsed=false;
 
867
                checkJoints();
 
868
        }
 
869
 
 
870
        // get start and begin time
 
871
//      setAnimationSpeed(Mesh->getAnimationSpeed());
 
872
        setFrameLoop(0, Mesh->getFrameCount());
 
873
}
 
874
 
 
875
 
 
876
// returns the absolute transformation for a special MD3 Tag if the mesh is a md3 mesh,
 
877
// or the absolutetransformation if it's a normal scenenode
 
878
const SMD3QuaternionTag* CAnimatedMeshSceneNode::getMD3TagTransformation(const core::stringc& tagname)
 
879
{
 
880
        return MD3Special ? MD3Special->AbsoluteTagList.get(tagname) : 0;
 
881
}
 
882
 
 
883
 
 
884
//! updates the absolute position based on the relative and the parents position
 
885
void CAnimatedMeshSceneNode::updateAbsolutePosition()
 
886
{
 
887
        IAnimatedMeshSceneNode::updateAbsolutePosition();
 
888
 
 
889
        if (!Mesh || Mesh->getMeshType() != EAMT_MD3)
 
890
                return;
 
891
 
 
892
        SMD3QuaternionTagList *taglist;
 
893
        taglist = ( (IAnimatedMeshMD3*) Mesh )->getTagList ( (s32)getFrameNr(),255,getStartFrame (),getEndFrame () );
 
894
        if (taglist)
 
895
        {
 
896
                if (!MD3Special)
 
897
                {
 
898
                        MD3Special = new SMD3Special();
 
899
                }
 
900
 
 
901
                SMD3QuaternionTag parent ( MD3Special->Tagname );
 
902
                if (Parent && Parent->getType() == ESNT_ANIMATED_MESH)
 
903
                {
 
904
                        const SMD3QuaternionTag * p = ((IAnimatedMeshSceneNode*) Parent)->getMD3TagTransformation
 
905
                                                                        ( MD3Special->Tagname );
 
906
 
 
907
                        if (p)
 
908
                                parent = *p;
 
909
                }
 
910
 
 
911
                SMD3QuaternionTag relative( RelativeTranslation, RelativeRotation );
 
912
 
 
913
                MD3Special->AbsoluteTagList.set_used ( taglist->size () );
 
914
                for ( u32 i=0; i!= taglist->size (); ++i )
 
915
                {
 
916
                        MD3Special->AbsoluteTagList[i].position = parent.position + (*taglist)[i].position + relative.position;
 
917
                        MD3Special->AbsoluteTagList[i].rotation = parent.rotation * (*taglist)[i].rotation * relative.rotation;
 
918
                }
 
919
        }
 
920
}
 
921
 
 
922
//! Set the joint update mode (0-unused, 1-get joints only, 2-set joints only, 3-move and set)
 
923
void CAnimatedMeshSceneNode::setJointMode(E_JOINT_UPDATE_ON_RENDER mode)
 
924
{
 
925
        checkJoints();
 
926
        JointMode=mode;
 
927
}
 
928
 
 
929
//! Sets the transition time in seconds (note: This needs to enable joints, and setJointmode maybe set to 2)
 
930
//! you must call animateJoints(), or the mesh will not animate
 
931
void CAnimatedMeshSceneNode::setTransitionTime(f32 time)
 
932
{
 
933
        const u32 ttime = (u32)core::floor32(time*1000.0f);
 
934
        if (TransitionTime==ttime)
 
935
                return;
 
936
        TransitionTime = ttime;
 
937
        if (ttime != 0)
 
938
                setJointMode(EJUOR_CONTROL);
 
939
        else
 
940
                setJointMode(EJUOR_NONE);
 
941
}
 
942
 
 
943
 
 
944
//! render mesh ignoring its transformation. Used with ragdolls. (culling is unaffected)
 
945
void CAnimatedMeshSceneNode::setRenderFromIdentity(bool enable)
 
946
{
 
947
        RenderFromIdentity=enable;
 
948
}
 
949
 
 
950
 
 
951
//! updates the joint positions of this mesh
 
952
void CAnimatedMeshSceneNode::animateJoints(bool CalculateAbsolutePositions)
 
953
{
 
954
#ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_
 
955
        return;
 
956
#else
 
957
        if (Mesh && Mesh->getMeshType() == EAMT_SKINNED )
 
958
        {
 
959
                checkJoints();
 
960
                const f32 frame = getFrameNr(); //old?
 
961
 
 
962
                CSkinnedMesh* skinnedMesh=reinterpret_cast<CSkinnedMesh*>(Mesh);
 
963
 
 
964
                skinnedMesh->transferOnlyJointsHintsToMesh( JointChildSceneNodes );
 
965
                skinnedMesh->animateMesh(frame, 1.0f);
 
966
                skinnedMesh->recoverJointsFromMesh( JointChildSceneNodes);
 
967
 
 
968
                //-----------------------------------------
 
969
                //              Transition
 
970
                //-----------------------------------------
 
971
 
 
972
                if (Transiting != 0.f)
 
973
                {
 
974
                        // Init additional matrices
 
975
                        if (PretransitingSave.size()<JointChildSceneNodes.size())
 
976
                        {
 
977
                                for(u32 n=PretransitingSave.size(); n<JointChildSceneNodes.size(); ++n)
 
978
                                        PretransitingSave.push_back(core::matrix4());
 
979
                        }
 
980
 
 
981
                        for (u32 n=0; n<JointChildSceneNodes.size(); ++n)
 
982
                        {
 
983
                                //------Position------
 
984
 
 
985
                                JointChildSceneNodes[n]->setPosition(
 
986
                                                core::lerp(
 
987
                                                        PretransitingSave[n].getTranslation(),
 
988
                                                        JointChildSceneNodes[n]->getPosition(),
 
989
                                                        TransitingBlend));
 
990
 
 
991
                                //------Rotation------
 
992
 
 
993
                                //Code is slow, needs to be fixed up
 
994
 
 
995
                                const core::quaternion RotationStart(PretransitingSave[n].getRotationDegrees()*core::DEGTORAD);
 
996
                                const core::quaternion RotationEnd(JointChildSceneNodes[n]->getRotation()*core::DEGTORAD);
 
997
 
 
998
                                core::quaternion QRotation;
 
999
                                QRotation.slerp(RotationStart, RotationEnd, TransitingBlend);
 
1000
 
 
1001
                                core::vector3df tmpVector;
 
1002
                                QRotation.toEuler(tmpVector);
 
1003
                                tmpVector*=core::RADTODEG; //convert from radians back to degrees
 
1004
                                JointChildSceneNodes[n]->setRotation( tmpVector );
 
1005
 
 
1006
                                //------Scale------
 
1007
 
 
1008
                                //JointChildSceneNodes[n]->setScale(
 
1009
                                //              core::lerp(
 
1010
                                //                      PretransitingSave[n].getScale(),
 
1011
                                //                      JointChildSceneNodes[n]->getScale(),
 
1012
                                //                      TransitingBlend));
 
1013
                        }
 
1014
                }
 
1015
 
 
1016
                if (CalculateAbsolutePositions)
 
1017
                {
 
1018
                        //---slow---
 
1019
                        for (u32 n=0;n<JointChildSceneNodes.size();++n)
 
1020
                        {
 
1021
                                if (JointChildSceneNodes[n]->getParent()==this)
 
1022
                                {
 
1023
                                        JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); //temp, should be an option
 
1024
                                }
 
1025
                        }
 
1026
                }
 
1027
        }
 
1028
#endif
 
1029
}
 
1030
 
 
1031
/*!
 
1032
*/
 
1033
void CAnimatedMeshSceneNode::checkJoints()
 
1034
{
 
1035
#ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_
 
1036
        return;
 
1037
#else
 
1038
 
 
1039
        if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)
 
1040
                return;
 
1041
 
 
1042
        if (!JointsUsed)
 
1043
        {
 
1044
                for (u32 i=0; i<JointChildSceneNodes.size(); ++i)
 
1045
                        removeChild(JointChildSceneNodes[i]);
 
1046
                JointChildSceneNodes.clear();
 
1047
 
 
1048
                //Create joints for SkinnedMesh
 
1049
                ((CSkinnedMesh*)Mesh)->addJoints(JointChildSceneNodes, this, SceneManager);
 
1050
                ((CSkinnedMesh*)Mesh)->recoverJointsFromMesh(JointChildSceneNodes);
 
1051
 
 
1052
                JointsUsed=true;
 
1053
                JointMode=EJUOR_READ;
 
1054
        }
 
1055
#endif
 
1056
}
 
1057
 
 
1058
/*!
 
1059
*/
 
1060
void CAnimatedMeshSceneNode::beginTransition()
 
1061
{
 
1062
        if (!JointsUsed)
 
1063
                return;
 
1064
 
 
1065
        if (TransitionTime != 0)
 
1066
        {
 
1067
                //Check the array is big enough
 
1068
                if (PretransitingSave.size()<JointChildSceneNodes.size())
 
1069
                {
 
1070
                        for(u32 n=PretransitingSave.size(); n<JointChildSceneNodes.size(); ++n)
 
1071
                                PretransitingSave.push_back(core::matrix4());
 
1072
                }
 
1073
 
 
1074
                //Copy the position of joints
 
1075
                for (u32 n=0;n<JointChildSceneNodes.size();++n)
 
1076
                        PretransitingSave[n]=JointChildSceneNodes[n]->getRelativeTransformation();
 
1077
 
 
1078
                Transiting = core::reciprocal((f32)TransitionTime);
 
1079
        }
 
1080
        TransitingBlend = 0.f;
 
1081
}
 
1082
 
 
1083
 
 
1084
/*!
 
1085
*/
 
1086
ISceneNode* CAnimatedMeshSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager)
 
1087
{
 
1088
        if (!newParent)
 
1089
                newParent = Parent;
 
1090
        if (!newManager)
 
1091
                newManager = SceneManager;
 
1092
 
 
1093
        CAnimatedMeshSceneNode* newNode =
 
1094
                new CAnimatedMeshSceneNode(Mesh, NULL, newManager, ID, RelativeTranslation,
 
1095
                                                 RelativeRotation, RelativeScale);
 
1096
 
 
1097
        if (newParent)
 
1098
        {
 
1099
                newNode->setParent(newParent);  // not in constructor because virtual overload for updateAbsolutePosition won't be called
 
1100
                newNode->drop();
 
1101
        }
 
1102
 
 
1103
        newNode->cloneMembers(this, newManager);
 
1104
 
 
1105
        newNode->Materials = Materials;
 
1106
        newNode->Box = Box;
 
1107
        newNode->Mesh = Mesh;
 
1108
        newNode->StartFrame = StartFrame;
 
1109
        newNode->EndFrame = EndFrame;
 
1110
        newNode->FramesPerSecond = FramesPerSecond;
 
1111
        newNode->CurrentFrameNr = CurrentFrameNr;
 
1112
        newNode->JointMode = JointMode;
 
1113
        newNode->JointsUsed = JointsUsed;
 
1114
        newNode->TransitionTime = TransitionTime;
 
1115
        newNode->Transiting = Transiting;
 
1116
        newNode->TransitingBlend = TransitingBlend;
 
1117
        newNode->Looping = Looping;
 
1118
        newNode->ReadOnlyMaterials = ReadOnlyMaterials;
 
1119
        newNode->LoopCallBack = LoopCallBack;
 
1120
        newNode->PassCount = PassCount;
 
1121
        newNode->Shadow = Shadow;
 
1122
        newNode->JointChildSceneNodes = JointChildSceneNodes;
 
1123
        newNode->PretransitingSave = PretransitingSave;
 
1124
        newNode->RenderFromIdentity = RenderFromIdentity;
 
1125
        newNode->MD3Special = MD3Special;
 
1126
 
 
1127
        return newNode;
 
1128
}
 
1129
 
 
1130
 
 
1131
} // end namespace scene
 
1132
} // end namespace irr