Advertisement

Rotating objects vertices with a matrix

Started by July 13, 2020 03:22 PM
17 comments, last by vadru 4 years, 6 months ago

I have a problem rotating an objects vertices with a matrix.
At the first few frames it looks okay, but after that it jumps around.
The final rotation is corect, but position is wrong.

D3DXVECTOR3& Transform(D3DXVECTOR3& V, D3DXMATRIX& M)
{
	D3DXVECTOR3 Out;
	float x = V.x * M._11 + V.y * M._21 + V.z * M._31 + M._41;
	float y = V.x * M._12 + V.y * M._22 + V.z * M._32 + M._42;
	float z = V.x * M._13 + V.y * M._23 + V.z * M._33 + M._43;
	//float w = V.x * M._14 + V.y * M._24 + V.z * M._34 + M._44; <- don't need scaling for now
	
	Out.x = x;
	Out.y = y;
	Out.z = z;
	return Out;
}

//this is called each frame
void Update()
{
//same matrix function I use in Level Editor
D3DXMatrixRotationYawPitchRoll(&nodeRotation, -angle.y, angle.x, angle.z);

//make a new bbox in a temp location so we can keep the actual bbox
bboxMax = D3DXVECTOR3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
bboxMin = D3DXVECTOR3(FLT_MAX, FLT_MAX, FLT_MAX);

//temporarly make the actual bbox very big
sector->bboxMax = D3DXVECTOR3(FLT_MAX, FLT_MAX, FLT_MAX);
sector->bboxMin = D3DXVECTOR3(-FLT_MAX, -FLT_MAX, -FLT_MAX);

for (DWORD i = 0; i < sector->numVertices; i++)
{
    //Move the object to origin and then transform it by the matrix and move it back to the position
     sector->vertices[i] -= pos;
     sector->vertices[i] = Transform(sector->vertices[i], nodeRotation);
     sector->vertices[i] += pos;

     //calculate the new bbox into the temp location
     if (bboxMax.x < sector->vertices[i].x)
         bboxMax.x = sector->vertices[i].x;
      if (bboxMin.x > sector->vertices[i].x)
          bboxMin.x = sector->vertices[i].x;

      if (bboxMax.y < sector->vertices[i].y)
           bboxMax.y = sector->vertices[i].y;
       if (bboxMin.y > sector->vertices[i].y)
               bboxMin.y = sector->vertices[i].y;

       if (bboxMax.z < sector->vertices[i].z)
           bboxMax.z = sector->vertices[i].z;
       if (bboxMin.z > sector->vertices[i].z)
           bboxMin.z = sector->vertices[i].z;
 }

 //set the actual bbox to be same as temp bbox
sector->bboxMax = bboxMax;
sector->bboxMin = bboxMin;
pos = (sector->bboxMax + sector->bboxMin) / 2.0f;
                
//Send state to update vertexbuffer
sector->mesh->Update();
}

First i would check angle to find if i move it with too small values you should consider 0.001 as smallest update number, probably you get NANs or INF or orverflow

Technically you dont update pos cause its constant - well that what your code assumes. That pos is the center of the rotation, so you do not recalc it if shape doesn't change. And check if you are applying any translation transformation to matrix which you shouldnt here, also not sure why you pass references everywhere & but its ok. (Not.familiar with D3DXVECTOR3)

Anyway its an interesting topic where there here should be a topic about applying rot matrix to a vector or a point with explanation why one doesnt take into account 4th component.

So disable 4th component in dot function….

Any thoughts would be apprecieated

Advertisement

The smallest number I update with is 0.0015339794921875f.
Now the object is not jumping around anymore, the reason for that was because my angle was too big. But it's still moving slightly and not rotating like it should.

My current code is this:

even = !even;//Update every 2nd frame since ps1 is running on 30 fps
if (even)
  return false;
if (acc.x || acc.y || acc.z || goalAngle.x || goalAngle.y || goalAngle.z)
{
	//Create rotation and apply the velocity+acceleration angles
	goalAngle.x += acc.x;
	D3DXMatrixRotationYawPitchRoll(&nodeRotation, 0, goalAngle.x, 0);


	//make a new bbox in a temp location so we can keep the actual bbox
	bboxMax = D3DXVECTOR3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
	bboxMin = D3DXVECTOR3(FLT_MAX, FLT_MAX, FLT_MAX);

	//temporarly make the actual bbox very big to prevent culling
	sector->bboxMax = D3DXVECTOR3(FLT_MAX, FLT_MAX, FLT_MAX);
	sector->bboxMin = D3DXVECTOR3(-FLT_MAX, -FLT_MAX, -FLT_MAX);

	for (DWORD i = 0; i < sector->numVertices; i++)
	{
		//Use the vertices that's translated to origin
		sector->vertices[i] -= pos;
		D3DXVec3TransformCoord(&sector->vertices[i], &sector->vertices[i], &nodeRotation);
		sector->vertices[i] += pos;

		//calculate the new bbox into the temp location
		if (bboxMax.x < sector->vertices[i].x)
			bboxMax.x = sector->vertices[i].x;
		if (bboxMin.x > sector->vertices[i].x)
			bboxMin.x = sector->vertices[i].x;

		if (bboxMax.y < sector->vertices[i].y)
			bboxMax.y = sector->vertices[i].y;
		if (bboxMin.y > sector->vertices[i].y)
			bboxMin.y = sector->vertices[i].y;

		if (bboxMax.z < sector->vertices[i].z)
			bboxMax.z = sector->vertices[i].z;
		if (bboxMin.z > sector->vertices[i].z)
			bboxMin.z = sector->vertices[i].z;
	}

	//set the actual bbox to be same as temp bbox
	sector->bboxMax = bboxMax;
	sector->bboxMin = bboxMin;
	return true;//Returns true to update vertexbuffer
}
return false;

The script that gives me the angles:
When V_ANGULAR_ACCELERATION is called I update the acc value.
When V_ANGULAR_VELOCITY is called I update the goalAngle.
The angles are multiplied by 0.0015339794921875f before being stored.
C_WAIT waits for (n) gameframes

C_WAIT (30)
V_ANGULAR_ACCELERATION (-1, 0, 0)
C_WAIT (25)
V_ANGULAR_ACCELERATION (-2, 0, 0)
C_WAIT (10)
V_ANGULAR_ACCELERATION (0, 0, 0)
V_ANGULAR_VELOCITY (0, 0, 0)
V_ANGULAR_ACCELERATION (-1, -2, 1)
C_WAIT (5)
V_ANGULAR_ACCELERATION (-2, -1, 2)
C_WAIT (10)
C_PLAY_SFX (225)
C_SHATTER
C_DONE

This is wrong if (acc.x || acc.y || acc.z || goalAngle.x || goalAngle.y || goalAngle.z) {

And check your drawing code if you say that its moving slightly, also define ' not rotating like it should)

No wonder even = !even;//Update every 2nd frame since ps1 is running on 30 fps
This line i dunno about ps iterations but its likely this is not suitable of running at 30 fps. Not every frame will get you constant draw/process time

Why is it wrong to check if delta angle is 0? I'm applying this angle to the current vertices of the mesh, so it includes the old angle and if delta angle is 0 then I don't need to update the new angle.

Here is what I want to happen:

And here is what currently happens:


I updated the code again to see what's going on. And the final angle looks okay at 89.657394. But why is the object moving and why is the final angle not there on the object?

goalAngle.x += acceleration.x*0.33f;//multiply acceleration by 0.33, or else we will get too big angle

float velocityAngle = max(goalAngle.x, 0.001f);//clamp the angle to be minimum 0.001
angle.x += velocityAngle;//add the velocity to current angle
//the final angle now is 89.657394, but why is the object moving??
printf("currentAngle %f velocityAngle %f accelerationAngle %f\n", angle.x * 180 / D3DX_PI, velocityAngle, acceleration.x*0.33f);

D3DXMatrixRotationYawPitchRoll(&nodeRotation, 0, velocityAngle, 0);

For some reason the video time link didn't work, check the video at 1:30 for the effect I want.

Btw to clerify this code is running on a pc game and the original game that this level is made for is ps1.

When I convert the level I use this code sucessfully to place the models:

//full circle is 4096(divider for Th2FixedVert) since level is flipped need - on all axes 
//but y needs to be rotated 180 degree after
//because level editor will rotate it 180 degree again to support the nodes in the newer games.
 angle.x = -((D3DX_PI / 2048.0f) * ((float)(int)((short*)pNode)[0] - 2048.0f));
 angle.y = -((D3DX_PI / 2048.0f) * ((float)(int)((short*)pNode)[1] - 2048.0f)) + D3DX_PI;
 angle.z = -((D3DX_PI / 2048.0f) * ((float)(int)((short*)pNode)[2] - 2048.0f));
 
 D3DXMATRIX rotation;
 D3DXMATRIX translation;
 D3DXMATRIX result;
 D3DXMatrixRotationYawPitchRoll(&rotation, -angle->y + D3DX_PI, angle->x, angle->z);
 D3DXMatrixTranslation(&translation, position->x, position->y, position->z);
 D3DXMatrixMultiply(&result, &rotation, &translation);
 
 //The vertices here is what are stored in the level and that I later try to rotate with the code posted here
 for (DWORD i = 0, numVertices = vertices.size(); i < numVertices; i++)
 {
   D3DXVec3TransformCoord((D3DXVECTOR3*)&vertices[i], (D3DXVECTOR3*)&vertices[i], &result);
 }

Advertisement

So actually your rotation origin is at the bottom of the pale not the obj geometric center. For a simple approach the pos doesnt change there and you are good with making it constant. So the rotation origin is at the bottom (it hits the ground) and this is where you rotate, now you need to position this pale accordingly that means pos is at the bottom and you rotate around that point.

I dunno… why wont just simply rotate around x or z axis till it reaches ground? Like in wannabe video.

Adding dx pis, negating everyframe, dividing by so weird number dont make any sense for other people. Have you even tried not to recalculate position and make it constant as i suggested earlier, however since your object aint rotating but.only goes down i believe you still have wrong drawing routine. (Not to mention that you have placed object pos wrongly where it should be at the bottom)

Ofcourse if I just want it to look similar I can change the whole aproach, but the idea is to be able to convert all levels with this format so I want to be able to convert the original script instead of needing to rebuild the script for each object.

I think you are right about that the object is placed too low. I place the object with a calculated bbox from the vertices, probably the original bbox was bigger than the actual object so that when you would use center of origin to rotate and place the object it would be the bottom of the object. Maybe it will work better if I change this.

Actually the object is not placed wrong, so there must be a value somewhere that determent the origin of the rotation.

But either way my rotation function does not seem to work like it should, since it's moving the object.

Maybe the whole angular_velocity script is actually more complex and uses physics like gravity and object linked to eachother?
But it sounds very complex for a ps1 game…

The reason why I'm multiplying with those weird numbers is becuase the original level is using fixedvertex to represent the models position and angles.
Each axis is 2 byte value and to get it's float value you need to divide by 4096.

And about my drawing function being wrong, I'm actually not drawing anything. I'm only modifying the vertices of the object and then telling the game to update the vertexbuffer and then the game will draw it normally. And this works fine for other functions that I have to rotate and move the object, so that's not the issue here. The issue is that for some reason the velocity and acceleration angle values makes the object move instead of rotate.

I did some search and msdn doesnt tell that d3dmatrix or d3dxmatrix have the initialization constructor so actually first of all you will have to identity each matrix before applying rotation and translation

D3DXMATRIX rotation; D3DXMATRIX translation; D3DXMATRIX result;

rotation.Loadaidentity();

Now use thia dxd3yawpitchroll func

translation.LoadIdentity();

Now use this d3dx translate func

It would be good to set identitt matrix to result too.

This topic is closed to new replies.

Advertisement