www.tombraiderforums.com  

Go Back   www.tombraiderforums.com > Tomb Raider Level Editor and Modding > Tomb Raider Modding

Reply
 
Thread Tools
Old 06-09-18, 15:38   #801
Kami
Hobbyist
 
Kami's Avatar
 
Join Date: Sep 2018
Location: South Africa
Posts: 61
Default

Quote:
Originally Posted by nakamichi680 View Post
Thank you for all your help!

I've looked on this site regularly I'm not sure if the classic Lara that Cristina has made is the same one though, I remember downloading the mod from Zac Medley's site when I was a kid but I never knew how to work the skin loader so I just left it. I no longer have the PC that I downloaded the mod to. All the old links are dead, I believe I saw the mod in a YouTube video but I could be mistaken.

https://youtu.be/FpKyWVpsiV8
__________________
"The entrance is underwater. No problem."

Last edited by Kami; 06-09-18 at 15:52.
Kami is offline   Reply With Quote
Old 06-09-18, 16:30   #802
edups
Hobbyist
 
Join Date: May 2018
Posts: 26
Default

Well i just tried to use SLGC to install an south pacific outfit i got from http://www.*****************/community...moddings2.html ,but even after following all instructions,when i try to inmport the bumpmap,it gives me a 'Runtime error 9' and i cant figure out what the issue is.
edups is offline   Reply With Quote
Old 11-09-18, 06:34   #803
nakamichi680
Explorer
 
nakamichi680's Avatar
 
Join Date: Feb 2015
Location: Italy
Posts: 873
Default

Hi! I don't know if you have ever noticed that gas on PS2 has a rotating effect while PC gas is static. I managed to restore this effect and, since I find this mod quite interesting from a modding perspective I'd like to know if someone is interested in learning how it works. I could write a nice post like what Arsunt did with the waving PS1 inventory a while ago. But before doing this I want to make sure that there is someone who is going to read it because I don't want to waste my time
Take note that there will be some mathematical equations and a lot of assembly stuff.
nakamichi680 is offline   Reply With Quote
Old 11-09-18, 06:57   #804
Deathly Karma
Historian
 
Deathly Karma's Avatar
 
Join Date: Mar 2015
Location: United Kingdom
Posts: 401
Default

Whilst I personally wouldn't understand the more complicated parts, I would be interested in reading about how you achieved this.
__________________
"D'you know, it's going to be real pleasure to shut you up."
Deathly Karma is offline   Reply With Quote
Old 14-09-18, 10:30   #805
nakamichi680
Explorer
 
nakamichi680's Avatar
 
Join Date: Feb 2015
Location: Italy
Posts: 873
Exclamation TR AoD Gas cloud rotation effect

Hello there!
I've recently fixed another PS2 missing effect in the PC version of The Angel of Darkness and this time I've decided to share with you the entire process that led to this result. I hope you can learn something from it.
The missing effect we're going to explore together is the rotating gas; it can be found in many levels: Derelict Apartment Block, Galleries Under Siege, Bio-Research Facility, Maximum Containment Area, to name a few. In PC version the gas does not rotate and this greatly reduces the immersivity.
But, without wasting any more time, let's start!


Introduction
To begin with, let's compare PS2 and PC versions.

https://www.youtube.com/watch?v=Dn7A6OKLOVI

Enabling wireframe mode in PC version (F11) and then hiding room and object meshes (SHIFT+F3, SHIFT+F4) allows us to see better our gas sprites. All these black squares are static whilst they should rotate. You can notice that each square has a diagonal; keep this mind, we'll return to it in a moment.




Finding the bug
Ok, to fix this bug we first have to find it, right? Let's move to IDA and have a look to game functions. Using filter function of "Function name" tab I've filtered all game functions containing "gas" in their name.



The first function we're going to explore is fxInsertGas. We can guess from its name that this function is designed to add gas sprites to the environment. Here's the function's body:



For simplicity I've already added three variables names here (GasCloud_Rotation, GasCloud_Diagonal and GasCloud_Colour). It wasn't like that the first time I opened that function. Anyway, every time this function is called, a new GASCLOUD struct is created. This struct contains all the information the game needs to proper render a single gas sprite like its position, its colour, its size and... its rotation. This is how this struct should have looked in the original source code:

Code:
struct GASCLOUD
{
	float X;			// Center of the sprite
	float Y;			// Center of the sprite
	float Z;			// Center of the sprite
	float W;			// Always 1
	float diagonal;			// Diagonal lenght
	float rot_angle;		// Rotation angle
	unsigned int ARGB;		// Gas colour
	unsigned short Unknown1;
	unsigned short Unknown2;
};
Ok, now we have to check if this function writes the rotation angle properly. To do this, we launch the game and open it inside Cheat Engine. After reaching the exact memory region we can have a look at all GASCLOUD structs.



Hummmm, all those red spots ranging from 0 to 360 look much like rotation degrees to me. So it means that this function is fine because it writes the rotation angle without a hitch. We can conclude that the rotation angle is written for nothing because there's no other function that reads it. Time to go back to IDA...



Now, let's move to fxParticleAdd_GasCloud function. This function has the following four parameters:
  • struct GASCLOUD *: a pointer to one GASCLOUD struct. This is one of the inputs of this function
  • struct VF_VCT **: a pointer to a list of pointers to a struct called VF_VCT. This is the output vertex buffer that will contain the 4 vertices the sprite is made of (remember that the sprite is a square). VCT stands for Vertex, Colour and Texture: each VF_VCT struct contains information on vertex position, vertex colour and texture coordinates. Each struct is 96 bytes long (24 bytes per vertex).
Code:
struct VF_VCT
{
	float v1_X;
	float v1_Y;
	float v1_Z;
	unsigned int v1_colorBGRA;
	float v1_U;
	float v1_V;
	float v2_X;
	float v2_Y;
	float v2_Z;
	unsigned int v2_colorBGRA;
	float v2_U;
	float v2_V;
	float v3_X;
	float v3_Y;
	float v3_Z;
	unsigned int v3_colorBGRA;
	float v3_U;
	float v3_V;
	float v4_X;
	float v4_Y;
	float v4_Z;
	unsigned int v4_colorBGRA;
	float v4_U;
	float v4_V;
};
  • const struct SYS_DRAW_CAMERA_VIEW *: a pointer to the camera matrix. This is another input and it's used to keep the sprite always parallel to the frustum view-plane
  • struct SYS_DRAW_BUFFER_DEVICE *: another input used to gaining information on the frustum (I guess)
So, basically, this function:
  1. Reads the center of the sprite and the diagonal lenght from GASCLOUD struct
  2. Creates 4 vertices
  3. Applies some perspective transforms to their coordinates
  4. Saves them in the vertex buffer ready to be shipped to their final destination, the VRAM

This is how vertices look inside the vertex buffer:



How to read the picture:
  • Four rows: four vertices, one sprite. Each row is a vertex
  • First column (A0): v_X
  • Second column: v_Y
  • Third column: v_Z
  • Fourth column: v_colorBGRA (this number looks strange in the picture because Cheat Engine is interpreting it as a float but it is not)
  • Fifth column: v_U
  • Sixth column: v_V

Finding a fix
Those who have studied trigonometry at school may know where this is going: we have 4 vertices and we need to rotate them around the center of the square and put them back inside the vertex buffer. That sounds easy when you say it, but it's not. I'll sum up here all the steps required to achieve this goal:
  1. Read the rotation angle A and convert it to radiants
  2. Calculate sine and cosine of A
  3. Read bottom-left (v1) and top-right (v4) vertices of the sprite and calculate the center of the square
  4. Translate the 4 vertices so that the center of the square is at the origin of the Cartesian plane
  5. Rotate the 4 vertices with this formula:
    - Xnew = X * cosA - Y * sinA
    - Ynew = X * sinA + Y * cosA
  6. Translate the 4 vertices back to the original position
  7. Overwrite old vertices coordinates inside the vertex buffer with the new ones

Creating the fix
It drives me crazy thinking that writing this fix in C++ would have taken no more than 10 minutes for a Core Design programmer. Unfortunately for us, there's no way to escape assembly so we have to do this in the hard way.
First we need a codecave, a region of memory where we can write our instructions. We will tell the game to jump to that codecave, run our instructions and then jump back to the ending of fxParticleAdd_GasCloud function. Since relying on codecaves is questionable and it's not easy to find large ones, I've implemented in my TRAOD Multipatcher an assembly injection engine that automatically allocates new space at the end of the file.
In this guide we'll assume that we already have our allocated space for the code. We will also allocate 16 extra bytes (4 double words) for temporarily storing some data during calculations; we'll call them DATA1, DATA2, DATA3 and DATA4.

1) Read the rotation angle A and convert it to radiants
Code:
	mov edx, [ebx+8]		// Reads the pointer to GASCLOUD struct
	fld dword ptr [edx+14]		// Pushes the rotation angle onto the top of the FPU (floating point unit) stack
	fldpi				// Pushes Pi onto the top of the FPU stack
	fmulp st(1),st(0)		// Multiplies the rotation angle by Pi and pops Pi
	fdiv dword ptr [009511C0]	// Divides the result of the multiplication by 180. The rotation angle in radiants is now at the top of the stack, st(0)
2) Calculate sine and cosine of A
Code:
	fsincos				// After this instruction we have the sine in st(1) and the cosine in st(0)
3) Read bottom-left (v1) and top-right (v4) vertices of the sprite and calculate the center of the square
Code:
(1)	movlps xmm0, [eax]		// Reads X and Y of vertex 1
(2)	movlps xmm1, [eax+48]		// Reads X and Y of vertex 4
(3)	addps xmm0, xmm1		// Sums X1+X4 and Y1+Y4
(4)	movlps xmm1, [00717940]		// Loads 2 - 2
(5)	divps xmm0, xmm1		// Divide (X1+X4) and (Y1+Y4) by 2
	movlps [DATA1], xmm0		// Save the result into memory. This step is required to load the result to the upper part of xmm0 register
(6)	movhps xmm0, [DATA1]		// After this instruction xmm0 contains xy coordinates of the center of the sprite in both lower and upper part of the register


4) Translate the 4 vertices so that the center of the square is at the origin of the Cartesian plane
Code:
	movlps xmm1, [eax]		// Loads X1/Y1
	movhps xmm1, [eax+18]		// Loads X2/Y2
	movlps xmm2, [eax+30]		// Loads X3/Y3
(7)	movhps xmm2, [eax+48]		// Loads X4/Y4
	subps xmm1, xmm0		// Subtracts the center of the sprite from v1 and v2
(8)	subps xmm2, xmm0		// Subtracts the center of the sprite from v3 and v4
In the following picture (X1+X4)/2 and (Y1+Y4)/2 have been respectevely renamed XC and YC



5) Rotate the 4 vertices
Code:
	movaps xmm3, xmm1		// Copies X1/Y1/X2/Y2 from xmm1 to xmm3 register
(9)	movaps xmm4, xmm2		// Copies X3/Y3/X4/Y4 from xmm2 to xmm4 register
	fst [DATA1]			// Stores cosine in DATA1
	fstp [DATA4]			// Stores and pops cosine in DATA4
	fst [DATA2]			// Stores sine in DATA2
(10)	fstp [DATA3]			// Stores and pops sine in DATA3
	movlps xmm7, [DATA1]		// Loads cos-sin into lower part of xmm7
(11)	movhps xmm7, [DATA1]		// Loads cos-sin into upper part of xmm7
	mulps xmm1, xmm7
(12)	mulps xmm2, xmm7
	movlps xmm7, [DATA3]		// Loads sin-cos into lower part of xmm7
(13)	movhps xmm7, [DATA3]		// Loads sin-cos into upper part of xmm7
	mulps xmm3, xmm7
(14)	mulps xmm4, xmm7
(15)	movaps xmm5, xmm1
(16)	psrldq xmm5, 4			// Shifts xmm5 register by 4 bytes to the lower part. In this way X and Y are aligned so we can subtract them
(17)	subps xmm1, xmm5		// In the picture below we're going to use only a and c values
	movaps xmm5, xmm2
	psrldq xmm5, 4
	subps xmm2, xmm5
	movaps xmm5, xmm3		// We now have X1new, X2new, X3new, X4new
	psrldq xmm5, 4			// Same as (16) but this time we need to add them
	addps xmm3, xmm5
	movaps xmm5, xmm4
	psrldq xmm5, 4
(18)	addps xmm4, xmm5		// We now have Y1new, Y2new, Y3new, Y4new
	movaps [DATA1], xmm3		// The purpose of the following instructions is to rearrange x1/x2 and y1/y2 values position inside xmm1 register
	mov ecx, [DATA1]
	mov ebp, [DATA3]
	movaps [DATA1], xmm1
	mov [DATA2], ecx
	mov [DATA4], ebp
	movaps xmm1, [DATA1]		// xmm1 is now ready
	movaps [DATA1], xmm4		// The purpose of the following instructions is to rearrange x3/x4 and y3/y4 values position inside xmm2 register
	mov ecx, [DATA1]
	mov ebp, [DATA3]
	movaps [DATA1], xmm2
	mov [DATA2], ecx
	mov [DATA4], ebp
(19)	movaps xmm2, [DATA1]		// xmm2 is now ready
In the following picture X1-XC, Y1-YC and so on have been respectevely renamed X1', Y1'...



6) Translate the 4 vertices back to the original position
Code:
	addps xmm1, xmm0
	addps xmm2, xmm0
This step is the opposite of step 4. Remember that we still have XC and YC in xmm0.

7) Overwrite old vertices coordinates inside the vertex buffer with the new ones
Code:
	movlps [eax], xmm1		// Copies X1new and Y1new in the vertex buffer
	movhps [eax+18], xmm1		// Copies X2new and Y2new in the vertex buffer
	movlps [eax+30], xmm2		// Copies X3new and Y3new in the vertex buffer
	movhps [eax+48], xmm2		// Copies X4new and Y4new in the vertex buffer
At the end of this step everything is done so we can jump back to fxParticleAdd_GasCloud. Time to test it ingame!!

https://www.youtube.com/watch?v=1IqPP4QZpzs

Last edited by nakamichi680; 17-11-19 at 11:06.
nakamichi680 is offline   Reply With Quote
Old 14-09-18, 18:37   #806
Joey79100
Professor
 
Joey79100's Avatar
 
Join Date: Mar 2012
Location: France
Posts: 3,686
Default

This sure was an interesting read!
I can't read assembler, because I never learned and couldn't figure out how it was working with so few instructions and seemingly cryptic language, what were registers and what they were for, etc, but with your explanations I think I understood some things about it (you work with bytes, and to make calculations between variables you store them in a stacka stack and then apply an operation on that stack, something along those lines? It's still very blurry but I have an idea now). It's very low-level but it seems it's even lower than I originally thought.
And gas definitely looks better with rotation! I always found it weird how it was so static, but I never knew on PS2 there was rotation.

Last edited by Joey79100; 14-09-18 at 18:39.
Joey79100 is offline   Reply With Quote
Old 14-09-18, 19:30   #807
nakamichi680
Explorer
 
nakamichi680's Avatar
 
Join Date: Feb 2015
Location: Italy
Posts: 873
Default

The code above makes use of ecx, edx and ebp (which are three 32 bits general purpose registers of x86 CPUs, the others are eax, ebx, esi, edi, esp and eip), FPU (which has 8 registers organized into a stack) and SSE registers (from xmm0 to xmm7, each one 128 bits large). This code is for p3 and p4 versions of the executable since both Pentium 3 and Pentium 4 support SSE instructions.

TRAOD.EXE is for CPUs that doesn't support SSE so I had to rewrite the code above to use only the FPU. This is the non-SSE code (tested and fully working):
Code:
mov ecx, [ebp+8]	
fld dword ptr [ecx+14]
fldpi
fmulp st(1),st(0)
fdiv dword ptr [0062C170]
fsincos
fstp [DATA1]
fstp [DATA2]
fld dword ptr [edi-48]
fadd dword ptr [edi]
fdiv dword ptr [00634F20]
fstp [DATA3]
fld dword ptr [edi-44]
fadd dword ptr [edi+4]
fdiv dword ptr [00634F20]
fstp [DATA4]
fld dword ptr [edi-48]
fsub dword ptr [DATA3]	
fmul dword ptr [DATA1]	
fld dword ptr [edi-44]
fsub dword ptr [DATA4]	
fmul dword ptr [DATA2]	
fsubp st(1), st(0)	
fadd dword ptr [DATA3]	
fld dword ptr [edi-48]
fsub dword ptr [DATA3]	
fmul dword ptr [DATA2]	
fld dword ptr [edi-44]
fsub dword ptr [DATA4]	
fmul dword ptr [DATA1]	
faddp st(1), st(0)	
fadd dword ptr [DATA4]	
fstp [edi-44]
fstp [edi-48]
fld dword ptr [edi-30]
fsub dword ptr [DATA3]	
fmul dword ptr [DATA1]	
fld dword ptr [edi-2C]
fsub dword ptr [DATA4]	
fmul dword ptr [DATA2]	
fsubp st(1), st(0)	
fadd dword ptr [DATA3]	
fld dword ptr [edi-30]
fsub dword ptr [DATA3]	
fmul dword ptr [DATA2]	
fld dword ptr [edi-2C]	
fsub dword ptr [DATA4]	
fmul dword ptr [DATA1]	
faddp st(1), st(0)	
fadd dword ptr [DATA4]	
fstp [edi-2C]
fstp [edi-30]
fld dword ptr [edi-18]
fsub dword ptr [DATA3]	
fmul dword ptr [DATA1]	
fld dword ptr [edi-14]
fsub dword ptr [DATA4]	
fmul dword ptr [DATA2]	
fsubp st(1), st(0)	
fadd dword ptr [DATA3]	
fld dword ptr [edi-18]
fsub dword ptr [DATA3]	
fmul dword ptr [DATA2]	
fld dword ptr [edi-14]
fsub dword ptr [DATA4]	
fmul dword ptr [DATA1]	
faddp st(1), st(0)	
fadd dword ptr [DATA4]	
fstp [edi-14]
fstp [edi-18]
fld dword ptr [edi]
fsub dword ptr [DATA3]	
fmul dword ptr [DATA1]	
fld dword ptr [edi+4]
fsub dword ptr [DATA4]	
fmul dword ptr [DATA2]	
fsubp st(1), st(0)	
fadd dword ptr [DATA3]	
fld dword ptr [edi]
fsub dword ptr [DATA3]	
fmul dword ptr [DATA2]	
fld dword ptr [edi+4]
fsub dword ptr [DATA4]	
fmul dword ptr [DATA1]	
faddp st(1), st(0)	
fadd dword ptr [DATA4]	
fstp [edi+4]
fstp [edi]
add edi, 18
mov [edx], edi
jmp 0046D28F

Last edited by nakamichi680; 18-09-18 at 21:08.
nakamichi680 is offline   Reply With Quote
Old 14-09-18, 20:31   #808
SLAYER
Archaeologist
 
SLAYER's Avatar
 
Join Date: Jun 2006
Location: Jordan
Posts: 2,259
Default

Quote:
Originally Posted by nakamichi680 View Post
I want to make sure that there is someone who is going to read it because I don't want to waste my time
Documenting your progress, especially in a highly technical and obscure topic, is never a waste of time.

Though this is going to take many cups of coffee to understand, even if you knew (some) assembly.

I admire your dedication
__________________
Right now, I'm losing patience
SLAYER is offline   Reply With Quote
Old 14-09-18, 21:11   #809
Ruu11
Archaeologist
 
Ruu11's Avatar
 
Join Date: Sep 2017
Location: Spain
Posts: 1,043
Default

Literally ME trying to understand all
those formulas to fixing the gas effect:


I'm in love with this little things. When PC version finally got all its issues fixed, the game will be so enjoyable to play...
Nice work as always, Nakamichi.

Last edited by tlr online; 14-09-18 at 21:29. Reason: Image removed. See FAQ on posting images.
Ruu11 is offline   Reply With Quote
Old 15-09-18, 01:35   #810
Laras Dream
Archaeologist
 
Laras Dream's Avatar
 
Join Date: Aug 2009
Location: Ireland
Posts: 2,106
Default

A very educational read
Laras Dream is offline   Reply With Quote
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off



All times are GMT. The time now is 15:51.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2023, vBulletin Solutions Inc.