Start work on dynamic loading of models

This commit is contained in:
James Lambert 2023-07-14 23:10:48 -06:00
parent 1e45fbc6aa
commit 148534a67f
10 changed files with 224 additions and 13 deletions

View file

@ -169,13 +169,12 @@ build/src/scene/elevator.o: build/assets/models/props/round_elevator_collision.h
#
MODEL_LIST = assets/models/cube/cube.blend \
assets/models/signage/clock.blend \
assets/models/signage/clock_digits.blend \
assets/models/player/chell.blend \
assets/models/portal_gun/v_portalgun.blend \
assets/models/portal_gun/w_portalgun.blend \
assets/models/props/autoportal_frame/autoportal_frame.blend \
assets/models/props/button.blend \
assets/models/signage/clock_digits.blend \
assets/models/props/door_01.blend \
assets/models/props/door_02.blend \
assets/models/props/combine_ball_catcher.blend \
@ -202,6 +201,9 @@ MODEL_LIST = assets/models/cube/cube.blend \
assets/models/grav_flare.blend \
assets/models/fleck_ash2.blend
DYNAMIC_MODEL_LIST = assets/models/signage/clock.blend
ANIM_LIST = build/assets/models/pedestal_anim.o \
build/assets/models/props/box_dropper_anim.o \
build/assets/models/props/combine_ball_catcher_anim.o \
@ -214,6 +216,9 @@ ANIM_LIST = build/assets/models/pedestal_anim.o \
MODEL_HEADERS = $(MODEL_LIST:%.blend=build/%.h)
MODEL_OBJECTS = $(MODEL_LIST:%.blend=build/%_geo.o)
DYNAMIC_MODEL_HEADERS = $(DYNAMIC_MODEL_LIST:%.blend=build/%.h)
DYNAMIC_MODEL_OBJECTS = $(DYNAMIC_MODEL_LIST:%.blend=build/%_geo.o)
build/assets/models/%.h build/assets/models/%_geo.c build/assets/models/%_anim.c: build/assets/models/%.fbx assets/models/%.flags assets/materials/elevator.skm.yaml assets/materials/objects.skm.yaml assets/materials/static.skm.yaml $(TEXTURE_IMAGES) $(SKELATOOL64)
$(SKELATOOL64) --fixed-point-scale 256 --model-scale 0.01 --name $(<:build/assets/models/%.fbx=%) $(shell cat $(<:build/assets/models/%.fbx=assets/models/%.flags)) -o $(<:%.fbx=%.h) $<
@ -233,7 +238,7 @@ build/src/scene/render_plan.o: $(MODEL_HEADERS)
build/src/scene/portal_render.o: $(MODEL_HEADERS)
build/src/scene/clock.o: $(MODEL_HEADERS)
build/src/scene/clock.o: build/assets/models/dynamic_model_list.h
build/src/scene/fizzler.o: $(MODEL_HEADERS)
@ -271,12 +276,15 @@ build/src/menu/joystick_options.o: build/assets/materials/ui.h build/src/audio/c
build/src/menu/controls.o: build/assets/materials/ui.h build/src/audio/clips.h
build/src/util/dynamic_asset_data.o: build/assets/models/dynamic_model_list_data.h
build/assets/models/player/chell.h: assets/materials/chell.skm.yaml
build/assets/models/props/combine_ball_catcher.h: assets/materials/ball_catcher.skm.yaml
build/assets/models/props/combine_ball_launcher.h: assets/materials/ball_catcher.skm.yaml
ANIM_TEST_CHAMBERS = build/assets/test_chambers/test_chamber_00/test_chamber_00_anim.o \
build/assets/test_chambers/test_chamber_03/test_chamber_03_anim.o \
build/assets/test_chambers/test_chamber_04/test_chamber_04_anim.o \
@ -329,9 +337,17 @@ build/assets/test_chambers/level_list.h: $(TEST_CHAMBER_HEADERS) tools/generate_
@mkdir -p $(@D)
node tools/generate_level_list.js $@ $(TEST_CHAMBER_HEADERS)
build/assets/models/dynamic_model_list.h build/assets/models/dynamic_model_list_data.h: $(DYNAMIC_MODEL_HEADERS) tools/generate_dynamic_model_list.js
@mkdir -p $(@D)
node tools/generate_dynamic_model_list.js build/assets/models/dynamic_model_list.h build/assets/models/dynamic_model_list_data.h $(DYNAMIC_MODEL_HEADERS)
build/levels.ld: $(TEST_CHAMBER_OBJECTS) tools/generate_level_ld.js
@mkdir -p $(@D)
node tools/generate_level_ld.js $@ $(TEST_CHAMBER_OBJECTS)
node tools/generate_level_ld.js $@ 0x02000000 $(TEST_CHAMBER_OBJECTS)
build/dynamic_models.ld: $(DYNAMIC_MODEL_OBJECTS) tools/generate_level_ld.js
@mkdir -p $(@D)
node tools/generate_level_ld.js $@ 0x03000000 $(DYNAMIC_MODEL_OBJECTS)
build/src/levels/levels.o: build/assets/test_chambers/level_list.h build/assets/materials/static.h
@ -407,7 +423,7 @@ $(CODESEGMENT)_no_debug.o: $(CODEOBJECTS_NO_DEBUG)
$(LD) -o $(CODESEGMENT)_no_debug.o -r $(CODEOBJECTS_NO_DEBUG) $(LDFLAGS)
$(CP_LD_SCRIPT)_no_debug.ld: $(LD_SCRIPT) build/levels.ld build/anims.ld
$(CP_LD_SCRIPT)_no_debug.ld: $(LD_SCRIPT) build/levels.ld build/dynamic_models.ld build/anims.ld
cpp -P -Wno-trigraphs $(LCDEFS) -DCODE_SEGMENT=$(CODESEGMENT)_no_debug.o -o $@ $<
$(BASE_TARGET_NAME).z64: $(CODESEGMENT)_no_debug.o $(OBJECTS) $(DATA_OBJECTS) $(CP_LD_SCRIPT)_no_debug.ld
@ -425,7 +441,7 @@ endif
$(CODESEGMENT)_debug.o: $(CODEOBJECTS_DEBUG)
$(LD) -o $(CODESEGMENT)_debug.o -r $(CODEOBJECTS_DEBUG) $(LDFLAGS)
$(CP_LD_SCRIPT)_debug.ld: $(LD_SCRIPT) build/levels.ld build/anims.ld
$(CP_LD_SCRIPT)_debug.ld: $(LD_SCRIPT) build/levels.ld build/dynamic_models.ld build/anims.ld
cpp -P -Wno-trigraphs $(LCDEFS) -DCODE_SEGMENT=$(CODESEGMENT)_debug.o -o $@ $<
$(BASE_TARGET_NAME)_debug.z64: $(CODESEGMENT)_debug.o $(OBJECTS) $(DATA_OBJECTS) $(CP_LD_SCRIPT)_debug.ld

View file

@ -78,6 +78,7 @@ SECTIONS
END_SEG(images)
#include "build/levels.ld"
#include "build/dynamic_models.ld"
#include "build/anims.ld"
/* Discard everything not specifically mentioned above. */

View file

@ -17,6 +17,7 @@
#define NUM_FIELDS 1
#define LEVEL_SEGMENT 2
#define DYNAMIC_MODEL_SEGMENT 3
#define DMA_QUEUE_SIZE 200

View file

@ -20,6 +20,7 @@
#include "levels/cutscene_runner.h"
#include "savefile/savefile.h"
#include "sk64/skelatool_animator.h"
#include "util/dynamic_asset_loader.h"
#include "levels/levels.h"
#include "savefile/checkpoint.h"
@ -232,6 +233,7 @@ static void gameProc(void* arg) {
heapInit(_heapStart, memoryEnd);
levelLoadWithCallbacks(levelGetQueued());
cutsceneRunnerReset();
dynamicAssetsReset();
gSceneCallbacks->initCallback(gSceneCallbacks->data);
}

View file

@ -5,11 +5,14 @@
#include "../levels/levels.h"
#include "../levels/cutscene_runner.h"
#include "../build/assets/models/signage/clock.h"
#include "../build/assets/models/signage/clock_digits.h"
#include "../build/assets/models/dynamic_model_list.h"
#include "../../build/assets/materials/static.h"
#include "../util/dynamic_asset_loader.h"
#include <math.h>
#define DIGIT_WIDTH 512
@ -86,7 +89,7 @@ void clockRenderRender(void* data, struct DynamicRenderDataList* renderList, str
dynamicRenderListAddData(
renderList,
signage_clock_model_gfx,
dynamicAssetModel(SIGNAGE_CLOCK_DYNAMIC_MODEL),
matrix,
AUTOPORTAL_FRAME_INDEX,
&clock->transform.position,
@ -104,6 +107,8 @@ void clockRenderRender(void* data, struct DynamicRenderDataList* renderList, str
}
void clockInit(struct Clock* clock, struct ClockDefinition* definition) {
dynamicAssetPreload(SIGNAGE_CLOCK_DYNAMIC_MODEL);
clock->transform.position = definition->position;
clock->transform.rotation = definition->rotation;
clock->transform.scale = gOneVec;

View file

@ -0,0 +1,2 @@
#include "../build/assets/models/dynamic_model_list_data.h"

View file

@ -0,0 +1,72 @@
#include "dynamic_asset_loader.h"
#include "memory.h"
#include "rom.h"
#include "../build/assets/models/dynamic_model_list.h"
extern struct DynamicAssetModel gDynamicModels[];
Gfx* gLoadedModels[DYNAMIC_MODEL_COUNT];
Gfx gBlankGfx[] = {
gsSPEndDisplayList(),
};
void dynamicAssetsReset() {
zeroMemory(gLoadedModels, sizeof(gLoadedModels));
}
#define ADJUST_POINTER_POS(ptr, offset) (void*)((ptr) ? (char*)(ptr) + (offset) : 0)
Gfx* dynamicAssetFixModelPointers(Gfx* gfx, u32 pointerOffset, u32 segmentStart, u32 segmentEnd) {
// only adjust pointers that land within the segment
if ((u32)gfx < segmentStart || (u32)gfx >= segmentEnd) {
return gfx;
}
gfx = ADJUST_POINTER_POS(gfx, pointerOffset);
while (1) {
int commandType = _SHIFTR(gfx->words.w0, 24, 8);
switch (commandType) {
case G_ENDDL:
return gfx;
case G_DL:
if (gfx->dma.par == G_DL_NOPUSH) {
break;
}
gfx->dma.addr = (u32)dynamicAssetFixModelPointers((Gfx*)gfx->dma.addr, pointerOffset, segmentStart, segmentEnd);
break;
case G_VTX:
gfx->dma.addr = (u32)ADJUST_POINTER_POS(gfx->dma.addr, pointerOffset);
break;
};
++gfx;
}
return NULL;
}
Gfx* dynamicAssetLoadModel(struct DynamicAssetModel* model) {
u32 length = (u32)model->addressEnd - (u32)model->addressStart;
void* assetMemoryChunk = malloc(length);
romCopy(model->addressStart, assetMemoryChunk, length);
u32 pointerOffset = (u32)assetMemoryChunk - (u32)model->segmentStart;
return dynamicAssetFixModelPointers(model->model, pointerOffset, (u32)model->segmentStart, (u32)model->segmentStart + length);
}
void dynamicAssetPreload(int index) {
if (index < 0 || index >= DYNAMIC_MODEL_COUNT || gLoadedModels[index] ) {
return;
}
gLoadedModels[index] = dynamicAssetLoadModel(&gDynamicModels[index]);
}
Gfx* dynamicAssetModel(int index) {
if (index < 0 || index >= DYNAMIC_MODEL_COUNT || !gLoadedModels[index]) {
return gBlankGfx;
}
return gLoadedModels[index];
}

View file

@ -0,0 +1,20 @@
#ifndef __DYNAMIC_ASSET_LOADER_H__
#define __DYNAMIC_ASSET_LOADER_H__
#include <ultra64.h>
struct DynamicAssetModel {
void* addressStart;
void* addressEnd;
void* segmentStart;
Gfx* model;
};
Gfx* dynamicAssetLoadModel(struct DynamicAssetModel* model);
void dynamicAssetsReset();
void dynamicAssetPreload(int index);
Gfx* dynamicAssetModel(int index);
#endif

View file

@ -0,0 +1,91 @@
const fs = require('fs');
const path = require('path');
function generateInclude(outputLocation, headerLocation) {
return `#include "${path.relative(path.dirname(outputLocation), headerLocation)}"`
}
const InvalidTokenCharacter = /[^A-Za-z0-9_]/gim;
function getSegmentName(headerLocation) {
const levelName = path.basename(headerLocation);
const noExtension = levelName.slice(0, levelName.length - path.extname(levelName).length);
return noExtension.replace(InvalidTokenCharacter, '_');
}
function generateModelName(outputLocation, headerLocation) {
const relative = path.relative(path.dirname(outputLocation), headerLocation).slice(0, -2);
return relative.replace(InvalidTokenCharacter, '_') + '_model_gfx';
}
function generateDynamicModelName(outputLocation, headerLocation, index) {
const relative = path.relative(path.dirname(outputLocation), headerLocation).slice(0, -2);
return relative.replace(InvalidTokenCharacter, '_') + '_dynamic_model';
}
function generateMetadata(outputLocation, headerLocation) {
const segmentName = getSegmentName(headerLocation);
return ` {
_${segmentName}_geoSegmentRomStart,
_${segmentName}_geoSegmentRomEnd,
_${segmentName}_geoSegmentStart,
${generateModelName(outputLocation, headerLocation)},
},`;
}
function generateExterns(headerLocation) {
const segmentName = getSegmentName(headerLocation);
return `
extern char _${segmentName}_geoSegmentRomStart[];
extern char _${segmentName}_geoSegmentRomEnd[];
extern char _${segmentName}_geoSegmentStart[];
`;
}
function generateModelList(outputLocation, headerLocations) {
return `struct DynamicAssetModel gDynamicModels[] = {
${headerLocations.map(headerLocation => generateMetadata(outputLocation, headerLocation)).join('\n')}
};`
}
function generateMetadataDeclaration(outputLocation, headerLocation, index) {
const segmentName = getSegmentName(headerLocation);
return `#define ${generateDynamicModelName(outputLocation, headerLocation).toUpperCase()} ${index}`;
}
function generateModelHeaderList(outputLocation, headerLocations) {
return headerLocations.map((headerLocation, index) => generateMetadataDeclaration(outputLocation, headerLocation, index)).join('\n');
}
function generateData(outputLocation, headerLocations) {
return `#ifndef __DYNAMIC_MODEL_LIST_DATA_H__
#define __DYNAMIC_MODEL_LIST_DATA_H__
#include "util/dynamic_asset_loader.h"
${headerLocations.map(headerLocation => generateInclude(outputLocation, headerLocation)).join('\n')}
${headerLocations.map(generateExterns).join('\n')}
${generateModelList(outputLocation, headerLocations)}
#endif
`;
}
function generateHeader(outputLocation, headerLocations) {
return `#ifndef __DYNAMIC_MODEL_LIST_DEFINITION_H__
#define __DYNAMIC_MODEL_LIST_DEFINITION_H__
#define DYNAMIC_MODEL_COUNT ${headerLocations.length}
${generateModelHeaderList(outputLocation, headerLocations)}
#endif
`;
}
fs.writeFileSync(process.argv[2], generateHeader(process.argv[2], process.argv.slice(4)));
fs.writeFileSync(process.argv[3], generateData(process.argv[3], process.argv.slice(4)));

View file

@ -10,11 +10,11 @@ function getSegmentName(objectLocation) {
return noExtension.replace(InvalidTokenCharacter, '_');
}
function generateLD(objectLocation) {
function generateLD(segmentLocation, objectLocation) {
const segmentName = getSegmentName(objectLocation);
return `
BEGIN_SEG(${segmentName}, 0x02000000)
BEGIN_SEG(${segmentName}, ${segmentLocation})
{
${objectLocation}(.data);
${objectLocation}(.bss);
@ -23,10 +23,11 @@ function generateLD(objectLocation) {
`;
}
function generateData(objectLocations) {
return objectLocations.map(objectLocation => generateLD(objectLocation)).join('\n');
function generateData(segmentLocation, objectLocations) {
return objectLocations.map(objectLocation => generateLD(segmentLocation, objectLocation)).join('\n');
}
const output = process.argv[2];
const segmentLocation = process.argv[3];
fs.writeFileSync(output, generateData(process.argv.slice(3)));
fs.writeFileSync(output, generateData(segmentLocation, process.argv.slice(4)));