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

Okay thank you for that information, but how come the object is placed fine already using the function above?

I will try to use this for the rotation and see what happens.

Actually I am already identity the matrix before rotating it, but even if I remove the identiy 
the result is the same. 
Actually I think D3DXMatrixRotationYawPitchRoll will create a matrix with identity+rotation

Anway here is the full code:
The follow_path_linked works fine without moving the object when I modify the angle, 
however I calculate the angle needed for the object to look in the direction wrong. 
But I will create a new topic about that problem.

bool Update(float delta)
{
	bool update = false;
	static D3DXVECTOR3 direction, Velocity;
	static D3DXMATRIX nodeRotation, nodeTranslation, world;
	D3DXMatrixIdentity(&nodeRotation);
	D3DXMatrixIdentity(&nodeTranslation);
	D3DXMatrixIdentity(&world);


	_printf("MovingObject on the move(%f %f %f) dt:%f timer:%f end:%f\n", pos.x, pos.y, pos.z, delta, timer, end);

	timer += delta;
	switch (Type)
	{
	case MOVE_TO_POS:
		if (timer >= end)
		{
			if (pos != goal)
			{
				D3DXVECTOR3 relPos = goal - pos;

				sector->bboxMax += relPos;
				sector->bboxMin += relPos;
				for (DWORD i = 0; i < sector->numVertices; i++)
				{
					sector->vertices[i] += relPos;
				}
				update = true;
			}

			RemoveMovingObject(sector);
			_printf("MovingObject final destination\n");
			return update;
		}

		direction = goal - pos;
		D3DXVec3Normalize(&amp;amp;Velocity, &amp;amp;direction);
		Velocity *= speed * delta;

		sector->bboxMax += Velocity;
		sector->bboxMin += Velocity;
		for (DWORD i = 0; i < sector->numVertices; i++)
		{
			sector->vertices[i] += Velocity;
		}
		pos += Velocity;

		return true;

	case FOLLOW_PATH_LINKED:


		if (timer >= end)
		{
			if (pos != goal)
			{
				D3DXVECTOR3 relPos = goal - pos;

				sector->bboxMax += relPos;
				sector->bboxMin += relPos;
				for (DWORD i = 0; i < sector->numVertices; i++)
				{
					sector->vertices[i] += relPos;
				}

				pos += relPos;

				update = true;
			}


			CArray* links = link->GetArray(Checksums::Links);
			if (!links)
			{
				RemoveMovingObject(sector);
				_printf("MovingObject final destination\n");
				return update;
			}

			if (link->GetNumItems() == 1)
				link = Node::GetNodeStructByIndex((*links)[0]);
			else
				link = Node::GetNodeStructByIndex((*links)[Rnd(link->GetNumItems())]);//Pick a random path
			if (!link)
			{
				RemoveMovingObject(sector);
				_printf("Couldn't find link[%d]...\n", (*links)[0]);
				return update;
			}

			CStructHeader* _pos;
			if (link->GetStruct(Checksums::Position, &amp;amp;_pos))
			{
				goal = *_pos->pVec;
				timer = 0;
				float distance = D3DXVec3Length(&amp;amp;(pos - goal));
				end = distance / speed;
				D3DXVECTOR3 pathHeading = goal - pos;
				goalAngle = D3DXVECTOR3(0, AngleY(orient, pos, goal), 0);

				if (angle.y != goalAngle.y &amp;amp;&amp;amp; goalAngle.y)
				{
					(*(Matrix*)&amp;amp;orient).RotateYLocal(goalAngle.y);
					(*(Matrix*)&amp;amp;orient).OrthoNormalizeAbout2(Y);

					bboxMax = D3DXVECTOR3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
					bboxMin = D3DXVECTOR3(FLT_MAX, FLT_MAX, FLT_MAX);
					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++)
					{
						sector->vertices[i] -= pos;
						D3DXVec3TransformCoord(&amp;amp;sector->vertices[i], &amp;amp;sector->vertices[i], &amp;amp;orient);
						sector->vertices[i] += pos;
						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;
					}

					sector->bboxMax = bboxMax;
					sector->bboxMin = bboxMin;

					angle = goalAngle;

					return true;
				}

				return update;
			}

			RemoveMovingObject(sector);
			_printf("Couldn't find node -> position[%d]...\n", (*links)[0]);
			return update;
		}
		else
		{
			direction = goal - pos;
			D3DXVec3Normalize(&amp;amp;Velocity, &amp;amp;direction);
			Velocity *= speed * delta;

			if (Velocity)
			{

				sector->bboxMax += Velocity;
				sector->bboxMin += Velocity;
				for (DWORD i = 0; i < sector->numVertices; i++)
				{
					sector->vertices[i] += Velocity;
				}
				pos += Velocity;

				return true;
			}
		}
		break;
	case ANGULAR_VELOCITY:
		if (acceleration.x || acceleration.y || acceleration.z || goalAngle.x || goalAngle.y || goalAngle.z)
		{
			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(&amp;amp;nodeRotation, 0, velocityAngle, 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(&amp;amp;sector->vertices[i], &amp;amp;sector->vertices[i], &amp;amp;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
		}
		break;
	}
	return false;
}
Advertisement

BTW is there anyway I can rotate the bbox so I don't need to recalculate it every frame?

vadru said:

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(&amp;amp;rotation, -angle->y + D3DX_PI, angle->x, angle->z);
 D3DXMatrixTranslation(&amp;amp;translation, position->x, position->y, position->z);
 D3DXMatrixMultiply(&amp;amp;result, &amp;amp;rotation, &amp;amp;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*)&amp;amp;vertices[i], (D3DXVECTOR3*)&amp;amp;vertices[i], &amp;amp;result);
 }

I was thinking that maybe I have to clarify that this function here is only used when converting the levels from original format to the targeted game format. I'm not modifying the object in any other way than this update function and the game itself is also not modifying the object in anyway since it's supposed to be static and that's why I have created this custom Update function that gets called each frame in a Hooked Present call. And since all of the models already are placed corectly in the level, this does not need to be changed.

_WeirdCat_ said:

not sure why you pass references everywhere & but its ok. (Not.familiar with D3DXVECTOR3)

As a sidenote, the reason I pass references is because of performance. Atleast the last time I checked if you pass a class or struct without reference it needs to do a copy of that object, howhever if you pass reference it don't need to make a copy and thus saves a few instructions. But maybe newer language standards allow to optimize this?

I really have no idea why it translates instead of rotation my guess is that you pass wrong data structure and it happens that translation is in place of rotation. Maybe you dont convert everything back again to this two byte float? And this is the reason? Bad alginement? Im out of ideas.

You say this is a game you mean thps2 for ps1 or what, it seems you update buffers that game already uses, game can change sizes of the buffers and it may be that you wont notice that, if this is a 3rd party game you attemp to modify. Its really difficult to find out whats going on.

Fixed timesteps seem bit awkward too but its not my place to judge

Code seems legit

You apply rot then translation

And transform each vertex by it, however this object does not change its position anyway, it stands in one place whole time, maybe you are fighting rigged(hardcoded) animation?

Advertisement

The game that I'm modifying is thps3 and the level is converted from THPS2x(xbox version of thps1-2 for ps1).
The object is a static object and is not modified in anyway and the code FOLLOW PATH LINKED works fine without any issues. So why does this code not work? It's weird. And I'm not modifying any vertexbuffers, I'm modifying the original vertices and then sending a state to the game to update the vertexbuffer. Which the game does fine by itself and it works for FOLLOW PATH LINKED and MOVE TO POS.

And the 2 byte float is not used in this game, it's in the original level file format from thps2x.

This is the only thing that I'm modifying and the game NEVER modifies the vertices
or update the vertexbuffer, unless you alt tab or have uv animation, 
which this object don't have and I have double checked that it don't update the buffer. 

Because the SuperSectors are supposed to be static, but I'm making them move by modifying the vertices
and then telling the game to update the vertexbuffer.

struct Mesh
{
	DWORD state;
	WORD flags;
	WORD numUnk;
	WORD numUnk1;
	WORD padding;
	DWORD* pNull;
	WORD* indices;


	void Update()
	{
		_printf("Updating VertexBuffer\n");
		state |= 0x200;
    }
};

EXTERN struct SuperSector
{
	DWORD FFFFFFFF;
	WORD* indices;
	D3DXVECTOR3* vertices;
//these are the vertices I modify
	DWORD* pUnk1;//always NULL?
	DWORD* pUnk2;//Looks like axis for CollisionTree?
	DWORD* pUnk3;//Huge data...
	DWORD* pUnk4;//Looks like colors?
	Object* object;
	DWORD* pUnk6[6];//Points to itself
	D3DXVECTOR3 bboxMax;
	D3DXVECTOR3 bboxMin;
	DWORD* pUnk7;//Always NULL?
	Mesh* mesh;
	DWORD* pUnk9;//Always NULL?
	WORD padding;
	WORD numVertices;
	WORD numIndices;
	WORD unk1;//maybe flags?
	DWORD* pUnk10;//maybe this is the leaf??
	DWORD* pUnk11;//Always NULL?
	DWORD flag;//always 6?
	DWORD* pUnk12;//Always NULL?
	WORD* pCollisionFlags;//the flags for skatable, trigger etc
	DWORD* pUnk13;//bunch of 0xFF and random data
	DWORD* pUnk14;//bunch of 00
	BYTE unknown2;//Used when collision checking
	BYTE padding2[3];
	DWORD unknownChecksum;// Checksum??
	DWORD* pUnk16;//bunch of floats, nothing happens when changing them maybe center/position?
	DWORD name;//crc32
	DWORD* pUnk17;//similar to pUnk16

	//004fea30 00412230
	EXTERN static SuperSector* GetSuperSector(DWORD checksum)
	{
		//code to get index from checksum
		static DWORD* pSectors = 0;
		_asm mov eax, [0x0085A4B8];
		_asm mov eax, [eax];
		_asm test eax, eax;
		_asm jne con;
		_asm xor eax, eax;
		_asm ret;
	con:
		_asm mov edx, checksum;
		_asm and edx, 0x00003FFF;
		_asm lea edx, [edx + edx * 2];
		_asm lea ecx, [eax + edx * 4];
		_asm mov pSectors, ecx;


		//the SuperSectors are stored in a list 
		//each item is 8 bytes, the first 4 bytes is checksum and last 4 bytes is pointer to the SuperSector
		//since 2 or more checksums can have the same index we need to loop until we get a checksum match
		//if we find an uninitialized item before we get a checksum match it means the checksum is not in the list
		while (*pSectors != 0)//Continue until found an uninitialized item
		{

			if (*pSectors == checksum)//Checksum match
			{
				pSectors++;//skip 4 bytes to get the pointer
				return (SuperSector*)*pSectors;//return the pointer
			}
			pSectors += 2;//skip 8 bytes to get next item in the list
		}
		MessageBox(0, "return NULL", "", 0);//Checksum is not in the list
		return NULL;
	}

	//Used in the scripts create/kill/shatter/visible/invisible, the state will get updated the next frame
	void SetState(MeshState state)
	{

		typedef void(__cdecl* const pSetMeshState)(DWORD index, DWORD state, CScript* pScript);
		CScript pScript;
		pSetMeshState(0x00418DD0)(Node::GetNodeIndex(name), state, &amp;amp;amp;pScript);
	}
};

This topic is closed to new replies.

Advertisement