Raycasting

From en64 wiki
Jump to navigation Jump to search

Ocarina of Time Raycasting Information

I am here to share a bit of knowledge on how the raycast function works in the zelda64 engine.

If you are not familiar with what a raycast is then watch this video: https://unity3d.com/learn/tutorials/modules/beginner/physics/raycasting

A raycast is a core game engine feature, it allows things to interact with the level geometry by finding intersection points, in this game's case the rays are being cast against the scene collision mesh because of its geometric simplicity.
What i wanted to find out about was how certain objects in the zelda engine get aligned to the ground upon the scene loading. First of all this effect seems to happen only for certain objects, primarily the grass blades, rocks and pots (only 3 afaik). Actors that are NPC's or enemies are raycasted every frame so they always stand on solid ground.

Knowing this information I set out on a quest to find the magical function:

First of all I found the main collision routine which Lee then analyzed to extract another version of the raycast (there are lots of variations in the code):

How to find the collision routine for a destructible object:

Use the mqd database by Spinout to find the overlays that share a common functionality and one that does not.
Extract the external function calls that are contained within each one.
Remove duplicates.
Leave only calls that are contained within all common overlays.
Subtract the uncommon overlay calls.
This leaves only the interesting calls unique to the types of objects we want.

How I found the raycast:

First of all thanks to Lee I have some collision functions named in Ida so I know roughly what is what.
Second of all thanks to Nintendo for leaving debug print messages in the overlays.
Go here and search for the overlay name: https://tcrf.net/Proto:The_Legend_of_Zelda:_Ocarina_of_Time_Master_Quest/Console_Output_Strings
For our purpose you can look at kusa, ishi and tsubo (grass, rock and pot). They are all the objects that are raycasted in the engine.
You can actually see they each have a certain debug message and only 3 versions of that debug message exist, one for each overlay.
The debug message is: 地面に付着失敗
Translated to english it is: The adhesion failure on the ground.
Eureka! this sounds like what we want lol.
Anyway you can find these debug strings in the read only section of the overlay.
A pointer to it is given as an argument to the N64 printf function (debug output).
The printf function is known to be 80002130 for the debug rom.
Knowing this you can find where that debug message is called.
I traced it back to a local overlay function containing only these 3 external function calls:

[DEBUG ROM]
8003C9A4 //Raycast (Got the internal call from Lee)
80077E40 //Copy actor pos to actor collision pos (Got this from Lee)
80002130 //N64 debug printf function

The local overlay function seems to not do much except for call those 3 functions and return 0 or 1.
You can find similar functions in the kusa, ishi and tsubo overlay sources too.

ASM information:

Pictured: Using IDA Pro and Hex-Rays


Knowing this information we can perform a raycast on any object in the zelda64 engine.
Some sample C code:

Code:
[OOT 1.0]
#define RAYCAST_ADDRESS 0x8002F4B8
typedef f32 (*RaycastProto)(void * scene_data, void * s1, void * s2, void * actor, f32 * pos);
RaycastProto Raycast = (RaycastProto)RAYCAST_ADDRESS;

#define LINKS_ACTOR 0x801DAA30
DWORD * LinksActor = (DWORD*)LINKS_ACTOR;

#define GLOBAL_CONTEXT 0x801C84A0

s16 CURRENT_X_SCALE_FACTOR = 1;
s16 CURRENT_Y_SCALE_FACTOR = 1;
s16 CURRENT_Z_SCALE_FACTOR = 1;

//In function that spawns a new actor:
if(actor != OOT_Link)
{
    DWORD Zero = 0;
    DWORD * SP[2]; //It's currently unknown what the stack pointers do but a lot of the time they are 0 and it works fine, it's probably some kind of offsets or bounds for individual objects
    SP[0] = &Zero;
    SP[1] = &Zero;
    
    f32 ActorPos[3];
    ActorPos[0] = fx * CURRENT_X_SCALE_FACTOR;
    ActorPos[1] = fy * CURRENT_Y_SCALE_FACTOR + 10.0f; //Add a small Y offset to make sure to always hit the ray from above the ground
    ActorPos[2] = fz * CURRENT_Z_SCALE_FACTOR;
    
    f32 NewY = Raycast((void*)(GLOBAL_CONTEXT + 0x7C0), &SP[0], &SP[1], LinksActor, ActorPos); //You can use link's actor as an argument or any actor pointer, the raycast does not write anything to the actor (afaik)
    
    if(NewY > -32000.0f) //The proper way to check success
    {
        return ActorSpawn(arg1, arg2, actor, ActorPos[0], NewY, ActorPos[2], rx, ry, rz, variable);
    }
}

Things like the owl will all be raycasted to the floor:

Ray2.png


Careful of things like this happening though:

Ray3.png


Ray4.png



>The debug message is: 地面に付着失敗
>Translated to english it is: The adhesion failure on the ground.

Failure adhering to the ground.

Japanese doesn't have a real gerund, so they say what would be "ing" phrases completely differently than we do.