sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

draw_objects.c (48625B)


      1 #include <PR/ultratypes.h>
      2 #include <stdio.h>
      3 
      4 #if defined(VERSION_JP) || defined(VERSION_US)
      5 #include "prevent_bss_reordering.h"
      6 #endif
      7 
      8 #include "debug_utils.h"
      9 #include "dynlist_proc.h"
     10 #include "gd_macros.h"
     11 #include "gd_main.h"
     12 #include "gd_math.h"
     13 #include "gd_types.h"
     14 #include "macros.h"
     15 #include "objects.h"
     16 #include "old_menu.h"
     17 #include "renderer.h"
     18 #include "shape_helper.h"
     19 #include "draw_objects.h"
     20 
     21 /**
     22  * @file draw_objects.c
     23  * This file contains the functions and helpers for rendering the various
     24  * GdObj primitives to the screen.
     25  */
     26 
     27 // forward declarations
     28 void func_80179B64(struct ObjGroup *);
     29 void update_shaders(struct ObjShape *, struct GdVec3f *);
     30 void draw_shape_faces(struct ObjShape *);
     31 void register_light(struct ObjLight *);
     32 
     33 // types
     34 /**
     35  * Modes for drawscene()
     36  */
     37 enum SceneType {
     38     RENDER_SCENE = 26, ///< render the primitives to screen
     39     FIND_PICKS = 27    ///< only check position of primitives relative to cursor click
     40 };
     41 
     42 /**
     43  * A possible remnant of an early `ObjVertex` structure that contained
     44  * texture S,T coordinates.
     45  */
     46 struct BetaVtx {
     47     /* 0x00 */ u8 filler[68];
     48     /* 0x44 */ f32 s;
     49     /* 0x48 */ f32 t;
     50 };
     51 
     52 // data
     53 static struct GdColour sClrWhite = { 1.0, 1.0, 1.0 };            // @ 801A8070
     54 static struct GdColour sClrRed = { 1.0, 0.0, 0.0 };              // @ 801A807C
     55 static struct GdColour sClrGreen = { 0.0, 1.0, 0.0 };            // @ 801A8088
     56 static struct GdColour sClrBlue = { 0.0, 0.0, 1.0 };             // @ 801A8094
     57 static struct GdColour sClrErrDarkBlue = { 0.0, 0.0, 6.0 };      // @ 801A80A0
     58 static struct GdColour sClrPink = { 1.0, 0.0, 1.0 };             // @ 801A80AC
     59 static struct GdColour sClrBlack = { 0.0, 0.0, 0.0 };            // @ 801A80B8
     60 static struct GdColour sClrGrey = { 0.6, 0.6, 0.6 };             // @ 801A80C4
     61 static struct GdColour sClrDarkGrey = { 0.4, 0.4, 0.4 };         // @ 801A80D0
     62 static struct GdColour sClrYellow = { 1.0, 1.0, 0.0 };           // @ 801A80DC
     63 static struct GdColour sLightColours[1] = { { 1.0, 1.0, 0.0 } }; // @ 801A80E8
     64 static struct GdColour *sSelectedColour = &sClrRed;              // @ 801A80F4
     65 struct ObjCamera *gViewUpdateCamera = NULL;                      // @ 801A80F8
     66 UNUSED static void *sUnref801A80FC = NULL;
     67 static s32 sUnreadShapeFlag = 0;       // @ 801A8100
     68 struct GdColour *sColourPalette[5] = { // @ 801A8104
     69     &sClrWhite, &sClrYellow, &sClrRed, &sClrBlack, &sClrBlack
     70 };
     71 struct GdColour *sWhiteBlack[2] = {
     72     //@ 801A8118
     73     &sClrWhite,
     74     &sClrBlack,
     75 };
     76 UNUSED static Mat4f sUnref801A8120 = {
     77     { 1.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 1.0, 0.0 }, { 0.0, 0.0, 0.0, 1.0 }
     78 };
     79 UNUSED static Mat4f sUnrefIden801A8160 = {
     80     { 1.0, 0.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0, 0.0 }, { 0.0, 0.0, 1.0, 0.0 }, { 0.0, 0.0, 0.0, 1.0 }
     81 };
     82 static s32 sLightDlCounter = 1; // @ 801A81A0
     83 UNUSED static s32 sUnref801A81A4[4] = { 0 };
     84 
     85 // bss
     86 u8 gUnref_801B9B30[0x88];
     87 struct ObjGroup *gGdLightGroup; // @ 801B9BB8; is this the main light group? only light group?
     88 
     89 UNUSED static u8 sUnref_801B9BBC[0x40];
     90 static enum SceneType sSceneProcessType; // @ 801B9C00
     91 static s32 sUseSelectedColor;            // @ 801B9C04
     92 static s16 sPickBuffer[100];             ///< buffer of objects near click
     93 static s32 sPickDataTemp;                ///< now, only data is the object number of a selected joint
     94 static f32 sPickObjDistance;             ///< distance between object position and cursor click location
     95 static struct GdObj *sPickedObject;      ///< object selected with cursor
     96 /// Various counters and pointers set in update_view() and used in various `draw_XXX` functions
     97 static struct {
     98     u8 filler1[4];        // @ 801B9CE0
     99     struct ObjView *view; // @ 801B9CE4
    100     s32 unreadCounter;    // @ 801B9CE8
    101     s32 mtlDlNum;         // @ 801B9CEC; name is a big guess
    102     s32 shapesDrawn;      // @ 801B9CF0
    103     s32 unused;           // @ 801B9CF4
    104 } sUpdateViewState;
    105 static struct ObjLight *sPhongLight;          // material light? phong light?
    106 static struct GdVec3f sPhongLightPosition;    //@ 801B9D00; guess; light source unit position for light
    107                                               // flagged 0x20 (sPhongLight)
    108 static struct GdVec3f sLightPositionOffset;   // @ 801B9D10
    109 static struct GdVec3f sLightPositionCache[8]; // @ 801B9D20; unit positions
    110 static s32 sNumActiveLights;                  // @ 801B9D80; maybe?
    111 static struct GdVec3f sGrabCords;             ///< x, y grabbable point near cursor
    112 
    113 /**
    114  * Set the ambient light color and turn on G_CULL_BACK.
    115  */
    116 void setup_lights(void) {
    117     set_light_num(NUMLIGHTS_2);
    118     gd_setproperty(GD_PROP_AMB_COLOUR, 0.5f, 0.5f, 0.5f);
    119     gd_setproperty(GD_PROP_CULLING, 1.0f, 0.0f, 0.0f); // set G_CULL_BACK
    120     return;
    121 
    122     // dead code
    123     gd_setproperty(GD_PROP_STUB17, 2.0f, 0.0f, 0.0f);
    124     gd_setproperty(GD_PROP_ZBUF_FN, 24.0f, 0.0f, 0.0f);
    125     gd_setproperty(GD_PROP_CULLING, 1.0f, 0.0f, 0.0f);
    126     return;
    127 }
    128 
    129 /**
    130  * @note Not called
    131  */
    132 void Unknown801781DC(struct ObjZone *zone) {
    133     struct GdVec3f lightPos; // 3c
    134     struct ObjUnk200000 *unk;
    135     f32 sp34;
    136     f32 sp30;
    137     f32 sp2C;
    138     struct ObjLight *light;
    139     register struct ListNode *link = zone->unk30->firstMember; // s0 (24)
    140     struct GdObj *obj;                                 // 20
    141 
    142     while (link != NULL) {
    143         obj = link->obj;
    144         light = (struct ObjLight *) gGdLightGroup->firstMember->obj;
    145         lightPos.x = light->position.x;
    146         lightPos.y = light->position.y;
    147         lightPos.z = light->position.z;
    148         unk = (struct ObjUnk200000 *) obj;
    149         sp34 = gd_dot_vec3f(&unk->unk34->normal, &unk->unk30->pos);
    150         sp30 = gd_dot_vec3f(&unk->unk34->normal, &lightPos);
    151         lightPos.x -= unk->unk34->normal.x * (sp30 - sp34);
    152         lightPos.y -= unk->unk34->normal.y * (sp30 - sp34);
    153         lightPos.z -= unk->unk34->normal.z * (sp30 - sp34);
    154         unk->unk30->pos.x = lightPos.x;
    155         unk->unk30->pos.y = lightPos.y;
    156         unk->unk30->pos.z = lightPos.z;
    157         sp2C = ABS((sp30 - sp34));
    158         if (sp2C > 600.0f) {
    159             sp2C = 600.0f;
    160         }
    161         sp2C = 1.0 - sp2C / 600.0;
    162         unk->unk30->normal.x = sp2C * light->colour.r;
    163         unk->unk30->normal.y = sp2C * light->colour.g;
    164         unk->unk30->normal.z = sp2C * light->colour.b;
    165         link = link->next;
    166     }
    167 }
    168 
    169 /* 226C6C -> 226FDC */
    170 void draw_shape(struct ObjShape *shape, s32 flag, f32 c, f32 d, f32 e, // "sweep" indices 0-2 x, y, z
    171                 f32 f, f32 g, f32 h, // translate shape + store offset (unused)
    172                 f32 i, f32 j, f32 k, // translate shape
    173                 f32 l, f32 m, f32 n, // rotate x, y, z
    174                 s32 colorIdx, Mat4f *rotMtx) {
    175     UNUSED u8 filler[8];
    176     struct GdVec3f sp1C;
    177 
    178     restart_timer("drawshape");
    179     sUpdateViewState.shapesDrawn++;
    180 
    181     if (shape == NULL) {
    182         return;
    183     }
    184 
    185     sp1C.x = sp1C.y = sp1C.z = 0.0f;
    186     if (flag & 2) {
    187         gd_dl_load_trans_matrix(f, g, h);
    188         sp1C.x += f;
    189         sp1C.y += g;
    190         sp1C.z += h;
    191     }
    192 
    193     if ((flag & 0x10) && rotMtx != NULL) {
    194         gd_dl_load_matrix(rotMtx);
    195         sp1C.x += (*rotMtx)[3][0];
    196         sp1C.y += (*rotMtx)[3][1];
    197         sp1C.z += (*rotMtx)[3][2];
    198     }
    199 
    200     if (flag & 8) {
    201         if (m != 0.0f) {
    202             func_8019F2C4(m, 121);
    203         }
    204         if (l != 0.0f) {
    205             func_8019F2C4(l, 120);
    206         }
    207         if (n != 0.0f) {
    208             func_8019F2C4(n, 122);
    209         }
    210     }
    211 
    212     if (colorIdx != 0) {
    213         sUseSelectedColor = TRUE;
    214         sSelectedColour = gd_get_colour(colorIdx);
    215         if (sSelectedColour != NULL) {
    216             gd_dl_material_lighting(-1, sSelectedColour, GD_MTL_LIGHTS);
    217         } else {
    218             fatal_print("Draw_shape(): Bad colour");
    219         }
    220     } else {
    221         sUseSelectedColor = FALSE;
    222         sSelectedColour = NULL;
    223     }
    224 
    225     if (sNumActiveLights != 0 && shape->mtlGroup != NULL) {
    226         if (rotMtx != NULL) {
    227             sp1C.x = (*rotMtx)[3][0];
    228             sp1C.y = (*rotMtx)[3][1];
    229             sp1C.z = (*rotMtx)[3][2];
    230         } else {
    231             sp1C.x = sp1C.y = sp1C.z = 0.0f;
    232         }
    233         update_shaders(shape, &sp1C);
    234     }
    235 
    236     if (flag & 4) {
    237         gd_dl_mul_trans_matrix(i, j, k);
    238     }
    239 
    240     if (flag & 1) {
    241         gd_dl_scale(c, d, e);
    242     }
    243 
    244     draw_shape_faces(shape);
    245     sUseSelectedColor = FALSE;
    246     split_timer("drawshape");
    247 }
    248 
    249 void draw_shape_2d(struct ObjShape *shape, s32 flag, UNUSED f32 c, UNUSED f32 d, UNUSED f32 e, f32 f,
    250                    f32 g, f32 h, UNUSED f32 i, UNUSED f32 j, UNUSED f32 k, UNUSED f32 l, UNUSED f32 m,
    251                    UNUSED f32 n, UNUSED s32 color, UNUSED s32 p) {
    252     UNUSED u8 filler[8];
    253     struct GdVec3f sp1C;
    254 
    255     restart_timer("drawshape2d");
    256     sUpdateViewState.shapesDrawn++;
    257 
    258     if (shape == NULL) {
    259         return;
    260     }
    261 
    262     if (flag & 2) {
    263         sp1C.x = f;
    264         sp1C.y = g;
    265         sp1C.z = h;
    266         if (gViewUpdateCamera != NULL) {
    267             gd_rotate_and_translate_vec3f(&sp1C, &gViewUpdateCamera->unkE8);
    268         }
    269         gd_dl_load_trans_matrix(sp1C.x, sp1C.y, sp1C.z);
    270     }
    271     draw_shape_faces(shape);
    272     split_timer("drawshape2d");
    273 }
    274 
    275 void draw_light(struct ObjLight *light) {
    276     struct GdVec3f sp94;
    277     Mat4f sp54;
    278     UNUSED Mat4f *uMatPtr;
    279     UNUSED f32 uMultiplier;
    280     struct ObjShape *shape;
    281 
    282     if (sSceneProcessType == FIND_PICKS) {
    283         return;
    284     }
    285 
    286     sLightColours[0].r = light->colour.r;
    287     sLightColours[0].g = light->colour.g;
    288     sLightColours[0].b = light->colour.b;
    289 
    290     if (light->flags & LIGHT_UNK02) {
    291         gd_set_identity_mat4(&sp54);
    292         sp94.x = -light->unk80.x;
    293         sp94.y = -light->unk80.y;
    294         sp94.z = -light->unk80.z;
    295         gd_create_origin_lookat(&sp54, &sp94, 0.0f);
    296         uMultiplier = light->unk38 / 45.0;
    297         shape = gSpotShape;
    298         uMatPtr = &sp54;
    299     } else {
    300         uMultiplier = 1.0f;
    301         shape = light->unk9C;
    302         uMatPtr = NULL;
    303         if (++sLightDlCounter >= 17) {
    304             sLightDlCounter = 1;
    305         }
    306         shape->unk50 = sLightDlCounter;
    307     }
    308 
    309     draw_shape_2d(shape, 2, 1.0f, 1.0f, 1.0f, light->position.x, light->position.y, light->position.z,
    310                   0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1, 0);
    311 }
    312 
    313 void draw_material(struct ObjMaterial *mtl) {
    314     s32 mtlType = mtl->type; // 24
    315 
    316     if (mtlType == GD_MTL_SHINE_DL) {
    317         if (sPhongLight != NULL && sPhongLight->unk30 > 0.0f) {
    318             if (gViewUpdateCamera != NULL) {
    319                 gd_dl_hilite(mtl->gddlNumber, gViewUpdateCamera, &sPhongLight->position,
    320                               &sLightPositionOffset, &sPhongLightPosition, &sPhongLight->colour);
    321             } else {
    322                 fatal_printf("draw_material() no active camera for phong");
    323             }
    324         } else {
    325             mtlType = GD_MTL_BREAK;
    326         }
    327     }
    328     if (sUseSelectedColor == FALSE) {
    329         gd_dl_material_lighting(mtl->gddlNumber, &mtl->Kd, mtlType);
    330     } else {
    331         gd_dl_material_lighting(mtl->gddlNumber, sSelectedColour, GD_MTL_LIGHTS);
    332     }
    333 }
    334 
    335 /**
    336  * Create a `GdDisplayList` and store its number in the input `ObjMaterial`
    337  * if this material doesn't have one
    338  */
    339 void create_mtl_gddl_if_empty(struct ObjMaterial *mtl) {
    340     if (mtl->gddlNumber == 0) {
    341         mtl->gddlNumber = create_mtl_gddl(mtl->type);
    342     }
    343 }
    344 
    345 /**
    346  * A function for checking if an `ObjFace` has bad vertices. These could be either
    347  * unconverted vertex data, or old vertex structures (like `BetaVtx`)
    348  * @note Not called
    349  */
    350 void check_face_bad_vtx(struct ObjFace *face) {
    351     s32 i;
    352     struct ObjVertex *vtx;
    353 
    354     for (i = 0; i < face->vtxCount; i++) {
    355         vtx = face->vertices[i];
    356         // These seem to be checks against bad conversions, or an outdated vertex structure..?
    357         if ((uintptr_t) vtx == 39) {
    358             gd_printf("bad1\n");
    359             return;
    360         }
    361         if ((uintptr_t) vtx->gbiVerts == 0x3F800000) {
    362             fatal_printf("bad2 %x,%d,%d,%d\n", (u32) (uintptr_t) vtx, vtx->scaleFactor, vtx->id, vtx->header.type);
    363         }
    364     }
    365 }
    366 
    367 /**
    368  * @brief Convert a numeric index into pointer to a struct GdColour
    369  *
    370  * A simple switch case to convert from index @p idx to a pointer to the
    371  * three f32 GdColour structure. Goddard stored the index in a structure,
    372  * and uses this function to get the colour RGB values if needed.
    373  * -1 uses the environment colour.
    374  * A possible enhancement for this is to ennumerate all colours, and then
    375  * use those enumerations and/or enum type where ever a colour is requested
    376  *
    377  * @param idx Index of colour
    378  * @return Pointer to a GdColour struct
    379  */
    380 struct GdColour *gd_get_colour(s32 idx) {
    381     switch (idx) {
    382         case COLOUR_BLACK:
    383             return &sClrBlack;
    384             break;
    385         case COLOUR_WHITE:
    386             return &sClrWhite;
    387             break;
    388         case COLOUR_RED:
    389             return &sClrRed;
    390             break;
    391         case COLOUR_GREEN:
    392             return &sClrGreen;
    393             break;
    394         case COLOUR_BLUE:
    395             return &sClrBlue;
    396             break;
    397         case COLOUR_GRAY:
    398             return &sClrGrey;
    399             break;
    400         case COLOUR_DARK_GRAY:
    401             return &sClrDarkGrey;
    402             break;
    403         case COLOUR_DARK_BLUE:
    404             return &sClrErrDarkBlue;
    405             break;
    406         case COLOUR_BLACK2:
    407             return &sClrBlack;
    408             break;
    409         case COLOUR_YELLOW:
    410             return &sClrYellow;
    411             break;
    412         case COLOUR_PINK:
    413             return &sClrPink;
    414             break;
    415         case -1:
    416             return &sLightColours[0];
    417             break;
    418         default:
    419             return NULL;
    420     }
    421 }
    422 
    423 /**
    424  * Uncalled function that would render a triangle
    425  * @note Not called
    426  */
    427 void Unknown80178ECC(f32 v0X, f32 v0Y, f32 v0Z, f32 v1X, f32 v1Y, f32 v1Z) {
    428     f32 difY = v1Y - v0Y;
    429     f32 difX = v1X - v0X;
    430     f32 difZ = v1Z - v0Z;
    431 
    432     gd_dl_make_triangle(v0X, v0Y, v0Z, v1X, v1Y, v1Z, v0X + difY * 0.1, v0Y + difX * 0.1, v0Z + difZ * 0.1);
    433 }
    434 
    435 /**
    436  * Rendering function for `ObjFace` structures. It has a fair amount
    437  * of stub code
    438  */
    439 void draw_face(struct ObjFace *face) {
    440     struct ObjVertex *vtx; // 3c
    441     f32 z;                 // 38
    442     f32 y;                 // 34
    443     f32 x;                 // 30
    444     UNUSED u8 filler[12];
    445     s32 i;             // 20; also used to store mtl's gddl number
    446     s32 hasTextCoords; // 1c
    447     Vtx *gbiVtx;       // 18
    448 
    449     imin("draw_face");
    450     hasTextCoords = FALSE;
    451     if (sUseSelectedColor == FALSE && face->mtlId >= 0) { // -1 == colored face
    452         if (face->mtl != NULL) {
    453             if ((i = face->mtl->gddlNumber) != 0) {
    454                 if (i != sUpdateViewState.mtlDlNum) {
    455                     gd_dl_flush_vertices();
    456                     branch_to_gddl(i);
    457                     sUpdateViewState.mtlDlNum = i;
    458                 }
    459             }
    460         }
    461 
    462         if (FALSE) {
    463         }
    464     }
    465 
    466     check_tri_display(face->vtxCount);
    467 
    468     if (!gGdUseVtxNormal) {
    469         set_Vtx_norm_buf_1(&face->normal);
    470     }
    471 
    472     for (i = 0; i < face->vtxCount; i++) {
    473         vtx = face->vertices[i];
    474         x = vtx->pos.x;
    475         y = vtx->pos.y;
    476         z = vtx->pos.z;
    477         if (gGdUseVtxNormal) {
    478             set_Vtx_norm_buf_2(&vtx->normal);
    479         }
    480         //! @bug This function seems to have some parts based on older versions of ObjVertex
    481         //!      as the struct requests fields passed the end of an ObjVertex.
    482         //!      The bad code is statically unreachable, so...
    483         if (hasTextCoords) {
    484             set_vtx_tc_buf(((struct BetaVtx *) vtx)->s, ((struct BetaVtx *) vtx)->t);
    485         }
    486 
    487         gbiVtx = gd_dl_make_vertex(x, y, z, vtx->alpha);
    488 
    489         if (gbiVtx != NULL) {
    490             vtx->gbiVerts = make_vtx_link(vtx->gbiVerts, gbiVtx);
    491         }
    492     }
    493     func_8019FEF0();
    494     imout();
    495 }
    496 
    497 /**
    498  * Render a filled rectangle from (`ulx`, `uly`) to (`lrx`, `lry`).
    499  *
    500  * @param color `GdColour` index
    501  * @param ulx,uly upper left point
    502  * @param lrx,lry lower right point
    503  */
    504 void draw_rect_fill(s32 color, f32 ulx, f32 uly, f32 lrx, f32 lry) {
    505     gd_dl_set_fill(gd_get_colour(color));
    506     gd_draw_rect(ulx, uly, lrx, lry);
    507 }
    508 
    509 /**
    510  * Render a stroked rectangle (aka border box) from (`ulx`, `uly`) to (`lrx`, `lry`).
    511  *
    512  * @param color `GdColour` index
    513  * @param ulx,uly upper left point
    514  * @param lrx,lry lower right point
    515  */
    516 void draw_rect_stroke(s32 color, f32 ulx, f32 uly, f32 lrx, f32 lry) {
    517     gd_dl_set_fill(gd_get_colour(color));
    518     gd_draw_border_rect(ulx, uly, lrx, lry);
    519 }
    520 
    521 /**
    522  * Uncalled function that calls other orphan stub functions.
    523  * @note Not called
    524  */
    525 void Unknown801792F0(struct GdObj *obj) {
    526     char objId[32];
    527     struct GdVec3f objPos;
    528 
    529     format_object_id(objId, obj);
    530     set_cur_dynobj(obj);
    531     d_get_world_pos(&objPos);
    532     func_801A4438(objPos.x, objPos.y, objPos.z);
    533     stub_draw_label_text(objId);
    534 }
    535 
    536 /**
    537  * Draws a label
    538  */
    539 void draw_label(struct ObjLabel *label) {
    540     struct GdVec3f position;
    541     char strbuf[0x100];
    542     UNUSED u8 filler[16];
    543     struct ObjValPtr *valptr;
    544     union ObjVarVal varval;
    545     valptrproc_t valfn = label->valfn;
    546 
    547     if ((valptr = label->valptr) != NULL) {
    548         if (valptr->flag == 0x40000) {
    549             // position is offset from object
    550             set_cur_dynobj(valptr->obj);
    551             d_get_world_pos(&position);
    552         } else {
    553             // position is absolute
    554             position.x = position.y = position.z = 0.0f;
    555         }
    556 
    557         switch (valptr->datatype) {
    558             case OBJ_VALUE_FLOAT:
    559                 get_objvalue(&varval, OBJ_VALUE_FLOAT, valptr->obj, valptr->offset);
    560                 if (valfn != NULL) {
    561                     valfn(&varval, varval);
    562                 }
    563                 sprintf(strbuf, label->fmtstr, varval.f);
    564                 break;
    565             case OBJ_VALUE_INT:
    566                 get_objvalue(&varval, OBJ_VALUE_INT, valptr->obj, valptr->offset);
    567                 if (valfn != NULL) {
    568                     valfn(&varval, varval);
    569                 }
    570                 sprintf(strbuf, label->fmtstr, varval.i);
    571                 break;
    572             default:
    573                 if (label->fmtstr != NULL) {
    574                     gd_strcpy(strbuf, label->fmtstr);
    575                 } else {
    576                     gd_strcpy(strbuf, "NONAME");
    577                 }
    578                 break;
    579         }
    580     } else {
    581         position.x = position.y = position.z = 0.0f;
    582         if (label->fmtstr != NULL) {
    583             gd_strcpy(strbuf, label->fmtstr);
    584         } else {
    585             gd_strcpy(strbuf, "NONAME");
    586         }
    587     }
    588     position.x += label->position.x;
    589     position.y += label->position.y;
    590     position.z += label->position.z;
    591     func_801A4438(position.x, position.y, position.z);
    592     stub_renderer_10(label->unk30);
    593     stub_draw_label_text(strbuf);
    594 }
    595 
    596 /* 227DF8 -> 227F3C; orig name: Proc80179628 */
    597 void draw_net(struct ObjNet *self) {
    598     struct ObjNet *net = self;
    599     s32 netColor;
    600     UNUSED u8 filler[80];
    601 
    602     if (sSceneProcessType == FIND_PICKS) {
    603         return;
    604     }
    605 
    606     if (net->header.drawFlags & OBJ_HIGHLIGHTED) {
    607         netColor = COLOUR_YELLOW;
    608     } else {
    609         netColor = net->colourNum;
    610     }
    611 
    612     if (net->shapePtr != NULL) {
    613         draw_shape(net->shapePtr, 0x10, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
    614                    0.0f, 0.0f, 0.0f, 0.0f, 0.0f, netColor, &net->mat168);
    615     }
    616 
    617     if (net->unk1C8 != NULL) {
    618         draw_group(net->unk1C8);
    619     }
    620 }
    621 
    622 /**
    623  * Draws a gadget
    624  */
    625 void draw_gadget(struct ObjGadget *gdgt) {
    626     s32 colour = 0;
    627 
    628     if (gdgt->colourNum != 0) {
    629         colour = gdgt->colourNum;
    630     }
    631 
    632     draw_rect_fill(colour,
    633         gdgt->worldPos.x,
    634         gdgt->worldPos.y,
    635         gdgt->worldPos.x + gdgt->sliderPos * gdgt->size.x,
    636         gdgt->worldPos.y + gdgt->size.y);
    637 
    638     if (gdgt->header.drawFlags & OBJ_HIGHLIGHTED) {
    639         draw_rect_stroke(COLOUR_YELLOW,
    640             gdgt->worldPos.x,
    641             gdgt->worldPos.y,
    642             gdgt->worldPos.x + gdgt->sliderPos * gdgt->size.x,
    643             gdgt->worldPos.y + gdgt->size.y);
    644     }
    645     gdgt->header.drawFlags &= ~OBJ_HIGHLIGHTED;
    646 }
    647 
    648 /* 22803C -> 22829C */
    649 void draw_camera(struct ObjCamera *cam) {
    650     struct GdVec3f sp44;
    651     UNUSED f32 sp40 = 0.0f;
    652 
    653     sp44.x = 0.0f;
    654     sp44.y = 0.0f;
    655     sp44.z = 0.0f;
    656     if (cam->unk30 != NULL) {
    657         set_cur_dynobj(cam->unk30);
    658         d_get_world_pos(&sp44);
    659         sp44.x += cam->lookAt.x;
    660         sp44.y += cam->lookAt.y;
    661         sp44.z += cam->lookAt.z;
    662         ; // needed to match
    663     } else {
    664         sp44.x = cam->lookAt.x;
    665         sp44.y = cam->lookAt.y;
    666         sp44.z = cam->lookAt.z;
    667     }
    668 
    669     if (0) {
    670         // dead code
    671         gd_printf("%f,%f,%f\n", cam->worldPos.x, cam->worldPos.y, cam->worldPos.z);
    672     }
    673 
    674     if (ABS(cam->worldPos.x - sp44.x) + ABS(cam->worldPos.z - sp44.z) == 0.0f) {
    675         gd_printf("Draw_Camera(): Zero view distance\n");
    676         return;
    677     }
    678     gd_dl_lookat(cam, cam->worldPos.x, cam->worldPos.y, cam->worldPos.z, sp44.x, sp44.y, sp44.z, cam->unkA4);
    679 }
    680 
    681 /**
    682  * Forms uncalled recursive loop with func_80179B64().
    683  * This function seems to turn off the otherwise unused `OBJ_DRAW_UNK01` flag
    684  * for the GdObj.drawFlags
    685  * @note Not called
    686  */
    687 void Unknown80179ACC(struct GdObj *obj) {
    688     switch (obj->type) {
    689         case OBJ_TYPE_NETS:
    690             if (((struct ObjNet *) obj)->unk1C8 != NULL) {
    691                 func_80179B64(((struct ObjNet *) obj)->unk1C8);
    692             }
    693             break;
    694         default:
    695             break;
    696     }
    697     obj->drawFlags &= ~OBJ_DRAW_UNK01;
    698 }
    699 
    700 /**
    701  * Forms uncalled recursive loop with Unknown80179ACC()
    702  * @note Not called
    703  */
    704 void func_80179B64(struct ObjGroup *group) {
    705     apply_to_obj_types_in_group(OBJ_TYPE_LABELS | OBJ_TYPE_GADGETS | OBJ_TYPE_CAMERAS | OBJ_TYPE_NETS
    706                                     | OBJ_TYPE_JOINTS | OBJ_TYPE_BONES,
    707                                 (applyproc_t) Unknown80179ACC, group);
    708 }
    709 
    710 /* 22836C -> 228498 */
    711 void world_pos_to_screen_coords(struct GdVec3f *pos, struct ObjCamera *cam, struct ObjView *view) {
    712     gd_rotate_and_translate_vec3f(pos, &cam->unkE8);
    713     if (pos->z > -256.0f) {
    714         return;
    715     }
    716 
    717     pos->x *= 256.0 / -pos->z;
    718     pos->y *= 256.0 / pos->z;
    719     pos->x += view->lowerRight.x / 2.0f;
    720     pos->y += view->lowerRight.y / 2.0f;
    721 }
    722 
    723 /**
    724  * Check if the current cursor position is near enough to @p input to
    725  * grab that `GdObj`. The range is +/- 20 units for being close to a
    726  * grab point.
    727  *
    728  * If the object can be grabbed, its information is stored in a buffer by
    729  * `store_in_pickbuf()`.
    730  *
    731  * @param input `GdObj` to check position of
    732  * @return void
    733  */
    734 void check_grabbable_click(struct GdObj *input) {
    735     struct GdVec3f objPos;
    736     UNUSED u8 filler[12];
    737     struct GdObj *obj;
    738     Mat4f *mtx;
    739 
    740     if (gViewUpdateCamera == NULL) {
    741         return;
    742     }
    743     obj = input;
    744     if (!(obj->drawFlags & OBJ_IS_GRABBABLE)) {
    745         return;
    746     }
    747 
    748     set_cur_dynobj(obj);
    749     mtx = d_get_rot_mtx_ptr();
    750     objPos.x = (*mtx)[3][0];
    751     objPos.y = (*mtx)[3][1];
    752     objPos.z = (*mtx)[3][2];
    753     world_pos_to_screen_coords(&objPos, gViewUpdateCamera, sUpdateViewState.view);
    754     if (ABS(gGdCtrl.csrX - objPos.x) < 20.0f) {
    755         if (ABS(gGdCtrl.csrY - objPos.y) < 20.0f) {
    756             // store (size, Obj Type, Obj Index) in s16 pick buffer array
    757             store_in_pickbuf(2);
    758             store_in_pickbuf(obj->type);
    759             store_in_pickbuf(obj->index);
    760             sGrabCords.x = objPos.x;
    761             sGrabCords.y = objPos.y;
    762         }
    763     }
    764 }
    765 
    766 /**
    767  * The main function for rendering the components of an `ObjView`. It called
    768  * both for drawing the various `GdObj` primatives as well as when checking
    769  * the location of a cursor click.
    770  * @note This has to be called from update_view() due to that function setting
    771  *       state variables on which this function relies
    772  *
    773  * @param process determines if this is rendering the scene
    774  *                or just checking click location
    775  * @param interactables components of `ObjView`
    776  * @param lightgrp lights of `ObjView
    777  */
    778 void drawscene(enum SceneType process, struct ObjGroup *interactables, struct ObjGroup *lightgrp) {
    779     UNUSED u8 filler[16];
    780 
    781     restart_timer("drawscene");
    782     imin("draw_scene()");
    783     sUnreadShapeFlag = 0;
    784     sUpdateViewState.unreadCounter = 0;
    785     restart_timer("draw1");
    786     set_gd_mtx_parameters(G_MTX_PROJECTION | G_MTX_MUL | G_MTX_PUSH);
    787     if (sUpdateViewState.view->projectionType == 1) {
    788         gd_create_perspective_matrix(sUpdateViewState.view->clipping.z,
    789                       sUpdateViewState.view->lowerRight.x / sUpdateViewState.view->lowerRight.y,
    790                       sUpdateViewState.view->clipping.x, sUpdateViewState.view->clipping.y);
    791     } else {
    792         gd_create_ortho_matrix(
    793             -sUpdateViewState.view->lowerRight.x / 2.0, sUpdateViewState.view->lowerRight.x / 2.0,
    794             -sUpdateViewState.view->lowerRight.y / 2.0, sUpdateViewState.view->lowerRight.y / 2.0,
    795             sUpdateViewState.view->clipping.x, sUpdateViewState.view->clipping.y);
    796     }
    797 
    798     if (lightgrp != NULL) {
    799         set_gd_mtx_parameters(G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_PUSH);
    800         apply_to_obj_types_in_group(OBJ_TYPE_LIGHTS | OBJ_TYPE_PARTICLES,
    801                                     (applyproc_t) apply_obj_draw_fn, lightgrp);
    802         set_gd_mtx_parameters(G_MTX_PROJECTION | G_MTX_MUL | G_MTX_PUSH);
    803     }
    804 
    805     if (gViewUpdateCamera != NULL) {
    806         draw_camera(gViewUpdateCamera);
    807     } else {
    808         gd_dl_mul_trans_matrix(0.0f, 0.0f, -1000.0f);
    809     }
    810 
    811     setup_lights();
    812     set_gd_mtx_parameters(G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_PUSH);
    813     gd_dl_push_matrix();
    814     sSceneProcessType = process;
    815 
    816     if ((sNumActiveLights = sUpdateViewState.view->flags & VIEW_LIGHT)) {
    817         sUpdateViewState.view->flags &= ~VIEW_LIGHT;
    818     }
    819 
    820     sNumActiveLights = 1;
    821     apply_to_obj_types_in_group(OBJ_TYPE_LIGHTS, (applyproc_t) register_light, gGdLightGroup);
    822     split_timer("draw1");
    823     restart_timer("drawobj");
    824     imin("process_group");
    825     if (sSceneProcessType == FIND_PICKS) {
    826         apply_to_obj_types_in_group(OBJ_TYPE_ALL, (applyproc_t) check_grabbable_click, interactables);
    827     } else {
    828         apply_to_obj_types_in_group(OBJ_TYPE_LIGHTS | OBJ_TYPE_GADGETS | OBJ_TYPE_NETS
    829                                         | OBJ_TYPE_PARTICLES,
    830                                     (applyproc_t) apply_obj_draw_fn, interactables);
    831     }
    832     imout();
    833     split_timer("drawobj");
    834     gd_setproperty(GD_PROP_LIGHTING, 0.0f, 0.0f, 0.0f);
    835     apply_to_obj_types_in_group(OBJ_TYPE_LABELS, (applyproc_t) apply_obj_draw_fn, interactables);
    836     gd_setproperty(GD_PROP_LIGHTING, 1.0f, 0.0f, 0.0f);
    837     gd_dl_pop_matrix();
    838     imout();
    839     split_timer("drawscene");
    840     return;
    841 }
    842 
    843 /**
    844  * A drawing function that does nothing. This function is used for
    845  * `GdObj`s that don't need to be rendered
    846  */
    847 void draw_nothing(UNUSED struct GdObj *nop) {
    848 }
    849 
    850 /**
    851  * Render the `faceGroup` of an `ObjShape`. This is called from
    852  * draw_shape() and draw_shape_2d(), or when creating the shape
    853  * `GdDisplayList` when calling create_shape_gddl()
    854  */
    855 void draw_shape_faces(struct ObjShape *shape) {
    856     sUpdateViewState.mtlDlNum = 0;
    857     sUpdateViewState.unreadCounter = 0;
    858     gddl_is_loading_stub_dl(FALSE);
    859     sUnreadShapeFlag = (s32) shape->flag & 1;
    860     set_render_alpha(shape->alpha);
    861     if (shape->dlNums[gGdFrameBufNum] != 0) {
    862         draw_indexed_dl(shape->dlNums[gGdFrameBufNum], shape->unk50);
    863     } else if (shape->faceGroup != NULL) {
    864         func_801A0038();
    865         draw_group(shape->faceGroup);
    866         gd_dl_flush_vertices();
    867     }
    868 }
    869 
    870 /**
    871  * Rendering function for `ObjParticle`.
    872  */
    873 void draw_particle(struct GdObj *obj) {
    874     struct ObjParticle *ptc = (struct ObjParticle *) obj;
    875     UNUSED u8 filler1[16];
    876     struct GdColour *white;
    877     struct GdColour *black;
    878     f32 brightness;
    879     UNUSED u8 filler2[16];
    880 
    881     if (ptc->timeout > 0) {
    882         white = sColourPalette[0];
    883         black = sWhiteBlack[1];
    884         brightness = ptc->timeout / 10.0;
    885         sLightColours[0].r = (white->r - black->r) * brightness + black->r;
    886         sLightColours[0].g = (white->g - black->g) * brightness + black->g;
    887         sLightColours[0].b = (white->b - black->b) * brightness + black->b;
    888         ; // needed to match
    889     } else {
    890         sLightColours[0].r = 0.0f;
    891         sLightColours[0].g = 0.0f;
    892         sLightColours[0].b = 0.0f;
    893     }
    894 
    895     if (ptc->timeout > 0) {
    896         ptc->shapePtr->unk50 = ptc->timeout;
    897         draw_shape_2d(ptc->shapePtr, 2, 1.0f, 1.0f, 1.0f, ptc->pos.x, ptc->pos.y, ptc->pos.z, 0.0f,
    898                       0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1, 0);
    899     }
    900     if (ptc->unk60 == 3) {
    901         if (ptc->subParticlesGrp != NULL) {
    902             draw_group(ptc->subParticlesGrp);
    903         }
    904     }
    905 }
    906 
    907 /**
    908  * Rendering fucntion for `ObjBone`.
    909  *
    910  * @note This function returns before doing any work. It seems
    911  *       that Goddard moved away from using bones in the final code
    912  */
    913 void draw_bone(struct GdObj *obj) {
    914     struct ObjBone *bone = (struct ObjBone *) obj;
    915     UNUSED u8 filler1[4];
    916     s32 colour;
    917     UNUSED u8 filler2[4];
    918     struct GdVec3f scale; // guess
    919 
    920     return;
    921 
    922     // dead code
    923     scale.x = 1.0f;
    924     scale.y = 1.0f;
    925     scale.z = bone->unkF8 / 50.0f;
    926 
    927     if (bone->header.drawFlags & OBJ_HIGHLIGHTED) {
    928         colour = COLOUR_YELLOW;
    929     } else {
    930         colour = bone->colourNum;
    931     }
    932     bone->header.drawFlags &= ~OBJ_HIGHLIGHTED;
    933 
    934     if (sSceneProcessType != FIND_PICKS) {
    935         draw_shape(bone->shapePtr, 0x1B, scale.x, scale.y, scale.z, bone->worldPos.x, bone->worldPos.y,
    936                    bone->worldPos.z, 0.0f, 0.0f, 0.0f, bone->unk28.x, bone->unk28.y, bone->unk28.z, colour,
    937                    &bone->mat70);
    938     }
    939 }
    940 
    941 /**
    942  * Rendering function for `ObjJoint`.
    943  */
    944 void draw_joint(struct GdObj *obj) {
    945     struct ObjJoint *joint = (struct ObjJoint *) obj;
    946     UNUSED u8 filler1[4];
    947     UNUSED f32 sp7C = 70.0f;
    948     UNUSED u8 filler2[4];
    949     UNUSED s32 sp74 = 1;
    950     s32 colour;
    951     UNUSED u8 filler3[8];
    952     struct ObjShape *boneShape;
    953     UNUSED u8 filler4[28];
    954 
    955     if ((boneShape = joint->shapePtr) == NULL) {
    956         return;
    957     }
    958 
    959     if (joint->header.drawFlags & OBJ_HIGHLIGHTED) {
    960         colour = COLOUR_YELLOW;
    961     } else {
    962         colour = joint->colourNum;
    963     }
    964 
    965     draw_shape(boneShape, 0x10, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
    966                colour, &joint->mat128);
    967 }
    968 
    969 /**
    970  * Call `apply_obj_draw_fn()` to all `GdObj` in input `ObjGroup`
    971  *
    972  * @param grp `ObjGroup` of objects to draw
    973  * @return void
    974  */
    975 void draw_group(struct ObjGroup *grp) {
    976     if (grp == NULL) {
    977         fatal_print("Draw_Group: Bad group definition!");
    978     }
    979 
    980     apply_to_obj_types_in_group(OBJ_TYPE_ALL, (applyproc_t) apply_obj_draw_fn, grp);
    981 }
    982 
    983 /**
    984  * Rendering function for `ObjPlane`.
    985  */
    986 void draw_plane(struct GdObj *obj) {
    987     struct ObjPlane *plane = (struct ObjPlane *) obj;
    988 
    989     if (obj->drawFlags & OBJ_HIGHLIGHTED) {
    990         obj->drawFlags &= ~OBJ_HIGHLIGHTED;
    991         ; // needed to match; presumably setting up the color to draw the plane with
    992     } else {
    993         sUseSelectedColor = FALSE;
    994     }
    995     draw_face(plane->unk40);
    996 }
    997 
    998 /**
    999  * Apply `GdObj.objDrawFn` to the input `GdObj` if that object is draw-able.
   1000  *
   1001  * @param obj `GdObj` to draw
   1002  * @return void
   1003  */
   1004 void apply_obj_draw_fn(struct GdObj *obj) {
   1005     if (obj == NULL) {
   1006         fatal_print("Bad object!");
   1007     }
   1008     if (obj->drawFlags & OBJ_INVISIBLE) {
   1009         return;
   1010     }
   1011 
   1012     obj->objDrawFn(obj);
   1013 }
   1014 
   1015 /**
   1016  * Count input `ObjLight` as an active light, if it wasn't already counted.
   1017  */
   1018 void register_light(struct ObjLight *light) {
   1019     set_light_id(light->id);
   1020     gd_setproperty(GD_PROP_LIGHTING, 2.0f, 0.0f, 0.0f);
   1021     if (light->flags & LIGHT_NEW_UNCOUNTED) {
   1022         sNumActiveLights++;
   1023     }
   1024     light->flags &= ~LIGHT_NEW_UNCOUNTED;
   1025 }
   1026 
   1027 /* 229180 -> 229564 */
   1028 void Proc8017A980(struct ObjLight *light) {
   1029     f32 sp24; // diffuse factor?
   1030     f32 sp20;
   1031     f32 sp1C;
   1032 
   1033     light->colour.r = light->diffuse.r * light->unk30;
   1034     light->colour.g = light->diffuse.g * light->unk30;
   1035     light->colour.b = light->diffuse.b * light->unk30;
   1036     sLightPositionCache[light->id].x = light->position.x - sLightPositionOffset.x;
   1037     sLightPositionCache[light->id].y = light->position.y - sLightPositionOffset.y;
   1038     sLightPositionCache[light->id].z = light->position.z - sLightPositionOffset.z;
   1039     gd_normalize_vec3f(&sLightPositionCache[light->id]);
   1040     if (light->flags & LIGHT_UNK20) {
   1041         sPhongLightPosition.x = sLightPositionCache[light->id].x;
   1042         sPhongLightPosition.y = sLightPositionCache[light->id].y;
   1043         sPhongLightPosition.z = sLightPositionCache[light->id].z;
   1044         sPhongLight = light;
   1045     }
   1046     sp24 = light->unk30;
   1047     if (light->flags & LIGHT_UNK02) {
   1048         sp20 = -gd_dot_vec3f(&sLightPositionCache[light->id], &light->unk80);
   1049         sp1C = 1.0 - light->unk38 / 90.0;
   1050         if (sp20 > sp1C) {
   1051             sp20 = (sp20 - sp1C) * (1.0 / (1.0 - sp1C));
   1052             if (sp20 > 1.0) {
   1053                 sp20 = 1.0;
   1054             } else if (sp20 < 0.0f) {
   1055                 sp20 = 0.0f;
   1056             }
   1057         } else {
   1058             sp20 = 0.0f;
   1059         }
   1060         sp24 *= sp20;
   1061     }
   1062     set_light_id(light->id);
   1063     gd_setproperty(GD_PROP_DIFUSE_COLOUR, light->diffuse.r * sp24, light->diffuse.g * sp24,
   1064                    light->diffuse.b * sp24);
   1065     gd_setproperty(GD_PROP_LIGHT_DIR, sLightPositionCache[light->id].x,
   1066                    sLightPositionCache[light->id].y, sLightPositionCache[light->id].z);
   1067     gd_setproperty(GD_PROP_LIGHTING, 2.0f, 0.0f, 0.0f);
   1068 }
   1069 
   1070 /* 229568 -> 229658; orig name: func_8017AD98 */
   1071 void update_shaders(struct ObjShape *shape, struct GdVec3f *offset) {
   1072     restart_timer("updateshaders");
   1073     stash_current_gddl();
   1074     sLightPositionOffset.x = offset->x;
   1075     sLightPositionOffset.y = offset->y;
   1076     sLightPositionOffset.z = offset->z;
   1077     sPhongLight = NULL;
   1078     if (gGdLightGroup != NULL) {
   1079         apply_to_obj_types_in_group(OBJ_TYPE_LIGHTS, (applyproc_t) Proc8017A980, gGdLightGroup);
   1080     }
   1081     if (shape->mtlGroup != NULL) {
   1082         apply_to_obj_types_in_group(OBJ_TYPE_MATERIALS, (applyproc_t) apply_obj_draw_fn,
   1083                                     shape->mtlGroup);
   1084     }
   1085     pop_gddl_stash();
   1086     split_timer("updateshaders");
   1087 }
   1088 
   1089 /**
   1090  * Create `GdDisplayList`s for any `ObjMaterial`s in `shape` that don't already
   1091  * have a GdDL. Doesn't do anything if `shape`'s `mtlGroup` is NULL
   1092  *
   1093  * @param shape Input `ObjShape` to create material GdDLs for
   1094  * @return void
   1095  */
   1096 void create_shape_mtl_gddls(struct ObjShape *shape) {
   1097     if (shape->mtlGroup != NULL) {
   1098         apply_to_obj_types_in_group(OBJ_TYPE_MATERIALS, (applyproc_t) create_mtl_gddl_if_empty,
   1099                                     shape->mtlGroup);
   1100     }
   1101 }
   1102 
   1103 /**
   1104  * Uncalled function that calls a stubbed function (`stub_objects_1()`) for all
   1105  * `GdObj`s in @p grp
   1106  *
   1107  * @param grp Unknown group of objects
   1108  * @return void
   1109  * @note Not called
   1110  */
   1111 void unref_8017AEDC(struct ObjGroup *grp) {
   1112     register struct ListNode *link = grp->firstMember;
   1113 
   1114     while (link != NULL) {
   1115         struct GdObj *obj = link->obj;
   1116 
   1117         stub_objects_1(grp, obj);
   1118         link = link->next;
   1119     }
   1120 }
   1121 
   1122 /**
   1123  * Start a new `GdDisplayList` struct and store its reference index
   1124  * in the input `ObjShape`.
   1125  *
   1126  * @param s `ObjShape` to create GdDL for
   1127  * @return Either `-1` if the DL couldn't be created,
   1128  *         or the created DL's reference index
   1129  * @bug Nothing is returned if the DL is created
   1130  * @note Contains string literals that suggest a removed `printf` call
   1131  */
   1132 #ifdef AVOID_UB
   1133 void
   1134 #else
   1135 s32
   1136 #endif
   1137 create_shape_gddl(struct ObjShape *s) {
   1138     struct ObjShape *shape = s; // 24
   1139     s32 shapedl;                // 20
   1140     UNUSED s32 enddl;           // 1C
   1141 
   1142     create_shape_mtl_gddls(shape);
   1143     shapedl = gd_startdisplist(7);
   1144     if (shapedl == 0) {
   1145 #ifdef AVOID_UB
   1146         return;
   1147 #else
   1148         return -1;
   1149 #endif
   1150     }
   1151 
   1152     setup_lights();
   1153     sUseSelectedColor = FALSE;
   1154     if (shape->unk3C == 0) {
   1155         draw_shape_faces(shape);
   1156     }
   1157     enddl = gd_enddlsplist_parent();
   1158     shape->dlNums[0] = shapedl;
   1159     shape->dlNums[1] = shapedl;
   1160 
   1161     if (shape->name[0] != '\0') {
   1162         printf("Generated '%s' (%d) display list ok.(%d)\n", shape->name, shapedl, enddl);
   1163     } else {
   1164         printf("Generated 'UNKNOWN' (%d) display list ok.(%d)\n", shapedl, enddl);
   1165     }
   1166 }
   1167 
   1168 /**
   1169  * Create `GdDisplayList` structs for all `ObjShapes` in `grp` by calling
   1170  * `create_shape_gddl()`.
   1171  *
   1172  * @param grp `ObjGroup` containing `ObjShape` to create GdDLs for
   1173  * @return void
   1174  * @note Contains string literals that suggest a removed `printf` call
   1175  */
   1176 void create_gddl_for_shapes(struct ObjGroup *grp) {
   1177     UNUSED s32 shapedls =
   1178         apply_to_obj_types_in_group(OBJ_TYPE_SHAPES, (applyproc_t) create_shape_gddl, grp);
   1179     printf("made %d display lists\n", shapedls);
   1180 }
   1181 
   1182 /**
   1183  * Map material id's to `ObjMaterial` pointers for an `ObjGroup` of `ObjFace` structs.
   1184  * This is the final function used in dynlist processing (see `chk_shapegen()`)
   1185  *
   1186  * @param[in,out] faces `ObjGroup` of `ObjFace` structs to map over
   1187  * @param[in]     mtls  `ObjGroup` of `ObjMaterial` structs to map ids to pointers
   1188  * @return void
   1189  */
   1190 void map_face_materials(struct ObjGroup *faces, struct ObjGroup *mtls) {
   1191     struct ObjFace *face;
   1192     register struct ListNode *linkFaces;
   1193     struct GdObj *temp;
   1194     register struct ListNode *linkMtls;
   1195     struct ObjMaterial *mtl;
   1196 
   1197     linkFaces = faces->firstMember;
   1198     while (linkFaces != NULL) {
   1199         temp = linkFaces->obj;
   1200         face = (struct ObjFace *) temp;
   1201         linkMtls = mtls->firstMember;
   1202         while (linkMtls != NULL) {
   1203             mtl = (struct ObjMaterial *) linkMtls->obj;
   1204             if (mtl->id == face->mtlId) {
   1205                 break;
   1206             }
   1207             linkMtls = linkMtls->next;
   1208         }
   1209 
   1210         if (linkMtls != NULL) {
   1211             face->mtl = mtl;
   1212         }
   1213 
   1214         linkFaces = linkFaces->next;
   1215     }
   1216 }
   1217 
   1218 /**
   1219  * @brief Calculate the normal for @p vtx by averaging the normals of all
   1220  *  `ObjFaces` in @p facegrp
   1221  *
   1222  * Calculate the normal for the input `ObjVetex` @p vtx based on the
   1223  * `ObjFace` structures in @p facegrp of which that vertex is a part.
   1224  *
   1225  * @param vtx     `ObjVertex` to update normal
   1226  * @param facegrp `ObjGroup` of `ObjFace` structures that use @p vtx
   1227  * @return void
   1228  */
   1229 static void calc_vtx_normal(struct ObjVertex *vtx, struct ObjGroup *facegrp) {
   1230     s32 i;
   1231     s32 faceCount;
   1232     register struct ListNode *node;
   1233     struct ObjFace *curFace;
   1234 
   1235     vtx->normal.x = vtx->normal.y = vtx->normal.z = 0.0f;
   1236 
   1237     faceCount = 0;
   1238     node = facegrp->firstMember;
   1239     while (node != NULL) {
   1240         curFace = (struct ObjFace *) node->obj;
   1241         for (i = 0; i < curFace->vtxCount; i++) {
   1242             if (curFace->vertices[i] == vtx) {
   1243                 vtx->normal.x += curFace->normal.x;
   1244                 vtx->normal.y += curFace->normal.y;
   1245                 vtx->normal.z += curFace->normal.z;
   1246                 faceCount++;
   1247             }
   1248         }
   1249         node = node->next;
   1250     }
   1251     if (faceCount != 0) {
   1252         vtx->normal.x /= faceCount;
   1253         vtx->normal.y /= faceCount;
   1254         vtx->normal.z /= faceCount;
   1255     }
   1256 }
   1257 
   1258 /**
   1259  * @brief Convert vertex indices in an `ObjFace` into pointers and computes the
   1260  * face's normal
   1261  *
   1262  * Using the group of `ObjVertex` or `ObjParticle` structures in @p verts,
   1263  * convert indices in @p face into pointers. The indices are indices
   1264  * into the list contained in @p vertexGrp group
   1265  *
   1266  * @param face  `ObjFace` to find vertices for
   1267  * @param vertexGrp  `ObjGroup` to index in for `ObjVertex` or `ObjPaticle` structures
   1268  * @return void
   1269  */
   1270 static void find_thisface_verts(struct ObjFace *face, struct ObjGroup *vertexGrp) {
   1271     s32 i;
   1272     u32 currIndex;
   1273     struct ListNode *node;
   1274 
   1275     for (i = 0; i < face->vtxCount; i++) {
   1276         // find the vertex or particle whose index in vertexGrp equals face->vertices[i]
   1277         node = vertexGrp->firstMember;
   1278         currIndex = 0;
   1279         while (node != NULL) {
   1280             if (node->obj->type == OBJ_TYPE_VERTICES || node->obj->type == OBJ_TYPE_PARTICLES) {
   1281                 if (currIndex++ == (u32) (uintptr_t) face->vertices[i]) {
   1282                     break;
   1283                 }
   1284             }
   1285             node = node->next;
   1286         }
   1287         if (node == NULL) {
   1288             fatal_printf("find_thisface_verts(): Vertex not found");
   1289         }
   1290 
   1291         // set the vertex to point to the resolved `ObjVertex`
   1292         face->vertices[i] = (struct ObjVertex *) node->obj;
   1293     }
   1294     calc_face_normal(face);
   1295 }
   1296 
   1297 /**
   1298  * @brief Convert vertex ID numbers for an `ObjGroup` of `ObjFace`s into pointers
   1299  *        to `ObjVertex` structures
   1300  *
   1301  * This function takes an `ObjGroup` of `ObjFace` structures whose `vertices` field
   1302  * has indices and not pointers. These indices are transformed into pointers of
   1303  * `ObjVertex` or `ObjParticle` structures from the @p vtxgrp `ObjGroup`.
   1304  *
   1305  * @param facegrp `ObjGroup` of `ObjFaces` to map vertex indices to pointers
   1306  * @param vtxgrp  `ObjGroup` of `ObjVertices`/`ObjParticles` to be mapped against
   1307  * @return void
   1308  * @note It seems that this function was replaced by `chk_shapegen()`, which performs
   1309  *       a very similar task...
   1310  */
   1311 void map_vertices(struct ObjGroup *facegrp, struct ObjGroup *vtxgrp) {
   1312     register struct ListNode *faceNode;
   1313     struct ObjFace *curFace;
   1314     register struct ListNode *vtxNode;
   1315     struct ObjVertex *vtx;
   1316 
   1317     imin("map_vertices");
   1318 
   1319     // resolve vertex indices to actual vertices
   1320     faceNode = facegrp->firstMember;
   1321     while (faceNode != NULL) {
   1322         curFace = (struct ObjFace *) faceNode->obj;
   1323         find_thisface_verts(curFace, vtxgrp);
   1324         faceNode = faceNode->next;
   1325     }
   1326 
   1327     // compute normals of vertices in vtxgrp
   1328     vtxNode = vtxgrp->firstMember;
   1329     while (vtxNode != NULL) {
   1330         vtx = (struct ObjVertex *) vtxNode->obj;
   1331         calc_vtx_normal(vtx, facegrp);
   1332         vtxNode = vtxNode->next;
   1333     }
   1334 
   1335     imout();
   1336 }
   1337 
   1338 /**
   1339  * Unselect a grabbable objects
   1340  *
   1341  * @param obj `GdObj` to unselect
   1342  * @return void
   1343  * @note Not Called
   1344  */
   1345 void unpick_obj(struct GdObj *obj) {
   1346     struct GdObj *why = obj;
   1347     if (why->drawFlags & OBJ_IS_GRABBABLE) {
   1348         why->drawFlags &= ~(OBJ_PICKED | OBJ_HIGHLIGHTED);
   1349     }
   1350 }
   1351 
   1352 /**
   1353  * @brief Find the closest object to the cursor on an A-button press
   1354  *
   1355  * This function is applied to all objects in an `ObjView.components` group
   1356  * to find the object closest to the cursor when there's an A press
   1357  *
   1358  * @param input `GdObj` to check
   1359  * @return void
   1360  */
   1361 void find_closest_pickable_obj(struct GdObj *input) {
   1362     struct GdObj *obj = input;
   1363     UNUSED u8 filler[12];
   1364     f32 distance;
   1365 
   1366     if (obj->drawFlags & OBJ_IS_GRABBABLE) {
   1367         if (obj->index == sPickDataTemp) {
   1368             if (gViewUpdateCamera != NULL) {
   1369                 distance = d_calc_world_dist_btwn(&gViewUpdateCamera->header, obj);
   1370             } else {
   1371                 distance = 0.0f;
   1372             }
   1373 
   1374             if (distance < sPickObjDistance) {
   1375                 sPickObjDistance = distance;
   1376                 sPickedObject = obj;
   1377             }
   1378         }
   1379     }
   1380 }
   1381 
   1382 /**
   1383  * @brief Set the global view camera if not already set.
   1384  *
   1385  * This function is used to find the first `ObjCamera` when running `update_view()`.
   1386  *
   1387  * @param cam `ObjCamera` to set to the update camera, if possible
   1388  * @return void
   1389  */
   1390 void set_view_update_camera(struct ObjCamera *cam) {
   1391     if (gViewUpdateCamera != NULL) {
   1392         return;
   1393     }
   1394 
   1395     gViewUpdateCamera = cam;
   1396 }
   1397 
   1398 /**
   1399  * @brief The main per-frame function for handling a view
   1400  *
   1401  * This function handles updating and rendering a given `ObjView` structure.
   1402  * It also handles the A button input for grabbing an area of an `ObjShape`
   1403  * that is contained in the `ObjView.components` group
   1404  *
   1405  * @param view The `ObjView` to update
   1406  * @return void
   1407  */
   1408 void update_view(struct ObjView *view) {
   1409     s32 i;
   1410     s32 pickOffset;
   1411     s32 pickDataSize;
   1412     s32 j;
   1413     s32 pickDataIdx;
   1414     s32 pickedObjType;
   1415     char objTypeAbbr[0x100];
   1416 
   1417     sUpdateViewState.shapesDrawn = 0;
   1418     sUpdateViewState.unused = 0;
   1419 
   1420     if (!(view->flags & VIEW_UPDATE)) {
   1421         view->flags &= ~VIEW_WAS_UPDATED;
   1422         return;
   1423     }
   1424 
   1425     imin("UpdateView()");
   1426     if (view->proc != NULL) {
   1427         view->proc(view);
   1428     }
   1429 
   1430     if (!(view->flags & VIEW_WAS_UPDATED)) {
   1431         view->flags |= VIEW_WAS_UPDATED;
   1432     }
   1433 
   1434     gViewUpdateCamera = NULL;
   1435     if (view->components != NULL) {
   1436         apply_to_obj_types_in_group(OBJ_TYPE_CAMERAS, (applyproc_t) set_view_update_camera,
   1437                                     view->components);
   1438         view->activeCam = gViewUpdateCamera;
   1439 
   1440         if (view->activeCam != NULL) {
   1441             gViewUpdateCamera->unk18C = view;
   1442         }
   1443     }
   1444 
   1445     if (view->flags & VIEW_MOVEMENT) {
   1446         split_timer("dlgen");
   1447         restart_timer("dynamics");
   1448         proc_view_movement(view);
   1449         split_timer("dynamics");
   1450         restart_timer("dlgen");
   1451         gViewUpdateCamera = view->activeCam;
   1452     }
   1453 
   1454     if (!(view->flags & VIEW_DRAW)) {
   1455         imout();
   1456         return;
   1457     }
   1458 
   1459     sUpdateViewState.view = view;
   1460     set_active_view(view);
   1461     view->gdDlNum = gd_startdisplist(8);
   1462     start_view_dl(sUpdateViewState.view);
   1463     gd_shading(9);
   1464 
   1465     if (view->flags & (VIEW_UNK_2000 | VIEW_UNK_4000)) {
   1466         gd_set_one_cycle();
   1467     }
   1468 
   1469     if (view->components != NULL) {
   1470         if (gGdCtrl.dragging) {
   1471             if (gd_getproperty(3, 0) != FALSE && gGdCtrl.startedDragging != FALSE) {
   1472                 init_pick_buf(sPickBuffer, ARRAY_COUNT(sPickBuffer));
   1473                 drawscene(FIND_PICKS, sUpdateViewState.view->components, NULL);
   1474                 pickOffset = get_cur_pickbuf_offset(sPickBuffer);
   1475                 sPickDataTemp = 0;
   1476                 sPickedObject = NULL;
   1477                 sPickObjDistance = 10000000.0f;
   1478 
   1479                 if (pickOffset < 0) {
   1480                     fatal_printf("UpdateView(): Pick buffer too small");
   1481                 } else if (pickOffset > 0) {
   1482                     pickDataIdx = 0;
   1483                     for (i = 0; i < pickOffset; i++) {
   1484                         pickDataSize = sPickBuffer[pickDataIdx++];
   1485                         if (pickDataSize != 0) {
   1486                             switch ((pickedObjType = sPickBuffer[pickDataIdx++])) {
   1487                                 case OBJ_TYPE_JOINTS:
   1488                                     gd_strcpy(objTypeAbbr, "J");
   1489                                     break;
   1490                                 case OBJ_TYPE_NETS:
   1491                                     gd_strcpy(objTypeAbbr, "N");
   1492                                     break;
   1493                                 case OBJ_TYPE_PARTICLES:
   1494                                     gd_strcpy(objTypeAbbr, "P");
   1495                                     break;
   1496                                 default:
   1497                                     gd_strcpy(objTypeAbbr, "?");
   1498                                     break;
   1499                             }
   1500                         }
   1501 
   1502                         if (pickDataSize >= 2) {
   1503                             for (j = 0; j < pickDataSize - 1; j++) {
   1504                                 sPickDataTemp = sPickBuffer[pickDataIdx++];
   1505                                 apply_to_obj_types_in_group(pickedObjType,
   1506                                                             (applyproc_t) find_closest_pickable_obj,
   1507                                                             sUpdateViewState.view->components);
   1508                             }
   1509                         }
   1510                     }
   1511                 }
   1512 
   1513                 if (sPickedObject != NULL) {
   1514                     sPickedObject->drawFlags |= OBJ_PICKED;
   1515                     sPickedObject->drawFlags |= OBJ_HIGHLIGHTED;
   1516                     sUpdateViewState.view->pickedObj = sPickedObject;
   1517                     gGdCtrl.dragStartX = gGdCtrl.csrX = sGrabCords.x;
   1518                     gGdCtrl.dragStartY = gGdCtrl.csrY = sGrabCords.y;
   1519                 }
   1520             }
   1521 
   1522             find_and_drag_picked_object(sUpdateViewState.view->components);
   1523         } else { // check for any previously picked objects, and turn off?
   1524             if (sUpdateViewState.view->pickedObj != NULL) {
   1525                 sUpdateViewState.view->pickedObj->drawFlags &= ~OBJ_PICKED;
   1526                 sUpdateViewState.view->pickedObj->drawFlags &= ~OBJ_HIGHLIGHTED;
   1527                 sUpdateViewState.view->pickedObj = NULL;
   1528             }
   1529         }
   1530 
   1531         drawscene(RENDER_SCENE, sUpdateViewState.view->components, sUpdateViewState.view->lights);
   1532     }
   1533 
   1534     border_active_view();
   1535     gd_enddlsplist_parent();
   1536     imout();
   1537     return;
   1538 }
   1539 /**
   1540  * Stub function.
   1541  * @note Not Called
   1542  */
   1543 void stub_draw_objects_1(void) {
   1544 }