• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
Saad Manzur

How often update a scene ?

13 posts in this topic

How often do I have to call the update function to render an animated scene ? If I call the update() function every millisecond the program get slow (obviously because of the recursive matrix calculation of each bone from the root all over again)... I call the update function right before it is rendered to the scene.. Any ideas ?  

0

Share this post


Link to post
Share on other sites

If I even try to update the scene in bind pose where no animation occurs fps counter shows 1 . I have calculated the dt, before because I rendered static animation. I have to pass the animation time adding dt to it every time right ?

 

Edit: fps is too low . is it a possibility that the animation is just too complex ? I play batman and other games smoothly but this program fps 1 ?? Seriously ?

 

Edit2 : from what I am guessing now do I have to use dynamic programming to precalculate the values ?

Edited by Saad Manzur
0

Share this post


Link to post
Share on other sites

Hi,

 

Scenes should typically be updated as many times as possible, from the information you provided the problem you're having is not due to the frequency of updates but rather because of something else going on in the update function (or in fact any function being called every frame). Could be a very expensive function being called many times during each update or a recursion/iteration large enough to slow down the entire application.

1

Share this post


Link to post
Share on other sites

Hi,

 

Scenes should typically be updated as many times as possible, from the information you provided the problem you're having is not due to the frequency of updates but rather because of something else going on in the update function (or in fact any function being called every frame). Could be a very expensive function being called many times during each update or a recursion/iteration large enough to slow down the entire application.

 

Exactly multiplying every matrix from the scene node each time . Really expensive.... 

0

Share this post


Link to post
Share on other sites

Post the code where you measure the time between updates. Unless your update code is completely FUBAR, the matrix calculations even for the most complex node hierarchy don't take a second. It's much more likely you're measuring the time incorrectly, or something else is hanging up.

 

If you really want to find the problem, you need to profile your code - maybe as simple as sprinkling some debug output lines before a few calls in your update routine to report the time. Run a few frames and see where the time is being taken up.

Edited by Buckeye
1

Share this post


Link to post
Share on other sites

You should probably update about once a vblank, if possible, though you can get away with much less, and there isn't much point in updating any faster than that, as the user won't see it.  (Or they might if they have vsync disabled, but it will probably be garbled gunk at that point)

1

Share this post


Link to post
Share on other sites

Scenes should typically be updated as many times as possible

Scene should be updated as infrequently as possible. Common rates are 30-40 times per second.


Exactly multiplying every matrix from the scene node each time . Really expensive....

Then why are you doing it?
Why don’t you have dirty flags that allow you to update only what has changed (and things affected by those changes such as children nodes)?


I have to pass the animation time adding dt to it every time right ?

If by that you mean you have pass a delta time between frames so your animation can accumulate time and determine where it is in its timeline, yes, you would have to do this anyway.


L. Spiro
2

Share this post


Link to post
Share on other sites


Why don’t you have dirty flags that allow you to update only what has changed (and things affected by those changes such as children nodes)?

 

I am trying to do that currently. Trying to figure out how to tell before time if it will be changed or not...


Post the code where you measure the time between updates.

void GameTimer::Tick()
{
	if( mStopped )
	{
		mDeltaTime = 0.0;
		return;
	}

	__int64 currTime;
	QueryPerformanceCounter((LARGE_INTEGER*)&currTime);
	mCurrTime = currTime;

	// Time difference between this frame and the previous.
	mDeltaTime = (mCurrTime - mPrevTime)*mSecondsPerCount;

	// Prepare for next frame.
	mPrevTime = mCurrTime;

	// Force nonnegative.  The DXSDK's CDXUTTimer mentions that if the 
	// processor goes into a power save mode or we get shuffled to another
	// processor, then mDeltaTime can be negative.
	if(mDeltaTime < 0.0)
	{
		mDeltaTime = 0.0;
	}
}

Its also taken from frank d luna. And I think it is not the source of the problem . Because if I use dt = 1/60.0f it does not effect at all.... Here is the matrix calculation calls which concern me....

void Bones::UpdateNode(float AnimationTime, const aiNode *pNode, const XMFLOAT4X4 parentMatrix) //Bone Index, Parent Matrix (Transformation)
{
	const char *name = pNode->mName.C_Str();
	const aiAnimation *pAnimation = m_pScene->mAnimations[0];
	XMFLOAT4X4 temp;
	AItoXM(pNode->mTransformation, temp);
	XMMATRIX NodeTransformation = XMLoadFloat4x4(&temp);

	const aiNodeAnim* pNodeAnim = FindNodeAnim(pAnimation, name);

	if (pNodeAnim) {
		aiVector3D Scaling;
		CalcInterpolatedScaling(Scaling, AnimationTime, pNodeAnim);
		XMMATRIX ScalingM;
		ScalingM = XMMatrixScaling(Scaling.x, Scaling.y, Scaling.z);
		ScalingM = XMMatrixTranspose(ScalingM);

		aiQuaternion RotationQ;
		CalcInterpolatedRotation(RotationQ, AnimationTime, pNodeAnim);
		XMMATRIX RotationM;
		XMFLOAT4 temp = XMFLOAT4(RotationQ.x, RotationQ.y, RotationQ.z, RotationQ.w);
		XMVECTOR temp2 = XMLoadFloat4(&temp);
		RotationM = XMMatrixRotationQuaternion(temp2);
		RotationM = XMMatrixTranspose(RotationM);
		

		aiVector3D Translation;
		CalcInterpolatedPosition(Translation, AnimationTime, pNodeAnim);
		XMMATRIX TranslationM;
		TranslationM = XMMatrixTranslation(Translation.x, Translation.y, Translation.z);
		TranslationM = XMMatrixTranspose(TranslationM);

		NodeTransformation = TranslationM * RotationM * ScalingM;
	}

	XMMATRIX finalTransform = XMLoadFloat4x4(&parentMatrix) * NodeTransformation;

	if (mBoneInfoMap.find(name) != mBoneInfoMap.end())
	{
		debug << "entered" << std::endl;
		int boneID = mBoneInfoMap[name];
		XMMATRIX temp2 = finalTransform * XMLoadFloat4x4(&mBoneInfo[boneID].offsetMatrix);
		temp2 = XMMatrixTranspose(temp2);
		XMStoreFloat4x4(&mBoneInfo[boneID].Transform, temp2);
	}

	XMFLOAT4X4 temp3;
	XMStoreFloat4x4(&temp3, finalTransform);

	for (int i = 0; i < pNode->mNumChildren; i++)
	{
		UpdateNode(AnimationTime, pNode->mChildren[i], temp3);
	}
}

const aiNodeAnim *Bones::FindNodeAnim(const aiAnimation *pAnimation, const std::string NodeName)
{
	for (UINT i = 0; i < pAnimation->mNumChannels; i++) {
		const aiNodeAnim* pNodeAnim = pAnimation->mChannels[i];

		if (std::string(pNodeAnim->mNodeName.data) == NodeName) {
			return pNodeAnim;
		}
	}

	return NULL;
}

void Bones::CalcInterpolatedRotation(aiQuaternion &out, float AnimationTime, const aiNodeAnim *pNodeAnim)
{
	if (pNodeAnim->mNumRotationKeys == 1) {
		out = pNodeAnim->mRotationKeys[0].mValue;
		return;
	}

	UINT RotationIndex = FindRotation(AnimationTime, pNodeAnim);
	UINT NextRotationIndex = (RotationIndex + 1);
	assert(NextRotationIndex < pNodeAnim->mNumRotationKeys);
	float DeltaTime = pNodeAnim->mRotationKeys[NextRotationIndex].mTime - pNodeAnim->mRotationKeys[RotationIndex].mTime;
	float Factor = (AnimationTime - (float)pNodeAnim->mRotationKeys[RotationIndex].mTime) / DeltaTime;
	assert(Factor >= 0.0f && Factor <= 1.0f);
	const aiQuaternion& StartRotationQ = pNodeAnim->mRotationKeys[RotationIndex].mValue;
	const aiQuaternion& EndRotationQ = pNodeAnim->mRotationKeys[NextRotationIndex].mValue;
	aiQuaternion::Interpolate(out, StartRotationQ, EndRotationQ, Factor);
	out = out.Normalize();
}

UINT Bones::FindRotation(float AnimationTime, const aiNodeAnim *pNodeAnim)
{
	assert(pNodeAnim->mNumRotationKeys > 0);

	for (UINT i = 0; i < pNodeAnim->mNumRotationKeys - 1; i++) {
		if (AnimationTime < (float)pNodeAnim->mRotationKeys[i + 1].mTime) {
			return i;
		}
	}

	assert(0);
}



UINT Bones::FindPosition(float AnimationTime, const aiNodeAnim *pNodeAnim)
{
	for (UINT i = 0; i < pNodeAnim->mNumPositionKeys - 1; i++) {
		if (AnimationTime < (float)pNodeAnim->mPositionKeys[i + 1].mTime) {
			return i;
		}
	}

	assert(0);

	return 0;
}

void Bones::CalcInterpolatedPosition(aiVector3D &out, float AnimationTime, const aiNodeAnim *pNodeAnim)
{
	if (pNodeAnim->mNumPositionKeys == 1) {
		out = pNodeAnim->mPositionKeys[0].mValue;
		return;
	}

	UINT PositionIndex = FindPosition(AnimationTime, pNodeAnim);
	UINT NextPositionIndex = (PositionIndex + 1);
	assert(NextPositionIndex < pNodeAnim->mNumPositionKeys);
	float DeltaTime = (float)(pNodeAnim->mPositionKeys[NextPositionIndex].mTime - pNodeAnim->mPositionKeys[PositionIndex].mTime);
	float Factor = (AnimationTime - (float)pNodeAnim->mPositionKeys[PositionIndex].mTime) / DeltaTime;
	assert(Factor >= 0.0f && Factor <= 1.0f);
	const aiVector3D& Start = pNodeAnim->mPositionKeys[PositionIndex].mValue;
	const aiVector3D& End = pNodeAnim->mPositionKeys[NextPositionIndex].mValue;
	aiVector3D Delta = End - Start;
	out = Start + Factor * Delta;
}

UINT Bones::FindScaling(float AnimationTime, const aiNodeAnim *pNodeAnim)
{
	assert(pNodeAnim->mNumScalingKeys > 0);

	for (UINT i = 0; i < pNodeAnim->mNumScalingKeys - 1; i++) {
		if (AnimationTime < (float)pNodeAnim->mScalingKeys[i + 1].mTime) {
			return i;
		}
	}

	assert(0);

	return 0;
}

void Bones::CalcInterpolatedScaling(aiVector3D &out, float AnimationTime, const aiNodeAnim *pNodeAnim)
{
	if (pNodeAnim->mNumScalingKeys == 1) {
		out = pNodeAnim->mScalingKeys[0].mValue;
		return;
	}

	UINT ScalingIndex = FindScaling(AnimationTime, pNodeAnim);
	UINT NextScalingIndex = (ScalingIndex + 1);
	assert(NextScalingIndex < pNodeAnim->mNumScalingKeys);
	float DeltaTime = (float)(pNodeAnim->mScalingKeys[NextScalingIndex].mTime - pNodeAnim->mScalingKeys[ScalingIndex].mTime);
	float Factor = (AnimationTime - (float)pNodeAnim->mScalingKeys[ScalingIndex].mTime) / DeltaTime;
	assert(Factor >= 0.0f && Factor <= 1.0f);
	const aiVector3D& Start = pNodeAnim->mScalingKeys[ScalingIndex].mValue;
	const aiVector3D& End = pNodeAnim->mScalingKeys[NextScalingIndex].mValue;
	aiVector3D Delta = End - Start;
	out = Start + Factor * Delta;
}

void Bones::XMBones()
{
	mBoneTransforms.clear();
	for (int i = 0; i < mBoneInfo.size(); i++)
	{
		XMFLOAT4X4 temp;
		XMStoreFloat4x4(&temp, XMMatrixIdentity());
		mBoneTransforms.push_back(mBoneInfo[i].Transform);
	}
}
0

Share this post


Link to post
Share on other sites

Some issues I see that can be more or less removed easily:

 

1.) Using strings as keys is slow when the strings are compared character-wise.

 

2.) Looking up an animation node for each bone on every iteration is ineffective. Instead use a runtime instance and bind on creation.

 

3.) Reduce transposing: Instead of

        XMMatrixTranspose(TranslationM) * XMMatrixTranspose(RotationM) * XMMatrixTranspose(ScalingM)

use the equivalent

        XMMatrixTranspose(Scaling * Rotation * Translation)

 
4.) What exactly do all the conversions between XMFLOAT4x4 and XMMATRIX?
 
5.) Do not iterate the animation tracks over and over again to look up the current keyframes. Remember the latest used pair of keyframes per track as starting points for the next look-ups.
 
 
An optimization is to take advantage of the knowledge about semantics of scaling, rotation, and translation and the belonging structure of the respective matrices. Assuming that XMMATRIX::operator* is implemented the usual way, you can reduce the amount of memory accesses, scalar multiplications and additions significantly when dropping all multiplications by 0 or 1 and all additions of 0 by not using the full matrix product.
 
Another thing is aiQuaternion::Interpolate. I don't know how it is implemented, but I would expect aiQuaternion to be a unit quaternion, so that its interpolation gives / should give you a unit quaternion back. Then there is no need to normalize it again explicitly.
Edited by haegarr
2

Share this post


Link to post
Share on other sites

And I think it is not the source of the problem . Because if I use dt = 1/60.0f it does not effect at all.

I didn't suggest it was the source of the problem. I was asking the first question(*) one might ask to begin investigating the problem. I further suggested you might want to do a time profile of your routines to determine if you have a routine that takes "too much" time. You said it appeared you were running at 1 frame/second. You had previously provided only the observation: "obviously because of the recursive matrix calculation of each bone from the root all over again" without any indication of why you think that's "obvious."

 

Rather than analyzing code that may not be the problem, determining where the problem actually lies first may be a better approach.

 

I would suggest you start with what you know to be an indication of a problem - the fps. (*)Do you measure time correctly? Do you calculate fps correctly? If so, what portions of your code are taking what amounts of time to execute?

Edited by Buckeye
1

Share this post


Link to post
Share on other sites


I didn't suggest it was the source of the problem. I was asking the first question(*) one might ask to begin investigating the problem. I further suggested you might want to do a time profile of your routines to determine if you have a routine that takes "too much" time. You said it appeared you were running at 1 frame/second. You had previously provided only the observation: "obviously because of the recursive matrix calculation of each bone from the root all over again" without any indication of why you think that's "obvious."

 

Sorry, Buck I misunderstood , my bad tongue.png .. Solved it somewhat but still I need to optimize the code as haegarr suggested...

 

I pre calculated the necessary frames and just fed the frame number to the render function .

 

I think my project is somewhat ready to be submitted. It would have been really tough without you guys especially you Buck  smile.png  . I got so many discouragements from my teachers and parents and also some friends... But thanks to you and no thanks to my impatience I got over it ... I look forward to learn more about graphics programming after the exam.... biggrin.png biggrin.png  Again thanks guys...

0

Share this post


Link to post
Share on other sites
FindRotation() and friends are all extremely slow. Your animations only move in one direction and as such the next key frame to be found as an animation progresses will likely be the very next one.
Meaning that after you find which index was last found you can store that value and on the next frame start your search from there instead of always from the beginning.


Aside from making the matrix math itself more efficient, I was talking before about adding dirty flags so you know what has changed and needs to be updated.

This is my full COrientation class to easily demonstrate what I mean:
LSMOrientation.h
[spoiler]
#ifndef __LSM_ORIENTATION_H__
#define __LSM_ORIENTATION_H__

#include "../LSMMathLib.h"
#include "../Vector/LSMVector3.h"
#include "../Matrix/LSMMatrix4x4.h"

namespace lsm {

	// == Types.
	/**
	 * Dirty flags for orientations.
	 */
	enum LSM_ORIENT_DIRTY_FLAGS {
		LSM_ODF_POSITION							= (1 << 0),			/*< Position is dirty. */
		LSM_ODF_ROTATION							= (1 << 1),			/*< Rotation is dirty. */
		LSM_ODF_SCALE								= (1 << 2),			/*< Scale is dirty. */
		LSM_ODF_NORMALIZATION						= (1 << 3),			/*< Vectors need normalizing. */
	};

	/**
	 * Class COrientation
	 * \brief Orientation of objects.
	 *
	 * Description: A base class for all objects that can have an orientation in 3D.
	 */
	class COrientation {
	public :
		// == Various constructors.
		LSE_INLINE LSE_CALLCTOR						COrientation();
		LSE_INLINE LSE_CALLCTOR						COrientation( const COrientation &_oSrc );


		// == Operators.
		/**
		 * Copy an orientation.  The dirty flag on the copied orientation is always set after this
		 *	operation.
		 *
		 * \param _oSrc The source orientation.
		 * \return Returns the copied object.
		 */
		LSE_INLINE COrientation & LSE_FCALL			operator = ( const COrientation &_oSrc );


		// == Functions.
		/**
		 * Gets the forward vector.
		 *
		 * \return Returns the forward vector of this orientation.
		 */
		LSE_INLINE const CVector3 & LSE_FCALL		GetForward() const;

		/**
		 * Gets the right vector.
		 *
		 * \return Returns the right vector of this orientation.
		 */
		LSE_INLINE const CVector3 & LSE_FCALL		GetRight() const;

		/**
		 * Gets the up vector.
		 *
		 * \return Returns the up vector of this orientation.
		 */
		LSE_INLINE const CVector3 & LSE_FCALL		GetUp() const;

		/**
		 * Sets the orientation to identity.
		 */
		LSVOID LSE_FCALL							SetIdentity();

		/**
		 * Sets the position to [0,0,0].
		 */
		LSVOID LSE_FCALL							ClearPosition();

		/**
		 * Sets the rotation to identity.
		 */
		LSVOID LSE_FCALL							ClearRotation();

		/**
		 * Sets the scale to [1,1,1].
		 */
		LSVOID LSE_FCALL							ClearScale();

		/**
		 * Gets the position as read-only.
		 *
		 * \return Returns the position as a read-only reference.
		 */
		LSE_INLINE const CVector3 & LSE_FCALL		Pos() const;

		/**
		 * Gets the position as write-only.
		 *
		 * \return Returns the position as a write-only reference.
		 */
		LSE_INLINE CVector3 & LSE_FCALL				Pos();

		/**
		 * Sets the absolute position.  Only causes a change if the given position is not the same as the
		 *	current position.
		 *
		 * \param _vPos The position to apply to this orientation.
		 */
		LSVOID LSE_FCALL							SetPosU( const CVector3 &_vPos );

		/**
		 * Sets the position of this orientation relative to its facing direction.
		 *
		 * \param _vPos The position to apply to this orientation.
		 */
		LSVOID LSE_FCALL							SetRelPos( const CVector3 &_vPos );

		/**
		 * Offset the position of this orientation relative to its facing direction.
		 *
		 * \param _vPos The position to add to this orientation.
		 */
		LSVOID LSE_FCALL							AddRelPos( const CVector3 &_vPos );

		/**
		 * Sets the rotation component of this orientation.
		 *
		 * \param _mMat The full rotational component to apply to this object.
		 */
		LSVOID LSE_FCALL							SetRotation( const CMatrix4x4 &_mMat );

		/**
		 * Add a rotation to the existing rotation of this object.
		 *
		 * \param _mMat The rotation, in matrix form, to add to the existing rotation of this object.
		 */
		LSVOID LSE_FCALL							AddRotation( const CMatrix4x4 &_mMat );

		/**
		 * Sets the rotation component of this object from forward and up vectors.  The right vector
		 *	is derived automatically.
		 *
		 * \param _vForward The forward component of the rotation.
		 * \param _vUp The up component of the rotation.
		 */
		LSVOID LSE_FCALL							SetRotation( const CVector3 &_vForward, const CVector3 &_vUp );

		/**
		 * Sets the rotation component of this object from forward and up vectors.  The right vector
		 *	is derived automatically and the up vector is adjusted.
		 *
		 * \param _vForward The forward component of the rotation.
		 * \param _vUp The up component of the rotation.
		 */
		LSVOID LSE_FCALL							SetRotationEasy( const CVector3 &_vForward, const CVector3 &_vUp );

		/**
		 * Add a rotation to this object from forward and up vectors.  The right vector is derived
		 *	automatically.
		 *
		 * \param _vForward The forward component of the rotation to add.
		 * \param _vUp The up component of the rotation to add.
		 */
		LSVOID LSE_FCALL							AddRotation( const CVector3 &_vForward, const CVector3 &_vUp );

		/**
		 * Yaw the orientation by the given amount.  The object always rotates around axis [0,1,0].
		 *
		 * \param _fAmount Amount, in radians, to yaw.
		 */
		LSVOID LSE_FCALL							Yaw( LSREAL _fAmount );

		/**
		 * Sets the yaw to the given amount.  The object always rotates around axis [0,1,0].
		 *
		 * \param _fAmount Amount, in radians, to which to set the yaw.
		 */
		LSVOID LSE_FCALL							SetYaw( LSREAL _fAmount );

		/**
		 * Pitch the orientation by the given amount.  The object always rotates around its right axis.
		 *
		 * \param _fAmount Amount, in radians, to pitch.
		 */
		LSVOID LSE_FCALL							Pitch( LSREAL _fAmount );

		/**
		 * Roll the orientation by the given amount.  The object always rotates around its forward axis.
		 *
		 * \param _fAmount Amount, in radians, to roll.
		 */
		LSVOID LSE_FCALL							Roll( LSREAL _fAmount );

		/**
		 * Sets the rotation by the given yaw, pitch, and roll.
		 *
		 * \param _fYaw Yaw around the Y axis, in radians.
		 * \param _fPitch Pitch around the X axis, in radians.
		 * \param _fRoll Roll around the Z axis, in radians.
		 */
		LSVOID LSE_FCALL							SetRotationYawPitchRoll( LSREAL _fYaw, LSREAL _fPitch, LSREAL _fRoll );

		/**
		 * Sets the scale.
		 *
		 * \param _fScale The new scale of this orientation.
		 */
		LSE_INLINE LSVOID LSE_FCALL					SetScale( LSREAL _fScale );

		/**
		 * Add to the current scale of this object.
		 *
		 * \param _fScale The scale to add to the existing scale of this object.
		 */
		LSE_INLINE LSVOID LSE_FCALL					AddScale( LSREAL _fScale );

		/**
		 * Gets the scale as read-only.
		 *
		 * \return Returns the scale as a read-only reference.
		 */
		LSE_INLINE const CVector3 & LSE_FCALL		Scale() const;

		/**
		 * Gets the scale as write-only.
		 *
		 * \return Returns the scale as a write-only reference.
		 */
		LSE_INLINE CVector3 & LSE_FCALL				Scale();

		/**
		 * Build a matrix from our data.
		 *
		 * \param _mRet Holds the created matrix.
		 */
		LSVOID LSE_FCALL							BuildMatrix( CMatrix4x4 &_mRet ) const;

		/**
		 * Updates, if necessary, the orientation, filling the given matrix with the result.  If the orientation
		 *	has not been modified since the last update, no action is performed.
		 *
		 * \param _mRet Holds the created matrix.
		 * \param _bClearDirty Determines whether the dirty flag will be cleared after this update.  This
		 *	function only updates the given matrix if the dirty flag is set, so clearing the dirty flag
		 *	results in no more updates until the orientation is modified in some way.
		 *	In this manner, updates are only performed when needed.
		 * \return Returns true if an update was performed.
		 */
		LSBOOL LSE_FCALL							UpdateMatrix( CMatrix4x4 &_mRet, LSBOOL _bClearDirty = true ) const;

		/**
		 * Are we dirty at all?
		 *
		 * \return Returns true if the orientation is dirty and needs to update matrices.
		 */
		LSE_INLINE LSBOOL LSE_FCALL					IsDirty() const;
		
		/**
		 * Make us dirty.  This causes an update at the next UpdateMatrix() call.
		 */
		LSE_INLINE LSVOID LSE_FCALL					MakeDirty();

		/**
		 * Undirty rotations if necessary.
		 */
		LSE_INLINE LSVOID LSE_FCALL					UndirtyRotations() const;
		
		
	protected :
		// == Members.
		/**
		 * Orientation.
		 */
		mutable CVector3							m_vRight,
													m_vUp,
													m_vForward;

		/**
		 * Position.
		 */
		CVector3									m_vPos;

		/**
		 * Scale.
		 */
		CVector3									m_vScale;

		/**
		 * Dirty flags.
		 */
		mutable LSUINT32							m_ui32Dirty;
	};


	// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	// DEFINITIONS
	// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	// == Various constructors.
	LSE_INLINE LSE_CALLCTOR COrientation::COrientation() :
		m_vRight( LSM_ONE, LSM_ZERO, LSM_ZERO ),
		m_vUp( LSM_ZERO, LSM_ONE, LSM_ZERO ),
		m_vForward( LSM_ZERO, LSM_ZERO, LSM_ONE ),
		m_vPos( LSM_ZERO, LSM_ZERO, LSM_ZERO ),
		m_vScale( LSM_ONE, LSM_ONE, LSM_ONE ),
		m_ui32Dirty( LSM_ODF_POSITION | LSM_ODF_ROTATION | LSM_ODF_SCALE ) {
	}
	LSE_INLINE LSE_CALLCTOR COrientation::COrientation( const COrientation &_oSrc ) :
		m_vRight( _oSrc.m_vRight ),
		m_vUp( _oSrc.m_vUp ),
		m_vForward( _oSrc.m_vForward ),
		m_vPos( _oSrc.m_vPos ),
		m_vScale( _oSrc.m_vScale ),
		m_ui32Dirty( _oSrc.m_ui32Dirty ) {
	}

	// == Operators.
	/**
	 * Copy an orientation.  The dirty flag on the copied orientation is always set after this
	 *	operation.
	 *
	 * \param _oSrc The source orientation.
	 * \return Returns the copied object.
	 */
	LSE_INLINE COrientation & LSE_FCALL COrientation::operator = ( const COrientation &_oSrc ) {
		m_vRight = _oSrc.m_vRight;
		m_vUp = _oSrc.m_vUp;
		m_vForward = _oSrc.m_vForward;
		m_vPos = _oSrc.m_vPos;
		m_vScale = _oSrc.m_vScale;
		m_ui32Dirty = LSM_ODF_NORMALIZATION | LSM_ODF_POSITION | LSM_ODF_ROTATION | LSM_ODF_SCALE;
		return (*this);
	}

	// == Functions.	
	/**
	 * Gets the forward vector.
	 *
	 * \return Returns the forward vector of this orientation.
	 */
	LSE_INLINE const CVector3 & LSE_FCALL COrientation::GetForward() const	{ const_cast<COrientation *>(this)->UndirtyRotations(); return m_vForward; }
	
	/**
	 * Gets the right vector.
	 *
	 * \return Returns the right vector of this orientation.
	 */
	LSE_INLINE const CVector3 & LSE_FCALL COrientation::GetRight() const	{ const_cast<COrientation *>(this)->UndirtyRotations(); return m_vRight; }
	
	/**
	 * Gets the up vector.
	 *
	 * \return Returns the up vector of this orientation.
	 */
	LSE_INLINE const CVector3 & LSE_FCALL COrientation::GetUp() const		{ const_cast<COrientation *>(this)->UndirtyRotations(); return m_vUp; }

	/**
	 * Gets the position as read-only.
	 *
	 * \return Returns the position as a read-only reference.
	 */
	LSE_INLINE const CVector3 & LSE_FCALL COrientation::Pos() const {
		return m_vPos;
	}

	/**
	 * Gets the position as write-only.
	 *
	 * \return Returns the position as a write-only reference.
	 */
	LSE_INLINE CVector3 & LSE_FCALL COrientation::Pos() {
		m_ui32Dirty |= LSM_ODF_POSITION;
		return m_vPos;
	}

	/**
	 * Sets the scale.
	 *
	 * \param _fScale The new scale of this orientation.
	 */
	LSE_INLINE LSVOID LSE_FCALL COrientation::SetScale( LSREAL _fScale ) {
		m_vScale.Set( _fScale, _fScale, _fScale );
		m_ui32Dirty |= LSM_ODF_SCALE;
	}

	/**
	 * Add to the current scale of this object.
	 *
	 * \param _fScale The scale to add to the existing scale of this object.
	 */
	LSE_INLINE LSVOID LSE_FCALL COrientation::AddScale( LSREAL _fScale ) {
		m_vScale.Set( m_vScale.x + _fScale, m_vScale.y + _fScale, m_vScale.z + _fScale );
		m_ui32Dirty |= LSM_ODF_SCALE;
	}

	/**
	 * Gets the scale as read-only.
	 *
	 * \return Returns the scale as a read-only reference.
	 */
	LSE_INLINE const CVector3 & LSE_FCALL COrientation::Scale() const {
		return m_vScale;
	}

	/**
	 * Gets the scale as write-only.
	 *
	 * \return Returns the scale as a write-only reference.
	 */
	LSE_INLINE CVector3 & LSE_FCALL COrientation::Scale() {
		m_ui32Dirty |= LSM_ODF_POSITION;
		return m_vScale;
	}

	/**
	 * Are we dirty at all?
	 *
	 * \return Returns true if the orientation is dirty and needs to update matrices.
	 */
	LSE_INLINE LSBOOL LSE_FCALL COrientation::IsDirty() const {
		return m_ui32Dirty != 0;
	}
	
	/**
	 * Make us dirty.  This causes an update at the next UpdateMatrix() call.
	 */
	LSE_INLINE LSVOID LSE_FCALL COrientation::MakeDirty() {
		m_ui32Dirty = LSM_ODF_NORMALIZATION | LSM_ODF_POSITION | LSM_ODF_ROTATION | LSM_ODF_SCALE;
	}

	/**
	 * Undirty rotations if necessary.
	 */
	LSE_INLINE LSVOID LSE_FCALL COrientation::UndirtyRotations() const {
		if ( m_ui32Dirty & LSM_ODF_NORMALIZATION ) {
			m_vRight.Normalize();
			m_vUp.Normalize();
			m_vForward.Normalize();
			m_ui32Dirty &= ~static_cast<LSUINT32>(LSM_ODF_NORMALIZATION);
		}
	}

}	// namespace lsm

#endif	// __LSM_ORIENTATION_H__
[/spoiler]


LSMOrientation.cpp
[spoiler]
#include "LSMOrientation.h"


namespace lsm {


	/**
	 * Sets the orientation to identity.
	 */
	LSVOID LSE_FCALL COrientation::SetIdentity() {
		m_vRight.Set( LSM_ONE, LSM_ZERO, LSM_ZERO );
		m_vUp.Set( LSM_ZERO, LSM_ONE, LSM_ZERO );
		m_vForward.Set( LSM_ZERO, LSM_ZERO, LSM_ONE );
		m_vPos.Set( LSM_ZERO, LSM_ZERO, LSM_ZERO );
		m_vScale.Set( LSM_ONE, LSM_ONE, LSM_ONE );
		m_ui32Dirty |= LSM_ODF_POSITION | LSM_ODF_ROTATION | LSM_ODF_SCALE;
	}

	/**
	 * Sets the position to [0,0,0].
	 */
	LSVOID LSE_FCALL COrientation::ClearPosition() {
		m_vPos.Set( LSM_ZERO, LSM_ZERO, LSM_ZERO );
		m_ui32Dirty |= LSM_ODF_POSITION;
	}

	/**
	 * Sets the rotation to identity.
	 */
	LSVOID LSE_FCALL COrientation::ClearRotation() {
		m_vRight.Set( LSM_ONE, LSM_ZERO, LSM_ZERO );
		m_vUp.Set( LSM_ZERO, LSM_ONE, LSM_ZERO );
		m_vForward.Set( LSM_ZERO, LSM_ZERO, LSM_ONE );
		m_ui32Dirty |= LSM_ODF_ROTATION;
	}

	/**
	 * Sets the scale to [1,1,1].
	 */
	LSVOID LSE_FCALL COrientation::ClearScale() {
		m_vScale.Set( LSM_ONE, LSM_ONE, LSM_ONE );
		m_ui32Dirty |= LSM_ODF_SCALE;
	}

	/**
	 * Sets the absolute position.  Only causes a change if the given position is not the same as the
	 *	current position.
	 *
	 * \param _vPos The position to apply to this orientation.
	 */
	LSVOID LSE_FCALL COrientation::SetPosU( const CVector3 &_vPos ) {
		if ( m_vPos == _vPos ) { return; }
		Pos() = _vPos;
	}

	/**
	 * Sets the position of this orientation relative to its facing direction.
	 *
	 * \param _vPos The position to apply to this orientation.
	 */
	LSVOID LSE_FCALL COrientation::SetRelPos( const CVector3 &_vPos ) {
		m_vPos = (m_vRight * _vPos.x) +
			(m_vUp * _vPos.y) +
			(m_vForward * _vPos.z);
		m_ui32Dirty |= LSM_ODF_POSITION;
	}

	/**
	 * Offset the position of this orientation relative to its facing direction.
	 *
	 * \param _vPos The position to add to this orientation.
	 */
	LSVOID LSE_FCALL COrientation::AddRelPos( const CVector3 &_vPos ) {
		m_vPos += (m_vRight * _vPos.x) +
			(m_vUp * _vPos.y) +
			(m_vForward * _vPos.z);
		m_ui32Dirty |= LSM_ODF_POSITION;
	}

	/**
	 * Sets the rotation component of this orientation.
	 *
	 * \param _mMat The full rotational component to apply to this object.
	 */
	LSVOID LSE_FCALL COrientation::SetRotation( const CMatrix4x4 &_mMat ) {
		m_vRight.Set( _mMat._11, _mMat._12, _mMat._13 );
		m_vUp.Set( _mMat._21, _mMat._22, _mMat._23 );
		m_vForward.Set( _mMat._31, _mMat._32, _mMat._33 );
		m_ui32Dirty |= (LSM_ODF_ROTATION | LSM_ODF_NORMALIZATION);
	}

	/**
	 * Add a rotation to the existing rotation of this object.
	 *
	 * \param _mMat The rotation, in matrix form, to add to the existing rotation of this object.
	 */
	LSVOID LSE_FCALL COrientation::AddRotation( const CMatrix4x4 &_mMat ) {
		m_vRight.Set(
			CVector3::DotProduct( m_vRight, CVector3( _mMat._11, _mMat._21, _mMat._31 ) ),
			CVector3::DotProduct( m_vRight, CVector3( _mMat._12, _mMat._22, _mMat._32 ) ),
			CVector3::DotProduct( m_vRight, CVector3( _mMat._13, _mMat._23, _mMat._33 ) ) );

		m_vForward.Set(
			CVector3::DotProduct( m_vForward, CVector3( _mMat._11, _mMat._21, _mMat._31 ) ),
			CVector3::DotProduct( m_vForward, CVector3( _mMat._12, _mMat._22, _mMat._32 ) ),
			CVector3::DotProduct( m_vForward, CVector3( _mMat._13, _mMat._23, _mMat._33 ) ) );

		m_vUp.Set(
			CVector3::DotProduct( m_vUp, CVector3( _mMat._11, _mMat._21, _mMat._31 ) ),
			CVector3::DotProduct( m_vUp, CVector3( _mMat._12, _mMat._22, _mMat._32 ) ),
			CVector3::DotProduct( m_vUp, CVector3( _mMat._13, _mMat._23, _mMat._33 ) ) );

		m_ui32Dirty |= (LSM_ODF_ROTATION | LSM_ODF_NORMALIZATION);
	}

	/**
	 * Sets the rotation component of this object from forward and up vectors.  The right vector
	 *	is derived automatically.
	 *
	 * \param _vForward The forward component of the rotation.
	 * \param _vUp The up component of the rotation.
	 */
	LSVOID LSE_FCALL COrientation::SetRotation( const CVector3 &_vForward, const CVector3 &_vUp ) {
		m_vForward = _vForward;
		m_vUp = _vUp;
		m_vRight = _vUp % _vForward;
		m_ui32Dirty |= (LSM_ODF_ROTATION | LSM_ODF_NORMALIZATION);
	}

	/**
	 * Sets the rotation component of this object from forward and up vectors.  The right vector
	 *	is derived automatically and the up vector is adjusted.
	 *
	 * \param _vForward The forward component of the rotation.
	 * \param _vUp The up component of the rotation.
	 */
	LSVOID LSE_FCALL COrientation::SetRotationEasy( const CVector3 &_vForward, const CVector3 &_vUp ) {
		m_vForward = _vForward;
		m_vRight = _vUp % _vForward;
		m_vUp = _vForward % m_vRight;
		m_ui32Dirty |= (LSM_ODF_ROTATION | LSM_ODF_NORMALIZATION);
	}

	/**
	 * Add a rotation to this object from forward and up vectors.  The right vector is derived
	 *	automatically.
	 *
	 * \param _vForward The forward component of the rotation to add.
	 * \param _vUp The up component of the rotation to add.
	 */
	LSVOID LSE_FCALL COrientation::AddRotation( const CVector3 &_vForward, const CVector3 &_vUp ) {
		CVector3 vRight( _vUp % _vForward );

		m_vRight.Set(
			CVector3::DotProduct( m_vRight, CVector3( vRight.x, _vUp.x, _vForward.x ) ),
			CVector3::DotProduct( m_vRight, CVector3( vRight.y, _vUp.y, _vForward.y ) ),
			CVector3::DotProduct( m_vRight, CVector3( vRight.z, _vUp.z, _vForward.z ) ) );

		m_vForward.Set(
			CVector3::DotProduct( m_vForward, CVector3( vRight.x, _vUp.x, _vForward.x ) ),
			CVector3::DotProduct( m_vForward, CVector3( vRight.y, _vUp.y, _vForward.y ) ),
			CVector3::DotProduct( m_vForward, CVector3( vRight.z, _vUp.z, _vForward.z ) ) );

		m_vUp.Set(
			CVector3::DotProduct( m_vUp, CVector3( vRight.x, _vUp.x, _vForward.x ) ),
			CVector3::DotProduct( m_vUp, CVector3( vRight.y, _vUp.y, _vForward.y ) ),
			CVector3::DotProduct( m_vUp, CVector3( vRight.z, _vUp.z, _vForward.z ) ) );
		m_ui32Dirty |= (LSM_ODF_ROTATION | LSM_ODF_NORMALIZATION);
	}

	/**
	 * Yaw the orientation by the given amount.  The object always rotates around axis [0,1,0].
	 *
	 * \param _fAmount Amount, in radians, to yaw.
	 */
	LSVOID LSE_FCALL COrientation::Yaw( LSREAL _fAmount ) {
		CMatrix4x4 mRot;
		mRot.MatrixRotationYLH( _fAmount );
		AddRotation( mRot );
	}

	/**
	 * Sets the yaw to the given amount.  The object always rotates around axis [0,1,0].
	 *
	 * \param _fAmount Amount, in radians, to which to set the yaw.
	 */
	LSVOID LSE_FCALL COrientation::SetYaw( LSREAL _fAmount ) {
		LSREAL fS;
		LSREAL fC;
		CMathLib::SinCos( _fAmount, fS, fC );

		CVector3 vForward;
		vForward.x = fS;
		vForward.y = LSM_ZERO;
		vForward.x = fC;

		m_vRight = vForward % m_vUp;
		m_vForward = m_vUp % m_vRight;
		m_vUp = m_vRight % m_vUp;

		m_ui32Dirty |= (LSM_ODF_ROTATION | LSM_ODF_NORMALIZATION);
	}

	/**
	 * Pitch the orientation by the given amount.  The object always rotates around its right axis.
	 *
	 * \param _fAmount Amount, in radians, to pitch.
	 */
	LSVOID LSE_FCALL COrientation::Pitch( LSREAL _fAmount ) {
		CMatrix4x4 mRot;
		mRot.MatrixRotationAxis( m_vRight, _fAmount );
		AddRotation( mRot );
	}

	/**
	 * Roll the orientation by the given amount.  The object always rotates around its forward axis.
	 *
	 * \param _fAmount Amount, in radians, to roll.
	 */
	LSVOID LSE_FCALL COrientation::Roll( LSREAL _fAmount ) {
		CMatrix4x4 mRot;
		mRot.MatrixRotationAxis( m_vForward, _fAmount );
		AddRotation( mRot );
	}

	/**
	 * Sets the rotation by the given yaw, pitch, and roll.
	 *
	 * \param _fYaw Yaw around the Y axis, in radians.
	 * \param _fPitch Pitch around the X axis, in radians.
	 * \param _fRoll Roll around the Z axis, in radians.
	 */
	LSVOID LSE_FCALL COrientation::SetRotationYawPitchRoll( LSREAL _fYaw, LSREAL _fPitch, LSREAL _fRoll ) {
		CMatrix4x4 mRot;
		mRot.MatrixRotationYawPitchRoll( _fYaw, _fPitch, _fRoll );
		SetRotation( mRot );
	}

	/**
	 * Build a matrix from our data.
	 *
	 * \param _mRet Holds the created matrix.
	 */
	LSVOID LSE_FCALL COrientation::BuildMatrix( CMatrix4x4 &_mRet ) const {
		_mRet._11 = m_vRight.x * m_vScale.x;
		_mRet._12 = m_vRight.y * m_vScale.x;
		_mRet._13 = m_vRight.z * m_vScale.x;
		_mRet._14 = LSM_ZERO;

		_mRet._21 = m_vUp.x * m_vScale.y;
		_mRet._22 = m_vUp.y * m_vScale.y;
		_mRet._23 = m_vUp.z * m_vScale.y;
		_mRet._24 = LSM_ZERO;

		_mRet._31 = m_vForward.x * m_vScale.z;
		_mRet._32 = m_vForward.y * m_vScale.z;
		_mRet._33 = m_vForward.z * m_vScale.z;
		_mRet._34 = LSM_ZERO;

		_mRet._41 = m_vPos.x;
		_mRet._42 = m_vPos.y;
		_mRet._43 = m_vPos.z;
		_mRet._44 = LSM_ONE;
	}

	/**
	 * Updates, if necessary, the orientation, filling the given matrix with the result.  If the orientation
	 *	has not been modified since the last update, no action is performed.
	 *
	 * \param _mRet Holds the created matrix.
	 * \param _bClearDirty Determines whether the dirty flag will be cleared after this update.  This
	 *	function only updates the given matrix if the dirty flag is set, so clearing the dirty flag
	 *	results in no more updates until the orientation is modified in some way.
	 *	In this manner, updates are only performed when needed.
	 * \return Returns true if an update was performed.
	 */
	LSBOOL LSE_FCALL COrientation::UpdateMatrix( CMatrix4x4 &_mRet, LSBOOL _bClearDirty ) const {
		if ( m_ui32Dirty != 0 ) {
			UndirtyRotations();
			BuildMatrix( _mRet );
			if ( _bClearDirty ) { m_ui32Dirty = 0; }
			return true;
		}
		return false;
	}

}	// namespace lsm
[/spoiler]


As you can see, UpdateMatrix() only rebuilds the matrix if the dirty flag is set, which happens when you modify the position, scale, or rotation. Normalizing is also expensive and avoided when possible.



This should obviously be applied to each object’s orientation when modified, but when a parent is modified it should (at a specific point, one time per update, not every time it is modified) also go down each of its children and call MakeDirty() so that they will be updated appropriately.


L. Spiro
2

Share this post


Link to post
Share on other sites


FindRotation() and friends are all extremely slow.

 

I have to optimize those. I followed a tutorial which did this way... Now that I've learned it now Ill try to optimize it....

And thanks for the spoiler but I'll sneak a peek only after I give it a go tongue.png when I will get frustrated

0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0