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 }