Jump to content


Photo
* * * * * 1 votes

Converting 3D World Coordinates to 2D Screen Coordinates


  • Please log in to reply
2 replies to this topic

#1 attilathedud

attilathedud

    Supervisor

  • Staff
  • 251 posts

Posted 05 February 2011 - 04:10 AM

Before I start, I would like to put forth that I would count Math amoungst my worst subjects - and Geometry amoungst my worst fields in Math. This partially has to do with the fact I took Geometry back when I was in eight-grade, and that, like every thirteen year-old, I was an idiot who did not care about learning. As such, there might be mistakes in the method I am about to put forth; all I know is that it works, and that to my mind, it seems correct. Feel free to mention any mistakes you find.

Consider the following scene; you are the entity marked "P," there is another entity marked "E," and there is the origin named "O:"

Posted Image

Strip away the buildings and all the fancy graphical effects, and this boils down to:

Posted Image

Now knowing this, how are we to translate the entity E's position into a 2D coordinate? Well the first measure is to understand what we actually mean. There is no way to directly translate a 3D coordinate into a 2D coordinate, just as there is no way to directly translate a cube into a square; however, notice the word "directly." Let us continue with the example of a cube - there obviously has to be a way to translate a cube into a square, or at least into a 2D space, otherwise something like:

Posted Image

would simply not be possible. So how do we go about this? Whenever we choose to draw a 3D object on a 2D plane (such as paper), we must first pick a view-port, which is exactly what it sounds like - a static view of the scene with a fixed source. To turn a cube into a square, we simply pick the view-port to be directly facing one of the sides of the cube, as so:

Posted Image

To model the earlier model (the common drawing that is used to represent a cube), we simply place the view-port so that it looks directly at one of the angles:

Posted Image

This same idea applies to any F.P.S.'s - in fact, it is central to the notion of them. Most modern F.P.S.'s conceive the illusion that as you move, you move your character around the map; this is simply not true. When "moving," all one is doing is adjusting their view-port in relation to the world. To track entity location then, most games make use of a series of 3D vectors that give the location of an entity relative to the origin of the map.

Knowing this, let us now go back to original representation of our scene and make it more accurate:

Posted Image

If we eliminate the Y-axis in our problem (we will assume the map is constantly flat), we can take a top down view of the above image, and greatly simplify our task:

Posted Image

Given that these two entities could be at any location on the map (even though we assume they are always in Quadrant I), it makes sense to stop trying to focus the scene around the map's origin, and instead makes sense to allow our entity P to be the origin - we can do this by subtracting our origin vector and the enemy's origin vector. The result will be the absolute distance of the entity E from the entity P.

Assume vAbsDistance is a three dimensional vector. Let vAbsDistance be equal to:

vAbsDistance = vEnemy - vPlayer


In relation to operations on vectors, let:

vAbsDistance.x = vEnemy.x - vPlayer.x
vAbsDistance.y = vEnemy.y - vPlayer.y [not needed right now, but we will keep it so we can add in the Y-Axis later]
vAbsDistance.z = vEnemy.z - vPlayer.z


The vector vAsbDistance will now be a vector from the origin to an object we will call E' (E prime):

Posted Image

Now let us incorporate our view-port. Most games make use of either trio of angles that represent the yaw, pitch, and roll of a player's view-port relative to some origin (usually facing directly North or South, East or West, directly above or directly below) or a trio of 3D vectors that represent the same thing, but in vector format. For us, working with vectors is the easiest way for us to complete our task - luckily, these view-angles can be translated to vectors with some math.

* Call of Duty 4 uses both of these methods; they are held in refdef->ViewAngles (a vec3_t) and refdef->ViewAxis (an array of vec3_t's) respectively. *

* A vec3_t is nothing more than float[ 3 ]. *

Imposing the vectors that represent the view-port, and assuming we are looking at the origins, we receive:

Posted Image

By eliminating the fluff, and separating the vectors, we are left with two vector compliments:

Posted Image

Enter the Dot Product. If we let A and B be two vectors, than the Dot Product is nothing more than ( A.x * B.x ) + ( A.y * B.y ) + ( A.z * B.z ). However, this value has some interesting attributes:

Let A and B be two vectors, and let |A| and |B| be the length of A and B respectively, and let |A| and |B| not equal 0. Then:

A . B = |A||B|cos( theta )


Where theta is the angle represented by the image below:

Posted Image

In our current example, this will give us the value that will represent the distance E' is away from our view-vector. As such, we will take the Dot Product of both sets of vectors, and store them appropriately.

Assume vRightTransform and vForwardTransform are two floats. Let these equal:

vRightTransform = vAbsDistance . vRightView
vForwardTransform = vAbsDistance . vForwardView


Now we quickly need to take a diversion to account for something - the Dot Product is signed operation, meaning it will work regardless of whether or not the vector is negative. Consider the case:

Posted Image

In our following equation to transform our values to a 2D coordinate, we assume vForwardTransform is positive; as such, if the enemy is behind us, then our equation will give us the same value as if he was in front of us. To account for this fact, we will need to ensure that vForwardTransform is positive before continuing! Keep this in mind for later, when we code this!

We are on to the final steps - let us now consider our problem focusing on our view-port:

Posted Image

In a picture I have now lost the link to, it was explained that X is equal to the result of:

Posted Image

This of course can be reduced to:

X = C + [ (C * X') / (Z'* FOV) ]


We, of course, have X' and Z' as result of our operations with the Dot Product - in addition, C and FOV are held in refdef->Width / 2 and refdef->FOVx respectively. Letting centerX equal refdef->Width / 2, this becomes:
X = centerX + ( ( centerX * vRightTransform ) / ( vForwardTransform * refdef->FOVx ) )

As such, we have concluded our series of steps to get our 2D coordinate (in the X-Axis, at least):
vAbsDistance = vEnemy - vPlayer

vRightTransform   = vAbsDistance . vRightView 
vForwardTransform = vAbsDistance . vForwardView 

if( vForwardTransform < 0 )
	return

Let centerX = refdef->Width / 2

X = centerX + ( ( centerX * vRightTransform ) / ( vForwardTransform * refdef->FOVx ) )

There is a quick issue to address before we start coding this - Call of Duty 4 holds our right view vector opposite of how we want it (which causes problems similar to the vForwardTransform being negative). To fix this, we simply need to multiply the three elements of the right view vector before using them. As such, our code becomes:
vAbsDistance = vEnemy - vPlayer

vRightView *= -1

vRightTransform   = vAbsDistance . vRightView 
vForwardTransform = vAbsDistance . vForwardView 

if( vForwardTransform < 0 )
	return

Let centerX = refdef->Width / 2

X = centerX + ( ( centerX * vRightTransform ) / ( vForwardTransform * refdef->FOVx ) )

* Brief break to jam out. *

Explaining how to hook, how to find a place to hook, and how to find the Draw_Text would be reduntant and make this already long article longer. You can find out how to do these by reading my tutorial "Drawing Text Using Engine Functions" located here: http://www.doxcoding....php?f=27&t=256

Likewise, explaining how to find the structures and their members is also pointless, as I explained such concepts in my tutorial "Understanding What Structures Mean in Relation to FPS Games" located here: http://www.doxcoding....php?f=27&t=386

* Please note a few things - in regards to the Draw_Text tutorial, in this code, I have hooked R_EndFrame located at 42c010h instead of the location I was hooking before. Also, I am using the game's Draw_Text_To_Frame_Scene located at 5f6b00h as opposed to the Draw_Ingame_Menu_Text I used in the tutorial. In regards to the structures, please note that even though I said the size of cg_entities is 77000h, this is simply the game clearing a giant section of memory to hold the cg_entities array. The true size of cg_entities is 1dch. *

With the above considerations, the following base code exists:
.386
.model flat, stdcall
option casemap: none

;include needed functions
VirtualAlloc proto stdcall :DWORD, :DWORD, :DWORD, :DWORD
VirtualProtect proto stdcall :DWORD, :DWORD, :DWORD, :DWORD
VirtualFree proto stdcall :DWORD, :DWORD, :DWORD

includelib \masm32\lib\kernel32.lib

.data
	ori_print_text  dd 		5f6b00h
	x				real4	0.0f
	y				real4	300.0f
	negate			real4	-1.0f
	
.code
	_main:
	
		push ebp
		mov ebp, esp
		mov eax, dword ptr ss:[ ebp + 0ch]
		cmp eax, 1
		jnz @DLL_NOT_ATTACH
		push eax
		push 40h
		push 1000h
		push 4
		push 0
		call VirtualAlloc 
		mov ebx, eax
		push ebx
		push 40h
		push 5
		push 42c011h
		call VirtualProtect 
		mov byte ptr ds:[ 42c011h ], 0e8h
		lea ecx, @R_EndFrame_Hook
		sub ecx, 42c016h
		mov dword ptr ds:[ 42c012h ], ecx
		push ebx
		push dword ptr ds:[ ebx ]
		push 5
		push 42c011h
		call VirtualProtect
		push 4000h
		push 4
		push ebx
		call VirtualFree	
		pop eax
		@DLL_NOT_ATTACH:
			leave
			retn 0ch
	
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;call CoD4's native Draw_Text function 
		;eax = x							   
		;push y on stack					   
		;edx = text							   
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		@draw_text:
			
			push 0
			push 0
			sub esp, 10h
			fld1
			fst dword ptr ss:[ esp + 0ch ]
			fstp dword ptr ss:[ esp + 8 ]
			fld dword ptr ds:[ ebx ]
			fstp dword ptr ss:[ esp + 4 ]
			fld dword ptr ds:[ eax ]
			fstp dword ptr ss:[ esp ]
			mov ecx, dword ptr ds:[ 84cd8ch ]
			push ecx
			push 7fffffffh
			push edx
			mov ecx, 6b4498h
			call dword ptr cs:[ ori_print_text ]
			add esp, 24h
			retn

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;convert 3d origin to 2d coordinates   
		;edi = origin						   
		;will return in variable x			   
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		@WorldToScreen:
		

			retn
			
		;the main display hook
		@R_EndFrame_Hook:
		
			pushad

			popad
			mov eax, dword ptr ds:[ 8c63b8h ]
			retn
		
	end _main

We will begin our job at the @WorldToScreen function; we will assume we are getting the enemy's origins via edi. Now, doing anything related to floats in assembly is well... a complete fucking mess, if you will excuse my French. For an example, take the following lines of C code:
float x = 1.0f;

x = x + 1;

The corresponding assembly code:
fld dword ptr ds:[ DEADBEEFh ] 		;location holding x
fld1					;push 1 on the stack
fadd					;pop st(1) and st(1), push their sum
fstp dword ptr ds:[ DEADBEEFh ]		;pop the top value of the stack into x again

Writing this code, I decided enough people already had fears about assembly, so instead of trying to optimise, I decided to write the code such that it took every piece one section at a time, and did not utilise operations such as FXCH to avoid the memory hit every FSTP brings.

Now, consider our outline:
vAbsDistance = vEnemy - vPlayer

vRightView *= -1

vRightTransform   = vAbsDistance . vRightView 
vForwardTransform = vAbsDistance . vForwardView 

if( vForwardTransform < 0 )
	return

Let centerX = refdef->Width / 2

X = centerX + ( ( centerX * vRightTransform ) / ( vForwardTransform * refdef->FOVx ) )

vPlayer is held in refdef->ViewOrg, which starts at 797618h. Taking the first section:
;vAbsDistance = vEnemy - vPlayer
fld dword ptr ds:[ edi ]			;push origin[ 0 ]
fsub dword ptr ds:[ 797618h ]			;subtract refdef->ViewOrg[ 0 ] from it
fstp dword ptr ds:[ esp ]			;store the result in esp
fld dword ptr ds:[ edi + 4 ]			;push origin[ 1 ]
fsub dword ptr ds:[ 79761ch ]			;subtract refdef->ViewOrg[ 1 ] from it
fstp dword ptr ds:[ esp + 4 ]			;store the result in esp + 4
fld dword ptr ds:[ edi + 8 ]			;push origin[ 2 ]
fsub dword ptr ds:[ 797620h ]			;subtract refdef->ViewOrg[ 2 ] from it
fstp dword ptr ds:[ esp + 8 ]			;store the result in esp + 8

Next, since we need to reverse vRightView, we need to store it in a temporary location. We can accomplish both these in one step:
;vRightView = refdef->Viewaxis[ 1 ]
;vRightView[ 0 ] *= -1
;vRightView[ 1 ] *= -1
;vRightView[ 2 ] *= -1
fld dword ptr ds:[ 797630h ]		;push refdef->ViewAxis[ 1 ][ 0 ]		
fmul dword ptr ds:[ negate ]		;multiply by -1
fstp dword ptr ss:[ esp + 0ch ]		;store in esp + ch
fld dword ptr ds:[ 797634h ]		;push refdef->ViewAxis[ 1 ][ 1 ]
fmul dword ptr ds:[ negate ]		;multiply by -1
fstp dword ptr ss:[ esp + 10h ]		;store in esp + 10h
fld dword ptr ds:[ 797638h ]		;push refdef->ViewAxis[ 1 ][ 2 ]
fmul dword ptr ds:[ negate ]		;multiply by -1
fstp dword ptr ss:[ esp + 14h ]		;store in esp + 14h

Hopefully you are getting the hang of this. For the next couple steps, we will be using esp + 18h, esp + 1ch, and esp + 20h for temporary storage. Recalling what we know about the Dot Product, and recalling from our earlier example about how FADD works, this next step should not be too hard:
;vRightTransform   = vAbsDistance . vRightView
fld dword ptr ds:[ esp ]		;push vAbsDistance[ 0 ]
fmul dword ptr ss:[ esp + 0ch ]		;multiply by vRightView[ 0 ]
fstp dword ptr ss:[ esp + 18h ]		;store in esp + 18h
fld dword ptr ds:[ esp + 4 ]		;push vAbsDistance[ 1 ]
fmul dword ptr ss:[ esp + 10h ]		;mutliply by vRightView
fstp dword ptr ss:[ esp + 1ch ]		;store in esp + 1ch
fld dword ptr ds:[ esp + 8 ]		;push vAbsDistance[ 2 ]
fmul dword ptr ss:[ esp + 14h ]		;multiply by vRightView[ 2 ]
fstp dword ptr ss:[ esp + 20h ]		;store in esp + 20h
			
fld dword ptr ss:[ esp + 18h ]		;push vAbsDistance[ 0 ] * vRightView[ 0 ]
fld dword ptr ss:[ esp + 1ch ]		;push vAbsDistance[ 1 ] * vRightView[ 1 ]
fadd					;pop st(1), st(0), put their sum in st(0)
fld dword ptr ss:[ esp + 20h ]		;push vAbsDistance[ 2 ] * vRightView[ 2 ]
fadd					;pop st(1), st(0), put their sum in st(0)
fstp dword ptr ss: [ esp + 24h ]	;store in esp + 24h

A similar approach for the next Dot Product:
;vForwardTransform = vAbsDistance . vForwardView 
;vForwardView = refdef->ViewAxis[ 0 ]
fld dword ptr ds:[ esp ]
fmul dword ptr ds:[ 797624h ]
fstp dword ptr ss:[ esp + 18h ]
fld dword ptr ds:[ esp + 4 ]
fmul dword ptr ds:[ 797628h ]
fstp dword ptr ss:[ esp + 1ch ]
fld dword ptr ds:[ esp + 8 ]
fmul dword ptr ds:[ 79762ch ]
fstp dword ptr ss:[ esp + 20h ]
			
fld dword ptr ss:[ esp + 18h ]
fld dword ptr ss:[ esp + 1ch ]
fadd
fld dword ptr ss:[ esp + 20h ]
fadd
fstp dword ptr ss:[ esp + 28h ]

Next, we need check if vForwardTransform is less than zero; for this, we will need to use FCOMP:
;if( vForwardTransform < 0 )
;return
fldz					;push 0 
fcomp dword ptr ss:[ esp + 28h ]	;compare this with vForwardTransform
fstsw ax				;throw the results in ax so we can use conditionals
test ah, ah				;compare ah to 0
jnz @continue				;if it is not, continue
add esp, 2ch				;will explain later
retn					;else return
@continue:

Time to grab the center; to reduce the amount of work on myself, I used a small trick to allow me to easily divide refdef->Width by two. Just accept this as it is, or if you desire, go look up why this works :D :
;Let centerX = refdef->Width / 2
mov eax, dword ptr ds:[ 797608h ]	;grab refdef->Width, put in eax
cdq					;extend eax to 64bits, place in edx:eax
sub eax, edx				;subtract edx from eax
sar eax, 1				;shift eax arithmetically right by one
mov dword ptr ss:[ esp + 20h ], eax	;place the result in esp + 20h

On to our last equation:
;X = centerX + ( ( centerX * vRightTransform ) / ( vForwardTransform * refdef->FOVx ) )
fild dword ptr ss:[ esp + 20h ]		;convert refdef->Width / 2 to a float, push
fmul dword ptr ss:[ esp + 24h ]		;multiplay this by vRightTransform
fstp dword ptr ss:[ esp + 18h ]		;store in esp + 18h
fld dword ptr ss:[ esp + 28h]		;push vForwardTransform
fmul dword ptr ds:[ 797610h ]		;multiply by refdef->FOVx
fstp dword ptr ss:[ esp + 1ch ]		;store in esp + 1ch
			
fld dword ptr ss:[ esp + 18h ]		;push centerX * vRightTransform
fld dword ptr ss:[ esp + 1ch ]		;push vForwardTransform * refdef->FOVx
fdiv					;divide the two
fild dword ptr ss:[ esp + 20h ]		;convert refdef->Width / 2 to a float, push
fadd					;add refdef->Width / 2 to the result of the division
fstp dword ptr ds:[ x ]			;store this in x

We are close to done - however, we cannot freely just start throwing data in esp without the risk of corrupting the stack. To use these sections, we need to first allocate them. At the top of our @WorldToScreen function:
sub esp, 2ch		;grab what we need + 4 to account for our call

Likewise, at the bottom:
add esp, 2ch
retn

A little bit of work there, but nothing mind-boggling.

On to the R_EndFrame hook; what we are doing can be summed up by this C code:
for( int i = 0; i < cgs->maxPlayers; i++ )
{
	if( cg_entities[ i ].isAlive && cg_entities[ i ].clientNum != cg->clientNum )
	{
		WorldToScreen( cg_entities[ i ].lerpOrigin, &x );
		Draw_Text( x, y, clientinfo[ i ].name );
	}
}

If we could do the @WorldToScreen function, this should be easy:
@R_EndFrame_Hook:
		
	pushad					;save the registers
	mov edi, 84f2f4h			;move cg_entities[ 0 ].lerpOrigins in edi
	mov edx, 83927ch			;mov clientinfo[ 0 ].name into edx
	xor esi, esi				;set esi to 0
	entities_loop:				;loop
		cmp dword ptr ds:[ edi + 1a4h ], 0	;is cg_entities[ i ] alive?
		jz @not_valid_entity			;jump if not
		mov eax, dword ptr ds:[ edi + 0b0h ]	;place cg_entities[ i ].clientNum in eax
		cmp eax, dword ptr ds:[ 74e338h ]	;is cg_entities[ i ] our player?
		jz @not_valid_entity			;jump if it is
		push edx				;save edx
		call @WorldToScreen			;call our WorldToScreen function
		pop edx					;restore edx
		lea eax, x				;place x in eax
		lea ebx, y				;place a static 300 in ebx
		push edx				;save edx
		call @draw_text				;call our printing routine
		pop edx					;restore edx

		@not_valid_entity:
		add edx, 4cch				;next element of clientinfo
		add edi, 1dch				;next element of cg_entities
		inc esi					;increase esi
		cmp esi, dword ptr ds:[ 74aa48h ]	;check if esi < cgs->maxPlayers
		jl entities_loop			;loop if it is
	popad					;restore registers
	mov eax, dword ptr ds:[ 8c63b8h ]	;restore original function we hooked
	retn					;return

Our final code for comparison:
.386
.model flat, stdcall
option casemap: none

;include needed functions
VirtualAlloc proto stdcall :DWORD, :DWORD, :DWORD, :DWORD
VirtualProtect proto stdcall :DWORD, :DWORD, :DWORD, :DWORD
VirtualFree proto stdcall :DWORD, :DWORD, :DWORD

includelib \masm32\lib\kernel32.lib

.data
	ori_print_text  dd 		5f6b00h
	x				real4	0.0f
	y				real4	300.0f
	negate			real4	-1.0f
	
.code
	_main:
	
		push ebp
		mov ebp, esp
		mov eax, dword ptr ss:[ ebp + 0ch]
		cmp eax, 1
		jnz @DLL_NOT_ATTACH
		push eax
		push 40h
		push 1000h
		push 4
		push 0
		call VirtualAlloc 
		mov ebx, eax
		push ebx
		push 40h
		push 5
		push 42c011h
		call VirtualProtect 
		mov byte ptr ds:[ 42c011h ], 0e8h
		lea ecx, @R_EndFrame_Hook
		sub ecx, 42c016h
		mov dword ptr ds:[ 42c012h ], ecx
		push ebx
		push dword ptr ds:[ ebx ]
		push 5
		push 42c011h
		call VirtualProtect
		push 4000h
		push 4
		push ebx
		call VirtualFree	
		pop eax
		@DLL_NOT_ATTACH:
			leave
			retn 0ch
	
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;call CoD4's native Draw_Text function 
		;eax = x							   
		;push y on stack					   
		;edx = text							   
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		@draw_text:
			
			push 0
			push 0
			sub esp, 10h
			fld1
			fst dword ptr ss:[ esp + 0ch ]
			fstp dword ptr ss:[ esp + 8 ]
			fld dword ptr ds:[ ebx ]
			fstp dword ptr ss:[ esp + 4 ]
			fld dword ptr ds:[ eax ]
			fstp dword ptr ss:[ esp ]
			mov ecx, dword ptr ds:[ 84cd8ch ]
			push ecx
			push 7fffffffh
			push edx
			mov ecx, 6b4498h
			call dword ptr cs:[ ori_print_text ]
			add esp, 24h
			retn
			
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;convert 3d origin to 2d coordinates   
		;edi = origin						   
		;will return in variable x			   
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		@WorldToScreen:
		
			sub esp, 2ch
			;vAbsDistance = vEnemy - vPlayer			
			fld dword ptr ds:[ edi ]
			fsub dword ptr ds:[ 797618h ]
			fstp dword ptr ds:[ esp ]
			fld dword ptr ds:[ edi + 4 ]
			fsub dword ptr ds:[ 79761ch ]
			fstp dword ptr ds:[ esp + 4 ]
			fld dword ptr ds:[ edi + 8 ]
			fsub dword ptr ds:[ 797620h ]
			fstp dword ptr ds:[ esp + 8 ]
			;vRightView = refdef->Viewaxis[ 1 ]
			;vRightView[ 0 ] *= -1
			;vRightView[ 1 ] *= -1
			;vRightView[ 2 ] *= -1
			fld dword ptr ds:[ 797630h ]
			fmul dword ptr ds:[ negate ]
			fstp dword ptr ss:[ esp + 0ch ]
			fld dword ptr ds:[ 797634h ]
			fmul dword ptr ds:[ negate ]
			fstp dword ptr ss:[ esp + 10h ]
			fld dword ptr ds:[ 797638h ]
			fmul dword ptr ds:[ negate ]
			fstp dword ptr ss:[ esp + 14h ]
			;vRightTransform   = vAbsDistance . vRightView
			fld dword ptr ds:[ esp ]
			fmul dword ptr ss:[ esp + 0ch ]
			fstp dword ptr ss:[ esp + 18h ]
			fld dword ptr ds:[ esp + 4 ]
			fmul dword ptr ss:[ esp + 10h ]
			fstp dword ptr ss:[ esp + 1ch ]
			fld dword ptr ds:[ esp + 8 ]
			fmul dword ptr ss:[ esp + 14h ]
			fstp dword ptr ss:[ esp + 20h ]
			
			fld dword ptr ss:[ esp + 18h ]
			fld dword ptr ss:[ esp + 1ch ]
			fadd
			fld dword ptr ss:[ esp + 20h ]
			fadd
			fstp dword ptr ss: [ esp + 24h ]
			;vForwardTransform = vAbsDistance . vForwardView 
			;vForwardView = refdef->ViewAxis[ 0 ]
			fld dword ptr ds:[ esp ]
			fmul dword ptr ds:[ 797624h ]
			fstp dword ptr ss:[ esp + 18h ]
			fld dword ptr ds:[ esp + 4 ]
			fmul dword ptr ds:[ 797628h ]
			fstp dword ptr ss:[ esp + 1ch ]
			fld dword ptr ds:[ esp + 8 ]
			fmul dword ptr ds:[ 79762ch ]
			fstp dword ptr ss:[ esp + 20h ]
			
			fld dword ptr ss:[ esp + 18h ]
			fld dword ptr ss:[ esp + 1ch ]
			fadd
			fld dword ptr ss:[ esp + 20h ]
			fadd
			fstp dword ptr ss:[ esp + 28h ]
			;if( vForwardTransform < 0 )
			;return
			fldz
			fcomp dword ptr ss:[ esp + 28h ]
			fstsw ax
			test ah, ah
			jnz @continue
			add esp, 2ch
			retn
			@continue:
			;Let centerX = refdef->Width / 2
			mov eax, dword ptr ds:[ 797608h ]
			cdq
			sub eax, edx
			sar eax, 1
			mov dword ptr ss:[ esp + 20h ], eax
			;X = centerX + ( ( centerX * vRightTransform ) / ( vForwardTransform * refdef->FOVx ) )
			fild dword ptr ss:[ esp + 20h ]
			fmul dword ptr ss:[ esp + 24h ]
			fstp dword ptr ss:[ esp + 18h ]
			fld dword ptr ss:[ esp + 28h]
			fmul dword ptr ds:[ 797610h ]
			fstp dword ptr ss:[ esp + 1ch ]
			
			fld dword ptr ss:[ esp + 18h ]
			fld dword ptr ss:[ esp + 1ch ]
			fdiv
			fild dword ptr ss:[ esp + 20h ]
			fadd
			fstp dword ptr ds:[ x ]
			add esp, 2ch
			retn
			
		;the main display hook
		@R_EndFrame_Hook:
		
			pushad
			mov edi, 84f2f4h
			mov edx, 83927ch
			xor esi, esi
			entities_loop:
				cmp dword ptr ds:[ edi + 1a4h ], 0
				jz @not_valid_entity
				mov eax, dword ptr ds:[ edi + 0b0h ]
				cmp eax, dword ptr ds:[ 74e338h ]
				jz @not_valid_entity
				push edx
				call @WorldToScreen
				pop edx
				lea eax, x
				lea ebx, y
				push edx
				call @draw_text
				pop edx

				@not_valid_entity:
				add edx, 4cch
				add edi, 1dch
				inc esi
				cmp esi, dword ptr ds:[ 74aa48h ]
				jl entities_loop
			popad
			mov eax, dword ptr ds:[ 8c63b8h ]
			retn
		
	end _main

The result:

Posted Image

Hopefully if you have gotten this far, you learned the method well enough to apply it to the Y-Axis, and to write it in your language of choice! I do apologise if anything I explained was unclear.

I would like to suffix this article with this:

Posted Image

My Dad found this while clearing out my Grandfather's house - it is a English to German translation book given to all troops occupying Germany after the end of the Second World War. My grandfather got this book after being deployed to Germany after the end of the Korean War (1953). I just thought it was a little cool thing, and I can now say "stay" in German, as in "I should Aufenthalt with English."

Until next time,
attilathedud

Shout-outs to atom0s because he loves when I spam up search results of him with links to my tutorials, which cause him to rage when looking for old work of his. <3

Pictures are either made by me in Paint, or taken from Wikipedia.

#2 Shannou06

Shannou06

    Member

  • Members
  • 72 posts

Posted 05 February 2011 - 10:53 PM

Man, you are really strange!
I'm looking for a way to do that in AutoIt. I started on the same basis as you did for the vector's theory. However, never managed to reach as far! <3

#3 KEMiCZA

KEMiCZA

    Administrator

  • Administrators
  • 400 posts
  • LocationBelgium

Posted 10 February 2011 - 04:20 PM

Nice one, will try it when I get the chance :)




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users