Collision Mesh Format

From CloudModding OoT Wiki
(Redirected from Collision Format)
Jump to: navigation, search

Header

Index Type Description
0x00 s16 Absolute minimum along the x-axis for collision vertices
0x02 s16 Absolute minimum along the y-axis for collision vertices
0x04 s16 Absolute minimum along the z-axis for collision vertices
0x06 s16 Absolute maximum along the x-axis for collision vertices
0x08 s16 Absolute maximum along the y-axis for collision vertices
0x0A s16 Absolute maximum along the z-axis for collision vertices
0x0C s16? Number of vertices to load
0x10 s32 Segment offset of vertex array
0x14 s16? Number of polygons to load
0x18 s32 Segment offset of polygon array
0x1C s32 Segment offset of polygon type definitions
0x20 s32 Segment offset of camera data*
0x24 s16? Number of water boxes*
0x28 s32 Segment offset of water boxes*

* = optional, set to 0 if it's not needed.

Vertex Array

Each vertex in the array is of the following structure:

/* 0x00 */ s16 x; //Position on x-axis
/* 0x02 */ s16 y; //Position on y-axis
/* 0x04 */ s16 z; //Position on z-axis

Polygon Array

/* 0x00 */ s16? //Polygon Type
/* 0x02 */ s16? //Vertex a (& 0x1FFF), Collision Detection Flags (& 0xE000)
/* 0x04 */ s16? //Vertex b (& 0x1FFF), Enable Conveyor Surface (land only) (& 0x2000)
/* 0x06 */ s16? //Vertex c (& 0x1FFF)
/* 0x08 */ s16 a; //Unit Normal Vector x component (0x8001 to 7FFF representing -1.0 to 1.0)
/* 0x0A */ s16 b; //Unit Normal Vector y component (0x8001 to 7FFF representing -1.0 to 1.0)
/* 0x0C */ s16 c; //Unit Normal Vector z component (0x8001 to 7FFF representing -1.0 to 1.0)
/* 0x0E */ s16 d; //Collision plane's relative distance from origin with respect to the normal. 

Collision Detection Flags

  • & 0x2000 - if 1, ignored by Camera collision detection
  • & 0x4000 = if 1, ignored by most entities
  • & 0x8000 = if 1, ignored by projectiles (slingshot seeds, arrows), bombchus (for bombchu bowling)

Poly Detection Notes

The equation of a plane is represented by the formula ax + by + cz + d = 0, where (a, b, c) is the unit normal vector of the plane, d is the distance, and (x,y,z) is a point along that plane.

The sign of the distance appears to be dependent on the sign of the normal. Given a flat plane located at y = 1000 with a unit normal vector of (0, 1, 0) (making it a floor), d will be -1000

The sign of the unit normal determines what face of the plane is solid. Collisions are detected when moving from the positive side of the face into the negative side. Given the example plane above, along with the previous coordinates of (0, 1030, 0) and next coordinates of (0, 990, 0):

0(0) + 1(1030) + 0(0) - 1000 =  30 //positive face
0(0) + 1( 990) + 0(0) - 1000 = -10 //negative face

See collision normals for a code snippet that preforms normal calculation.

Polygon Types

High Value (+ 0x00)
& 0x8000 0000 //?
& 0x4000 0000 //?
& 0x3C00 0000 //?
& 0x03E0 0000 //?
& 0x001C 0000 //?
& 0x0003 E000 //?
& 0x0000 1F00 //Scene Exit Table Index
& 0x0000 00FF //Camera S id (see Cameras for more info)
Low Value (+ 0x04)
& 0x0800 0000 //?
& 0x07E0 0000 //Conveyor Surface Direction (value << 10)
& 0x001C 0000 //Conveyor Surface Speed. 0 = None, 1 = slow, 2 = mid, 3 = fast. 4-7 = keeps momentum when entering after stepping on a polygon with speed 1-3
& 0x0002 0000 //Hookshot Surface
& 0x0001 F800 //Echo?
& 0x0000 07C0 //Lighting Setting
& 0x0000 0030 //Terrain Slope Surface
& 0x0000 000F //Sound Effect/Material Type?

aabbbxcc #w#hkels (repeats for each polygon type)
a = Object set switcher, maybe?  Or instantaneous exit?
Bit 0: Nothing by itself?
Bit 1: Nothing by itself?
Bit 2: Nothing by itself?; 2 + 3 trigger exit immediately (Link stops walking)
Bit 3: Nothing by itself?; 2 + 3 trigger exit immediately (Link stops walking)
Bit 4 through 7 define the object set/exit number, it appears, where illegal values result in nothing happening (game continues as normal).
b = Special Effect Flags
Bit 0: Climbable; 0 + 2 makes Child Link enter crawling mode (only works in center of polygon; can be glitchy if done improperly)
Bit 1: Prevents 0 from working (sometimes climbable; may produce different noise; if so, then 1 + 2 + 3 enters climb-down mode); 1 + 2 enters climb- down mode (used on ladders so Link positions himself properly when approaching from the other side)
Bit 2: Prevents 0 from working; 0 + 1 + 2 makes Link able to grab the surface
Bit 3: Nothing by itself; part of climbable/grabbable/crawlable combos above
Bit 4: Nothing?
Bit 5: Nothing?
Bit 6: Prevents 7 from working?
Bit 7: Jabu-Jabu reaction if struck
Bit 8: Out of Bounds (Link screams, level restarts)
Bit 9: Damages Link (can be slowed by the Goron Tunic)
Bit 10: Nothing?
Bit 11: Warp Transition (must be paired with exit number, otherwise crashes)

If set to 00C : Link can't take Falling Damage.

If set to 006 : Lava/Goo

x = Exit trigger (set it to the exit number as loaded in the scene; values over the defined amount will crash the game); 0 disables trigger
c = Camera view angle
0 Default distance and behavior (same as 5, actually)
1 Extremely high birds-eye view
2 Same
3 Camera follows Link at a close distance
4: Camera follows Link further away
5: Camera stays at a moderate distance away from Link (less than 4, more than 3)
6+: Similar to 5, but at a slightly higher angle
w = Special effects (for walls only)
Bit 1: Damages Link
Bit 2: Nothing?
Bit 3: Nothing?
Bit 4: Nothing?
h = Hookshot
Bit 1: Nothing?
Bit 2: Nothing?
Bit 3: Hookshotable
Bit 4: Nothing?
k = Echo (overrides map)
0: Use map value
1-F: Echo ranges
e = Ambient lighting/color/fog effect (only works if fog or environment fog is enabled)
0: Normal
1: Fog becomes turquoise and intensifies to max; can trigger overflow which trips the z-buffer
2: Fog disappears, z-buffering fails and actors get a green tint
3: Fog disappears, z-buffering fails and actors go black
4: Fog becomes black and intensifies to max; can trigger overflow which trips the z-buffer; blue sometimes seeps in
5: Fog fades almost immediately to black; z-buffering might fail; actors are completely black
6: Everything goes black except collision polygons...?
7: Everything goes black, but graphics are rendered properly
8-F are the same as 0-7.
l = Terrain type (note: the hues only appear if fog or environment fog is enabled)
0: Walkable
1: High angle (Link immediately begins to slide)
2: Walkable
3: Walkable
4: Ambient light dims, reddish hue?
5: Ambient light dims, reddish hue, Link begins to slide
6: Ambient light dims, reddish hue
7: Ambient light dims, reddish hue
8: Ambient light dims, blackish
9: Ambient light dims, blackish, Link begins to slide
A: Ambient light dims, blackish
B: Ambient light dims, blackish
C: Ambient light dims slightly with a bit of red
D: Ambient light dims slightly with a bit of red, Link begins to slide
E: Ambient light dims slightly with a bit of red
F: Ambient light dims slightly with a bit of red

Sound effect or Material Type

Id Description
0 Earth/dirt
1 Sand
2 Stone
3 Wet stone
4 Shallow water
5 Not-as-shallow water (lower-pitched sound)
6 Underbrush/grass
7 Lava/goo
8 Earth/dirt
9 Wooden plank
A Packed earth/wood
B Earth/dirt
C Ceramic
D Loose earth
E Earth/dirt
F Earth/dirt

Conveyor Surfaces

Conveyor surfaces push Link in a specific direction, or set the direction Link will walk in when touching a scene exit poly. Conveyor surfaces are ignored completely if the player is wearing Iron Boots.

To configure a land based conveyor surface, set bit flag stored next to Vertex B for that poly to 1, then set a surface speed between 1 and 3 and a direction for the surface to push Link towards. When configuring a conveyor surface for scene exits, the conveyor speed attribute will be ignored, but the angle will be used to set the direction for Link to head towards at the speed he touched the exit poly. When configuring a water based conveyor surface, the Vertex B bit flag does not have to be set.

When setting a conveyor speed of 4+, the poly surface appears to inherit the speed and direction property of the previous polygon's conveyor.

Camera Data

xxxx yyyy zzzzzzzz (array of headers, one per camera)
x = Camera type
y = Number of cameras
z = Offset of camera position data

Camera Type
Id Description
00 Keeps previous camera type (except 1F), becomes 01-03 if no other camera exists
01 Default, a bit far
02 Default, far
03 Default, zoomed
04 Default, more zoomed
05 Default, like 01 but higher Y-Axis
06 CRASH
07 Default, like 01 but lower Y-Axis
08 Default, farther than 07
09 Default, like 08 but lower Y-Axis
0A Default, like 07 but higher Y-Axis
0B Default, farther than 02
0C Default, same as 0B?
0D Default, zoomed, slightly angle to the sky, FOV changes (but can't be adjusted)
0E Default, slightly farther than 07
0F Default, same as 0D but without FOV
10 Default, like 01 but higher Y-Axis
11 Default, far
12 Default, zoomed, high Y-Axis, weird rotation
13 Default, zoomed, high Y-Axis, weird rotation but to the opposite direction as 12
14 Fixed camera, no rotation
15 Fixed camera, no rotation
16 Fixed camera, focuses on link
17  ??? black bars appear, camera points at random rotation
18 Fixed camera, farther than 16, focuses on link
19 Fixed camera, no rotation, instant transition
1A Fixed camera, disables the rest of the cameras once triggered, focuses on link
1B Fixed camera, position updates towards east/west of the camera when link moves
1C Fixed camera, same as 1A but with black bars
1D CRASH
1E  ???, keeps zooming to link until it glitches
1F Fixed camera, no rotation, instant transition, resets with camera type 00
20 Fixed camera, no rotation, instant transition, disables the rest of the cameras once triggered
21 Camera position and rotation freezes until another camera type is triggered
22 Same as 21
23 Fixed camera, focuses on link, instant transition
24  ???, focuses on link, behavior changes depending on which camera type you had before?
25 CRASH
26 Same as 21
27 Fixed camera, rotation is fixed but position follows link, smooth transition
28  ???, blue warp cutscene?, disables the rest of the cameras once triggered
29 Same as 21
2A CRASH
2B Same as 21 but with black bars
2C Overhead at 45º, close to link
2D Same as 03
2E CRASH
2F  ???, camera position freezes, keeps zooming on link until it glitches
30  ???, camera goes extremely far, focuses on link
31 CRASH (FOV overflow?)
32 Same as 21
33 CRASH
34 Overhead at 45º, close to link
35 Overhead at 80º
36 Overhead at 80º, farther than 35
37 Same as 35
38 Ocarina camera, movement controls are inverted, camera stucks into this state until you use the ocarina
39 Same as 21 but with black bars
3A Same as 03
3B Default, like 3A but higher Y-Axis
3C Same as 21 but with black bars
3D Default, camera is at ground level and angles to the sky
3E Same as 03
3F Same as 3D
40 Fixed camera, focuses on link
41 Same as 03


Camera Position Data

xxxx yyyy zzzz aaaa bbbb cccc dddd FFFF

x = x coordinate
y = y coordinate
z = z coordinate
a = x rotation
b = y rotation
c = z rotation
d = FOV (only for some camera types)

Water

xxxxyyyy zzzzssss rrrr0000 0000qqqq (repeats for each water box)
Note: water is bottomless
Note2: water actors may take over some of the properties described here, such as the level of the top surface.
x = x minimum coordinite for water
y = top surface of water's height.
z = z minimum coordinite for water
s = x size of water
r = z size of water
q = water properties(camera effects?):
0100 = "normal water" I reccomend this variable
0102 = seems to be the same as 0100
0105 = Camera flys off and goes to a random spot?

Credits

MNGoldenEagle/JSA for mostly everything. Water was figured out by spinout. Documentation on camera data has been released by DeathBasket and Nokaubure.

ZHC Collision

// -*- mode: c;-*-
typedef struct {
    local string thisName = "ZHC_CollisionWaterBox";
    SIBE;
    SIT;
    short minX;
    SIT;
    short topY;
    SIT;
    short minZ;
    SIT;
    short sizeX;
    SIT;
    short sizeZ;
    SIT;
    ubyte zeroes[4];
    AssertUBytesNull(zeroes, thisName);
    SIT;
    enum <short> WATER_PROPS {
        WATER_PROP_NORMAL       = 0x0100,
        WATER_PROP_ABNORMAL     = 0x0102,
        WATER_PROP_CAMERAFUCKER = 0x0105,
    } maybeWaterPropertiesOrCameraEffects;
    /*
    AnomEnum( 
        EnumToString( maybeWaterPropertiesOrCameraEffects ),
        maybeWaterPropertiesOrCameraEffects,
        thisName+"->maybeWaterPropertiesOrCameraEffects"
    );
    */
} ZHC_CollisionWaterBox;

typedef struct {
    SIBE;
    SIT;
    ushort type;
    SIT;
    ZVector verts;
    SIT;
    ZNormal normal;
    SIT;
    short displacement;
    SIEND;
} ZHC_CollisionPolyList;

/*
differences (quicksand-major):
terrain_trigger_reset_enable
terrain_hurt_fire_enable
terrain_hookshot_enable
unknown3 = 1 (else: 0)
unknown4 = 15
*/
/*
other notes (for both):
specialEffects == 1
TERRAIN_CAMERA_PROXIMITY_NOTSO_CLOSEUP
1 \ 0 \15 \ TERRAIN_SLOPE_BURGOR \ TERRAIN_SOUND_DIRT
*/

typedef struct {
    // This data closely resembles the NIFF_Mat data type (Material)
    // Specifically: ambient/primary/fog lighting and user values per-poly
    // Outside the typical "UserExpansionBlock"
    local string thisName = "ZHC_CollisionPolyTypeList";
    SIBE;

    SIT; UNK;
    ubyte unknown1 : 2;

    SIT;
    // if these two bits aren't set at the same time, instant transitions
    // do not happen
    enum <ubyte> TERRAIN_TRIGGER_TRANSITION {
        TERRAIN_TRIGGER_TRANSITION_DESERTLOST = 0x3
    } do_trigger_transit : 2;

    SIT;
    ubyte obj_set_or_exit_number : 4;

    // == milestone: 8 bits

    SIT;
    enum <ubyte> TERRAIN_HANDS {
        TERRAIN_HANDS_DO_NOTHING   = 0x0, // 0 0 0 0
                                          // 0 0 0 1 0x1 ?
                                          // 0 0 1 0 0x2 ?
                                          // 0 0 1 1 0x3 ?
                                          // 0 1 0 0 0x4 ?
                                          // 0 1 0 1 0x5 ?
        TERRAIN_HANDS_CLIMB_DOWN   = 0x6, // 0 1 1 0
        TERRAIN_HANDS_CLIMB_DOWN2  = 0x7, // 0 1 1 1
        TERRAIN_HANDS_ALLOW_CLIMB  = 0x8, // 1 0 0 0
                                          // 1 0 0 1 0x9 ?
        TERRAIN_HANDS_ALLOW_CRAWL  = 0xA, // 1 0 1 0
                                          // 1 0 1 1 0xB ?
                                          // 1 1 0 0 0xC ?
                                          // 1 1 0 1 0xD ?
        TERRAIN_HANDS_GRAB_SURFACE = 0xE, // 1 1 1 0
                                          // 1 1 1 1 0xF ? 
    } climbability : 4;

    SIT; UNK;
    ubyte unknown2 : 2;

    SIT;
    // the first bit impedes the second bit
    enum <ubyte> TERRAIN_ANGER_JABBU {
        TERRAIN_ANGER_JABBU_DISABLE = 0x0,
        TERRAIN_ANGER_JABBU_ENABLE  = 0x1,
    } do_anger_jabbu : 2;

    // == milestone: 16 bits

    SIT;
    enum <ubyte> TERRAIN_TRIGGER_RESET {
        TERRAIN_TRIGGER_RESET_DISABLE = 0x0,
        TERRAIN_TRIGGER_RESET_ENABLE  = 0x1,
    } do_out_of_bounds_reset : 1;

    SIT;
    // the second half of this is only used for quicksand?
    enum <ubyte> TERRAIN_HURT_FIRE {
        TERRAIN_HURT_FIRE_DISABLE = 0x0,
        TERRAIN_HURT_FIRE_ENABLE  = 0x1,
        TERRAIN_HURT_FIRE_DISABLE2= 0x2,
        TERRAIN_HURT_FIRE_ENABLE2 = 0x3,
    } do_trigger_hurt_fire : 2;

    SIT;
    // if exitTrigger isn't a valid exit then enabling this will crash
    enum <ubyte> TERRAIN_WARP {
        TERRAIN_WARP_DISABLE = 0x0,
        TERRAIN_WARP_ENABLE  = 0x1,
    } do_warp : 1;

    SIT;
    // 0 == disabled, exit to jump to if this poly triggers one
    ubyte exitTrigger : 4;

    // == milestone: 24 bits

    SIT;
    // anything above 6 is just a higher angle of 5
    enum <ubyte> TERRAIN_CAMERA_PROXIMITY {
        TERRAIN_CAMERA_PROXIMITY_NORMAL         = 0x00,
        TERRAIN_CAMERA_PROXIMITY_HIGHBIRDSEYE   = 0x01,
        TERRAIN_CAMERA_PROXIMITY_HIGHBIRDSEYE2  = 0x02,
        TERRAIN_CAMERA_PROXIMITY_CLOSEUP        = 0x03,
        TERRAIN_CAMERA_PROXIMITY_NOTSO_CLOSEUP  = 0x04,
        TERRAIN_CAMERA_PROXIMITY_NORMAL2        = 0x05, // same as 0x5
        TERRAIN_CAMERA_PROXIMITY_HIGHER         = 0x06,
        TERRAIN_CAMERA_PROXIMITY_PROBABLYUNUSED = 0xFF,
    } camera_modifier;

    // == milestone: 32 bits

    /*
    AnomEnum( 
        EnumToString( camera_modifier ),
        camera_modifier,
        thisName+"->camera_modifier"
    );
    */
    SIT; UNK;
    ubyte unknown3 : 4;

    SIT;
    enum WALLS_HURT {
        WALLS_HURT_DISABLE = 0,
        WALLS_HURT_ENABLE  = 1,
    } do_walls_hurt : 1;

    SIT; UNK;
    ubyte unknown4 : 3;

    // == milestone: 40 bits

    SIT; UNK;
    ubyte unknown5 : 4;

    SIT; UNK;
    ubyte unknown6 : 2;

    SIT;
    enum TERRAIN_HOOKSHOT {
        TERRAIN_HOOKSHOT_DISABLE = 0x0,
        TERRAIN_HOOKSHOT_ENABLE  = 0x1,
    } hookshot : 1;

    SIT; UNK;
    ubyte unknown7 : 1;

    // == milestone: 48 bits

    SIT;
    // Presumably, this is for trick walls that are 
    // bombable or metal surfaces.
    // WILL *OVERRIDE* SCENE ECHO SETTING
    ubyte impact_sound_echo : 4;

    SIT; UNK;
    // this and the next unknown composed ambient settings
    ubyte unknown8 : 4;

    // milestone: 56 bits
    
    SIT; UNK;
    ubyte unknown9 : 2;

    SIT;
    // this spans 2 bits because of how it wraps,
    // only TERRAIN_SLOPE_ENABLE triggers sloping(?)
    enum TERRAIN_SLOPE {
        TERRAIN_SLOPE_DISABLE = 0x0,
        TERRAIN_SLOPE_ENABLE  = 0x1,
        TERRAIN_SLOPE_HOTDOG  = 0x2,
        TERRAIN_SLOPE_BURGOR  = 0x3,
    } slope : 2;

    SIT;
    // this is footstep sounds, but also the sound it makes
    // if struck with a sword
    enum TERRAIN_SOUND {
        TERRAIN_SOUND_DIRT                  = 0x0,
        TERRAIN_SOUND_SAND                  = 0x1,
        TERRAIN_SOUND_STONE_DRY             = 0x2,
        TERRAIN_SOUND_STONE_WET             = 0x3,
        TERRAIN_SOUND_WATER_SHALLOW         = 0x4,
        TERRAIN_SOUND_WATER_DEEP            = 0x5,
        TERRAIN_SOUND_GRASS                 = 0x6,
        TERRAIN_SOUND_LAVA_OR_GOO           = 0x7,
        TERRAIN_SOUND_DIRT2                 = 0x8,
        TERRAIN_SOUND_WOOD_PLANK            = 0x9,
        TERRAIN_SOUND_DIRT_PACKED_OR_WOOD   = 0xA,
        TERRAIN_SOUND_DIRT3                 = 0xB,
        TERRAIN_SOUND_CERAMIC               = 0xC,
        TERRAIN_SOUND_DIRT_LOOSE            = 0xD,
        TERRAIN_SOUND_DIRT4                 = 0xE,
        TERRAIN_SOUND_DIRT5                 = 0xF,
    } terrainSound : 4;

    // milestone: 64 bits
} ZHC_CollisionPolyTypeList;

typedef struct {
    local string thisName = "ZHC_CollisionHeader";
    SIBE;
    SIT;
    ZVector min;
    SIT;
    ZVector max;
    SIT;
    ushort numVerts;
    SIT;
    ubyte zeroes1[2];
    AssertUBytesNull(zeroes1, thisName);
    SIT;
    ZBankPointer vertArrayOffset;
    if( vertArrayOffset.zb.bankNo == bankType ){
        SAVE;
        FSeek(headerStart + vertArrayOffset.offset);
        ZVector verts[numVerts];
        REST;
    } else {
        Assert( 0, "External Bank" );
    }
    SIT;
    ushort numPolys;
    SIT;
    ubyte zeroes2[2];
    AssertUBytesNull(zeroes2, thisName);
    SIT;
    ZBankPointer polyArrayOffset;
    if( polyArrayOffset.zb.bankNo == bankType ){
        SAVE;
        FSeek(headerStart + polyArrayOffset.offset);
        struct {
            ZHC_CollisionPolyList polys[numPolys]<optimize=false>;
        } AllThesePolyLists;
        REST;
    } else {
        Assert( 0, "External Bank" );
    }
    SIT;
    ZBankPointer polyTypeArrayOffset;
    if( polyTypeArrayOffset.zb.bankNo == bankType ){
        SAVE;
        FSeek(headerStart + polyTypeArrayOffset.offset);
        struct {
            ZHC_CollisionPolyTypeList polyTypes[numPolys]<optimize=false>;
        } AllThesePolyTypeLists;
        REST;
    } else {
        Assert( 0, "External Bank" );
    }
    SIT;
    ZBankPointer cameraDataOffset; //if not zeroes
    if( cameraDataOffset.zb.bankNo == bankType ){
        SAVE;
        FSeek(headerStart + cameraDataOffset.offset);
        ZHC_AltCamera cameraData;
        REST;
    } else {
        Assert( 0, "External Bank" );
    }
    SIT;
    ushort numWaterBoxes; //if not zeroes
    SIT;
    ubyte zeroes3[2];
    AssertUBytesNull(zeroes3, thisName);
    
    SIT;
    ZBankPointer waterBoxArrayOffset;
    if( numWaterBoxes ){
        if( waterBoxArrayOffset.zb.bankNo == bankType ){
            SAVE;
            FSeek(headerStart + waterBoxArrayOffset.offset);
            struct {
                ZHC_CollisionWaterBox waterBoxes[numWaterBoxes]<optimize=false>;
            } AllTheseWaterBoxes;
            REST;
        } else {
            Assert( 0, "External Bank" );
        }
    }
    SIEND;
} ZHC_CollisionHeader;

typedef struct {
    local string thisName = "ZHC_CollisionHeader";
    SIBE;
    SIT;
    ubyte command;
    AssertNumberEquals(command, 0x03, thisName);

    SIT;
    ubyte zeroes[3];
    AssertUBytesNull(zeroes, thisName);
    
    ZBankPointer listOffset;
    if( listOffset.zb.bankNo == bankType ){
        SAVE;
        FSeek(headerStart + listOffset.offset);
        ZHC_CollisionHeader collisionHeaders;
        REST;
    } else {
        Assert( 0, "External Bank" );
    }
    SIEND;
} ZHC_CollisionList;
(Source: DerrikeG)