• Create Account

## How often update a scene ?

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

13 replies to this topic

Posted 27 March 2014 - 03:35 PM

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 ?

### #2Buckeye  GDNet+

Posted 27 March 2014 - 03:43 PM

Call it as often as you like but pass it the delta-time since the last render.

You can also take a look at Fix Your Timestep.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Posted 27 March 2014 - 03:44 PM

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, 27 March 2014 - 04:20 PM.

### #4ZenithSal  Members

Posted 27 March 2014 - 04:21 PM

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.

Posted 27 March 2014 - 04:29 PM

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....

### #6Buckeye  GDNet+

Posted 27 March 2014 - 06:21 PM

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, 27 March 2014 - 06:29 PM.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

### #7ferrous  Members

Posted 27 March 2014 - 06:37 PM

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)

### #8L. Spiro  Members

Posted 27 March 2014 - 07:58 PM

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

Posted 28 March 2014 - 12:13 PM

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);

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);
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);
}
}


### #10haegarr  Members

Posted 28 March 2014 - 01:39 PM

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.

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, 28 March 2014 - 02:01 PM.

### #11Buckeye  GDNet+

Posted 28 March 2014 - 02:03 PM

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, 28 March 2014 - 02:10 PM.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Posted 28 March 2014 - 04:23 PM

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  .. 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    . 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....    Again thanks guys...

### #13L. Spiro  Members

Posted 28 March 2014 - 11:23 PM

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

LSMOrientation.cpp
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

Posted 29 March 2014 - 02:10 AM

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  when I will get frustrated

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.