From 2a78404ef5cd04e8bae04e7a95beeb4fe6a1146b Mon Sep 17 00:00:00 2001 From: Fazana <52551480+FazanaJ@users.noreply.github.com> Date: Tue, 7 Sep 2021 12:13:40 +0100 Subject: [PATCH] Puppylights --- actors/mario/model.inc.c | 12 +- include/config.h | 2 + include/level_commands.h | 1 + include/object_constants.h | 5 +- include/object_fields.h | 3 + include/types.h | 60 ++++--- src/boot/main.c | 4 + src/engine/behavior_script.c | 5 + src/engine/level_script.c | 56 ++++++- src/game/behavior_actions.c | 1 + src/game/level_update.c | 28 +++- src/game/obj_behaviors.c | 1 + src/game/obj_behaviors_2.c | 1 + src/game/puppylights.c | 311 +++++++++++++++++++++++++++++++++++ src/game/puppylights.h | 50 ++++++ src/game/spawn_object.c | 1 + 16 files changed, 505 insertions(+), 36 deletions(-) create mode 100644 src/game/puppylights.c create mode 100644 src/game/puppylights.h diff --git a/actors/mario/model.inc.c b/actors/mario/model.inc.c index 9fccd9c7..9fdf3bd4 100644 --- a/actors/mario/model.inc.c +++ b/actors/mario/model.inc.c @@ -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 ); diff --git a/include/config.h b/include/config.h index 0b0cf0bf..730ed557 100644 --- a/include/config.h +++ b/include/config.h @@ -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 diff --git a/include/level_commands.h b/include/level_commands.h index 940706a4..07328e0e 100644 --- a/include/level_commands.h +++ b/include/level_commands.h @@ -5,6 +5,7 @@ #include "level_table.h" #include "config.h" +#include "game/puppylights.h" #define OP_AND 0 #define OP_NAND 1 diff --git a/include/object_constants.h b/include/object_constants.h index cca7c870..17a18c01 100644 --- a/include/object_constants.h +++ b/include/object_constants.h @@ -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 diff --git a/include/object_fields.h b/include/object_fields.h index f474fe29..922abb14 100644 --- a/include/object_fields.h +++ b/include/object_fields.h @@ -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) diff --git a/include/types.h b/include/types.h index f2467ea0..32aed982 100644 --- a/include/types.h +++ b/include/types.h @@ -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 diff --git a/src/boot/main.c b/src/boot/main.c index d27d0b5a..c4e3b8f6 100644 --- a/src/boot/main.c +++ b/src/boot/main.c @@ -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) { diff --git a/src/engine/behavior_script.c b/src/engine/behavior_script.c index 5617e5b9..001da0ba 100644 --- a/src/engine/behavior_script.c +++ b/src/engine/behavior_script.c @@ -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. diff --git a/src/engine/level_script.c b/src/engine/level_script.c index e656482e..becb9bf5 100644 --- a/src/engine/level_script.c +++ b/src/engine/level_script.c @@ -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) { diff --git a/src/game/behavior_actions.c b/src/game/behavior_actions.c index 0240e095..d3566d5f 100644 --- a/src/game/behavior_actions.c +++ b/src/game/behavior_actions.c @@ -44,6 +44,7 @@ #include "spawn_object.h" #include "spawn_sound.h" #include "rumble_init.h" +#include "puppylights.h" #define o gCurrentObject diff --git a/src/game/level_update.c b/src/game/level_update.c index 5d8e1bf5..64204b2d 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -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; } diff --git a/src/game/obj_behaviors.c b/src/game/obj_behaviors.c index 4432645a..6b869ec1 100644 --- a/src/game/obj_behaviors.c +++ b/src/game/obj_behaviors.c @@ -32,6 +32,7 @@ #include "spawn_object.h" #include "spawn_sound.h" #include "rumble_init.h" +#include "puppylights.h" /** * @file obj_behaviors.c diff --git a/src/game/obj_behaviors_2.c b/src/game/obj_behaviors_2.c index 323074ea..c46d7a65 100644 --- a/src/game/obj_behaviors_2.c +++ b/src/game/obj_behaviors_2.c @@ -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 diff --git a/src/game/puppylights.c b/src/game/puppylights.c new file mode 100644 index 00000000..0263729a --- /dev/null +++ b/src/game/puppylights.c @@ -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 +#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; +} diff --git a/src/game/puppylights.h b/src/game/puppylights.h new file mode 100644 index 00000000..3c063b64 --- /dev/null +++ b/src/game/puppylights.h @@ -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 diff --git a/src/game/spawn_object.c b/src/game/spawn_object.c index 4cc1636b..cb850eff 100644 --- a/src/game/spawn_object.c +++ b/src/game/spawn_object.c @@ -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; }