Refactor: Merge [IC]SkinnedMesh into SkinnedMesh (#15511)

This commit is contained in:
Lars Müller 2024-12-06 18:03:44 +01:00 committed by GitHub
parent 810f39767c
commit 3e10d9ccf5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 326 additions and 646 deletions

@ -48,7 +48,7 @@ const c8 *const BoneAnimationModeNames[] = {
}; };
//! Interface for bones used for skeletal animation. //! Interface for bones used for skeletal animation.
/** Used with ISkinnedMesh and IAnimatedMeshSceneNode. */ /** Used with SkinnedMesh and IAnimatedMeshSceneNode. */
class IBoneSceneNode : public ISceneNode class IBoneSceneNode : public ISceneNode
{ {
public: public:

@ -11,7 +11,6 @@
#include "SColor.h" #include "SColor.h"
#include "ESceneNodeTypes.h" #include "ESceneNodeTypes.h"
#include "SceneParameters.h" // IWYU pragma: export #include "SceneParameters.h" // IWYU pragma: export
#include "ISkinnedMesh.h"
namespace irr namespace irr
{ {
@ -93,6 +92,7 @@ class IBillboardSceneNode;
class ICameraSceneNode; class ICameraSceneNode;
class IDummyTransformationSceneNode; class IDummyTransformationSceneNode;
class IMesh; class IMesh;
class SkinnedMesh;
class IMeshBuffer; class IMeshBuffer;
class IMeshCache; class IMeshCache;
class ISceneCollisionManager; class ISceneCollisionManager;
@ -121,189 +121,6 @@ public:
//! Get pointer to an animatable mesh. Loads the file if not loaded already. //! Get pointer to an animatable mesh. Loads the file if not loaded already.
/** /**
* If you want to remove a loaded mesh from the cache again, use removeMesh(). * If you want to remove a loaded mesh from the cache again, use removeMesh().
* Currently there are the following mesh formats supported:
* <TABLE border="1" cellpadding="2" cellspacing="0">
* <TR>
* <TD>Format</TD>
* <TD>Description</TD>
* </TR>
* <TR>
* <TD>3D Studio (.3ds)</TD>
* <TD>Loader for 3D-Studio files which lots of 3D packages
* are able to export. Only static meshes are currently
* supported by this importer.</TD>
* </TR>
* <TR>
* <TD>3D World Studio (.smf)</TD>
* <TD>Loader for Leadwerks SMF mesh files, a simple mesh format
* containing static geometry for games. The proprietary .STF texture format
* is not supported yet. This loader was originally written by Joseph Ellis. </TD>
* </TR>
* <TR>
* <TD>Bliz Basic B3D (.b3d)</TD>
* <TD>Loader for blitz basic files, developed by Mark
* Sibly. This is the ideal animated mesh format for game
* characters as it is both rigidly defined and widely
* supported by modeling and animation software.
* As this format supports skeletal animations, an
* ISkinnedMesh will be returned by this importer.</TD>
* </TR>
* <TR>
* <TD>Cartography shop 4 (.csm)</TD>
* <TD>Cartography Shop is a modeling program for creating
* architecture and calculating lighting. Irrlicht can
* directly import .csm files thanks to the IrrCSM library
* created by Saurav Mohapatra which is now integrated
* directly in Irrlicht.
* </TR>
* <TR>
* <TD>Delgine DeleD (.dmf)</TD>
* <TD>DeleD (delgine.com) is a 3D editor and level-editor
* combined into one and is specifically designed for 3D
* game-development. With this loader, it is possible to
* directly load all geometry is as well as textures and
* lightmaps from .dmf files. To set texture and
* material paths, see scene::DMF_USE_MATERIALS_DIRS.
* It is also possible to flip the alpha texture by setting
* scene::DMF_FLIP_ALPHA_TEXTURES to true and to set the
* material transparent reference value by setting
* scene::DMF_ALPHA_CHANNEL_REF to a float between 0 and
* 1. The loader is based on Salvatore Russo's .dmf
* loader, I just changed some parts of it. Thanks to
* Salvatore for his work and for allowing me to use his
* code in Irrlicht and put it under Irrlicht's license.
* For newer and more enhanced versions of the loader,
* take a look at delgine.com.
* </TD>
* </TR>
* <TR>
* <TD>DirectX (.x)</TD>
* <TD>Platform independent importer (so not D3D-only) for
* .x files. Most 3D packages can export these natively
* and there are several tools for them available, e.g.
* the Maya exporter included in the DX SDK.
* .x files can include skeletal animations and Irrlicht
* is able to play and display them, users can manipulate
* the joints via the ISkinnedMesh interface. Currently,
* Irrlicht only supports uncompressed .x files.</TD>
* </TR>
* <TR>
* <TD>Half-Life model (.mdl)</TD>
* <TD>This loader opens Half-life 1 models, it was contributed
* by Fabio Concas and adapted by Thomas Alten.</TD>
* </TR>
* <TR>
* <TD>LightWave (.lwo)</TD>
* <TD>Native to NewTek's LightWave 3D, the LWO format is well
* known and supported by many exporters. This loader will
* import LWO2 models including lightmaps, bumpmaps and
* reflection textures.</TD>
* </TR>
* <TR>
* <TD>Maya (.obj)</TD>
* <TD>Most 3D software can create .obj files which contain
* static geometry without material data. The material
* files .mtl are also supported. This importer for
* Irrlicht can load them directly. </TD>
* </TR>
* <TR>
* <TD>Milkshape (.ms3d)</TD>
* <TD>.MS3D files contain models and sometimes skeletal
* animations from the Milkshape 3D modeling and animation
* software. Like the other skeletal mesh loaders, joints
* are exposed via the ISkinnedMesh animated mesh type.</TD>
* </TR>
* <TR>
* <TD>My3D (.my3d)</TD>
* <TD>.my3D is a flexible 3D file format. The My3DTools
* contains plug-ins to export .my3D files from several
* 3D packages. With this built-in importer, Irrlicht
* can read and display those files directly. This
* loader was written by Zhuck Dimitry who also created
* the whole My3DTools package.
* </TD>
* </TR>
* <TR>
* <TD>OCT (.oct)</TD>
* <TD>The oct file format contains 3D geometry and
* lightmaps and can be loaded directly by Irrlicht. OCT
* files<br> can be created by FSRad, Paul Nette's
* radiosity processor or exported from Blender using
* OCTTools which can be found in the exporters/OCTTools
* directory of the SDK. Thanks to Murphy McCauley for
* creating all this.</TD>
* </TR>
* <TR>
* <TD>OGRE Meshes (.mesh)</TD>
* <TD>Ogre .mesh files contain 3D data for the OGRE 3D
* engine. Irrlicht can read and display them directly
* with this importer. To define materials for the mesh,
* copy a .material file named like the corresponding
* .mesh file where the .mesh file is. (For example
* ogrehead.material for ogrehead.mesh). Thanks to
* Christian Stehno who wrote and contributed this
* loader.</TD>
* </TR>
* <TR>
* <TD>Pulsar LMTools (.lmts)</TD>
* <TD>LMTools is a set of tools (Windows &amp; Linux) for
* creating lightmaps. Irrlicht can directly read .lmts
* files thanks to<br> the importer created by Jonas
* Petersen.
* Notes for<br> this version of the loader:<br>
* - It does not recognize/support user data in the
* *.lmts files.<br>
* - The TGAs generated by LMTools don't work in
* Irrlicht for some reason (the textures are upside
* down). Opening and resaving them in a graphics app
* will solve the problem.</TD>
* </TR>
* <TR>
* <TD>Quake 3 levels (.bsp)</TD>
* <TD>Quake 3 is a popular game by IDSoftware, and .pk3
* files contain .bsp files and textures/lightmaps
* describing huge prelighted levels. Irrlicht can read
* .pk3 and .bsp files directly and thus render Quake 3
* levels directly. Written by Nikolaus Gebhardt
* enhanced by Dean P. Macri with the curved surfaces
* feature. </TD>
* </TR>
* <TR>
* <TD>Quake 2 models (.md2)</TD>
* <TD>Quake 2 models are characters with morph target
* animation. Irrlicht can read, display and animate
* them directly with this importer. </TD>
* </TR>
* <TR>
* <TD>Quake 3 models (.md3)</TD>
* <TD>Quake 3 models are characters with morph target
* animation, they contain mount points for weapons and body
* parts and are typically made of several sections which are
* manually joined together.</TD>
* </TR>
* <TR>
* <TD>Stanford Triangle (.ply)</TD>
* <TD>Invented by Stanford University and known as the native
* format of the infamous "Stanford Bunny" model, this is a
* popular static mesh format used by 3D scanning hardware
* and software. This loader supports extremely large models
* in both ASCII and binary format, but only has rudimentary
* material support in the form of vertex colors and texture
* coordinates.</TD>
* </TR>
* <TR>
* <TD>Stereolithography (.stl)</TD>
* <TD>The STL format is used for rapid prototyping and
* computer-aided manufacturing, thus has no support for
* materials.</TD>
* </TR>
* </TABLE>
*
* To load and display a mesh quickly, just do this:
* \code
* SceneManager->addAnimatedMeshSceneNode(
* SceneManager->getMesh("yourmesh.3ds"));
* \endcode
* If you would like to implement and add your own file format loader to Irrlicht, * If you would like to implement and add your own file format loader to Irrlicht,
* see addExternalMeshLoader(). * see addExternalMeshLoader().
* \param file File handle of the mesh to load. * \param file File handle of the mesh to load.
@ -594,7 +411,7 @@ public:
//! Get a skinned mesh, which is not available as header-only code //! Get a skinned mesh, which is not available as header-only code
/** Note: You need to drop() the pointer after use again, see IReferenceCounted::drop() /** Note: You need to drop() the pointer after use again, see IReferenceCounted::drop()
for details. */ for details. */
virtual ISkinnedMesh *createSkinnedMesh() = 0; virtual SkinnedMesh *createSkinnedMesh() = 0;
//! Get current render pass. //! Get current render pass.
virtual E_SCENE_NODE_RENDER_PASS getCurrentRenderPass() const = 0; virtual E_SCENE_NODE_RENDER_PASS getCurrentRenderPass() const = 0;

@ -1,226 +0,0 @@
// Copyright (C) 2002-2012 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#pragma once
#include "irrArray.h"
#include "IAnimatedMesh.h"
#include "SSkinMeshBuffer.h"
#include "quaternion.h"
#include <optional>
#include <string>
namespace irr
{
namespace scene
{
enum E_INTERPOLATION_MODE
{
// constant does use the current key-values without interpolation
EIM_CONSTANT = 0,
// linear interpolation
EIM_LINEAR,
//! count of all available interpolation modes
EIM_COUNT
};
//! Interface for using some special functions of Skinned meshes
class ISkinnedMesh : public IAnimatedMesh
{
public:
//! Gets joint count.
/** \return Amount of joints in the skeletal animated mesh. */
virtual u32 getJointCount() const = 0;
//! Gets the name of a joint.
/** \param number: Zero based index of joint. The last joint
has the number getJointCount()-1;
\return Name of joint and null if an error happened. */
virtual const std::optional<std::string> &getJointName(u32 number) const = 0;
//! Gets a joint number from its name
/** \param name: Name of the joint.
\return Number of the joint or std::nullopt if not found. */
virtual std::optional<u32> getJointNumber(const std::string &name) const = 0;
//! Use animation from another mesh
/** The animation is linked (not copied) based on joint names
so make sure they are unique.
\return True if all joints in this mesh were
matched up (empty names will not be matched, and it's case
sensitive). Unmatched joints will not be animated. */
virtual bool useAnimationFrom(const ISkinnedMesh *mesh) = 0;
//! Update Normals when Animating
/** \param on If false don't animate, which is faster.
Else update normals, which allows for proper lighting of
animated meshes. */
virtual void updateNormalsWhenAnimating(bool on) = 0;
//! Sets Interpolation Mode
virtual void setInterpolationMode(E_INTERPOLATION_MODE mode) = 0;
//! Animates this mesh's joints based on frame input
virtual void animateMesh(f32 frame, f32 blend) = 0;
//! Preforms a software skin on this mesh based of joint positions
virtual void skinMesh() = 0;
//! converts the vertex type of all meshbuffers to tangents.
/** E.g. used for bump mapping. */
virtual void convertMeshToTangents() = 0;
//! Allows to enable hardware skinning.
/* This feature is not implemented in Irrlicht yet */
virtual bool setHardwareSkinning(bool on) = 0;
//! Refreshes vertex data cached in joints such as positions and normals
virtual void refreshJointCache() = 0;
//! Moves the mesh into static position.
virtual void resetAnimation() = 0;
//! A vertex weight
struct SWeight
{
//! Index of the mesh buffer
u16 buffer_id; // I doubt 32bits is needed
//! Index of the vertex
u32 vertex_id; // Store global ID here
//! Weight Strength/Percentage (0-1)
f32 strength;
private:
//! Internal members used by CSkinnedMesh
friend class CSkinnedMesh;
char *Moved;
core::vector3df StaticPos;
core::vector3df StaticNormal;
};
//! Animation keyframe which describes a new position
struct SPositionKey
{
f32 frame;
core::vector3df position;
};
//! Animation keyframe which describes a new scale
struct SScaleKey
{
f32 frame;
core::vector3df scale;
};
//! Animation keyframe which describes a new rotation
struct SRotationKey
{
f32 frame;
core::quaternion rotation;
};
//! Joints
struct SJoint
{
SJoint() :
UseAnimationFrom(0), GlobalSkinningSpace(false),
positionHint(-1), scaleHint(-1), rotationHint(-1)
{
}
//! The name of this joint
std::optional<std::string> Name;
//! Local matrix of this joint
core::matrix4 LocalMatrix;
//! List of child joints
core::array<SJoint *> Children;
//! List of attached meshes
core::array<u32> AttachedMeshes;
//! Animation keys causing translation change
core::array<SPositionKey> PositionKeys;
//! Animation keys causing scale change
core::array<SScaleKey> ScaleKeys;
//! Animation keys causing rotation change
core::array<SRotationKey> RotationKeys;
//! Skin weights
core::array<SWeight> Weights;
//! Unnecessary for loaders, will be overwritten on finalize
core::matrix4 GlobalMatrix; // loaders may still choose to set this (temporarily) to calculate absolute vertex data.
core::matrix4 GlobalAnimatedMatrix;
core::matrix4 LocalAnimatedMatrix;
//! These should be set by loaders.
core::vector3df Animatedposition;
core::vector3df Animatedscale;
core::quaternion Animatedrotation;
// The .x and .gltf formats pre-calculate this
std::optional<core::matrix4> GlobalInversedMatrix;
private:
//! Internal members used by CSkinnedMesh
friend class CSkinnedMesh;
SJoint *UseAnimationFrom;
bool GlobalSkinningSpace;
s32 positionHint;
s32 scaleHint;
s32 rotationHint;
};
// Interface for the mesh loaders (finalize should lock these functions, and they should have some prefix like loader_
// these functions will use the needed arrays, set values, etc to help the loaders
//! exposed for loaders: to add mesh buffers
virtual core::array<SSkinMeshBuffer *> &getMeshBuffers() = 0;
//! exposed for loaders: joints list
virtual core::array<SJoint *> &getAllJoints() = 0;
//! exposed for loaders: joints list
virtual const core::array<SJoint *> &getAllJoints() const = 0;
//! loaders should call this after populating the mesh
virtual void finalize() = 0;
//! Adds a new meshbuffer to the mesh, access it as last one
virtual SSkinMeshBuffer *addMeshBuffer() = 0;
//! Adds a new meshbuffer to the mesh, access it as last one
virtual void addMeshBuffer(SSkinMeshBuffer *meshbuf) = 0;
//! Adds a new joint to the mesh, access it as last one
virtual SJoint *addJoint(SJoint *parent = 0) = 0;
//! Adds a new weight to the mesh, access it as last one
virtual SWeight *addWeight(SJoint *joint) = 0;
//! Adds a new position key to the mesh, access it as last one
virtual SPositionKey *addPositionKey(SJoint *joint) = 0;
//! Adds a new scale key to the mesh, access it as last one
virtual SScaleKey *addScaleKey(SJoint *joint) = 0;
//! Adds a new rotation key to the mesh, access it as last one
virtual SRotationKey *addRotationKey(SJoint *joint) = 0;
//! Check if the mesh is non-animated
virtual bool isStatic() = 0;
};
} // end namespace scene
} // end namespace irr

@ -2,32 +2,46 @@
// This file is part of the "Irrlicht Engine". // This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h // For conditions of distribution and use, see copyright notice in irrlicht.h
// New skinned mesh
#pragma once #pragma once
#include "IAnimatedMesh.h"
#include "ISceneManager.h" #include "ISceneManager.h"
#include "ISkinnedMesh.h"
#include "SMeshBuffer.h" #include "SMeshBuffer.h"
#include "SSkinMeshBuffer.h"
#include "quaternion.h" #include "quaternion.h"
#include <optional>
#include <string>
namespace irr namespace irr
{ {
namespace scene namespace scene
{ {
enum E_INTERPOLATION_MODE
{
// constant does use the current key-values without interpolation
EIM_CONSTANT = 0,
// linear interpolation
EIM_LINEAR,
//! count of all available interpolation modes
EIM_COUNT
};
class IAnimatedMeshSceneNode; class IAnimatedMeshSceneNode;
class IBoneSceneNode; class IBoneSceneNode;
class ISceneManager; class ISceneManager;
class CSkinnedMesh : public ISkinnedMesh class SkinnedMesh : public IAnimatedMesh
{ {
public: public:
//! constructor //! constructor
CSkinnedMesh(); SkinnedMesh();
//! destructor //! destructor
virtual ~CSkinnedMesh(); virtual ~SkinnedMesh();
//! If the duration is 0, it is a static (=non animated) mesh. //! If the duration is 0, it is a static (=non animated) mesh.
f32 getMaxFrameNumber() const override; f32 getMaxFrameNumber() const override;
@ -46,10 +60,10 @@ public:
//! Animates this mesh's joints based on frame input //! Animates this mesh's joints based on frame input
//! blend: {0-old position, 1-New position} //! blend: {0-old position, 1-New position}
void animateMesh(f32 frame, f32 blend) override; void animateMesh(f32 frame, f32 blend);
//! Preforms a software skin on this mesh based of joint positions //! Performs a software skin on this mesh based of joint positions
void skinMesh() override; void skinMesh();
//! returns amount of mesh buffers. //! returns amount of mesh buffers.
u32 getMeshBufferCount() const override; u32 getMeshBufferCount() const override;
@ -83,83 +97,53 @@ public:
E_ANIMATED_MESH_TYPE getMeshType() const override; E_ANIMATED_MESH_TYPE getMeshType() const override;
//! Gets joint count. //! Gets joint count.
u32 getJointCount() const override; u32 getJointCount() const;
//! Gets the name of a joint. //! Gets the name of a joint.
const std::optional<std::string> &getJointName(u32 number) const override; /** \param number: Zero based index of joint.
\return Name of joint and null if an error happened. */
const std::optional<std::string> &getJointName(u32 number) const;
//! Gets a joint number from its name //! Gets a joint number from its name
std::optional<u32> getJointNumber(const std::string &name) const override; /** \param name: Name of the joint.
\return Number of the joint or std::nullopt if not found. */
//! uses animation from another mesh std::optional<u32> getJointNumber(const std::string &name) const;
bool useAnimationFrom(const ISkinnedMesh *mesh) override;
//! Update Normals when Animating //! Update Normals when Animating
//! False= Don't (default) /** \param on If false don't animate, which is faster.
//! True = Update normals, slower Else update normals, which allows for proper lighting of
void updateNormalsWhenAnimating(bool on) override; animated meshes. */
void updateNormalsWhenAnimating(bool on);
//! Sets Interpolation Mode //! Sets Interpolation Mode
void setInterpolationMode(E_INTERPOLATION_MODE mode) override; void setInterpolationMode(E_INTERPOLATION_MODE mode);
//! Convertes the mesh to contain tangent information //! converts the vertex type of all meshbuffers to tangents.
void convertMeshToTangents() override; /** E.g. used for bump mapping. */
void convertMeshToTangents();
//! Does the mesh have no animation //! Does the mesh have no animation
bool isStatic() override; bool isStatic() const;
//! (This feature is not implemented in irrlicht yet) //! Allows to enable hardware skinning.
bool setHardwareSkinning(bool on) override; /* This feature is not implemented in Irrlicht yet */
bool setHardwareSkinning(bool on);
//! Refreshes vertex data cached in joints such as positions and normals //! Refreshes vertex data cached in joints such as positions and normals
void refreshJointCache() override; void refreshJointCache();
//! Moves the mesh into static position. //! Moves the mesh into static position.
void resetAnimation() override; void resetAnimation();
// Interface for the mesh loaders (finalize should lock these functions, and they should have some prefix like loader_ virtual void updateBoundingBox();
// these functions will use the needed arrays, set values, etc to help the loaders
//! exposed for loaders to add mesh buffers
core::array<SSkinMeshBuffer *> &getMeshBuffers() override;
//! alternative method for adding joints
core::array<SJoint *> &getAllJoints() override;
//! alternative method for adding joints
const core::array<SJoint *> &getAllJoints() const override;
//! loaders should call this after populating the mesh
void finalize() override;
//! Adds a new meshbuffer to the mesh, access it as last one
SSkinMeshBuffer *addMeshBuffer() override;
//! Adds a new meshbuffer to the mesh, access it as last one
void addMeshBuffer(SSkinMeshBuffer *meshbuf) override;
//! Adds a new joint to the mesh, access it as last one
SJoint *addJoint(SJoint *parent = 0) override;
//! Adds a new position key to the mesh, access it as last one
SPositionKey *addPositionKey(SJoint *joint) override;
//! Adds a new rotation key to the mesh, access it as last one
SRotationKey *addRotationKey(SJoint *joint) override;
//! Adds a new scale key to the mesh, access it as last one
SScaleKey *addScaleKey(SJoint *joint) override;
//! Adds a new weight to the mesh, access it as last one
SWeight *addWeight(SJoint *joint) override;
virtual void updateBoundingBox(void);
//! Recovers the joints from the mesh //! Recovers the joints from the mesh
void recoverJointsFromMesh(core::array<IBoneSceneNode *> &jointChildSceneNodes); void recoverJointsFromMesh(core::array<IBoneSceneNode *> &jointChildSceneNodes);
//! Tranfers the joint data to the mesh //! Transfers the joint data to the mesh
void transferJointsToMesh(const core::array<IBoneSceneNode *> &jointChildSceneNodes); void transferJointsToMesh(const core::array<IBoneSceneNode *> &jointChildSceneNodes);
//! Tranfers the joint hints to the mesh //! Transfers the joint hints to the mesh
void transferOnlyJointsHintsToMesh(const core::array<IBoneSceneNode *> &jointChildSceneNodes); void transferOnlyJointsHintsToMesh(const core::array<IBoneSceneNode *> &jointChildSceneNodes);
//! Creates an array of joints from this mesh as children of node //! Creates an array of joints from this mesh as children of node
@ -167,6 +151,138 @@ public:
IAnimatedMeshSceneNode *node, IAnimatedMeshSceneNode *node,
ISceneManager *smgr); ISceneManager *smgr);
//! A vertex weight
struct SWeight
{
//! Index of the mesh buffer
u16 buffer_id; // I doubt 32bits is needed
//! Index of the vertex
u32 vertex_id; // Store global ID here
//! Weight Strength/Percentage (0-1)
f32 strength;
private:
//! Internal members used by SkinnedMesh
friend class SkinnedMesh;
char *Moved;
core::vector3df StaticPos;
core::vector3df StaticNormal;
};
//! Animation keyframe which describes a new position
struct SPositionKey
{
f32 frame;
core::vector3df position;
};
//! Animation keyframe which describes a new scale
struct SScaleKey
{
f32 frame;
core::vector3df scale;
};
//! Animation keyframe which describes a new rotation
struct SRotationKey
{
f32 frame;
core::quaternion rotation;
};
//! Joints
struct SJoint
{
SJoint() :
UseAnimationFrom(0), GlobalSkinningSpace(false),
positionHint(-1), scaleHint(-1), rotationHint(-1)
{
}
//! The name of this joint
std::optional<std::string> Name;
//! Local matrix of this joint
core::matrix4 LocalMatrix;
//! List of child joints
core::array<SJoint *> Children;
//! List of attached meshes
core::array<u32> AttachedMeshes;
//! Animation keys causing translation change
core::array<SPositionKey> PositionKeys;
//! Animation keys causing scale change
core::array<SScaleKey> ScaleKeys;
//! Animation keys causing rotation change
core::array<SRotationKey> RotationKeys;
//! Skin weights
core::array<SWeight> Weights;
//! Unnecessary for loaders, will be overwritten on finalize
core::matrix4 GlobalMatrix; // loaders may still choose to set this (temporarily) to calculate absolute vertex data.
core::matrix4 GlobalAnimatedMatrix;
core::matrix4 LocalAnimatedMatrix;
//! These should be set by loaders.
core::vector3df Animatedposition;
core::vector3df Animatedscale;
core::quaternion Animatedrotation;
// The .x and .gltf formats pre-calculate this
std::optional<core::matrix4> GlobalInversedMatrix;
private:
//! Internal members used by SkinnedMesh
friend class SkinnedMesh;
SJoint *UseAnimationFrom;
bool GlobalSkinningSpace;
s32 positionHint;
s32 scaleHint;
s32 rotationHint;
};
// Interface for the mesh loaders (finalize should lock these functions, and they should have some prefix like loader_
// these functions will use the needed arrays, set values, etc to help the loaders
//! exposed for loaders to add mesh buffers
core::array<SSkinMeshBuffer *> &getMeshBuffers();
//! alternative method for adding joints
core::array<SJoint *> &getAllJoints();
//! alternative method for reading joints
const core::array<SJoint *> &getAllJoints() const;
//! loaders should call this after populating the mesh
void finalize();
//! Adds a new meshbuffer to the mesh, access it as last one
SSkinMeshBuffer *addMeshBuffer();
//! Adds a new meshbuffer to the mesh, access it as last one
void addMeshBuffer(SSkinMeshBuffer *meshbuf);
//! Adds a new joint to the mesh, access it as last one
SJoint *addJoint(SJoint *parent = 0);
//! Adds a new position key to the mesh, access it as last one
SPositionKey *addPositionKey(SJoint *joint);
//! Adds a new rotation key to the mesh, access it as last one
SRotationKey *addRotationKey(SJoint *joint);
//! Adds a new scale key to the mesh, access it as last one
SScaleKey *addScaleKey(SJoint *joint);
//! Adds a new weight to the mesh, access it as last one
SWeight *addWeight(SJoint *joint);
private: private:
void checkForAnimation(); void checkForAnimation();

@ -7,7 +7,7 @@
#include "ISceneManager.h" #include "ISceneManager.h"
#include "S3DVertex.h" #include "S3DVertex.h"
#include "os.h" #include "os.h"
#include "CSkinnedMesh.h" #include "SkinnedMesh.h"
#include "IDummyTransformationSceneNode.h" #include "IDummyTransformationSceneNode.h"
#include "IBoneSceneNode.h" #include "IBoneSceneNode.h"
#include "IMaterialRenderer.h" #include "IMaterialRenderer.h"
@ -165,7 +165,7 @@ IMesh *CAnimatedMeshSceneNode::getMeshForCurrentFrame()
// As multiple scene nodes may be sharing the same skinned mesh, we have to // As multiple scene nodes may be sharing the same skinned mesh, we have to
// re-animate it every frame to ensure that this node gets the mesh that it needs. // re-animate it every frame to ensure that this node gets the mesh that it needs.
CSkinnedMesh *skinnedMesh = static_cast<CSkinnedMesh *>(Mesh); SkinnedMesh *skinnedMesh = static_cast<SkinnedMesh *>(Mesh);
if (JointMode == EJUOR_CONTROL) // write to mesh if (JointMode == EJUOR_CONTROL) // write to mesh
skinnedMesh->transferJointsToMesh(JointChildSceneNodes); skinnedMesh->transferJointsToMesh(JointChildSceneNodes);
@ -299,8 +299,8 @@ void CAnimatedMeshSceneNode::render()
if (Mesh->getMeshType() == EAMT_SKINNED) { if (Mesh->getMeshType() == EAMT_SKINNED) {
// draw skeleton // draw skeleton
for (u32 g = 0; g < ((ISkinnedMesh *)Mesh)->getAllJoints().size(); ++g) { for (u32 g = 0; g < ((SkinnedMesh *)Mesh)->getAllJoints().size(); ++g) {
ISkinnedMesh::SJoint *joint = ((ISkinnedMesh *)Mesh)->getAllJoints()[g]; auto *joint = ((SkinnedMesh *)Mesh)->getAllJoints()[g];
for (u32 n = 0; n < joint->Children.size(); ++n) { for (u32 n = 0; n < joint->Children.size(); ++n) {
driver->draw3DLine(joint->GlobalAnimatedMatrix.getTranslation(), driver->draw3DLine(joint->GlobalAnimatedMatrix.getTranslation(),
@ -404,7 +404,7 @@ IBoneSceneNode *CAnimatedMeshSceneNode::getJointNode(const c8 *jointName)
checkJoints(); checkJoints();
ISkinnedMesh *skinnedMesh = (ISkinnedMesh *)Mesh; auto *skinnedMesh = (SkinnedMesh *)Mesh;
const std::optional<u32> number = skinnedMesh->getJointNumber(jointName); const std::optional<u32> number = skinnedMesh->getJointNumber(jointName);
@ -446,7 +446,7 @@ u32 CAnimatedMeshSceneNode::getJointCount() const
if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED) if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)
return 0; return 0;
ISkinnedMesh *skinnedMesh = (ISkinnedMesh *)Mesh; auto *skinnedMesh = (SkinnedMesh *)Mesh;
return skinnedMesh->getJointCount(); return skinnedMesh->getJointCount();
} }
@ -596,7 +596,7 @@ void CAnimatedMeshSceneNode::animateJoints(bool CalculateAbsolutePositions)
checkJoints(); checkJoints();
const f32 frame = getFrameNr(); // old? const f32 frame = getFrameNr(); // old?
CSkinnedMesh *skinnedMesh = static_cast<CSkinnedMesh *>(Mesh); SkinnedMesh *skinnedMesh = static_cast<SkinnedMesh *>(Mesh);
skinnedMesh->transferOnlyJointsHintsToMesh(JointChildSceneNodes); skinnedMesh->transferOnlyJointsHintsToMesh(JointChildSceneNodes);
skinnedMesh->animateMesh(frame, 1.0f); skinnedMesh->animateMesh(frame, 1.0f);
@ -671,8 +671,8 @@ void CAnimatedMeshSceneNode::checkJoints()
JointChildSceneNodes.clear(); JointChildSceneNodes.clear();
// Create joints for SkinnedMesh // Create joints for SkinnedMesh
((CSkinnedMesh *)Mesh)->addJoints(JointChildSceneNodes, this, SceneManager); ((SkinnedMesh *)Mesh)->addJoints(JointChildSceneNodes, this, SceneManager);
((CSkinnedMesh *)Mesh)->recoverJointsFromMesh(JointChildSceneNodes); ((SkinnedMesh *)Mesh)->recoverJointsFromMesh(JointChildSceneNodes);
JointsUsed = true; JointsUsed = true;
JointMode = EJUOR_READ; JointMode = EJUOR_READ;

@ -51,7 +51,7 @@ IAnimatedMesh *CB3DMeshFileLoader::createMesh(io::IReadFile *file)
return 0; return 0;
B3DFile = file; B3DFile = file;
AnimatedMesh = new scene::CSkinnedMesh(); AnimatedMesh = new scene::SkinnedMesh();
ShowWarning = true; // If true a warning is issued if too many textures are used ShowWarning = true; // If true a warning is issued if too many textures are used
VerticesStart = 0; VerticesStart = 0;
@ -111,7 +111,7 @@ bool CB3DMeshFileLoader::load()
if (!readChunkBRUS()) if (!readChunkBRUS())
return false; return false;
} else if (strncmp(B3dStack.getLast().name, "NODE", 4) == 0) { } else if (strncmp(B3dStack.getLast().name, "NODE", 4) == 0) {
if (!readChunkNODE((CSkinnedMesh::SJoint *)0)) if (!readChunkNODE((SkinnedMesh::SJoint *)0))
return false; return false;
} else { } else {
os::Printer::log("Unknown chunk found in mesh base - skipping"); os::Printer::log("Unknown chunk found in mesh base - skipping");
@ -133,9 +133,9 @@ bool CB3DMeshFileLoader::load()
return true; return true;
} }
bool CB3DMeshFileLoader::readChunkNODE(CSkinnedMesh::SJoint *inJoint) bool CB3DMeshFileLoader::readChunkNODE(SkinnedMesh::SJoint *inJoint)
{ {
CSkinnedMesh::SJoint *joint = AnimatedMesh->addJoint(inJoint); SkinnedMesh::SJoint *joint = AnimatedMesh->addJoint(inJoint);
joint->Name = readString(); joint->Name = readString();
#ifdef _B3D_READER_DEBUG #ifdef _B3D_READER_DEBUG
@ -211,7 +211,7 @@ bool CB3DMeshFileLoader::readChunkNODE(CSkinnedMesh::SJoint *inJoint)
return true; return true;
} }
bool CB3DMeshFileLoader::readChunkMESH(CSkinnedMesh::SJoint *inJoint) bool CB3DMeshFileLoader::readChunkMESH(SkinnedMesh::SJoint *inJoint)
{ {
#ifdef _B3D_READER_DEBUG #ifdef _B3D_READER_DEBUG
core::stringc logStr; core::stringc logStr;
@ -302,7 +302,7 @@ VRTS:
float tex_coords[tex_coord_sets][tex_coord_set_size] ;tex coords float tex_coords[tex_coord_sets][tex_coord_set_size] ;tex coords
} }
*/ */
bool CB3DMeshFileLoader::readChunkVRTS(CSkinnedMesh::SJoint *inJoint) bool CB3DMeshFileLoader::readChunkVRTS(SkinnedMesh::SJoint *inJoint)
{ {
#ifdef _B3D_READER_DEBUG #ifdef _B3D_READER_DEBUG
core::stringc logStr; core::stringc logStr;
@ -521,7 +521,7 @@ bool CB3DMeshFileLoader::readChunkTRIS(scene::SSkinMeshBuffer *meshBuffer, u32 m
return true; return true;
} }
bool CB3DMeshFileLoader::readChunkBONE(CSkinnedMesh::SJoint *inJoint) bool CB3DMeshFileLoader::readChunkBONE(SkinnedMesh::SJoint *inJoint)
{ {
#ifdef _B3D_READER_DEBUG #ifdef _B3D_READER_DEBUG
core::stringc logStr; core::stringc logStr;
@ -552,7 +552,7 @@ bool CB3DMeshFileLoader::readChunkBONE(CSkinnedMesh::SJoint *inJoint)
if (AnimatedVertices_VertexID[globalVertexID] == -1) { if (AnimatedVertices_VertexID[globalVertexID] == -1) {
os::Printer::log("B3dMeshLoader: Weight has bad vertex id (no link to meshbuffer index found)"); os::Printer::log("B3dMeshLoader: Weight has bad vertex id (no link to meshbuffer index found)");
} else if (strength > 0) { } else if (strength > 0) {
CSkinnedMesh::SWeight *weight = AnimatedMesh->addWeight(inJoint); SkinnedMesh::SWeight *weight = AnimatedMesh->addWeight(inJoint);
weight->strength = strength; weight->strength = strength;
// Find the meshbuffer and Vertex index from the Global Vertex ID: // Find the meshbuffer and Vertex index from the Global Vertex ID:
weight->vertex_id = AnimatedVertices_VertexID[globalVertexID]; weight->vertex_id = AnimatedVertices_VertexID[globalVertexID];
@ -565,7 +565,7 @@ bool CB3DMeshFileLoader::readChunkBONE(CSkinnedMesh::SJoint *inJoint)
return true; return true;
} }
bool CB3DMeshFileLoader::readChunkKEYS(CSkinnedMesh::SJoint *inJoint) bool CB3DMeshFileLoader::readChunkKEYS(SkinnedMesh::SJoint *inJoint)
{ {
#ifdef _B3D_READER_DEBUG #ifdef _B3D_READER_DEBUG
// Only print first, that's just too much output otherwise // Only print first, that's just too much output otherwise
@ -584,11 +584,11 @@ bool CB3DMeshFileLoader::readChunkKEYS(CSkinnedMesh::SJoint *inJoint)
flags = os::Byteswap::byteswap(flags); flags = os::Byteswap::byteswap(flags);
#endif #endif
CSkinnedMesh::SPositionKey *oldPosKey = 0; SkinnedMesh::SPositionKey *oldPosKey = 0;
core::vector3df oldPos[2]; core::vector3df oldPos[2];
CSkinnedMesh::SScaleKey *oldScaleKey = 0; SkinnedMesh::SScaleKey *oldScaleKey = 0;
core::vector3df oldScale[2]; core::vector3df oldScale[2];
CSkinnedMesh::SRotationKey *oldRotKey = 0; SkinnedMesh::SRotationKey *oldRotKey = 0;
core::quaternion oldRot[2]; core::quaternion oldRot[2];
bool isFirst[3] = {true, true, true}; bool isFirst[3] = {true, true, true};
while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats

@ -10,7 +10,7 @@
#include "IMeshLoader.h" #include "IMeshLoader.h"
#include "ISceneManager.h" #include "ISceneManager.h"
#include "CSkinnedMesh.h" #include "SkinnedMesh.h"
#include "SB3DStructs.h" #include "SB3DStructs.h"
#include "IReadFile.h" #include "IReadFile.h"
@ -39,12 +39,12 @@ public:
private: private:
bool load(); bool load();
bool readChunkNODE(CSkinnedMesh::SJoint *InJoint); bool readChunkNODE(SkinnedMesh::SJoint *InJoint);
bool readChunkMESH(CSkinnedMesh::SJoint *InJoint); bool readChunkMESH(SkinnedMesh::SJoint *InJoint);
bool readChunkVRTS(CSkinnedMesh::SJoint *InJoint); bool readChunkVRTS(SkinnedMesh::SJoint *InJoint);
bool readChunkTRIS(scene::SSkinMeshBuffer *MeshBuffer, u32 MeshBufferID, s32 Vertices_Start); bool readChunkTRIS(scene::SSkinMeshBuffer *MeshBuffer, u32 MeshBufferID, s32 Vertices_Start);
bool readChunkBONE(CSkinnedMesh::SJoint *InJoint); bool readChunkBONE(SkinnedMesh::SJoint *InJoint);
bool readChunkKEYS(CSkinnedMesh::SJoint *InJoint); bool readChunkKEYS(SkinnedMesh::SJoint *InJoint);
bool readChunkANIM(); bool readChunkANIM();
bool readChunkTEXS(); bool readChunkTEXS();
bool readChunkBRUS(); bool readChunkBRUS();
@ -63,7 +63,7 @@ private:
core::array<video::S3DVertex2TCoords> BaseVertices; core::array<video::S3DVertex2TCoords> BaseVertices;
CSkinnedMesh *AnimatedMesh; SkinnedMesh *AnimatedMesh;
io::IReadFile *B3DFile; io::IReadFile *B3DFile;
// B3Ds have Vertex ID's local within the mesh I don't want this // B3Ds have Vertex ID's local within the mesh I don't want this

@ -5,7 +5,7 @@
#include "SMaterialLayer.h" #include "SMaterialLayer.h"
#include "coreutil.h" #include "coreutil.h"
#include "CSkinnedMesh.h" #include "SkinnedMesh.h"
#include "IAnimatedMesh.h" #include "IAnimatedMesh.h"
#include "IReadFile.h" #include "IReadFile.h"
#include "irrTypes.h" #include "irrTypes.h"
@ -344,7 +344,7 @@ IAnimatedMesh* SelfType::createMesh(io::IReadFile* file)
const char *filename = file->getFileName().c_str(); const char *filename = file->getFileName().c_str();
try { try {
tiniergltf::GlTF model = parseGLTF(file); tiniergltf::GlTF model = parseGLTF(file);
irr_ptr<CSkinnedMesh> mesh(new CSkinnedMesh()); irr_ptr<SkinnedMesh> mesh(new SkinnedMesh());
MeshExtractor extractor(std::move(model), mesh.get()); MeshExtractor extractor(std::move(model), mesh.get());
try { try {
extractor.load(); extractor.load();
@ -410,7 +410,7 @@ static video::E_TEXTURE_CLAMP convertTextureWrap(const Wrap wrap) {
void SelfType::MeshExtractor::addPrimitive( void SelfType::MeshExtractor::addPrimitive(
const tiniergltf::MeshPrimitive &primitive, const tiniergltf::MeshPrimitive &primitive,
const std::optional<std::size_t> skinIdx, const std::optional<std::size_t> skinIdx,
CSkinnedMesh::SJoint *parent) SkinnedMesh::SJoint *parent)
{ {
auto vertices = getVertices(primitive); auto vertices = getVertices(primitive);
if (!vertices.has_value()) if (!vertices.has_value())
@ -510,7 +510,7 @@ void SelfType::MeshExtractor::addPrimitive(
if (strength == 0) if (strength == 0)
continue; continue;
CSkinnedMesh::SWeight *weight = m_irr_model->addWeight(m_loaded_nodes.at(skin.joints.at(jointIdx))); SkinnedMesh::SWeight *weight = m_irr_model->addWeight(m_loaded_nodes.at(skin.joints.at(jointIdx)));
weight->buffer_id = meshbufNr; weight->buffer_id = meshbufNr;
weight->vertex_id = v; weight->vertex_id = v;
weight->strength = strength; weight->strength = strength;
@ -527,7 +527,7 @@ void SelfType::MeshExtractor::addPrimitive(
void SelfType::MeshExtractor::deferAddMesh( void SelfType::MeshExtractor::deferAddMesh(
const std::size_t meshIdx, const std::size_t meshIdx,
const std::optional<std::size_t> skinIdx, const std::optional<std::size_t> skinIdx,
CSkinnedMesh::SJoint *parent) SkinnedMesh::SJoint *parent)
{ {
m_mesh_loaders.emplace_back([=] { m_mesh_loaders.emplace_back([=] {
for (std::size_t pi = 0; pi < getPrimitiveCount(meshIdx); ++pi) { for (std::size_t pi = 0; pi < getPrimitiveCount(meshIdx); ++pi) {
@ -547,7 +547,7 @@ static const core::matrix4 leftToRight = core::matrix4(
); );
static const core::matrix4 rightToLeft = leftToRight; static const core::matrix4 rightToLeft = leftToRight;
static core::matrix4 loadTransform(const tiniergltf::Node::Matrix &m, CSkinnedMesh::SJoint *joint) static core::matrix4 loadTransform(const tiniergltf::Node::Matrix &m, SkinnedMesh::SJoint *joint)
{ {
// Note: Under the hood, this casts these doubles to floats. // Note: Under the hood, this casts these doubles to floats.
core::matrix4 mat = convertHandedness(core::matrix4( core::matrix4 mat = convertHandedness(core::matrix4(
@ -576,7 +576,7 @@ static core::matrix4 loadTransform(const tiniergltf::Node::Matrix &m, CSkinnedMe
return mat; return mat;
} }
static core::matrix4 loadTransform(const tiniergltf::Node::TRS &trs, CSkinnedMesh::SJoint *joint) static core::matrix4 loadTransform(const tiniergltf::Node::TRS &trs, SkinnedMesh::SJoint *joint)
{ {
const auto &trans = trs.translation; const auto &trans = trs.translation;
const auto &rot = trs.rotation; const auto &rot = trs.rotation;
@ -594,7 +594,7 @@ static core::matrix4 loadTransform(const tiniergltf::Node::TRS &trs, CSkinnedMes
} }
static core::matrix4 loadTransform(std::optional<std::variant<tiniergltf::Node::Matrix, tiniergltf::Node::TRS>> transform, static core::matrix4 loadTransform(std::optional<std::variant<tiniergltf::Node::Matrix, tiniergltf::Node::TRS>> transform,
CSkinnedMesh::SJoint *joint) { SkinnedMesh::SJoint *joint) {
if (!transform.has_value()) { if (!transform.has_value()) {
return core::matrix4(); return core::matrix4();
} }
@ -603,7 +603,7 @@ static core::matrix4 loadTransform(std::optional<std::variant<tiniergltf::Node::
void SelfType::MeshExtractor::loadNode( void SelfType::MeshExtractor::loadNode(
const std::size_t nodeIdx, const std::size_t nodeIdx,
CSkinnedMesh::SJoint *parent) SkinnedMesh::SJoint *parent)
{ {
const auto &node = m_gltf_model.nodes->at(nodeIdx); const auto &node = m_gltf_model.nodes->at(nodeIdx);
auto *joint = m_irr_model->addJoint(parent); auto *joint = m_irr_model->addJoint(parent);
@ -626,7 +626,7 @@ void SelfType::MeshExtractor::loadNode(
void SelfType::MeshExtractor::loadNodes() void SelfType::MeshExtractor::loadNodes()
{ {
m_loaded_nodes = std::vector<CSkinnedMesh::SJoint *>(m_gltf_model.nodes->size()); m_loaded_nodes = std::vector<SkinnedMesh::SJoint *>(m_gltf_model.nodes->size());
std::vector<bool> isChild(m_gltf_model.nodes->size()); std::vector<bool> isChild(m_gltf_model.nodes->size());
for (const auto &node : *m_gltf_model.nodes) { for (const auto &node : *m_gltf_model.nodes) {

@ -3,7 +3,7 @@
#pragma once #pragma once
#include "CSkinnedMesh.h" #include "SkinnedMesh.h"
#include "IMeshLoader.h" #include "IMeshLoader.h"
#include "IReadFile.h" #include "IReadFile.h"
#include "irrTypes.h" #include "irrTypes.h"
@ -100,7 +100,7 @@ private:
{ {
public: public:
MeshExtractor(tiniergltf::GlTF &&model, MeshExtractor(tiniergltf::GlTF &&model,
CSkinnedMesh *mesh) noexcept SkinnedMesh *mesh) noexcept
: m_gltf_model(std::move(model)), m_irr_model(mesh) {}; : m_gltf_model(std::move(model)), m_irr_model(mesh) {};
/* Gets indices for the given mesh/primitive. /* Gets indices for the given mesh/primitive.
@ -124,10 +124,10 @@ private:
private: private:
const tiniergltf::GlTF m_gltf_model; const tiniergltf::GlTF m_gltf_model;
CSkinnedMesh *m_irr_model; SkinnedMesh *m_irr_model;
std::vector<std::function<void()>> m_mesh_loaders; std::vector<std::function<void()>> m_mesh_loaders;
std::vector<CSkinnedMesh::SJoint *> m_loaded_nodes; std::vector<SkinnedMesh::SJoint *> m_loaded_nodes;
std::vector<std::string> warnings; std::vector<std::string> warnings;
void warn(const std::string &warning) { void warn(const std::string &warning) {
@ -145,13 +145,13 @@ private:
void addPrimitive(const tiniergltf::MeshPrimitive &primitive, void addPrimitive(const tiniergltf::MeshPrimitive &primitive,
const std::optional<std::size_t> skinIdx, const std::optional<std::size_t> skinIdx,
CSkinnedMesh::SJoint *parent); SkinnedMesh::SJoint *parent);
void deferAddMesh(const std::size_t meshIdx, void deferAddMesh(const std::size_t meshIdx,
const std::optional<std::size_t> skinIdx, const std::optional<std::size_t> skinIdx,
CSkinnedMesh::SJoint *parentJoint); SkinnedMesh::SJoint *parentJoint);
void loadNode(const std::size_t nodeIdx, CSkinnedMesh::SJoint *parentJoint); void loadNode(const std::size_t nodeIdx, SkinnedMesh::SJoint *parentJoint);
void loadNodes(); void loadNodes();

@ -308,7 +308,7 @@ set(IRRMESHLOADER
) )
add_library(IRRMESHOBJ OBJECT add_library(IRRMESHOBJ OBJECT
CSkinnedMesh.cpp SkinnedMesh.cpp
CBoneSceneNode.cpp CBoneSceneNode.cpp
CMeshSceneNode.cpp CMeshSceneNode.cpp
CAnimatedMeshSceneNode.cpp CAnimatedMeshSceneNode.cpp

@ -3,7 +3,7 @@
// For conditions of distribution and use, see copyright notice in irrlicht.h // For conditions of distribution and use, see copyright notice in irrlicht.h
#include "CMeshManipulator.h" #include "CMeshManipulator.h"
#include "ISkinnedMesh.h" #include "SkinnedMesh.h"
#include "SMesh.h" #include "SMesh.h"
#include "CMeshBuffer.h" #include "CMeshBuffer.h"
#include "SAnimatedMesh.h" #include "SAnimatedMesh.h"
@ -101,7 +101,7 @@ void CMeshManipulator::recalculateNormals(scene::IMesh *mesh, bool smooth, bool
return; return;
if (mesh->getMeshType() == EAMT_SKINNED) { if (mesh->getMeshType() == EAMT_SKINNED) {
ISkinnedMesh *smesh = (ISkinnedMesh *)mesh; auto *smesh = (SkinnedMesh *)mesh;
smesh->resetAnimation(); smesh->resetAnimation();
} }
@ -110,7 +110,7 @@ void CMeshManipulator::recalculateNormals(scene::IMesh *mesh, bool smooth, bool
recalculateNormals(mesh->getMeshBuffer(b), smooth, angleWeighted); recalculateNormals(mesh->getMeshBuffer(b), smooth, angleWeighted);
if (mesh->getMeshType() == EAMT_SKINNED) { if (mesh->getMeshType() == EAMT_SKINNED) {
ISkinnedMesh *smesh = (ISkinnedMesh *)mesh; auto *smesh = (SkinnedMesh *)mesh;
smesh->refreshJointCache(); smesh->refreshJointCache();
} }
} }

@ -5,10 +5,8 @@
#include "CMeshSceneNode.h" #include "CMeshSceneNode.h"
#include "IVideoDriver.h" #include "IVideoDriver.h"
#include "ISceneManager.h" #include "ISceneManager.h"
#include "S3DVertex.h"
#include "ICameraSceneNode.h"
#include "IMeshCache.h" #include "IMeshCache.h"
#include "IAnimatedMesh.h" #include "IMeshBuffer.h"
#include "IMaterialRenderer.h" #include "IMaterialRenderer.h"
#include "IFileSystem.h" #include "IFileSystem.h"

@ -16,7 +16,7 @@
#include "os.h" #include "os.h"
#include "CSkinnedMesh.h" #include "SkinnedMesh.h"
#include "CXMeshFileLoader.h" #include "CXMeshFileLoader.h"
#include "COBJMeshFileLoader.h" #include "COBJMeshFileLoader.h"
#include "CB3DMeshFileLoader.h" #include "CB3DMeshFileLoader.h"
@ -771,9 +771,9 @@ ISceneManager *CSceneManager::createNewSceneManager(bool cloneContent)
} }
//! Get a skinned mesh, which is not available as header-only code //! Get a skinned mesh, which is not available as header-only code
ISkinnedMesh *CSceneManager::createSkinnedMesh() SkinnedMesh *CSceneManager::createSkinnedMesh()
{ {
return new CSkinnedMesh(); return new SkinnedMesh();
} }
// creates a scenemanager // creates a scenemanager

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "SkinnedMesh.h"
#include "ISceneManager.h" #include "ISceneManager.h"
#include "ISceneNode.h" #include "ISceneNode.h"
#include "ICursorControl.h" #include "ICursorControl.h"
@ -22,6 +23,8 @@ namespace scene
{ {
class IMeshCache; class IMeshCache;
class SkinnedMesh;
/*! /*!
The Scene Manager manages scene nodes, mesh resources, cameras and all the other stuff. The Scene Manager manages scene nodes, mesh resources, cameras and all the other stuff.
*/ */
@ -167,8 +170,8 @@ public:
//! Returns type of the scene node //! Returns type of the scene node
ESCENE_NODE_TYPE getType() const override { return ESNT_SCENE_MANAGER; } ESCENE_NODE_TYPE getType() const override { return ESNT_SCENE_MANAGER; }
//! Get a skinned mesh, which is not available as header-only code //! Get a skinned mesh
ISkinnedMesh *createSkinnedMesh() override; SkinnedMesh *createSkinnedMesh() override;
//! Get current render time. //! Get current render time.
E_SCENE_NODE_RENDER_PASS getCurrentRenderPass() const override { return CurrentRenderPass; } E_SCENE_NODE_RENDER_PASS getCurrentRenderPass() const override { return CurrentRenderPass; }

@ -57,7 +57,7 @@ IAnimatedMesh *CXMeshFileLoader::createMesh(io::IReadFile *file)
u32 time = os::Timer::getRealTime(); u32 time = os::Timer::getRealTime();
#endif #endif
AnimatedMesh = new CSkinnedMesh(); AnimatedMesh = new SkinnedMesh();
if (load(file)) { if (load(file)) {
AnimatedMesh->finalize(); AnimatedMesh->finalize();
@ -203,8 +203,8 @@ bool CXMeshFileLoader::load(io::IReadFile *file)
} }
for (u32 j = 0; j < mesh->WeightJoint.size(); ++j) { for (u32 j = 0; j < mesh->WeightJoint.size(); ++j) {
ISkinnedMesh::SJoint *joint = AnimatedMesh->getAllJoints()[mesh->WeightJoint[j]]; SkinnedMesh::SJoint *joint = AnimatedMesh->getAllJoints()[mesh->WeightJoint[j]];
ISkinnedMesh::SWeight &weight = joint->Weights[mesh->WeightNum[j]]; SkinnedMesh::SWeight &weight = joint->Weights[mesh->WeightNum[j]];
u32 id = weight.vertex_id; u32 id = weight.vertex_id;
@ -219,7 +219,7 @@ bool CXMeshFileLoader::load(io::IReadFile *file)
weight.buffer_id = verticesLinkBuffer[id][0]; weight.buffer_id = verticesLinkBuffer[id][0];
} else if (verticesLinkBuffer[id].size() != 0) { } else if (verticesLinkBuffer[id].size() != 0) {
for (u32 k = 1; k < verticesLinkBuffer[id].size(); ++k) { for (u32 k = 1; k < verticesLinkBuffer[id].size(); ++k) {
ISkinnedMesh::SWeight *WeightClone = AnimatedMesh->addWeight(joint); SkinnedMesh::SWeight *WeightClone = AnimatedMesh->addWeight(joint);
WeightClone->strength = weight.strength; WeightClone->strength = weight.strength;
WeightClone->vertex_id = verticesLinkIndex[id][k]; WeightClone->vertex_id = verticesLinkIndex[id][k];
WeightClone->buffer_id = verticesLinkBuffer[id][k]; WeightClone->buffer_id = verticesLinkBuffer[id][k];
@ -314,7 +314,7 @@ bool CXMeshFileLoader::load(io::IReadFile *file)
} }
for (u32 j = 0; j < mesh->WeightJoint.size(); ++j) { for (u32 j = 0; j < mesh->WeightJoint.size(); ++j) {
ISkinnedMesh::SWeight &weight = (AnimatedMesh->getAllJoints()[mesh->WeightJoint[j]]->Weights[mesh->WeightNum[j]]); SkinnedMesh::SWeight &weight = (AnimatedMesh->getAllJoints()[mesh->WeightJoint[j]]->Weights[mesh->WeightNum[j]]);
u32 id = weight.vertex_id; u32 id = weight.vertex_id;
@ -486,7 +486,7 @@ bool CXMeshFileLoader::parseDataObjectTemplate()
return true; return true;
} }
bool CXMeshFileLoader::parseDataObjectFrame(CSkinnedMesh::SJoint *Parent) bool CXMeshFileLoader::parseDataObjectFrame(SkinnedMesh::SJoint *Parent)
{ {
#ifdef _XREADER_DEBUG #ifdef _XREADER_DEBUG
os::Printer::log("CXFileReader: Reading frame", ELL_DEBUG); os::Printer::log("CXFileReader: Reading frame", ELL_DEBUG);
@ -508,7 +508,7 @@ bool CXMeshFileLoader::parseDataObjectFrame(CSkinnedMesh::SJoint *Parent)
SET_ERR_AND_RETURN(); SET_ERR_AND_RETURN();
} }
CSkinnedMesh::SJoint *joint = 0; SkinnedMesh::SJoint *joint = 0;
if (name.size()) { if (name.size()) {
auto n = AnimatedMesh->getJointNumber(name.c_str()); auto n = AnimatedMesh->getJointNumber(name.c_str());
@ -947,7 +947,7 @@ bool CXMeshFileLoader::parseDataObjectSkinWeights(SXMesh &mesh)
mesh.HasSkinning = true; mesh.HasSkinning = true;
auto n = AnimatedMesh->getJointNumber(TransformNodeName.c_str()); auto n = AnimatedMesh->getJointNumber(TransformNodeName.c_str());
CSkinnedMesh::SJoint *joint = n.has_value() ? AnimatedMesh->getAllJoints()[*n] : nullptr; SkinnedMesh::SJoint *joint = n.has_value() ? AnimatedMesh->getAllJoints()[*n] : nullptr;
if (!joint) { if (!joint) {
#ifdef _XREADER_DEBUG #ifdef _XREADER_DEBUG
@ -974,7 +974,7 @@ bool CXMeshFileLoader::parseDataObjectSkinWeights(SXMesh &mesh)
mesh.WeightJoint.push_back(*n); mesh.WeightJoint.push_back(*n);
mesh.WeightNum.push_back(joint->Weights.size()); mesh.WeightNum.push_back(joint->Weights.size());
CSkinnedMesh::SWeight *weight = AnimatedMesh->addWeight(joint); SkinnedMesh::SWeight *weight = AnimatedMesh->addWeight(joint);
weight->buffer_id = 0; weight->buffer_id = 0;
weight->vertex_id = readInt(); weight->vertex_id = readInt();
@ -1358,7 +1358,7 @@ bool CXMeshFileLoader::parseDataObjectAnimation()
// anim.closed = true; // anim.closed = true;
// anim.linearPositionQuality = true; // anim.linearPositionQuality = true;
CSkinnedMesh::SJoint animationDump; SkinnedMesh::SJoint animationDump;
core::stringc FrameName; core::stringc FrameName;
@ -1400,7 +1400,7 @@ bool CXMeshFileLoader::parseDataObjectAnimation()
#endif #endif
auto n = AnimatedMesh->getJointNumber(FrameName.c_str()); auto n = AnimatedMesh->getJointNumber(FrameName.c_str());
CSkinnedMesh::SJoint *joint; SkinnedMesh::SJoint *joint;
if (n.has_value()) { if (n.has_value()) {
joint = AnimatedMesh->getAllJoints()[*n]; joint = AnimatedMesh->getAllJoints()[*n];
} else { } else {
@ -1431,7 +1431,7 @@ bool CXMeshFileLoader::parseDataObjectAnimation()
return true; return true;
} }
bool CXMeshFileLoader::parseDataObjectAnimationKey(ISkinnedMesh::SJoint *joint) bool CXMeshFileLoader::parseDataObjectAnimationKey(SkinnedMesh::SJoint *joint)
{ {
#ifdef _XREADER_DEBUG #ifdef _XREADER_DEBUG
os::Printer::log("CXFileReader: reading animation key", ELL_DEBUG); os::Printer::log("CXFileReader: reading animation key", ELL_DEBUG);
@ -1488,7 +1488,7 @@ bool CXMeshFileLoader::parseDataObjectAnimationKey(ISkinnedMesh::SJoint *joint)
os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING);
} }
ISkinnedMesh::SRotationKey *key = AnimatedMesh->addRotationKey(joint); SkinnedMesh::SRotationKey *key = AnimatedMesh->addRotationKey(joint);
key->frame = time; key->frame = time;
key->rotation.set(X, Y, Z, W); key->rotation.set(X, Y, Z, W);
key->rotation.normalize(); key->rotation.normalize();
@ -1514,11 +1514,11 @@ bool CXMeshFileLoader::parseDataObjectAnimationKey(ISkinnedMesh::SJoint *joint)
} }
if (keyType == 2) { if (keyType == 2) {
ISkinnedMesh::SPositionKey *key = AnimatedMesh->addPositionKey(joint); SkinnedMesh::SPositionKey *key = AnimatedMesh->addPositionKey(joint);
key->frame = time; key->frame = time;
key->position = vector; key->position = vector;
} else { } else {
ISkinnedMesh::SScaleKey *key = AnimatedMesh->addScaleKey(joint); SkinnedMesh::SScaleKey *key = AnimatedMesh->addScaleKey(joint);
key->frame = time; key->frame = time;
key->scale = vector; key->scale = vector;
} }
@ -1547,14 +1547,14 @@ bool CXMeshFileLoader::parseDataObjectAnimationKey(ISkinnedMesh::SJoint *joint)
// core::vector3df rotation = mat.getRotationDegrees(); // core::vector3df rotation = mat.getRotationDegrees();
ISkinnedMesh::SRotationKey *keyR = AnimatedMesh->addRotationKey(joint); SkinnedMesh::SRotationKey *keyR = AnimatedMesh->addRotationKey(joint);
keyR->frame = time; keyR->frame = time;
// IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched from mat to mat.getTransposed() for downward compatibility. // IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched from mat to mat.getTransposed() for downward compatibility.
// Not tested so far if this was correct or wrong before quaternion fix! // Not tested so far if this was correct or wrong before quaternion fix!
keyR->rotation = core::quaternion(mat.getTransposed()); keyR->rotation = core::quaternion(mat.getTransposed());
ISkinnedMesh::SPositionKey *keyP = AnimatedMesh->addPositionKey(joint); SkinnedMesh::SPositionKey *keyP = AnimatedMesh->addPositionKey(joint);
keyP->frame = time; keyP->frame = time;
keyP->position = mat.getTranslation(); keyP->position = mat.getTranslation();
@ -1567,7 +1567,7 @@ bool CXMeshFileLoader::parseDataObjectAnimationKey(ISkinnedMesh::SJoint *joint)
scale.Y=1; scale.Y=1;
if (scale.Z==0) if (scale.Z==0)
scale.Z=1; scale.Z=1;
ISkinnedMesh::SScaleKey *keyS=AnimatedMesh->addScaleKey(joint); SkinnedMesh::SScaleKey *keyS=AnimatedMesh->addScaleKey(joint);
keyS->frame=time; keyS->frame=time;
keyS->scale=scale; keyS->scale=scale;
*/ */

@ -6,7 +6,7 @@
#include "IMeshLoader.h" #include "IMeshLoader.h"
#include "irrString.h" #include "irrString.h"
#include "CSkinnedMesh.h" #include "SkinnedMesh.h"
namespace irr namespace irr
{ {
@ -86,7 +86,7 @@ private:
bool parseDataObjectTemplate(); bool parseDataObjectTemplate();
bool parseDataObjectFrame(CSkinnedMesh::SJoint *parent); bool parseDataObjectFrame(SkinnedMesh::SJoint *parent);
bool parseDataObjectTransformationMatrix(core::matrix4 &mat); bool parseDataObjectTransformationMatrix(core::matrix4 &mat);
@ -110,7 +110,7 @@ private:
bool parseDataObjectAnimation(); bool parseDataObjectAnimation();
bool parseDataObjectAnimationKey(ISkinnedMesh::SJoint *joint); bool parseDataObjectAnimationKey(SkinnedMesh::SJoint *joint);
bool parseDataObjectTextureFilename(core::stringc &texturename); bool parseDataObjectTextureFilename(core::stringc &texturename);
@ -155,7 +155,7 @@ private:
bool readRGB(video::SColor &color); bool readRGB(video::SColor &color);
bool readRGBA(video::SColor &color); bool readRGBA(video::SColor &color);
CSkinnedMesh *AnimatedMesh; SkinnedMesh *AnimatedMesh;
c8 *Buffer; c8 *Buffer;
const c8 *P; const c8 *P;
@ -167,7 +167,7 @@ private:
bool ErrorState; bool ErrorState;
CSkinnedMesh::SJoint *CurFrame; SkinnedMesh::SJoint *CurFrame;
core::array<SXMesh *> Meshes; core::array<SXMesh *> Meshes;

@ -2,8 +2,8 @@
// This file is part of the "Irrlicht Engine". // This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h // For conditions of distribution and use, see copyright notice in irrlicht.h
#include "CSkinnedMesh.h" #include "SkinnedMesh.h"
#include <optional> #include "IBoneSceneNode.h"
#include "CBoneSceneNode.h" #include "CBoneSceneNode.h"
#include "IAnimatedMeshSceneNode.h" #include "IAnimatedMeshSceneNode.h"
#include "SSkinMeshBuffer.h" #include "SSkinMeshBuffer.h"
@ -63,17 +63,17 @@ irr::u32 dropMiddleKeys(irr::core::array<T> &array, Cmp &cmp)
return d; return d;
} }
bool identicalPos(const irr::scene::ISkinnedMesh::SPositionKey &a, const irr::scene::ISkinnedMesh::SPositionKey &b) bool identicalPos(const irr::scene::SkinnedMesh::SPositionKey &a, const irr::scene::SkinnedMesh::SPositionKey &b)
{ {
return a.position == b.position; return a.position == b.position;
} }
bool identicalScale(const irr::scene::ISkinnedMesh::SScaleKey &a, const irr::scene::ISkinnedMesh::SScaleKey &b) bool identicalScale(const irr::scene::SkinnedMesh::SScaleKey &a, const irr::scene::SkinnedMesh::SScaleKey &b)
{ {
return a.scale == b.scale; return a.scale == b.scale;
} }
bool identicalRotation(const irr::scene::ISkinnedMesh::SRotationKey &a, const irr::scene::ISkinnedMesh::SRotationKey &b) bool identicalRotation(const irr::scene::SkinnedMesh::SRotationKey &a, const irr::scene::SkinnedMesh::SRotationKey &b)
{ {
return a.rotation == b.rotation; return a.rotation == b.rotation;
} }
@ -85,7 +85,7 @@ namespace scene
{ {
//! constructor //! constructor
CSkinnedMesh::CSkinnedMesh() : SkinnedMesh::SkinnedMesh() :
SkinningBuffers(0), EndFrame(0.f), FramesPerSecond(25.f), SkinningBuffers(0), EndFrame(0.f), FramesPerSecond(25.f),
LastAnimatedFrame(-1), SkinnedLastFrame(false), LastAnimatedFrame(-1), SkinnedLastFrame(false),
InterpolationMode(EIM_LINEAR), InterpolationMode(EIM_LINEAR),
@ -93,14 +93,14 @@ CSkinnedMesh::CSkinnedMesh() :
AnimateNormals(true), HardwareSkinning(false) AnimateNormals(true), HardwareSkinning(false)
{ {
#ifdef _DEBUG #ifdef _DEBUG
setDebugName("CSkinnedMesh"); setDebugName("SkinnedMesh");
#endif #endif
SkinningBuffers = &LocalBuffers; SkinningBuffers = &LocalBuffers;
} }
//! destructor //! destructor
CSkinnedMesh::~CSkinnedMesh() SkinnedMesh::~SkinnedMesh()
{ {
for (u32 i = 0; i < AllJoints.size(); ++i) for (u32 i = 0; i < AllJoints.size(); ++i)
delete AllJoints[i]; delete AllJoints[i];
@ -111,14 +111,14 @@ CSkinnedMesh::~CSkinnedMesh()
} }
} }
f32 CSkinnedMesh::getMaxFrameNumber() const f32 SkinnedMesh::getMaxFrameNumber() const
{ {
return EndFrame; return EndFrame;
} }
//! Gets the default animation speed of the animated mesh. //! Gets the default animation speed of the animated mesh.
/** \return Amount of frames per second. If the amount is 0, it is a static, non animated mesh. */ /** \return Amount of frames per second. If the amount is 0, it is a static, non animated mesh. */
f32 CSkinnedMesh::getAnimationSpeed() const f32 SkinnedMesh::getAnimationSpeed() const
{ {
return FramesPerSecond; return FramesPerSecond;
} }
@ -126,13 +126,13 @@ f32 CSkinnedMesh::getAnimationSpeed() const
//! Gets the frame count of the animated mesh. //! Gets the frame count of the animated mesh.
/** \param fps Frames per second to play the animation with. If the amount is 0, it is not animated. /** \param fps Frames per second to play the animation with. If the amount is 0, it is not animated.
The actual speed is set in the scene node the mesh is instantiated in.*/ The actual speed is set in the scene node the mesh is instantiated in.*/
void CSkinnedMesh::setAnimationSpeed(f32 fps) void SkinnedMesh::setAnimationSpeed(f32 fps)
{ {
FramesPerSecond = fps; FramesPerSecond = fps;
} }
//! returns the animated mesh based //! returns the animated mesh based
IMesh *CSkinnedMesh::getMesh(f32 frame) IMesh *SkinnedMesh::getMesh(f32 frame)
{ {
// animate(frame,startFrameLoop, endFrameLoop); // animate(frame,startFrameLoop, endFrameLoop);
if (frame == -1) if (frame == -1)
@ -149,7 +149,7 @@ IMesh *CSkinnedMesh::getMesh(f32 frame)
//! Animates this mesh's joints based on frame input //! Animates this mesh's joints based on frame input
//! blend: {0-old position, 1-New position} //! blend: {0-old position, 1-New position}
void CSkinnedMesh::animateMesh(f32 frame, f32 blend) void SkinnedMesh::animateMesh(f32 frame, f32 blend)
{ {
if (!HasAnimation || LastAnimatedFrame == frame) if (!HasAnimation || LastAnimatedFrame == frame)
return; return;
@ -205,7 +205,7 @@ void CSkinnedMesh::animateMesh(f32 frame, f32 blend)
updateBoundingBox(); updateBoundingBox();
} }
void CSkinnedMesh::buildAllLocalAnimatedMatrices() void SkinnedMesh::buildAllLocalAnimatedMatrices()
{ {
for (u32 i = 0; i < AllJoints.size(); ++i) { for (u32 i = 0; i < AllJoints.size(); ++i) {
SJoint *joint = AllJoints[i]; SJoint *joint = AllJoints[i];
@ -270,7 +270,7 @@ void CSkinnedMesh::buildAllLocalAnimatedMatrices()
SkinnedLastFrame = false; SkinnedLastFrame = false;
} }
void CSkinnedMesh::buildAllGlobalAnimatedMatrices(SJoint *joint, SJoint *parentJoint) void SkinnedMesh::buildAllGlobalAnimatedMatrices(SJoint *joint, SJoint *parentJoint)
{ {
if (!joint) { if (!joint) {
for (u32 i = 0; i < RootJoints.size(); ++i) for (u32 i = 0; i < RootJoints.size(); ++i)
@ -288,7 +288,7 @@ void CSkinnedMesh::buildAllGlobalAnimatedMatrices(SJoint *joint, SJoint *parentJ
buildAllGlobalAnimatedMatrices(joint->Children[j], joint); buildAllGlobalAnimatedMatrices(joint->Children[j], joint);
} }
void CSkinnedMesh::getFrameData(f32 frame, SJoint *joint, void SkinnedMesh::getFrameData(f32 frame, SJoint *joint,
core::vector3df &position, s32 &positionHint, core::vector3df &position, s32 &positionHint,
core::vector3df &scale, s32 &scaleHint, core::vector3df &scale, s32 &scaleHint,
core::quaternion &rotation, s32 &rotationHint) core::quaternion &rotation, s32 &rotationHint)
@ -453,7 +453,7 @@ void CSkinnedMesh::getFrameData(f32 frame, SJoint *joint,
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
//! Preforms a software skin on this mesh based of joint positions //! Preforms a software skin on this mesh based of joint positions
void CSkinnedMesh::skinMesh() void SkinnedMesh::skinMesh()
{ {
if (!HasAnimation || SkinnedLastFrame) if (!HasAnimation || SkinnedLastFrame)
return; return;
@ -491,7 +491,7 @@ void CSkinnedMesh::skinMesh()
updateBoundingBox(); updateBoundingBox();
} }
void CSkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint) void SkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint)
{ {
if (joint->Weights.size()) { if (joint->Weights.size()) {
// Find this joints pull on vertices... // Find this joints pull on vertices...
@ -541,19 +541,19 @@ void CSkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint)
skinJoint(joint->Children[j], joint); skinJoint(joint->Children[j], joint);
} }
E_ANIMATED_MESH_TYPE CSkinnedMesh::getMeshType() const E_ANIMATED_MESH_TYPE SkinnedMesh::getMeshType() const
{ {
return EAMT_SKINNED; return EAMT_SKINNED;
} }
//! Gets joint count. //! Gets joint count.
u32 CSkinnedMesh::getJointCount() const u32 SkinnedMesh::getJointCount() const
{ {
return AllJoints.size(); return AllJoints.size();
} }
//! Gets the name of a joint. //! Gets the name of a joint.
const std::optional<std::string> &CSkinnedMesh::getJointName(u32 number) const const std::optional<std::string> &SkinnedMesh::getJointName(u32 number) const
{ {
if (number >= getJointCount()) { if (number >= getJointCount()) {
static const std::optional<std::string> nullopt; static const std::optional<std::string> nullopt;
@ -563,7 +563,7 @@ const std::optional<std::string> &CSkinnedMesh::getJointName(u32 number) const
} }
//! Gets a joint number from its name //! Gets a joint number from its name
std::optional<u32> CSkinnedMesh::getJointNumber(const std::string &name) const std::optional<u32> SkinnedMesh::getJointNumber(const std::string &name) const
{ {
for (u32 i = 0; i < AllJoints.size(); ++i) { for (u32 i = 0; i < AllJoints.size(); ++i) {
if (AllJoints[i]->Name == name) if (AllJoints[i]->Name == name)
@ -574,13 +574,13 @@ std::optional<u32> CSkinnedMesh::getJointNumber(const std::string &name) const
} }
//! returns amount of mesh buffers. //! returns amount of mesh buffers.
u32 CSkinnedMesh::getMeshBufferCount() const u32 SkinnedMesh::getMeshBufferCount() const
{ {
return LocalBuffers.size(); return LocalBuffers.size();
} }
//! returns pointer to a mesh buffer //! returns pointer to a mesh buffer
IMeshBuffer *CSkinnedMesh::getMeshBuffer(u32 nr) const IMeshBuffer *SkinnedMesh::getMeshBuffer(u32 nr) const
{ {
if (nr < LocalBuffers.size()) if (nr < LocalBuffers.size())
return LocalBuffers[nr]; return LocalBuffers[nr];
@ -589,7 +589,7 @@ IMeshBuffer *CSkinnedMesh::getMeshBuffer(u32 nr) const
} }
//! Returns pointer to a mesh buffer which fits a material //! Returns pointer to a mesh buffer which fits a material
IMeshBuffer *CSkinnedMesh::getMeshBuffer(const video::SMaterial &material) const IMeshBuffer *SkinnedMesh::getMeshBuffer(const video::SMaterial &material) const
{ {
for (u32 i = 0; i < LocalBuffers.size(); ++i) { for (u32 i = 0; i < LocalBuffers.size(); ++i) {
if (LocalBuffers[i]->getMaterial() == material) if (LocalBuffers[i]->getMaterial() == material)
@ -598,29 +598,29 @@ IMeshBuffer *CSkinnedMesh::getMeshBuffer(const video::SMaterial &material) const
return 0; return 0;
} }
u32 CSkinnedMesh::getTextureSlot(u32 meshbufNr) const u32 SkinnedMesh::getTextureSlot(u32 meshbufNr) const
{ {
return TextureSlots.at(meshbufNr); return TextureSlots.at(meshbufNr);
} }
void CSkinnedMesh::setTextureSlot(u32 meshbufNr, u32 textureSlot) { void SkinnedMesh::setTextureSlot(u32 meshbufNr, u32 textureSlot) {
TextureSlots.at(meshbufNr) = textureSlot; TextureSlots.at(meshbufNr) = textureSlot;
} }
//! returns an axis aligned bounding box //! returns an axis aligned bounding box
const core::aabbox3d<f32> &CSkinnedMesh::getBoundingBox() const const core::aabbox3d<f32> &SkinnedMesh::getBoundingBox() const
{ {
return BoundingBox; return BoundingBox;
} }
//! set user axis aligned bounding box //! set user axis aligned bounding box
void CSkinnedMesh::setBoundingBox(const core::aabbox3df &box) void SkinnedMesh::setBoundingBox(const core::aabbox3df &box)
{ {
BoundingBox = box; BoundingBox = box;
} }
//! set the hardware mapping hint, for driver //! set the hardware mapping hint, for driver
void CSkinnedMesh::setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint, void SkinnedMesh::setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint,
E_BUFFER_TYPE buffer) E_BUFFER_TYPE buffer)
{ {
for (u32 i = 0; i < LocalBuffers.size(); ++i) for (u32 i = 0; i < LocalBuffers.size(); ++i)
@ -628,71 +628,43 @@ void CSkinnedMesh::setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint,
} }
//! flags the meshbuffer as changed, reloads hardware buffers //! flags the meshbuffer as changed, reloads hardware buffers
void CSkinnedMesh::setDirty(E_BUFFER_TYPE buffer) void SkinnedMesh::setDirty(E_BUFFER_TYPE buffer)
{ {
for (u32 i = 0; i < LocalBuffers.size(); ++i) for (u32 i = 0; i < LocalBuffers.size(); ++i)
LocalBuffers[i]->setDirty(buffer); LocalBuffers[i]->setDirty(buffer);
} }
//! uses animation from another mesh
bool CSkinnedMesh::useAnimationFrom(const ISkinnedMesh *mesh)
{
bool unmatched = false;
for (u32 i = 0; i < AllJoints.size(); ++i) {
SJoint *joint = AllJoints[i];
joint->UseAnimationFrom = 0;
if (joint->Name == "")
unmatched = true;
else {
for (u32 j = 0; j < mesh->getAllJoints().size(); ++j) {
SJoint *otherJoint = mesh->getAllJoints()[j];
if (joint->Name == otherJoint->Name) {
joint->UseAnimationFrom = otherJoint;
}
}
if (!joint->UseAnimationFrom)
unmatched = true;
}
}
checkForAnimation();
return !unmatched;
}
//! Update Normals when Animating //! Update Normals when Animating
//! False= Don't animate them, faster //! False= Don't animate them, faster
//! True= Update normals (default) //! True= Update normals (default)
void CSkinnedMesh::updateNormalsWhenAnimating(bool on) void SkinnedMesh::updateNormalsWhenAnimating(bool on)
{ {
AnimateNormals = on; AnimateNormals = on;
} }
//! Sets Interpolation Mode //! Sets Interpolation Mode
void CSkinnedMesh::setInterpolationMode(E_INTERPOLATION_MODE mode) void SkinnedMesh::setInterpolationMode(E_INTERPOLATION_MODE mode)
{ {
InterpolationMode = mode; InterpolationMode = mode;
} }
core::array<scene::SSkinMeshBuffer *> &CSkinnedMesh::getMeshBuffers() core::array<scene::SSkinMeshBuffer *> &SkinnedMesh::getMeshBuffers()
{ {
return LocalBuffers; return LocalBuffers;
} }
core::array<CSkinnedMesh::SJoint *> &CSkinnedMesh::getAllJoints() core::array<SkinnedMesh::SJoint *> &SkinnedMesh::getAllJoints()
{ {
return AllJoints; return AllJoints;
} }
const core::array<CSkinnedMesh::SJoint *> &CSkinnedMesh::getAllJoints() const const core::array<SkinnedMesh::SJoint *> &SkinnedMesh::getAllJoints() const
{ {
return AllJoints; return AllJoints;
} }
//! (This feature is not implemented in irrlicht yet) //! (This feature is not implemented in irrlicht yet)
bool CSkinnedMesh::setHardwareSkinning(bool on) bool SkinnedMesh::setHardwareSkinning(bool on)
{ {
if (HardwareSkinning != on) { if (HardwareSkinning != on) {
if (on) { if (on) {
@ -715,7 +687,7 @@ bool CSkinnedMesh::setHardwareSkinning(bool on)
return HardwareSkinning; return HardwareSkinning;
} }
void CSkinnedMesh::refreshJointCache() void SkinnedMesh::refreshJointCache()
{ {
// copy cache from the mesh... // copy cache from the mesh...
for (u32 i = 0; i < AllJoints.size(); ++i) { for (u32 i = 0; i < AllJoints.size(); ++i) {
@ -729,7 +701,7 @@ void CSkinnedMesh::refreshJointCache()
} }
} }
void CSkinnedMesh::resetAnimation() void SkinnedMesh::resetAnimation()
{ {
// copy from the cache to the mesh... // copy from the cache to the mesh...
for (u32 i = 0; i < AllJoints.size(); ++i) { for (u32 i = 0; i < AllJoints.size(); ++i) {
@ -745,7 +717,7 @@ void CSkinnedMesh::resetAnimation()
LastAnimatedFrame = -1; LastAnimatedFrame = -1;
} }
void CSkinnedMesh::calculateGlobalMatrices(SJoint *joint, SJoint *parentJoint) void SkinnedMesh::calculateGlobalMatrices(SJoint *joint, SJoint *parentJoint)
{ {
if (!joint && parentJoint) // bit of protection from endless loops if (!joint && parentJoint) // bit of protection from endless loops
return; return;
@ -775,7 +747,7 @@ void CSkinnedMesh::calculateGlobalMatrices(SJoint *joint, SJoint *parentJoint)
SkinnedLastFrame = false; SkinnedLastFrame = false;
} }
void CSkinnedMesh::checkForAnimation() void SkinnedMesh::checkForAnimation()
{ {
u32 i, j; u32 i, j;
// Check for animation... // Check for animation...
@ -868,7 +840,7 @@ void CSkinnedMesh::checkForAnimation()
} }
//! called by loader after populating with mesh and bone data //! called by loader after populating with mesh and bone data
void CSkinnedMesh::finalize() void SkinnedMesh::finalize()
{ {
os::Printer::log("Skinned Mesh - finalize", ELL_DEBUG); os::Printer::log("Skinned Mesh - finalize", ELL_DEBUG);
u32 i; u32 i;
@ -1046,7 +1018,7 @@ void CSkinnedMesh::finalize()
} }
} }
void CSkinnedMesh::updateBoundingBox(void) void SkinnedMesh::updateBoundingBox(void)
{ {
if (!SkinningBuffers) if (!SkinningBuffers)
return; return;
@ -1065,7 +1037,7 @@ void CSkinnedMesh::updateBoundingBox(void)
} }
} }
scene::SSkinMeshBuffer *CSkinnedMesh::addMeshBuffer() scene::SSkinMeshBuffer *SkinnedMesh::addMeshBuffer()
{ {
scene::SSkinMeshBuffer *buffer = new scene::SSkinMeshBuffer(); scene::SSkinMeshBuffer *buffer = new scene::SSkinMeshBuffer();
TextureSlots.push_back(LocalBuffers.size()); TextureSlots.push_back(LocalBuffers.size());
@ -1073,13 +1045,13 @@ scene::SSkinMeshBuffer *CSkinnedMesh::addMeshBuffer()
return buffer; return buffer;
} }
void CSkinnedMesh::addMeshBuffer(SSkinMeshBuffer *meshbuf) void SkinnedMesh::addMeshBuffer(SSkinMeshBuffer *meshbuf)
{ {
TextureSlots.push_back(LocalBuffers.size()); TextureSlots.push_back(LocalBuffers.size());
LocalBuffers.push_back(meshbuf); LocalBuffers.push_back(meshbuf);
} }
CSkinnedMesh::SJoint *CSkinnedMesh::addJoint(SJoint *parent) SkinnedMesh::SJoint *SkinnedMesh::addJoint(SJoint *parent)
{ {
SJoint *joint = new SJoint; SJoint *joint = new SJoint;
@ -1094,7 +1066,7 @@ CSkinnedMesh::SJoint *CSkinnedMesh::addJoint(SJoint *parent)
return joint; return joint;
} }
CSkinnedMesh::SPositionKey *CSkinnedMesh::addPositionKey(SJoint *joint) SkinnedMesh::SPositionKey *SkinnedMesh::addPositionKey(SJoint *joint)
{ {
if (!joint) if (!joint)
return 0; return 0;
@ -1103,7 +1075,7 @@ CSkinnedMesh::SPositionKey *CSkinnedMesh::addPositionKey(SJoint *joint)
return &joint->PositionKeys.getLast(); return &joint->PositionKeys.getLast();
} }
CSkinnedMesh::SScaleKey *CSkinnedMesh::addScaleKey(SJoint *joint) SkinnedMesh::SScaleKey *SkinnedMesh::addScaleKey(SJoint *joint)
{ {
if (!joint) if (!joint)
return 0; return 0;
@ -1112,7 +1084,7 @@ CSkinnedMesh::SScaleKey *CSkinnedMesh::addScaleKey(SJoint *joint)
return &joint->ScaleKeys.getLast(); return &joint->ScaleKeys.getLast();
} }
CSkinnedMesh::SRotationKey *CSkinnedMesh::addRotationKey(SJoint *joint) SkinnedMesh::SRotationKey *SkinnedMesh::addRotationKey(SJoint *joint)
{ {
if (!joint) if (!joint)
return 0; return 0;
@ -1121,7 +1093,7 @@ CSkinnedMesh::SRotationKey *CSkinnedMesh::addRotationKey(SJoint *joint)
return &joint->RotationKeys.getLast(); return &joint->RotationKeys.getLast();
} }
CSkinnedMesh::SWeight *CSkinnedMesh::addWeight(SJoint *joint) SkinnedMesh::SWeight *SkinnedMesh::addWeight(SJoint *joint)
{ {
if (!joint) if (!joint)
return 0; return 0;
@ -1130,12 +1102,12 @@ CSkinnedMesh::SWeight *CSkinnedMesh::addWeight(SJoint *joint)
return &joint->Weights.getLast(); return &joint->Weights.getLast();
} }
bool CSkinnedMesh::isStatic() bool SkinnedMesh::isStatic() const
{ {
return !HasAnimation; return !HasAnimation;
} }
void CSkinnedMesh::normalizeWeights() void SkinnedMesh::normalizeWeights()
{ {
// note: unsure if weights ids are going to be used. // note: unsure if weights ids are going to be used.
@ -1176,7 +1148,7 @@ void CSkinnedMesh::normalizeWeights()
} }
} }
void CSkinnedMesh::recoverJointsFromMesh(core::array<IBoneSceneNode *> &jointChildSceneNodes) void SkinnedMesh::recoverJointsFromMesh(core::array<IBoneSceneNode *> &jointChildSceneNodes)
{ {
for (u32 i = 0; i < AllJoints.size(); ++i) { for (u32 i = 0; i < AllJoints.size(); ++i) {
IBoneSceneNode *node = jointChildSceneNodes[i]; IBoneSceneNode *node = jointChildSceneNodes[i];
@ -1193,7 +1165,7 @@ void CSkinnedMesh::recoverJointsFromMesh(core::array<IBoneSceneNode *> &jointChi
} }
} }
void CSkinnedMesh::transferJointsToMesh(const core::array<IBoneSceneNode *> &jointChildSceneNodes) void SkinnedMesh::transferJointsToMesh(const core::array<IBoneSceneNode *> &jointChildSceneNodes)
{ {
for (u32 i = 0; i < AllJoints.size(); ++i) { for (u32 i = 0; i < AllJoints.size(); ++i) {
const IBoneSceneNode *const node = jointChildSceneNodes[i]; const IBoneSceneNode *const node = jointChildSceneNodes[i];
@ -1214,7 +1186,7 @@ void CSkinnedMesh::transferJointsToMesh(const core::array<IBoneSceneNode *> &joi
SkinnedLastFrame = false; SkinnedLastFrame = false;
} }
void CSkinnedMesh::transferOnlyJointsHintsToMesh(const core::array<IBoneSceneNode *> &jointChildSceneNodes) void SkinnedMesh::transferOnlyJointsHintsToMesh(const core::array<IBoneSceneNode *> &jointChildSceneNodes)
{ {
for (u32 i = 0; i < AllJoints.size(); ++i) { for (u32 i = 0; i < AllJoints.size(); ++i) {
const IBoneSceneNode *const node = jointChildSceneNodes[i]; const IBoneSceneNode *const node = jointChildSceneNodes[i];
@ -1227,7 +1199,7 @@ void CSkinnedMesh::transferOnlyJointsHintsToMesh(const core::array<IBoneSceneNod
SkinnedLastFrame = false; SkinnedLastFrame = false;
} }
void CSkinnedMesh::addJoints(core::array<IBoneSceneNode *> &jointChildSceneNodes, void SkinnedMesh::addJoints(core::array<IBoneSceneNode *> &jointChildSceneNodes,
IAnimatedMeshSceneNode *node, ISceneManager *smgr) IAnimatedMeshSceneNode *node, ISceneManager *smgr)
{ {
// Create new joints // Create new joints
@ -1264,7 +1236,7 @@ void CSkinnedMesh::addJoints(core::array<IBoneSceneNode *> &jointChildSceneNodes
SkinnedLastFrame = false; SkinnedLastFrame = false;
} }
void CSkinnedMesh::convertMeshToTangents() void SkinnedMesh::convertMeshToTangents()
{ {
// now calculate tangents // now calculate tangents
for (u32 b = 0; b < LocalBuffers.size(); ++b) { for (u32 b = 0; b < LocalBuffers.size(); ++b) {
@ -1315,7 +1287,7 @@ void CSkinnedMesh::convertMeshToTangents()
} }
} }
void CSkinnedMesh::calculateTangents( void SkinnedMesh::calculateTangents(
core::vector3df &normal, core::vector3df &normal,
core::vector3df &tangent, core::vector3df &tangent,
core::vector3df &binormal, core::vector3df &binormal,

@ -12,7 +12,7 @@
#include "IFileSystem.h" #include "IFileSystem.h"
#include "IReadFile.h" #include "IReadFile.h"
#include "ISceneManager.h" #include "ISceneManager.h"
#include "ISkinnedMesh.h" #include "SkinnedMesh.h"
#include "irrlicht.h" #include "irrlicht.h"
#include "catch.h" #include "catch.h"
@ -379,14 +379,14 @@ SECTION("simple sparse accessor")
// https://github.com/KhronosGroup/glTF-Sample-Models/tree/main/2.0/SimpleSkin // https://github.com/KhronosGroup/glTF-Sample-Models/tree/main/2.0/SimpleSkin
SECTION("simple skin") SECTION("simple skin")
{ {
using ISkinnedMesh = irr::scene::ISkinnedMesh; using SkinnedMesh = irr::scene::SkinnedMesh;
const auto mesh = loadMesh(model_stem + "simple_skin.gltf"); const auto mesh = loadMesh(model_stem + "simple_skin.gltf");
REQUIRE(mesh != nullptr); REQUIRE(mesh != nullptr);
auto csm = dynamic_cast<const ISkinnedMesh*>(mesh); auto csm = dynamic_cast<const SkinnedMesh*>(mesh);
const auto joints = csm->getAllJoints(); const auto joints = csm->getAllJoints();
REQUIRE(joints.size() == 3); REQUIRE(joints.size() == 3);
const auto findJoint = [&](const std::function<bool(ISkinnedMesh::SJoint*)> &predicate) { const auto findJoint = [&](const std::function<bool(SkinnedMesh::SJoint*)> &predicate) {
for (std::size_t i = 0; i < joints.size(); ++i) { for (std::size_t i = 0; i < joints.size(); ++i) {
if (predicate(joints[i])) { if (predicate(joints[i])) {
return joints[i]; return joints[i];
@ -420,7 +420,7 @@ SECTION("simple skin")
SECTION("weights are correct") SECTION("weights are correct")
{ {
const auto weights = [&](const ISkinnedMesh::SJoint *joint) { const auto weights = [&](const SkinnedMesh::SJoint *joint) {
std::unordered_map<irr::u32, irr::f32> weights; std::unordered_map<irr::u32, irr::f32> weights;
for (std::size_t i = 0; i < joint->Weights.size(); ++i) { for (std::size_t i = 0; i < joint->Weights.size(); ++i) {
const auto weight = joint->Weights[i]; const auto weight = joint->Weights[i];