Puppylights

This commit is contained in:
Fazana
2021-09-07 12:13:40 +01:00
parent 2d58bddd06
commit 2a78404ef5
16 changed files with 505 additions and 36 deletions

View File

@@ -1,37 +1,37 @@
// Mario
// 0x04000000 # solid color blue - butt, left thigh, right thigh - all poly types
static const Lights1 mario_blue_lights_group = gdSPDefLights1(
const Lights1 mario_blue_lights_group = gdSPDefLights1(
0x00, 0x00, 0x7f,
0x00, 0x00, 0xff, 0x28, 0x28, 0x28
);
// 0x04000018 # solid color red - left & right arm, torso (tshirt part), caps - all poly types
static const Lights1 mario_red_lights_group = gdSPDefLights1(
const Lights1 mario_red_lights_group = gdSPDefLights1(
0x7f, 0x00, 0x00,
0xff, 0x00, 0x00, 0x28, 0x28, 0x28
);
// 0x04000030 # solid color white - metal butt & left thigh - normal left & right hand closed & open (with cap too) and all wings - all poly types
static const Lights1 mario_white_lights_group = gdSPDefLights1(
const Lights1 mario_white_lights_group = gdSPDefLights1(
0x7f, 0x7f, 0x7f,
0xff, 0xff, 0xff, 0x28, 0x28, 0x28
);
// 0x04000048 # solid color brown 1 - foot - all poly types
static const Lights1 mario_brown1_lights_group = gdSPDefLights1(
const Lights1 mario_brown1_lights_group = gdSPDefLights1(
0x39, 0x0e, 0x07,
0x72, 0x1c, 0x0e, 0x28, 0x28, 0x28
);
// 0x04000060 # solid color beige skin - face (cap on and off dls) - all poly types
static const Lights1 mario_beige_lights_group = gdSPDefLights1(
const Lights1 mario_beige_lights_group = gdSPDefLights1(
0x7f, 0x60, 0x3c,
0xfe, 0xc1, 0x79, 0x28, 0x28, 0x28
);
// 0x04000078 # solid color brown 2 - hair - all poly types
static const Lights1 mario_brown2_lights_group = gdSPDefLights1(
const Lights1 mario_brown2_lights_group = gdSPDefLights1(
0x39, 0x03, 0x00,
0x73, 0x06, 0x00, 0x28, 0x28, 0x28
);

View File

@@ -127,6 +127,8 @@
//#define VISUAL_DEBUG
// Number of supported areas per level.
#define AREA_COUNT 8
// Lightweight directional lighting engine by Fazana. Intended for giving proximity and positional pointlights to small objects.
#define PUPPYLIGHTS
// BUG/GAME QOL FIXES
// Fix instant warp offset not working when warping across different areas

View File

@@ -5,6 +5,7 @@
#include "level_table.h"
#include "config.h"
#include "game/puppylights.h"
#define OP_AND 0
#define OP_NAND 1

View File

@@ -47,6 +47,7 @@
#ifdef AUTO_COLLISION_DISTANCE
#define OBJ_FLAG_DONT_CALC_COLL_DIST (1 << 16) // 0x00010000
#endif
#define OBJ_FLAG_EMIT_LIGHT (1 << 17) // 0x00020000
#define OBJ_FLAG_HITBOX_WAS_SET (1 << 30) // 0x40000000
/* oHeldState */
@@ -283,7 +284,7 @@
#define BOWSER_SUB_ACT_HIT_MINE_START 0
#define BOWSER_SUB_ACT_HIT_MINE_FALL 1
#define BOWSER_SUB_ACT_HIT_MINE_STOP 2
#define BOWSER_SUB_ACT_JUMP_ON_STAGE_IDLE 0
#define BOWSER_SUB_ACT_JUMP_ON_STAGE_START 1
#define BOWSER_SUB_ACT_JUMP_ON_STAGE_LAND 2
@@ -299,7 +300,7 @@
/* oAction */
#define FISH_SPAWNER_ACT_SPAWN 0
#define FISH_SPAWNER_ACT_IDLE 1
#define FISH_SPAWNER_ACT_RESPAWN 2
#define FISH_SPAWNER_ACT_RESPAWN 2
/* oBehParams2ndByte */
#define FISH_SPAWNER_BP_MANY_BLUE 0
#define FISH_SPAWNER_BP_FEW_BLUE 1

View File

@@ -134,6 +134,9 @@
#define /*0x1BC*/ oAngleToHome OBJECT_FIELD_S32(0x4D)
#define /*0x1C0*/ oFloor OBJECT_FIELD_SURFACE(0x4E)
#define /*0x1C4*/ oDeathSound OBJECT_FIELD_S32(0x4F)
#ifdef PUPPYLIGHTS
#define /*0x1C4*/ oLightID OBJECT_FIELD_S32(0x50)
#endif
/* Pathed (see obj_follow_path) */
#define /*0x0FC*/ oPathedStartWaypoint OBJECT_FIELD_WAYPOINT(0x1D)

View File

@@ -147,9 +147,22 @@ struct ObjectNode
struct ObjectNode *prev;
};
#ifdef PUPPYLIGHTS
struct PuppyLight {
Vec3t pos[2]; //The location of the light. First index is the absolute position, second index are offsets.
s16 yaw; //Used by cubes. Allows epic rotating of the volume.
s8 epicentre; //What percentage inside the volume you'll be before maximum light strength is applied. (E.g: 100 will be full strength always, and 0 will be full strength at the centre.)
u8 flags; //Some stuff to define how the volume is used. Mostly just shape stuff, but can potentially have other uses.
u8 rgba[4]; //Colour. Go on, take even the tiniest guess as to what this entails.
u8 active:1; //Whether the light will actually work. Mostly intended to be used for objects.
};
#endif
// NOTE: Since ObjectNode is the first member of Object, it is difficult to determine
// whether some of these pointers point to ObjectNode or Object.
#define MAX_OBJECT_FIELDS 0x51
struct Object
{
/*0x000*/ struct ObjectNode header;
@@ -163,33 +176,33 @@ struct Object
union
{
// Object fields. See object_fields.h.
u32 asU32[0x50];
s32 asS32[0x50];
s16 asS16[0x50][2];
f32 asF32[0x50];
u32 asU32[MAX_OBJECT_FIELDS];
s32 asS32[MAX_OBJECT_FIELDS];
s16 asS16[MAX_OBJECT_FIELDS][2];
f32 asF32[MAX_OBJECT_FIELDS];
#if !IS_64_BIT
s16 *asS16P[0x50];
s32 *asS32P[0x50];
struct Animation **asAnims[0x50];
struct Waypoint *asWaypoint[0x50];
struct ChainSegment *asChainSegment[0x50];
struct Object *asObject[0x50];
struct Surface *asSurface[0x50];
void *asVoidPtr[0x50];
const void *asConstVoidPtr[0x50];
s16 *asS16P[MAX_OBJECT_FIELDS];
s32 *asS32P[MAX_OBJECT_FIELDS];
struct Animation **asAnims[MAX_OBJECT_FIELDS];
struct Waypoint *asWaypoint[MAX_OBJECT_FIELDS];
struct ChainSegment *asChainSegment[MAX_OBJECT_FIELDS];
struct Object *asObject[MAX_OBJECT_FIELDS];
struct Surface *asSurface[MAX_OBJECT_FIELDS];
void *asVoidPtr[MAX_OBJECT_FIELDS];
const void *asConstVoidPtr[MAX_OBJECT_FIELDS];
#endif
} rawData;
#if IS_64_BIT
union {
s16 *asS16P[0x50];
s32 *asS32P[0x50];
struct Animation **asAnims[0x50];
struct Waypoint *asWaypoint[0x50];
struct ChainSegment *asChainSegment[0x50];
struct Object *asObject[0x50];
struct Surface *asSurface[0x50];
void *asVoidPtr[0x50];
const void *asConstVoidPtr[0x50];
s16 *asS16P[MAX_OBJECT_FIELDS];
s32 *asS32P[MAX_OBJECT_FIELDS];
struct Animation **asAnims[MAX_OBJECT_FIELDS];
struct Waypoint *asWaypoint[MAX_OBJECT_FIELDS];
struct ChainSegment *asChainSegment[MAX_OBJECT_FIELDS];
struct Object *asObject[MAX_OBJECT_FIELDS];
struct Surface *asSurface[MAX_OBJECT_FIELDS];
void *asVoidPtr[MAX_OBJECT_FIELDS];
const void *asConstVoidPtr[MAX_OBJECT_FIELDS];
} ptrData;
#endif
/*0x1C8*/ u32 unused1;
@@ -209,6 +222,9 @@ struct Object
/*0x218*/ void *collisionData;
/*0x21C*/ Mat4 transform;
/*0x25C*/ void *respawnInfo;
#ifdef PUPPYLIGHTS
struct PuppyLight puppylight;
#endif
};
struct ObjectHitbox

View File

@@ -19,6 +19,7 @@
#include "usb/debug.h"
#endif
#include "game/puppyprint.h"
#include "game/puppylights.h"
// Message IDs
#define MESG_SP_COMPLETE 100
@@ -115,6 +116,9 @@ void alloc_pool(void) {
main_pool_init(start, end);
gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT);
#ifdef PUPPYLIGHTS
gLightsPool = mem_pool_init(PUPPYLIGHTS_POOL, MEMORY_POOL_LEFT);
#endif
}
void create_thread(OSThread *thread, OSId id, void (*entry)(void *), void *arg, void *sp, OSPri pri) {

View File

@@ -13,6 +13,7 @@
#include "game/object_list_processor.h"
#include "graph_node.h"
#include "surface_collision.h"
#include "game/puppylights.h"
// Macros for retrieving arguments from behavior scripts.
#define BHV_CMD_GET_1ST_U8(index) (u8)((gCurBhvCommand[index] >> 24) & 0xFF) // unused
@@ -982,6 +983,10 @@ void cur_obj_update(void) {
obj_update_gfx_pos_and_angle(gCurrentObject);
}
#ifdef PUPPYLIGHTS
puppylights_object_emit(gCurrentObject);
#endif
// Handle visibility of object
if (gCurrentObject->oRoom != -1) {
// If the object is in a room, only show it when Mario is in the room.

View File

@@ -24,7 +24,10 @@
#include "math_util.h"
#include "surface_collision.h"
#include "surface_load.h"
#include "string.h"
#include "game/puppycam2.h"
#include "game/puppyprint.h"
#include "game/puppylights.h"
#include "config.h"
@@ -801,6 +804,9 @@ static void level_cmd_puppyvolume(void)
{
sCurrentCmd = CMD_NEXT;
gPuppyError |= PUPPY_ERROR_POOL_FULL;
#if PUPPYPRINT_DEBUG
append_puppyprint_log("Puppycamera volume allocation failed.");
#endif
return;
}
@@ -830,6 +836,52 @@ static void level_cmd_puppyvolume(void)
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_puppylight_environment(void)
{
#ifdef PUPPYLIGHTS
Lights1 temp = gdSPDefLights1(CMD_GET(u8, 2), CMD_GET(u8, 3), CMD_GET(u8, 4), CMD_GET(u8, 5), CMD_GET(u8, 6), CMD_GET(u8, 7), CMD_GET(u8, 8), CMD_GET(u8, 9), CMD_GET(u8, 10));
memcpy(&gLevelLight, &temp, sizeof(Lights1));
levelAmbient = TRUE;
#endif
sCurrentCmd = CMD_NEXT;
}
static void level_cmd_puppylight_node(void)
{
#ifdef PUPPYLIGHTS
if ((gPuppyLights[gNumLights] = mem_pool_alloc(gLightsPool, sizeof(struct PuppyLight))) == NULL)
{
#if PUPPYPRINT_DEBUG
append_puppyprint_log("Puppylight allocation failed.");
#endif
sCurrentCmd = CMD_NEXT;
return;
}
gPuppyLights[gNumLights]->rgba[0] = CMD_GET(u8, 2);
gPuppyLights[gNumLights]->rgba[1] = CMD_GET(u8, 3);
gPuppyLights[gNumLights]->rgba[2] = CMD_GET(u8, 4);
gPuppyLights[gNumLights]->rgba[3] = CMD_GET(u8, 5);
gPuppyLights[gNumLights]->pos[0][0] = CMD_GET(s16, 6);
gPuppyLights[gNumLights]->pos[0][1] = CMD_GET(s16, 8);
gPuppyLights[gNumLights]->pos[0][2] = CMD_GET(s16, 10);
gPuppyLights[gNumLights]->pos[1][0] = CMD_GET(s16, 12);
gPuppyLights[gNumLights]->pos[1][1] = CMD_GET(s16, 14);
gPuppyLights[gNumLights]->pos[1][2] = CMD_GET(s16, 16);
gPuppyLights[gNumLights]->yaw = CMD_GET(s16, 18);
gPuppyLights[gNumLights]->epicentre = CMD_GET(u8, 20);
gPuppyLights[gNumLights]->flags |= CMD_GET(u8, 21);
gPuppyLights[gNumLights]->active = TRUE;
gNumLights++;
#endif
sCurrentCmd = CMD_NEXT;
}
static void (*LevelScriptJumpTable[])(void) = {
/*00*/ level_cmd_load_and_execute,
@@ -894,7 +946,9 @@ static void (*LevelScriptJumpTable[])(void) = {
/*3B*/ level_cmd_create_whirlpool,
/*3C*/ level_cmd_get_or_set_var,
/*3D*/ level_cmd_puppyvolume,
/*3E*/ level_cmd_change_area_skybox,
/*3E*/ level_cmd_change_area_skybox,
/*3F*/ level_cmd_puppylight_environment,
/*40*/ level_cmd_puppylight_node,
};
struct LevelCommand *level_script_execute(struct LevelCommand *cmd) {

View File

@@ -44,6 +44,7 @@
#include "spawn_object.h"
#include "spawn_sound.h"
#include "rumble_init.h"
#include "puppylights.h"
#define o gCurrentObject

View File

@@ -30,6 +30,7 @@
#include "rumble_init.h"
#include "puppycam2.h"
#include "puppyprint.h"
#include "puppylights.h"
#include "config.h"
@@ -642,9 +643,11 @@ void initiate_warp(s16 destLevel, s16 destArea, s16 destWarpNode, s32 arg3) {
sWarpDest.areaIdx = destArea;
sWarpDest.nodeId = destWarpNode;
sWarpDest.arg = arg3;
#ifdef PUPPYCAM
//lol
#if defined(PUPPYCAM) || defined(PUPPYLIGHTS)
s32 i = 0;
#endif
#ifdef PUPPYCAM
if (sWarpDest.type != WARP_TYPE_SAME_AREA)
{
for (i = 0; i < gPuppyVolumeCount; i++)
@@ -653,7 +656,18 @@ void initiate_warp(s16 destLevel, s16 destArea, s16 destWarpNode, s32 arg3) {
}
gPuppyVolumeCount = 0;
}
#endif
#endif
#ifdef PUPPYLIGHTS
if (sWarpDest.type != WARP_TYPE_SAME_AREA)
{
for (i = 0; i < gNumLights; i++)
{
mem_pool_free(gLightsPool, gPuppyLights[i]);
}
gNumLights = 0;
levelAmbient = FALSE;
}
#endif
}
// From Surface 0xD3 to 0xFC
@@ -1265,10 +1279,14 @@ s32 init_level(void) {
sound_banks_disable(SEQ_PLAYER_SFX, SOUND_BANKS_DISABLED_DURING_INTRO_CUTSCENE);
}
#if PUPPYPRINT_DEBUG
#ifdef PUPPYLIGHTS
puppylights_allocate();
#endif
#if PUPPYPRINT_DEBUG
sprintf(textBytes, "Level loaded in %dus", (s32)(OS_CYCLES_TO_USEC(osGetTime() - first)));
append_puppyprint_log(textBytes);
#endif
#endif
return 1;
}

View File

@@ -32,6 +32,7 @@
#include "spawn_object.h"
#include "spawn_sound.h"
#include "rumble_init.h"
#include "puppylights.h"
/**
* @file obj_behaviors.c

View File

@@ -45,6 +45,7 @@
#include "save_file.h"
#include "seq_ids.h"
#include "spawn_sound.h"
#include "puppylights.h"
#define POS_OP_SAVE_POSITION 0
#define POS_OP_COMPUTE_VELOCITY 1

311
src/game/puppylights.c Normal file
View File

@@ -0,0 +1,311 @@
///Puppylights 2.0 by Fazana. What happened to 1.0? Tragic accident.
/**
Intended for use with manipulating existing Lights1 structs for objects in real time.
Can support static lights that are loaded with the level, or lights created by objects.
Puppylights is generally intended to be used with things that don't directly use lights to colour
themselves. Inside the main function, you can pass through a colour to override the default light
but it will not be affected by environmental tinting. If you wish for an object to emit a light,
simply set the object flag OBJ_FLAG_EMIT_LIGHT and set some values to o->puppylight.
**/
#include <ultra64.h>
#include "types.h"
#include "puppylights.h"
#include "area.h"
#include "engine/math_util.h"
#include "string.h"
#include "object_fields.h"
#include "object_constants.h"
#include "camera.h"
#include "memory.h"
#include "print.h"
#include "debug_box.h"
#include "object_list_processor.h"
#include "level_update.h"
Lights1 gLevelLight; //Existing ambient light in the area. Will be set by the level script, though can always be changed afterwards if desired.
u8 levelAmbient = FALSE;
Lights1 *sLightBase; //The base value where lights are written to when worked with.
Lights1 sDefaultLights = gdSPDefLights1(0x7F, 0x7F, 0x7F, 0xFE, 0xFE, 0xFE, 0x28, 0x28, 0x28); //Default lights default lights
u16 gNumLights = 0; //How many lights are loaded.
u16 gDynLightStart = 0; //Where the dynamic lights will start.
struct PuppyLight *gPuppyLights[MAX_LIGHTS]; //This contains all the loaded data.
struct MemoryPool *gLightsPool; //The memory pool where the above is stored.
//Runs after an area load, allocates the dynamic light slots.
void puppylights_allocate(void)
{
s32 numAllocate = MIN(MAX_LIGHTS - gNumLights, MAX_LIGHTS_DYNAMIC);
s32 i;
gDynLightStart = gNumLights;
if (numAllocate <= 0) //If this happens you've allocated too many static lights and therefore cucked dynamic.
return;
//Now it has the number it wants, it will allocate this many extra lights, intended for dynamic lights.
for (i = 0; i < numAllocate; i++)
{
gPuppyLights[gNumLights] = mem_pool_alloc(gLightsPool, sizeof(struct PuppyLight));
if (gPuppyLights[gNumLights] == NULL)
return;
gPuppyLights[gNumLights]->flags = PUPPYLIGHT_DYNAMIC;
gPuppyLights[gNumLights]->active = FALSE;
gNumLights++;
}
}
extern Mat4 gMatStack[32];
//Function that iterates through each light.
void puppylights_iterate(struct PuppyLight *light, Lights1 *src, struct Object *obj)
{
Lights1 *tempLight;
s32 lightPos[2];
Vec3i lightRelative;
Vec3i lightDir;
s32 lightIntensity = 0;
s32 i;
s32 colour;
s32 ambient;
f64 scale = 1.0f;
f32 scale2;
f64 scaleVal = 1.0f;
f64 preScale;
//Relative positions of the object vs the centre of the node.
lightRelative[0] = light->pos[0][0] - obj->oPosX;
lightRelative[1] = light->pos[0][1] - obj->oPosY;
lightRelative[2] = light->pos[0][2] - obj->oPosZ;
//If the node is a cube, then calculate the distance a bit differently.
if (light->flags & PUPPYLIGHT_SHAPE_CUBE)
{
//Get the position based off the rotation of the cube.
lightPos[0] = lightRelative[2] * sins(light->yaw) + lightRelative[0] * coss(light->yaw);
lightPos[1] = lightRelative[2] * coss(light->yaw) - lightRelative[0] * sins(light->yaw);
#ifdef VISUAL_DEBUG
Vec3f debugPos[2];
vec3f_set(debugPos[0], light->pos[0][0], light->pos[0][1], light->pos[0][2]);
vec3f_set(debugPos[1], light->pos[1][0], light->pos[1][1], light->pos[1][2]);
debug_box_color(0x00FFFF00);
debug_box_rot(debugPos[0], debugPos[1], light->yaw, DEBUG_SHAPE_BOX);
#endif
if (-light->pos[1][0] < lightPos[0] && lightPos[0] < light->pos[1][0] &&
-light->pos[1][1] < lightRelative[1] && lightRelative[1] < light->pos[1][1] &&
-light->pos[1][2] < lightPos[1] && lightPos[1] < light->pos[1][2])
{
scale = (lightPos[0] * lightPos[0]) + (lightRelative[1] * lightRelative[1]) + (lightPos[1] * lightPos[1]);
scaleVal = (light->pos[1][0] + light->pos[1][1] + light->pos[1][2])/3;
scaleVal *= scaleVal;
}
else
return;
}
else
if (light->flags & PUPPYLIGHT_SHAPE_CYLINDER)
{
#ifdef VISUAL_DEBUG
Vec3f debugPos[2];
vec3f_set(debugPos[0], light->pos[0][0], light->pos[0][1], light->pos[0][2]);
vec3f_set(debugPos[1], light->pos[1][0], light->pos[1][1], light->pos[1][2]);
debug_box_color(0x00FFFF00);
debug_box_rot(debugPos[0], debugPos[1], 0, DEBUG_SHAPE_CYLINDER);
#endif
//First do an AABB check, since it's a fair bit faster than doing just a distance check.
if (-light->pos[1][0] < lightRelative[0] && lightRelative[0] < light->pos[1][0] &&
-light->pos[1][1] < lightRelative[1] && lightRelative[1] < light->pos[1][1] &&
-light->pos[1][2] < lightRelative[2] && lightRelative[2] < light->pos[1][2])
{
//Okay now we can do a distance check
scale = (lightRelative[0] * lightRelative[0]) + (lightRelative[1] * lightRelative[1]) + (lightRelative[2] * lightRelative[2]);
scaleVal = (light->pos[1][0] + light->pos[1][1] + light->pos[1][2])/3;
scaleVal *= scaleVal;
if (scale > scaleVal)
return;
}
else
return;
}
else
return;
tempLight = segmented_to_virtual(src);
//Now we have a scale value and a scale factor, we can start lighting things up.
//Convert to a percentage.
preScale = scale;
scale /= scaleVal;
scale = CLAMP(scale, 0.0f, 1.0f);
//Reduce scale2 by the epicentre.
scale2 = (scale - (f32)(light->epicentre/100.0f)) * (1+(f32)(light->epicentre/100.0f));
scale2 = CLAMP(scale2, 0.0f, 1.0f);
//Normalise the light brightness.
lightIntensity = (tempLight->a.l.col[0]+tempLight->a.l.col[1]+tempLight->a.l.col[2])/3.0f;
//Get the direction numbers we want by applying some maths to the relative positions. We use 64 because light directions range from -64 to 63.
//Note: can this be optimised further? Simply squaring lightRelative and then dividing it by preScale doesn't work.
if (light->flags & PUPPYLIGHT_DIRECTIONAL)
{
lightDir[0] = ((lightRelative[0]) * 64.0f) / sqrtf(preScale);
lightDir[1] = ((lightRelative[1]) * 64.0f) / sqrtf(preScale);
lightDir[2] = ((lightRelative[2]) * 64.0f) / sqrtf(preScale);
}
//Get direction if applicable.
for (i = 0; i < 3; i++)
{
//So it works by starting from the final colour, and then lerping to the original colour, by a factor of the epicentre corrected scale. Light opacity affects this further.
colour = approach_f32_asymptotic(light->rgba[i], sLightBase->l[0].l.col[i], scale2*((f32)light->rgba[3]/255.0f));
//If it's a directional light, then increase the current ambient by 50%, to give the effect better.
//Otherwise, just normalise the brightness to keep it in line with the current ambient.
if (light->flags & PUPPYLIGHT_DIRECTIONAL)
ambient = approach_f32_asymptotic(MIN(tempLight->a.l.col[i]*1.5f, 0xFF), tempLight->a.l.col[i], scale*((f32)light->rgba[3]/255.0f));
else
ambient = approach_f32_asymptotic(lightIntensity, tempLight->a.l.col[i], scale*((f32)light->rgba[3]/255.0f));
//And now to apply the values.
tempLight->l[0].l.col[i] = colour;
tempLight->l[0].l.colc[i] = colour;
//Ambient, too.
tempLight->a.l.col[i] = ambient;
tempLight->a.l.colc[i] = ambient;
//Apply direction. It takes the relative positions, and then multiplies them with the perspective matrix to get a correct direction.
//Index 1 of the first dimension of gMatStack is perspective. Note that if you ever decide to cheat your way into rendering things after the game does :^)
if (light->flags & PUPPYLIGHT_DIRECTIONAL)
tempLight->l->l.dir[i] = approach_f32_asymptotic((s8)(lightDir[0] * gMatStack[1][0][i] + lightDir[1] * gMatStack[1][1][i] + lightDir[2] * gMatStack[1][2][i]), tempLight->l->l.dir[i], scale);
}
}
//Main function. Run this in the object you wish to illuminate, and just give it its light, object pointer and any potential flags if you want to use them.
//If the object has multiple lights, then you run this for each light.
void puppylights_run(Lights1 *src, struct Object *obj, s32 flags, s32 baseColour)
{
s32 i;
if (gCurrLevelNum < 4)
return;
//Checks if there's a hardset colour. Colours are only the first 3 bytes, so you can really put whatever you want in the last.
//If there isn't a colour, then it decides whether to apply the ambient lighting, or the default lighting as the baseline.
//Otherwise, it hardsets a colour to begin with. I don't recommend you use this, simply because it's intended to be used
//As a hacky quick-fix for models coloured by lights. Lightcoloured models don't blend nearly as nicely as ones coloured
//By other means.
if (baseColour < 0x100)
{
if (levelAmbient)
sLightBase = &gLevelLight;
else
sLightBase = &sDefaultLights;
}
else
{
s32 colour;
Lights1 tempLight;
sLightBase = &tempLight;
for (i = 0; i < 3; i++)
{
colour = (((baseColour >> (24-(i*8)))) & 0xFF);
sLightBase->l[0].l.col[i] = colour;
sLightBase->l[0].l.colc[i] = colour;
sLightBase->a.l.col[i] = colour/2;
sLightBase->a.l.colc[i] = colour/2;
sLightBase->l->l.dir[i] = 0x28;
}
}
memcpy(segmented_to_virtual(src), &sLightBase[0], sizeof(Lights1));
for (i = 0; i < gNumLights; i++)
{
if (gPuppyLights[i]->rgba[3] > 0 && gPuppyLights[i]->active == TRUE)
puppylights_iterate(gPuppyLights[i], src, obj);
}
}
//Sets and updates dynamic lights from objects.
//0xFFFF is essentially the null ID. If the display flag is met, it will find and set an ID, otherwise it frees up the spot.
void puppylights_object_emit(struct Object *obj)
{
s32 i;
if (gCurrLevelNum < 4)
return;
if (obj->oFlags & OBJ_FLAG_EMIT_LIGHT)
{
f64 dist = ((obj->oPosX - gMarioState->pos[0]) * (obj->oPosX - gMarioState->pos[0])) +
((obj->oPosY - gMarioState->pos[1]) * (obj->oPosY - gMarioState->pos[1])) +
((obj->oPosZ - gMarioState->pos[2]) * (obj->oPosZ - gMarioState->pos[2]));
f64 lightSize = ((obj->puppylight.pos[1][0]) * (obj->puppylight.pos[1][0])) +
((obj->puppylight.pos[1][1]) * (obj->puppylight.pos[1][1])) +
((obj->puppylight.pos[1][2]) * (obj->puppylight.pos[1][2]));
if (dist > lightSize)
goto deallocate; //That's right. I used a goto. Eat your heart out xkcd.
if (obj->oLightID == 0xFFFF)
{
if (ABS(gNumLights - gDynLightStart) < MAX_LIGHTS_DYNAMIC)
goto deallocate;
for (i = gDynLightStart; i < MAX_LIGHTS; i++)
{
if (gPuppyLights[i]->active == TRUE)
continue;
memcpy(gPuppyLights[i], &obj->puppylight, sizeof(struct PuppyLight));
gPuppyLights[i]->active = TRUE;
obj->oLightID = i;
goto updatepos;
}
}
else
{
updatepos:
gPuppyLights[obj->oLightID]->pos[0][0] = obj->oPosX;
gPuppyLights[obj->oLightID]->pos[0][1] = obj->oPosY;
gPuppyLights[obj->oLightID]->pos[0][2] = obj->oPosZ;
}
}
else
{
deallocate:
if (obj->oLightID != 0xFFFF)
gPuppyLights[obj->oLightID]->active = FALSE;
obj->oLightID = 0xFFFF;
}
}
//A bit unorthodox, but anything to avoid having to set up data to pass through in the original function.
//Objects will completely ignore X, Y, Z and active though.
void set_light_properties(struct PuppyLight *light, s32 x, s32 y, s32 z, s32 offsetX, s32 offsetY, s32 offsetZ, s32 yaw, s32 epicentre, s32 colour, s32 flags, s32 active)
{
light->active = active;
light->pos[0][0] = x;
light->pos[0][1] = y;
light->pos[0][2] = z;
light->pos[1][0] = offsetX;
light->pos[1][1] = offsetY;
light->pos[1][2] = offsetZ;
light->rgba[0] = (colour >> 24) & 0xFF;
light->rgba[1] = (colour >> 16) & 0xFF;
light->rgba[2] = (colour >> 8) & 0xFF;
light->rgba[3] = colour & 0xFF;
light->yaw = yaw;
light->epicentre = epicentre;
light->flags |= flags | PUPPYLIGHT_DYNAMIC;
}
//You can run these in objects to enable or disable their light properties.
void cur_obj_enable_light(void)
{
gCurrentObject->oFlags |= OBJ_FLAG_EMIT_LIGHT;
}
void cur_obj_disable_light(void)
{
gCurrentObject->oFlags &= ~OBJ_FLAG_EMIT_LIGHT;
}
void obj_enable_light(struct Object *obj)
{
obj->oFlags |= OBJ_FLAG_EMIT_LIGHT;
}
void obj_disable_light(struct Object *obj)
{
obj->oFlags &= ~OBJ_FLAG_EMIT_LIGHT;
}

50
src/game/puppylights.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef PUPPYLIGHTS_H
#define PUPPYLIGHTS_H
#include "types.h"
#include "command_macros_base.h"
//The maximum number of lights that can be loaded at once. Any further lights that attempt to be created past this will simply not spawn.
#define MAX_LIGHTS 32
//The maximum number of dynamic lights available at one time.
#define MAX_LIGHTS_DYNAMIC 8
//Two shapes. Choose your destiny.
#define PUPPYLIGHT_SHAPE_CUBE 0x1
#define PUPPYLIGHT_SHAPE_CYLINDER 0x2
#define PUPPYLIGHT_DYNAMIC 0x4
#define PUPPYLIGHT_DIRECTIONAL 0x8
#define PUPPYLIGHT_FLAG_SHADOW 0x1
#define PUPPYLIGHT_FLAG_WET 0x2
#define PUPPYLIGHT_ENVIRONMENT(ambientR, ambientG, ambientB, diffuseR, diffuseG, diffuseB, diffuseX, diffuseY, diffuseZ) \
CMD_BBBB(0x3F, 0x0C, ambientR, ambientG), \
CMD_BBBB(ambientB, diffuseR, diffuseG, diffuseB), \
CMD_BBBB(diffuseX, diffuseY, diffuseZ, 0x0)
#define PUPPYLIGHT_NODE(r, g, b, a, x, y, z, offsetX, offsetY, offsetZ, yaw, epicentre, flags) \
CMD_BBBB(0x40, 0x18, r, g), \
CMD_BBH(b, a, x), \
CMD_HH(y, z), \
CMD_HH(offsetX, offsetY), \
CMD_HH(offsetZ, yaw), \
CMD_BBH(epicentre, flags, 0x0)
//How much RAM is allocated to puppylights
#define PUPPYLIGHTS_POOL sizeof(struct PuppyLight) * MAX_LIGHTS
extern Lights1 gLevelLight;
extern u16 gNumLights;
extern u8 levelAmbient;
extern struct PuppyLight *gPuppyLights[MAX_LIGHTS];
extern struct MemoryPool *gLightsPool;
extern void puppylights_run(Lights1 *src, struct Object *obj, s32 flags, s32 baseColour);
extern void puppylights_object_emit(struct Object *obj);
extern void cur_obj_enable_light(void);
extern void cur_obj_disable_light(void);
extern void obj_enable_light(struct Object *obj);
extern void obj_disable_light(struct Object *obj);
extern void set_light_properties(struct PuppyLight *light, s32 x, s32 y, s32 z, s32 offsetX, s32 offsetY, s32 offsetZ, s32 yaw, s32 epicentre, s32 colour, s32 flags, s32 active);
extern void puppylights_allocate(void);
#endif

View File

@@ -289,6 +289,7 @@ struct Object *allocate_object(struct ObjectNode *objList) {
obj->header.gfx.pos[1] = -10000.0f;
obj->header.gfx.pos[2] = -10000.0f;
obj->header.gfx.throwMatrix = NULL;
obj->oLightID = 0xFFFF;
return obj;
}