1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
7 #include "IAnimatedMesh.h"
8 #include "ISceneManager.h"
9 #include "SMeshBuffer.h"
10 #include "SSkinMeshBuffer.h"
11 #include "quaternion.h"
22 class IAnimatedMeshSceneNode
;
26 class SkinnedMesh
: public IAnimatedMesh
31 EndFrame(0.f
), FramesPerSecond(25.f
),
32 LastAnimatedFrame(-1), SkinnedLastFrame(false),
33 HasAnimation(false), PreparedForSkinning(false),
34 AnimateNormals(true), HardwareSkinning(false)
36 SkinningBuffers
= &LocalBuffers
;
40 virtual ~SkinnedMesh();
42 //! If the duration is 0, it is a static (=non animated) mesh.
43 f32
getMaxFrameNumber() const override
;
45 //! Gets the default animation speed of the animated mesh.
46 /** \return Amount of frames per second. If the amount is 0, it is a static, non animated mesh. */
47 f32
getAnimationSpeed() const override
;
49 //! Gets the frame count of the animated mesh.
50 /** \param fps Frames per second to play the animation with. If the amount is 0, it is not animated.
51 The actual speed is set in the scene node the mesh is instantiated in.*/
52 void setAnimationSpeed(f32 fps
) override
;
54 //! returns the animated mesh for the given frame
55 IMesh
*getMesh(f32
) override
;
57 //! Animates joints based on frame input
58 void animateMesh(f32 frame
);
60 //! Performs a software skin on this mesh based of joint positions
63 //! returns amount of mesh buffers.
64 u32
getMeshBufferCount() const override
;
66 //! returns pointer to a mesh buffer
67 IMeshBuffer
*getMeshBuffer(u32 nr
) const override
;
69 //! Returns pointer to a mesh buffer which fits a material
70 /** \param material: material to search for
71 \return Returns the pointer to the mesh buffer or
72 NULL if there is no such mesh buffer. */
73 IMeshBuffer
*getMeshBuffer(const video::SMaterial
&material
) const override
;
75 u32
getTextureSlot(u32 meshbufNr
) const override
;
77 void setTextureSlot(u32 meshbufNr
, u32 textureSlot
);
79 //! returns an axis aligned bounding box
80 const core::aabbox3d
<f32
> &getBoundingBox() const override
{
84 //! set user axis aligned bounding box
85 void setBoundingBox(const core::aabbox3df
&box
) override
{
89 //! set the hardware mapping hint, for driver
90 void setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint
, E_BUFFER_TYPE buffer
= EBT_VERTEX_AND_INDEX
) override
;
92 //! flags the meshbuffer as changed, reloads hardware buffers
93 void setDirty(E_BUFFER_TYPE buffer
= EBT_VERTEX_AND_INDEX
) override
;
95 //! Returns the type of the animated mesh.
96 E_ANIMATED_MESH_TYPE
getMeshType() const override
{
100 //! Gets joint count.
101 u32
getJointCount() const;
103 //! Gets the name of a joint.
104 /** \param number: Zero based index of joint.
105 \return Name of joint and null if an error happened. */
106 const std::optional
<std::string
> &getJointName(u32 number
) const;
108 //! Gets a joint number from its name
109 /** \param name: Name of the joint.
110 \return Number of the joint or std::nullopt if not found. */
111 std::optional
<u32
> getJointNumber(const std::string
&name
) const;
113 //! Update Normals when Animating
114 /** \param on If false don't animate, which is faster.
115 Else update normals, which allows for proper lighting of
116 animated meshes (default). */
117 void updateNormalsWhenAnimating(bool on
) {
121 //! converts the vertex type of all meshbuffers to tangents.
122 /** E.g. used for bump mapping. */
123 void convertMeshToTangents();
125 //! Does the mesh have no animation
126 bool isStatic() const {
127 return !HasAnimation
;
130 //! Allows to enable hardware skinning.
131 /* This feature is not implemented in Irrlicht yet */
132 bool setHardwareSkinning(bool on
);
134 //! Refreshes vertex data cached in joints such as positions and normals
135 void refreshJointCache();
137 //! Moves the mesh into static position.
138 void resetAnimation();
140 void updateBoundingBox();
142 //! Recovers the joints from the mesh
143 void recoverJointsFromMesh(std::vector
<IBoneSceneNode
*> &jointChildSceneNodes
);
145 //! Transfers the joint data to the mesh
146 void transferJointsToMesh(const std::vector
<IBoneSceneNode
*> &jointChildSceneNodes
);
148 //! Creates an array of joints from this mesh as children of node
149 void addJoints(std::vector
<IBoneSceneNode
*> &jointChildSceneNodes
,
150 IAnimatedMeshSceneNode
*node
,
151 ISceneManager
*smgr
);
156 //! Index of the mesh buffer
157 u16 buffer_id
; // I doubt 32bits is needed
159 //! Index of the vertex
160 u32 vertex_id
; // Store global ID here
162 //! Weight Strength/Percentage (0-1)
166 //! Internal members used by SkinnedMesh
167 friend class SkinnedMesh
;
169 core::vector3df StaticPos
;
170 core::vector3df StaticNormal
;
179 std::vector
<Frame
> frames
;
180 bool interpolate
= true;
183 return frames
.empty();
186 f32
getEndFrame() const {
187 return frames
.empty() ? 0 : frames
.back().time
;
190 void pushBack(f32 time
, const T
&value
) {
191 frames
.push_back({time
, value
});
194 void append(const Channel
<T
> &other
) {
195 frames
.insert(frames
.end(), other
.frames
.begin(), other
.frames
.end());
202 std::vector
<Frame
> ordered
;
203 ordered
.push_back(frames
.front());
204 // Drop out-of-order frames
205 for (auto it
= frames
.begin() + 1; it
!= frames
.end(); ++it
) {
206 if (it
->time
> ordered
.back().time
) {
207 ordered
.push_back(*it
);
211 // Drop redundant middle keys
212 frames
.push_back(ordered
.front());
213 for (u32 i
= 1; i
< ordered
.size() - 1; ++i
) {
214 if (ordered
[i
- 1].value
!= ordered
[i
].value
215 || ordered
[i
+ 1].value
!= ordered
[i
].value
) {
216 frames
.push_back(ordered
[i
]);
219 if (ordered
.size() > 1)
220 frames
.push_back(ordered
.back());
221 frames
.shrink_to_fit();
224 static core::quaternion
interpolateValue(core::quaternion from
, core::quaternion to
, f32 time
) {
225 core::quaternion result
;
226 result
.slerp(from
, to
, time
, 0.001f
);
230 static core::vector3df
interpolateValue(core::vector3df from
, core::vector3df to
, f32 time
) {
231 // Note: `from` and `to` are swapped here compared to quaternion slerp
232 return to
.getInterpolated(from
, time
);
235 std::optional
<T
> get(f32 time
) const {
239 const auto next
= std::lower_bound(frames
.begin(), frames
.end(), time
, [](const auto& frame
, f32 time
) {
240 return frame
.time
< time
;
242 if (next
== frames
.begin())
244 if (next
== frames
.end())
245 return frames
.back().value
;
247 const auto prev
= next
- 1;
251 return interpolateValue(prev
->value
, next
->value
, (time
- prev
->time
) / (next
->time
- prev
->time
));
256 Channel
<core::vector3df
> position
;
257 Channel
<core::quaternion
> rotation
;
258 Channel
<core::vector3df
> scale
;
261 return position
.empty() && rotation
.empty() && scale
.empty();
264 void append(const Keys
&other
) {
265 position
.append(other
.position
);
266 rotation
.append(other
.rotation
);
267 scale
.append(other
.scale
);
270 f32
getEndFrame() const {
272 position
.getEndFrame(),
273 rotation
.getEndFrame(),
278 void updateTransform(f32 frame
,
279 core::vector3df
&t
, core::quaternion
&r
, core::vector3df
&s
) const
281 if (auto pos
= position
.get(frame
))
283 if (auto rot
= rotation
.get(frame
))
285 if (auto scl
= scale
.get(frame
))
299 SJoint() : GlobalSkinningSpace(false) {}
301 //! The name of this joint
302 std::optional
<std::string
> Name
;
304 //! Local matrix of this joint
305 core::matrix4 LocalMatrix
;
307 //! List of child joints
308 std::vector
<SJoint
*> Children
;
310 //! List of attached meshes
311 std::vector
<u32
> AttachedMeshes
;
313 // Animation keyframes for translation, rotation, scale
317 std::vector
<SWeight
> Weights
;
319 //! Unnecessary for loaders, will be overwritten on finalize
320 core::matrix4 GlobalMatrix
; // loaders may still choose to set this (temporarily) to calculate absolute vertex data.
321 core::matrix4 GlobalAnimatedMatrix
;
322 core::matrix4 LocalAnimatedMatrix
;
324 //! These should be set by loaders.
325 core::vector3df Animatedposition
;
326 core::vector3df Animatedscale
;
327 core::quaternion Animatedrotation
;
329 // The .x and .gltf formats pre-calculate this
330 std::optional
<core::matrix4
> GlobalInversedMatrix
;
332 //! Internal members used by SkinnedMesh
333 friend class SkinnedMesh
;
335 bool GlobalSkinningSpace
;
338 const std::vector
<SJoint
*> &getAllJoints() const {
343 void checkForAnimation();
345 void normalizeWeights();
347 void buildAllLocalAnimatedMatrices();
349 void buildAllGlobalAnimatedMatrices(SJoint
*Joint
= 0, SJoint
*ParentJoint
= 0);
351 void calculateGlobalMatrices(SJoint
*Joint
, SJoint
*ParentJoint
);
353 void skinJoint(SJoint
*Joint
, SJoint
*ParentJoint
);
355 void calculateTangents(core::vector3df
&normal
,
356 core::vector3df
&tangent
, core::vector3df
&binormal
,
357 const core::vector3df
&vt1
, const core::vector3df
&vt2
, const core::vector3df
&vt3
,
358 const core::vector2df
&tc1
, const core::vector2df
&tc2
, const core::vector2df
&tc3
);
360 std::vector
<SSkinMeshBuffer
*> *SkinningBuffers
; // Meshbuffer to skin, default is to skin localBuffers
362 std::vector
<SSkinMeshBuffer
*> LocalBuffers
;
363 //! Mapping from meshbuffer number to bindable texture slot
364 std::vector
<u32
> TextureSlots
;
366 std::vector
<SJoint
*> AllJoints
;
367 std::vector
<SJoint
*> RootJoints
;
369 // bool can't be used here because std::vector<bool>
370 // doesn't allow taking a reference to individual elements.
371 std::vector
<std::vector
<char>> Vertices_Moved
;
373 core::aabbox3d
<f32
> BoundingBox
{{0, 0, 0}};
378 f32 LastAnimatedFrame
;
379 bool SkinnedLastFrame
;
382 bool PreparedForSkinning
;
384 bool HardwareSkinning
;
387 // Interface for mesh loaders
388 class SkinnedMeshBuilder
: public SkinnedMesh
{
390 SkinnedMeshBuilder() : SkinnedMesh() {}
392 //! loaders should call this after populating the mesh
393 // returns *this, so do not try to drop the mesh builder instance
394 SkinnedMesh
*finalize();
396 //! alternative method for adding joints
397 std::vector
<SJoint
*> &getAllJoints() {
401 //! Adds a new meshbuffer to the mesh, access it as last one
402 SSkinMeshBuffer
*addMeshBuffer();
404 //! Adds a new meshbuffer to the mesh, access it as last one
405 void addMeshBuffer(SSkinMeshBuffer
*meshbuf
);
407 //! Adds a new joint to the mesh, access it as last one
408 SJoint
*addJoint(SJoint
*parent
= nullptr);
410 void addPositionKey(SJoint
*joint
, f32 frame
, core::vector3df pos
);
411 void addRotationKey(SJoint
*joint
, f32 frame
, core::quaternion rotation
);
412 void addScaleKey(SJoint
*joint
, f32 frame
, core::vector3df scale
);
414 //! Adds a new weight to the mesh, access it as last one
415 SWeight
*addWeight(SJoint
*joint
);
418 } // end namespace scene
419 } // end namespace irr