Coverage Report

Created: 2025-08-25 06:48

/src/ogre/OgreMain/include/OgreInstancedEntity.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
-----------------------------------------------------------------------------
3
This source file is part of OGRE
4
    (Object-oriented Graphics Rendering Engine)
5
For the latest info, see http://www.ogre3d.org/
6
7
Copyright (c) 2000-2014 Torus Knot Software Ltd
8
9
Permission is hereby granted, free of charge, to any person obtaining a copy
10
of this software and associated documentation files (the "Software"), to deal
11
in the Software without restriction, including without limitation the rights
12
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
copies of the Software, and to permit persons to whom the Software is
14
furnished to do so, subject to the following conditions:
15
16
The above copyright notice and this permission notice shall be included in
17
all copies or substantial portions of the Software.
18
19
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
THE SOFTWARE.
26
-----------------------------------------------------------------------------
27
*/
28
#ifndef __InstancedEntity_H__
29
#define __InstancedEntity_H__
30
31
#include "OgreMovableObject.h"
32
#include "OgreNode.h"
33
#include "OgreHeaderPrefix.h"
34
35
namespace Ogre
36
{
37
    class NameGenerator;
38
39
    /** \addtogroup Core
40
    *  @{
41
    */
42
    /** \addtogroup Scene
43
    *  @{
44
    */
45
46
    /** @see InstanceBatch to understand how instancing works.
47
48
        Instanced entities work in a very similar way as how an Entity works, as this interface
49
        tries to mimic it as most as possible to make the transition between Entity and InstancedEntity
50
        as straightforward and simple as possible.
51
        There are a couple inherent limitations, for example setRenderQueueGroup only works on
52
        the InstanceBatch level, not the individual instance. This limits Z sorting for alpha blending
53
        quite significantly
54
        An InstancedEntity won't appear in scene until a SceneNode is attached to them. Once the
55
        InstancedEntity is no longer needed, call InstanceBatch::removeInstancedEntity to put them
56
        back into a queue so the batch can return it back again when a new instance is requested.
57
        @par
58
        Internally, instanced entities that are not visible (i.e. by setting setVisible(false) or
59
        when they don't have a SceneNode attached to them) a Zero world matrix is sent to the vertex shader
60
        which in turn causes a zero area triangle.
61
        This obviously means no Vertex shader benefit, but saves a bit of pixel shader processing power.
62
        Also this means this object won't be considered when sizing the InstanceBatch's bounding box.
63
        @par
64
        Each instance has an instance ID unique within the batch, which matches the ID sent to the vertex
65
        shader by some techniques (like InstanceBatchShader).
66
        @par
67
        Differences between Entity and InstancedEntity:
68
        * Setting RenderQueueGroup and other Renderable stuff works at InstanceBatch level, not
69
        InstancedEntity. This is obviously a limitation from instancing in general, not this particular
70
        implementation
71
72
        @author
73
            Matias N. Goldberg ("dark_sylinc")
74
     */
75
    class _OgreExport InstancedEntity : public MovableObject
76
    {
77
        friend class InstanceBatch;
78
        friend class InstanceBatchShader;
79
        friend class InstanceBatchHW;
80
        friend class InstanceBatchHW_VTF;
81
        friend class BaseInstanceBatchVTF;
82
    private:
83
        typedef TransformBase<3, float>        Matrix3x4f;
84
        uint16 mInstanceId; //Note it may change after defragmenting!
85
        bool mInUse;
86
        InstanceBatch *mBatchOwner;
87
88
        AnimationStateSet *mAnimationState;
89
        SkeletonInstance *mSkeletonInstance;
90
        Affine3 *mBoneMatrices;  //Local space
91
        Affine3 *mBoneWorldMatrices; //World space
92
        unsigned long mFrameAnimationLastUpdated;
93
94
        InstancedEntity* mSharedTransformEntity;    //When not null, another InstancedEntity controls the skeleton
95
                                                
96
        /** Used in conjunction with bone matrix lookup. Tells the number of the transform for
97
            as arranged in the vertex texture */
98
        uint16 mTransformLookupNumber;
99
100
        /// Stores the master when we're the slave, store our slaves when we're the master
101
        typedef std::vector<InstancedEntity*> InstancedEntityVec;
102
        InstancedEntityVec mSharingPartners;
103
104
        //////////////////////////////////////////////////////////////////////////
105
        // Parameters used for local transformation offset information
106
        // The 
107
        //////////////////////////////////////////////////////////////////////////
108
109
        /// Object position
110
        Vector3 mPosition;
111
        Vector3 mDerivedLocalPosition;
112
        /// Object orientation
113
        Quaternion mOrientation;
114
        /// Object scale
115
        Vector3 mScale;
116
        /// The maximum absolute scale for all dimension
117
        Real mMaxScaleLocal;
118
        /// Full world transform
119
        Affine3 mFullLocalTransform;
120
        /// Tells if mFullTransform needs an updated
121
        bool mNeedTransformUpdate;
122
        /// Tells if the animation world transform needs an update
123
        bool mNeedAnimTransformUpdate;
124
        /// Tells whether to use the local transform parameters
125
        bool mUseLocalTransform;
126
127
128
        /// Returns number of matrices written to transform, assumes transform has enough space
129
        size_t getTransforms( Matrix4 *xform ) const;
130
        /// Returns number of 32-bit values written
131
        size_t getTransforms3x4( Matrix3x4f *xform ) const;
132
133
        /// Returns true if this InstancedObject is visible to the current camera
134
        bool findVisible( Camera *camera ) const;
135
136
        /// Creates/destroys our own skeleton, also tells slaves to unlink if we're destroying
137
        void createSkeletonInstance();
138
        void destroySkeletonInstance();
139
140
        /// When this entity is a slave, stopSharingTransform delegates to this function.
141
        /// nofityMaster = false is used to prevent iterator invalidation in specific cases.
142
        void stopSharingTransformAsSlave( bool notifyMaster );
143
144
        /// Just unlinks, and tells our master we're no longer sharing
145
        void unlinkTransform( bool notifyMaster=true );
146
147
        /// Called when a slave has unlinked from us
148
        void notifyUnlink( const InstancedEntity *slave );
149
150
        /// Mark the transformation matrixes as dirty
151
        inline void markTransformDirty();
152
153
        /// Incremented count for next name extension
154
        static NameGenerator msNameGenerator;
155
156
    public:
157
        InstancedEntity( InstanceBatch *batchOwner, uint32 instanceID, InstancedEntity* sharedTransformEntity = NULL);
158
        virtual ~InstancedEntity();
159
160
        /** Shares the entire transformation with another InstancedEntity. This is useful when a mesh
161
            has more than one submeshes, therefore creating multiple InstanceManagers (one for each
162
            submesh). With this function, sharing makes the skeleton to be shared (less memory) and
163
            updated once (performance optimization).
164
            Note that one InstancedEntity (i.e. submesh 0) must be chosen as "master" which will share
165
            with the other instanced entities (i.e. submeshes 1-N) which are called "slaves"
166
            @par
167
            Requirements to share trasnformations:
168
                * Both InstancedEntities must have use the same skeleton
169
                * An InstancedEntity can't be both "master" and "slave" at the same time
170
171
            Sharing does nothing if the original mesh doesn't have a skeleton
172
            When an InstancedEntity is removed (@see InstanceBatch::removeInstancedEntity), it stops
173
            sharing the transform. If the instanced entity was the master one, all it's slaves stop
174
            sharing and start having their own transform too.
175
            @param slave The InstancedEntity that should share with us and become our slave
176
            @return true if successfully shared (may fail if they aren't skeletally animated)
177
        */
178
        bool shareTransformWith( InstancedEntity *slave );
179
180
        /** @see shareTransformWith
181
            Stops sharing the transform if this is a slave, and notifies the master we're no longer
182
            a slave.
183
            If this is a master, tells all it's slave to stop sharing
184
185
            This function is automatically called in InstanceBatch::removeInstancedEntity
186
        */
187
        void stopSharingTransform();
188
189
0
        InstanceBatch* _getOwner() const { return mBatchOwner; }
190
191
        const String& getMovableType(void) const override;
192
193
        const AxisAlignedBox& getBoundingBox(void) const override;
194
        Real getBoundingRadius(void) const override;
195
196
        /** This is used by our batch owner to get the closest entity's depth, returns infinity
197
            when not attached to a scene node */
198
        Real getSquaredViewDepth( const Camera* cam ) const;
199
200
        /// Overridden so we can tell the InstanceBatch it needs to update it's bounds
201
        void _notifyMoved(void) override;
202
        void _notifyAttached( Node* parent, bool isTagPoint = false ) override;
203
204
        /// Do nothing, InstanceBatch takes care of this.
205
0
        void _updateRenderQueue( RenderQueue* queue ) override   {}
206
0
        void visitRenderables( Renderable::Visitor* visitor, bool debugRenderables = false ) override {}
207
208
        /** @see Entity::hasSkeleton */
209
0
        bool hasSkeleton(void) const { return mSkeletonInstance != 0; }
210
        /** @see Entity::getSkeleton */
211
0
        SkeletonInstance* getSkeleton(void) const { return mSkeletonInstance; }
212
213
        /** @see Entity::getAnimationState */
214
        AnimationState* getAnimationState(const String& name) const;
215
        /** @see Entity::getAllAnimationStates */
216
        AnimationStateSet* getAllAnimationStates(void) const;
217
218
        /** Called by InstanceBatch in <i>his</i> _updateRenderQueue to tell us we need
219
            to calculate our bone matrices.
220
            @remarks Assumes it has a skeleton (mSkeletonInstance != 0)
221
            @return true if something was actually updated
222
        */
223
        virtual bool _updateAnimation(void);
224
225
        /** Sets the transformation look up number */
226
0
        void setTransformLookupNumber(uint16 num) { mTransformLookupNumber = num;}
227
228
        /** Retrieve the position */
229
0
        const Vector3& getPosition() const { return mPosition; }
230
        /** Set the position or the offset from the parent node if a parent node exists */ 
231
        void setPosition(const Vector3& position, bool doUpdate = true);
232
233
        /** Retrieve the orientation */
234
0
        const Quaternion& getOrientation() const { return mOrientation; }
235
        /** Set the orientation or the offset from the parent node if a parent node exists */
236
        void setOrientation(const Quaternion& orientation, bool doUpdate = true);
237
238
        /** Retrieve the local scale */ 
239
0
        const Vector3& getScale() const { return mScale; }
240
        /** Set the  scale or the offset from the parent node if a parent node exists  */ 
241
        void setScale(const Vector3& scale, bool doUpdate = true);
242
243
        /** Returns the maximum derived scale coefficient among the xyz values */
244
        Real getMaxScaleCoef() const;
245
246
        /** Update the world transform and derived values */
247
        void updateTransforms();
248
249
        /** Tells if the entity is in use. */
250
0
        bool isInUse() const { return mInUse; }
251
        /** Sets whether the entity is in use. */
252
        void setInUse(bool used);
253
254
        /** Returns the world transform of the instanced entity including local transform */
255
0
        const Affine3& _getParentNodeFullTransform(void) const override {
256
0
            assert((!mNeedTransformUpdate || !mUseLocalTransform) && "Transform data should be updated at this point");
257
0
            return mUseLocalTransform ? mFullLocalTransform :
258
0
                mParentNode ? mParentNode->_getFullTransform() : Affine3::IDENTITY;
259
0
        }
260
261
        /** Returns the derived position of the instanced entity including local transform */
262
0
        const Vector3& _getDerivedPosition() const {
263
0
            assert((!mNeedTransformUpdate || !mUseLocalTransform) && "Transform data should be updated at this point");
264
0
            return mUseLocalTransform ? mDerivedLocalPosition :
265
0
                mParentNode ? mParentNode->_getDerivedPosition() : Vector3::ZERO;
266
0
        }
267
268
        /** @copydoc MovableObject::isInScene */
269
        bool isInScene(void) const override
270
0
        {
271
            //We assume that the instanced entity is in the scene if it is in use
272
            //It is in the scene whether it has a parent node or not
273
0
            return mInUse;
274
0
        }
275
276
        /** Sets the custom parameter for this instance @see InstanceManager::setNumCustomParams
277
            Because not all techniques support custom params, and some users may not need it while
278
            using millions of InstancedEntities, the params have been detached from InstancedEntity
279
            and stored in it's InstanceBatch instead, to reduce memory overhead.
280
281
            If this function is never called, all instances default to Vector4::ZERO. Watch out!
282
            If you destroy an instanced entity and then create it again (remember! Instanced entities
283
            are pre-allocated) it's custom param will contain the old value when it was destroyed.
284
        @param idx of the param. In the range [0; InstanceManager::getNumCustomParams())
285
        @param newParam New parameter
286
        */
287
        void setCustomParam( unsigned char idx, const Vector4f &newParam );
288
        const Vector4f& getCustomParam( unsigned char idx );
289
    };
290
}
291
292
#include "OgreHeaderSuffix.h"
293
294
#endif