#include "global.h" #include "objects/gameplay_keep/gameplay_keep.h" LightsBuffer sLightsBuffer; void Lights_PointSetInfo(LightInfo* info, s16 x, s16 y, s16 z, u8 r, u8 g, u8 b, s16 radius, s32 type) { info->type = type; info->params.point.x = x; info->params.point.y = y; info->params.point.z = z; Lights_PointSetColorAndRadius(info, r, g, b, radius); } void Lights_PointNoGlowSetInfo(LightInfo* info, s16 x, s16 y, s16 z, u8 r, u8 g, u8 b, s16 radius) { Lights_PointSetInfo(info, x, y, z, r, g, b, radius, LIGHT_POINT_NOGLOW); } void Lights_PointGlowSetInfo(LightInfo* info, s16 x, s16 y, s16 z, u8 r, u8 g, u8 b, s16 radius) { Lights_PointSetInfo(info, x, y, z, r, g, b, radius, LIGHT_POINT_GLOW); } void Lights_PointSetColorAndRadius(LightInfo* info, u8 r, u8 g, u8 b, s16 radius) { info->params.point.color[0] = r; info->params.point.color[1] = g; info->params.point.color[2] = b; info->params.point.radius = radius; } void Lights_PointSetPosition(LightInfo* info, s16 x, s16 y, s16 z) { info->params.point.x = x; info->params.point.y = y; info->params.point.z = z; } void Lights_DirectionalSetInfo(LightInfo* info, s8 x, s8 y, s8 z, u8 r, u8 g, u8 b) { info->type = LIGHT_DIRECTIONAL; info->params.dir.x = x; info->params.dir.y = y; info->params.dir.z = z; info->params.dir.color[0] = r; info->params.dir.color[1] = g; info->params.dir.color[2] = b; } void Lights_Reset(Lights* lights, u8 r, u8 g, u8 b) { lights->l.a.l.colc[0] = r; lights->l.a.l.col[0] = r; lights->l.a.l.colc[1] = g; lights->l.a.l.col[1] = g; lights->l.a.l.colc[2] = b; lights->l.a.l.col[2] = b; lights->numLights = 0; } /* * Draws every light in the provided Lights group */ void Lights_Draw(Lights* lights, GraphicsContext* gfxCtx) { Light* l; s32 i; OPEN_DISPS(gfxCtx); gSPNumLights(POLY_OPA_DISP++, lights->numLights); gSPNumLights(POLY_XLU_DISP++, lights->numLights); l = lights->l.l; for (i = 0; i < lights->numLights;) { gSPLight(POLY_OPA_DISP++, l, ++i); gSPLight(POLY_XLU_DISP++, l++, i); } gSPLight(POLY_OPA_DISP++, &lights->l.a, ++i); gSPLight(POLY_XLU_DISP++, &lights->l.a, i); CLOSE_DISPS(gfxCtx); } Light* Lights_FindSlot(Lights* lights) { if (lights->numLights >= 7) { return NULL; } return &lights->l.l[lights->numLights++]; } void Lights_BindPointWithReference(Lights* lights, LightParams* params, Vec3f* pos) { f32 xDiff; f32 yDiff; f32 zDiff; f32 posDiff; f32 scale; Light* light; if ((pos != NULL) && (params->point.radius >= 1)) { xDiff = params->point.x - pos->x; yDiff = params->point.y - pos->y; zDiff = params->point.z - pos->z; scale = params->point.radius; posDiff = SQ(xDiff) + SQ(yDiff) + SQ(zDiff); if (SQ(scale) > posDiff) { light = Lights_FindSlot(lights); if (light != NULL) { posDiff = sqrtf(posDiff); scale = posDiff / scale; scale = 1 - SQ(scale); light->l.col[0] = params->point.color[0] * scale; light->l.colc[0] = light->l.col[0]; light->l.col[1] = params->point.color[1] * scale; light->l.colc[1] = light->l.col[1]; light->l.col[2] = params->point.color[2] * scale; light->l.colc[2] = light->l.col[2]; scale = (posDiff < 1) ? 120 : 120 / posDiff; light->l.dir[0] = xDiff * scale; light->l.dir[1] = yDiff * scale; light->l.dir[2] = zDiff * scale; } } } } void Lights_BindPoint(Lights* lights, LightParams* params, PlayState* play) { Light* light; f32 radiusF = params->point.radius; Vec3f posF; Vec3f adjustedPos; u32 pad; if (radiusF > 0) { posF.x = params->point.x; posF.y = params->point.y; posF.z = params->point.z; SkinMatrix_Vec3fMtxFMultXYZ(&play->viewProjectionMtxF, &posF, &adjustedPos); if ((adjustedPos.z > -radiusF) && (600 + radiusF > adjustedPos.z) && (400 > fabsf(adjustedPos.x) - radiusF) && (400 > fabsf(adjustedPos.y) - radiusF)) { light = Lights_FindSlot(lights); if (light != NULL) { radiusF = 4500000.0f / (radiusF * radiusF); if (radiusF > 255) { radiusF = 255; } else if (20 > radiusF) { radiusF = 20; } light->p.col[0] = params->point.color[0]; light->p.colc[0] = light->p.col[0]; light->p.col[1] = params->point.color[1]; light->p.colc[1] = light->p.col[1]; light->p.col[2] = params->point.color[2]; light->p.colc[2] = light->p.col[2]; light->p.pos[0] = params->point.x; light->p.pos[1] = params->point.y; light->p.pos[2] = params->point.z; light->p.unk3 = 0x8; light->p.unk7 = 0xFF; light->p.unkE = (s32)radiusF; } } } } void Lights_BindDirectional(Lights* lights, LightParams* params, void* unused) { Light* light = Lights_FindSlot(lights); if (light != NULL) { light->l.col[0] = params->dir.color[0]; light->l.colc[0] = light->l.col[0]; light->l.col[1] = params->dir.color[1]; light->l.colc[1] = light->l.col[1]; light->l.col[2] = params->dir.color[2]; light->l.colc[2] = light->l.col[2]; light->l.dir[0] = params->dir.x; light->l.dir[1] = params->dir.y; light->l.dir[2] = params->dir.z; light->l.pad1 = 0; // TODO the fact that pad1 is set here means that it now does something in MM's microcode } } /** * For every light in a provided list, try to find a free slot in the provided Lights group and bind * a light to it. Then apply color and positional/directional info for each light * based on the parameters supplied by the node. * * Note: Lights in a given list can only be binded to however many free slots are * available in the Lights group. This is at most 7 slots for a new group, but could be less. */ void Lights_BindAll(Lights* lights, LightNode* listHead, Vec3f* refPos, PlayState* play) { static LightsPosBindFunc posBindFuncs[] = { Lights_BindPoint, (LightsPosBindFunc)Lights_BindDirectional, Lights_BindPoint, }; static LightsBindFunc dirBindFuncs[] = { Lights_BindPointWithReference, (LightsBindFunc)Lights_BindDirectional, Lights_BindPointWithReference, }; if (listHead != NULL) { if ((refPos == NULL) && (lights->enablePosLights == 1)) { do { posBindFuncs[listHead->info->type](lights, &listHead->info->params, play); listHead = listHead->next; } while (listHead != NULL); } else { do { dirBindFuncs[listHead->info->type](lights, &listHead->info->params, refPos); listHead = listHead->next; } while (listHead != NULL); } } } LightNode* Lights_FindBufSlot(void) { LightNode* ret; if (sLightsBuffer.numOccupied >= LIGHTS_BUFFER_SIZE) { return NULL; } ret = &sLightsBuffer.lights[sLightsBuffer.searchIndex]; while (ret->info != NULL) { sLightsBuffer.searchIndex++; if (sLightsBuffer.searchIndex < LIGHTS_BUFFER_SIZE) { ret++; } else { sLightsBuffer.searchIndex = 0; ret = &sLightsBuffer.lights[0]; } } sLightsBuffer.numOccupied++; return ret; } void Lights_FreeNode(LightNode* light) { if (light != NULL) { sLightsBuffer.numOccupied--; light->info = NULL; sLightsBuffer.searchIndex = (light - sLightsBuffer.lights) / (s32)sizeof(LightNode); //! @bug Due to pointer arithmetic, the division is unnecessary } } void LightContext_Init(PlayState* play, LightContext* lightCtx) { LightContext_InitList(play, lightCtx); LightContext_SetAmbientColor(lightCtx, 80, 80, 80); func_80102544(lightCtx, 0, 0, 0, 0x3E4, 0x3200); bzero(&sLightsBuffer, sizeof(LightsBuffer)); } void LightContext_SetAmbientColor(LightContext* lightCtx, u8 r, u8 g, u8 b) { lightCtx->ambient.r = r; lightCtx->ambient.g = g; lightCtx->ambient.b = b; } void func_80102544(LightContext* lightCtx, u8 a1, u8 a2, u8 a3, s16 numLights, s16 sp16) { lightCtx->unk7 = a1; lightCtx->unk8 = a2; lightCtx->unk9 = a3; lightCtx->unkA = numLights; lightCtx->unkC = sp16; } /** * Allocate a new Lights group and initilize the ambient color with that provided by LightContext */ Lights* LightContext_NewLights(LightContext* lightCtx, GraphicsContext* gfxCtx) { return Lights_New(gfxCtx, lightCtx->ambient.r, lightCtx->ambient.g, lightCtx->ambient.b); } void LightContext_InitList(PlayState* play, LightContext* lightCtx) { lightCtx->listHead = NULL; } void LightContext_DestroyList(PlayState* play, LightContext* lightCtx) { while (lightCtx->listHead != NULL) { LightContext_RemoveLight(play, lightCtx, lightCtx->listHead); lightCtx->listHead = lightCtx->listHead->next; } } /** * Insert a new light into the list pointed to by LightContext * * Note: Due to the limited number of slots in a Lights group, inserting too many lights in the * list may result in older entries not being bound to a Light when calling Lights_BindAll */ LightNode* LightContext_InsertLight(PlayState* play, LightContext* lightCtx, LightInfo* info) { LightNode* light; light = Lights_FindBufSlot(); if (light != NULL) { light->info = info; light->prev = NULL; light->next = lightCtx->listHead; if (lightCtx->listHead != NULL) { lightCtx->listHead->prev = light; } lightCtx->listHead = light; } return light; } void LightContext_RemoveLight(PlayState* play, LightContext* lightCtx, LightNode* light) { if (light != NULL) { if (light->prev != NULL) { light->prev->next = light->next; } else { lightCtx->listHead = light->next; } if (light->next != NULL) { light->next->prev = light->prev; } Lights_FreeNode(light); } } Lights* Lights_NewAndDraw(GraphicsContext* gfxCtx, u8 ambientR, u8 ambientG, u8 ambientB, u8 numLights, u8 r, u8 g, u8 b, s8 x, s8 y, s8 z) { Lights* lights; s32 i; lights = GRAPH_ALLOC(gfxCtx, sizeof(Lights)); lights->l.a.l.col[0] = lights->l.a.l.colc[0] = ambientR; lights->l.a.l.col[1] = lights->l.a.l.colc[1] = ambientG; lights->l.a.l.col[2] = lights->l.a.l.colc[2] = ambientB; lights->enablePosLights = 0; lights->numLights = numLights; for (i = 0; i < numLights; i++) { lights->l.l[i].l.col[0] = lights->l.l[i].l.colc[0] = r; lights->l.l[i].l.col[1] = lights->l.l[i].l.colc[1] = g; lights->l.l[i].l.col[2] = lights->l.l[i].l.colc[2] = b; lights->l.l[i].l.dir[0] = x; lights->l.l[i].l.dir[1] = y; lights->l.l[i].l.dir[2] = z; } Lights_Draw(lights, gfxCtx); return lights; } Lights* Lights_New(GraphicsContext* gfxCtx, u8 ambientR, u8 ambientG, u8 ambientB) { Lights* lights; lights = GRAPH_ALLOC(gfxCtx, sizeof(Lights)); lights->l.a.l.col[0] = ambientR; lights->l.a.l.colc[0] = ambientR; lights->l.a.l.col[1] = ambientG; lights->l.a.l.colc[1] = ambientG; lights->l.a.l.col[2] = ambientB; lights->l.a.l.colc[2] = ambientB; lights->enablePosLights = 0; lights->numLights = 0; return lights; } void Lights_GlowCheck(PlayState* play) { LightNode* light = play->lightCtx.listHead; while (light != NULL) { LightPoint* params = &light->info->params.point; if (light->info->type == LIGHT_POINT_GLOW) { Vec3f pos; Vec3f multDest; f32 wDest; pos.x = params->x; pos.y = params->y; pos.z = params->z; Actor_GetProjectedPos(play, &pos, &multDest, &wDest); params->drawGlow = 0; if ((multDest.z > 1) && (fabsf(multDest.x * wDest) < 1) && (fabsf(multDest.y * wDest) < 1)) { s32 wX = multDest.x * wDest * 160 + 160; s32 wY = multDest.y * wDest * -120 + 120; s32 wZ = (s32)((multDest.z * wDest) * 16352) + 16352; s32 zBuf = func_80178A94(wX, wY); if (wZ < zBuf) { params->drawGlow = 1; } } } light = light->next; } } void Lights_DrawGlow(PlayState* play) { Gfx* dl; LightPoint* params; LightNode* light = play->lightCtx.listHead; if (light != NULL) { OPEN_DISPS(play->state.gfxCtx); dl = func_8012C7FC(POLY_XLU_DISP); gDPSetDither(dl++, G_CD_NOISE); gDPSetCombineLERP(dl++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0); gSPDisplayList(dl++, gameplay_keep_DL_029CB0); do { if (light->info->type == LIGHT_POINT_GLOW) { params = &light->info->params.point; if (params->drawGlow) { f32 scale = SQ((f32)params->radius) * 2e-6f; gDPSetPrimColor(dl++, 0, 0, params->color[0], params->color[1], params->color[2], 50); Matrix_Translate(params->x, params->y, params->z, MTXMODE_NEW); Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); gSPMatrix(dl++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(dl++, gameplay_keep_DL_029CF0); } } light = light->next; } while (light != NULL); POLY_XLU_DISP = dl; CLOSE_DISPS(play->state.gfxCtx); } }