Generate dynamic model lists with CMake, + refactor

* When building with CMake, dynamic model lists are now generated
* Factored common code out of generate_*_list.js files to deduplicate
* Better separated generated model lists from main game code
    `dynamic_asset_data.c` and `dynamic_animated_asset_data.c`
    previously #included the corresponding generated headers, and
    `dynamic_asset_loader.c` declared externs for the contents.

    This messiness was likely done so the generated code would be built
    automatically (the Makefile globs all C files under `src/`).

    Now, model list data is output to C source files which are built
    explicitly. This, with some refactoring, allows the previously
    mentioned source files and externs to be removed.

    This is a bit hacky in the Makefile but will be automatic under
    CMake by using target properties.
* Reorganized some files under `tools/`
This commit is contained in:
Matt Penny 2024-10-05 21:11:49 -04:00
parent 9643719a31
commit 1d0b22df61
25 changed files with 419 additions and 357 deletions

View file

@ -19,11 +19,6 @@ set(SOX "sox")
set(VPK "vpk") set(VPK "vpk")
set(VTF2PNG "vtf2png") set(VTF2PNG "vtf2png")
set(CONVERT_ASSET "${PROJECT_SOURCE_DIR}/tools/convert_asset.py")
set(EXPORT_FBX "${PROJECT_SOURCE_DIR}/tools/export_fbx.py")
set(GEN_LEVEL_LIST "${PROJECT_SOURCE_DIR}/tools/generate_level_list.js")
set(GEN_SOUND_IDS "${PROJECT_SOURCE_DIR}/tools/generate_sound_ids.js")
set(JSOX "${PROJECT_SOURCE_DIR}/tools/jsox.js")
set(SKELETOOL64 "${PROJECT_SOURCE_DIR}/skelatool64/skeletool64") set(SKELETOOL64 "${PROJECT_SOURCE_DIR}/skelatool64/skeletool64")
# Directories # Directories

View file

@ -359,7 +359,9 @@ ANIM_LIST = build/assets/models/pedestal_anim.o \
build/assets/models/props/switch001_anim.o build/assets/models/props/switch001_anim.o
MODEL_HEADERS = $(MODEL_LIST:%.blend=build/%.h) MODEL_HEADERS = $(MODEL_LIST:%.blend=build/%.h)
MODEL_OBJECTS = $(MODEL_LIST:%.blend=build/%_geo.o) MODEL_OBJECTS = $(MODEL_LIST:%.blend=build/%_geo.o) \
build/assets/models/dynamic_model_list.o \
build/assets/models/dynamic_animated_model_list.o
DYNAMIC_MODEL_HEADERS = $(DYNAMIC_MODEL_LIST:%.blend=build/%.h) DYNAMIC_MODEL_HEADERS = $(DYNAMIC_MODEL_LIST:%.blend=build/%.h)
DYNAMIC_MODEL_OBJECTS = $(DYNAMIC_MODEL_LIST:%.blend=build/%_geo.o) DYNAMIC_MODEL_OBJECTS = $(DYNAMIC_MODEL_LIST:%.blend=build/%_geo.o)
@ -415,8 +417,6 @@ build/src/scene/render_plan.o: $(MODEL_HEADERS)
build/src/scene/security_camera.o: build/src/audio/clips.h build/assets/models/props/security_camera.h build/assets/models/dynamic_animated_model_list.h build/src/scene/security_camera.o: build/src/audio/clips.h build/assets/models/props/security_camera.h build/assets/models/dynamic_animated_model_list.h
build/src/scene/signage.o: $(MODEL_HEADERS) build/src/scene/signage.o: $(MODEL_HEADERS)
build/src/scene/switch.o: build/assets/models/props/switch001.h build/assets/materials/static.h build/assets/models/dynamic_animated_model_list.h build/src/scene/switch.o: build/assets/models/props/switch001.h build/assets/materials/static.h build/assets/models/dynamic_animated_model_list.h
build/src/util/dynamic_asset_data.o: build/assets/models/dynamic_model_list_data.h
build/src/util/dynamic_animated_asset_data.o: build/assets/models/dynamic_animated_model_list_data.h
build/src/util/dynamic_asset_loader.o: build/assets/models/dynamic_model_list.h build/assets/models/dynamic_animated_model_list.h build/src/util/dynamic_asset_loader.o: build/assets/models/dynamic_model_list.h build/assets/models/dynamic_animated_model_list.h
build/src/menu/audio_options.o: build/src/audio/subtitles.h build/src/menu/audio_options.o: build/src/audio/subtitles.h
build/src/menu/video_options.o: build/src/audio/subtitles.h build/src/menu/video_options.o: build/src/audio/subtitles.h
@ -460,7 +460,7 @@ LUA_FILES = $(shell find tools/ -type f -name '*.lua')
build/%.fbx: %.blend build/%.fbx: %.blend
@mkdir -p $(@D) @mkdir -p $(@D)
$(BLENDER_3_6) $< --background --python tools/export_fbx.py -- $@ $(BLENDER_3_6) $< --background --python tools/models/export_fbx.py -- $@
build/assets/test_chambers/%.h build/assets/test_chambers/%_geo.c build/assets/test_chambers/%_anim.c: build/assets/test_chambers/%.fbx assets/test_chambers/%.yaml build/assets/materials/static.h build/src/audio/subtitles.h $(SKELATOOL64) $(TEXTURE_IMAGES) $(LUA_FILES) build/assets/test_chambers/%.h build/assets/test_chambers/%_geo.c build/assets/test_chambers/%_anim.c: build/assets/test_chambers/%.fbx assets/test_chambers/%.yaml build/assets/materials/static.h build/src/audio/subtitles.h $(SKELATOOL64) $(TEXTURE_IMAGES) $(LUA_FILES)
$(SKELATOOL64) --script tools/level_scripts/export_level.lua --fixed-point-scale ${SCENE_SCALE} --model-scale 0.01 --name $(<:build/assets/test_chambers/%.fbx=%) -m assets/materials/static.skm.yaml -o $(<:%.fbx=%.h) $< $(SKELATOOL64) --script tools/level_scripts/export_level.lua --fixed-point-scale ${SCENE_SCALE} --model-scale 0.01 --name $(<:build/assets/test_chambers/%.fbx=%) -m assets/materials/static.skm.yaml -o $(<:%.fbx=%.h) $<
@ -478,17 +478,17 @@ build/assets/materials/%_mat.o: build/assets/materials/%_mat.c
levels: $(TEST_CHAMBER_HEADERS) levels: $(TEST_CHAMBER_HEADERS)
echo $(TEST_CHAMBER_HEADERS) echo $(TEST_CHAMBER_HEADERS)
build/assets/test_chambers/level_list.h: $(TEST_CHAMBER_HEADERS) tools/generate_level_list.js build/assets/test_chambers/level_list.h: $(TEST_CHAMBER_HEADERS) tools/models/generate_level_list.js tools/models/model_list_utils.js
@mkdir -p $(@D) @mkdir -p $(@D)
node tools/generate_level_list.js $@ $(TEST_CHAMBER_HEADERS) node tools/models/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 build/assets/models/cube/cube.h build/assets/models/dynamic_model_list.h build/assets/models/dynamic_model_list.c: $(DYNAMIC_MODEL_HEADERS) tools/models/generate_dynamic_model_list.js tools/models/model_list_utils.js build/assets/models/cube/cube.h
@mkdir -p $(@D) @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) node tools/models/generate_dynamic_model_list.js build/assets/models/dynamic_model_list.h $(DYNAMIC_MODEL_HEADERS)
build/assets/models/dynamic_animated_model_list.h build/assets/models/dynamic_animated_model_list_data.h: $(DYNAMIC_ANIMATED_MODEL_HEADERS) tools/generate_dynamic_animated_model_list.js build/assets/models/dynamic_animated_model_list.h build/assets/models/dynamic_animated_model_list.c: $(DYNAMIC_ANIMATED_MODEL_HEADERS) tools/models/generate_dynamic_animated_model_list.js tools/models/model_list_utils.js
@mkdir -p $(@D) @mkdir -p $(@D)
node tools/generate_dynamic_animated_model_list.js build/assets/models/dynamic_animated_model_list.h build/assets/models/dynamic_animated_model_list_data.h $(DYNAMIC_ANIMATED_MODEL_HEADERS) node tools/models/generate_dynamic_animated_model_list.js build/assets/models/dynamic_animated_model_list.h $(DYNAMIC_ANIMATED_MODEL_HEADERS)
build/levels.ld: $(TEST_CHAMBER_OBJECTS) tools/generate_level_ld.js build/levels.ld: $(TEST_CHAMBER_OBJECTS) tools/generate_level_ld.js
@mkdir -p $(@D) @mkdir -p $(@D)
@ -536,9 +536,9 @@ build/assets/%.aifc: assets/%.sox portal_pak_dir/%.wav
sox $(<:assets/%.sox=portal_pak_dir/%.wav) $(shell cat $<) $(@:%.aifc=%.wav) sox $(<:assets/%.sox=portal_pak_dir/%.wav) $(shell cat $<) $(@:%.aifc=%.wav)
$(SFZ2N64) -o $@ $(@:%.aifc=%.wav) $(SFZ2N64) -o $@ $(@:%.aifc=%.wav)
build/assets/%.aifc: assets/%.jsox tools/jsox.js portal_pak_dir/%.wav build/assets/%.aifc: assets/%.jsox tools/sound/jsox.js portal_pak_dir/%.wav
@mkdir -p $(@D) @mkdir -p $(@D)
node tools/jsox.js $< $(<:assets/%.jsox=portal_pak_dir/%.wav) $(@:%.aifc=%.wav) node tools/sound/jsox.js $< $(<:assets/%.jsox=portal_pak_dir/%.wav) $(@:%.aifc=%.wav)
$(SFZ2N64) -o $@ $(@:%.aifc=%.wav) $(SFZ2N64) -o $@ $(@:%.aifc=%.wav)
build/assets/%.aifc: assets/%.msox portal_pak_dir/%.mp3 build/assets/%.aifc: assets/%.msox portal_pak_dir/%.mp3
@ -554,9 +554,9 @@ build/assets/sound/sounds.sounds build/assets/sound/sounds.sounds.tbl: $(SOUND_C
build/asm/sound_data.o: build/assets/sound/sounds.sounds build/assets/sound/sounds.sounds.tbl build/asm/sound_data.o: build/assets/sound/sounds.sounds build/assets/sound/sounds.sounds.tbl
build/src/audio/clips.h build/src/audio/languages.h build/src/audio/languages.c: tools/generate_sound_ids.js $(SOUND_CLIPS) build/src/audio/clips.h build/src/audio/languages.h build/src/audio/languages.c: tools/sound/generate_sound_ids.js $(SOUND_CLIPS)
@mkdir -p $(@D) @mkdir -p $(@D)
node tools/generate_sound_ids.js -o $(@D) -p SOUNDS_ $(SOUND_CLIPS) node tools/sound/generate_sound_ids.js -o $(@D) -p SOUNDS_ $(SOUND_CLIPS)
build/src/audio/clips.o: build/src/audio/clips.h build/src/audio/clips.o: build/src/audio/clips.h
build/src/decor/decor_object_list.o: build/src/audio/clips.h build/src/decor/decor_object_list.o: build/src/audio/clips.h

View file

@ -24,6 +24,11 @@ set(MODEL_SCALE 0.01)
# Scaling factor for render space # Scaling factor for render space
set(SCENE_SCALE 128) set(SCENE_SCALE 128)
# Tools for asset conversion
set(CONVERT_ASSET "${PROJECT_SOURCE_DIR}/tools/convert_asset.py")
set(EXPORT_FBX "${PROJECT_SOURCE_DIR}/tools/models/export_fbx.py")
set(MODEL_LIST_UTILS "${PROJECT_SOURCE_DIR}/tools/models/model_list_utils.js")
add_subdirectory(materials) add_subdirectory(materials)
add_subdirectory(models) add_subdirectory(models)
add_subdirectory(sound) add_subdirectory(sound)

View file

@ -1,14 +1,51 @@
cmake_path(
RELATIVE_PATH CMAKE_CURRENT_SOURCE_DIR
BASE_DIRECTORY "${PROJECT_SOURCE_DIR}"
OUTPUT_VARIABLE RELATIVE_CURRENT_DIR
)
################## ##################
## Model export ## ## Model export ##
################## ##################
# TODO: dynamic and animated models # Models whose data is loaded/unloaded as needed
set(DYNAMIC_MODELS
set(MODELS
cube/cube cube/cube
props/autoportal_frame/autoportal_frame
props/box_dropper_glass
props/cylinder_test
props/lab_chair
props/lab_desk/lab_desk01
props/lab_desk/lab_desk02
props/lab_desk/lab_desk03
props/lab_desk/lab_desk04
props/lab_monitor
props/light_rail_endcap
props/portal_cleanser
props/radio
signage/clock
signage/clock_digits
)
# Animated models whose data is loaded/unloaded as needed
set(DYNAMIC_ANIMATED_MODELS
pedestal
props/box_dropper
props/button
props/combine_ball_catcher
props/combine_ball_launcher
props/door_01
props/door_02
props/security_camera
props/switch001
)
# All models
set(MODELS
${DYNAMIC_MODELS}
${DYNAMIC_ANIMATED_MODELS}
fleck_ash2 fleck_ash2
grav_flare grav_flare
pedestal
player/chell player/chell
portal_gun/ball_trail portal_gun/ball_trail
portal_gun/v_portalgun portal_gun/v_portalgun
@ -21,32 +58,10 @@ set(MODELS
portal/portal_orange portal/portal_orange
portal/portal_orange_face portal/portal_orange_face
portal/portal_orange_filled portal/portal_orange_filled
props/autoportal_frame/autoportal_frame
props/box_dropper
props/box_dropper_glass
props/button
props/combine_ball_catcher
props/combine_ball_launcher
props/cylinder_test
props/door_01
props/door_02
props/lab_chair
props/lab_desk/lab_desk01
props/lab_desk/lab_desk02
props/lab_desk/lab_desk03
props/lab_desk/lab_desk04
props/lab_monitor
props/light_rail_endcap
props/portal_cleanser
props/radio
props/round_elevator props/round_elevator
props/round_elevator_collision props/round_elevator_collision
props/round_elevator_interior props/round_elevator_interior
props/security_camera
props/signage props/signage
props/switch001
signage/clock
signage/clock_digits
) )
# Most models depend on both of these. Some only depend on one or the other, # Most models depend on both of these. Some only depend on one or the other,
@ -56,13 +71,10 @@ set(MODEL_DEFAULT_MATERIALS
${ASSETS_DIR}/materials/objects.skm.yaml ${ASSETS_DIR}/materials/objects.skm.yaml
) )
function(_add_model_export_command MODEL_NAME OUTPUT_VARIABLE) set(GEN_DYNAMIC_MODEL_LIST "${PROJECT_SOURCE_DIR}/tools/models/generate_dynamic_model_list.js")
cmake_path( set(GEN_DYNAMIC_ANIM_MODEL_LIST "${PROJECT_SOURCE_DIR}/tools/models/generate_dynamic_animated_model_list.js")
RELATIVE_PATH CMAKE_CURRENT_SOURCE_DIR
BASE_DIRECTORY "${PROJECT_SOURCE_DIR}"
OUTPUT_VARIABLE RELATIVE_CURRENT_DIR
)
function(_add_model_export_command MODEL_NAME OUTPUT_VARIABLE)
set(INPUT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${MODEL_NAME}.blend") set(INPUT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${MODEL_NAME}.blend")
set(OUTPUT_FILE "${CMAKE_BINARY_DIR}/${RELATIVE_CURRENT_DIR}/${MODEL_NAME}.fbx") set(OUTPUT_FILE "${CMAKE_BINARY_DIR}/${RELATIVE_CURRENT_DIR}/${MODEL_NAME}.fbx")
@ -121,7 +133,7 @@ function(_add_model_generate_command MODEL_NAME MODEL_FBX OUTPUT_LIST)
COMMAND COMMAND
${CMAKE_COMMAND} -E make_directory ${OUTPUT_DIR} ${CMAKE_COMMAND} -E make_directory ${OUTPUT_DIR}
COMMAND COMMAND
${CONVERT_ASSET} ${PYTHON3} ${CONVERT_ASSET}
${SKELETOOL64} ${SKELETOOL64}
${MODEL_FBX} ${MODEL_FBX}
${MODEL_FLAGS} ${MODEL_FLAGS}
@ -184,3 +196,73 @@ add_custom_target(
models models
DEPENDS ${MODEL_GENERATED_FILES} DEPENDS ${MODEL_GENERATED_FILES}
) )
# Add commands for generating dynamic model lists
set(MODEL_GENERATED_HEADERS ${MODEL_GENERATED_FILES})
list(FILTER MODEL_GENERATED_HEADERS INCLUDE REGEX "\.h$")
set(DYNAMIC_MODEL_HEADERS "")
set(DYNAMIC_ANIMATED_MODEL_HEADERS "")
foreach(GENERATED_HEADER ${MODEL_GENERATED_HEADERS})
cmake_path(
RELATIVE_PATH GENERATED_HEADER
BASE_DIRECTORY "${CMAKE_BINARY_DIR}/${RELATIVE_CURRENT_DIR}"
OUTPUT_VARIABLE MODEL_NAME
)
cmake_path(
REMOVE_EXTENSION MODEL_NAME
OUTPUT_VARIABLE MODEL_NAME
)
if(MODEL_NAME IN_LIST DYNAMIC_MODELS)
list(APPEND DYNAMIC_MODEL_HEADERS ${GENERATED_HEADER})
elseif(MODEL_NAME IN_LIST DYNAMIC_ANIMATED_MODELS)
list(APPEND DYNAMIC_ANIMATED_MODEL_HEADERS ${GENERATED_HEADER})
endif()
endforeach()
set(DYNAMIC_MODEL_LIST_OUT_DIR "${CMAKE_BINARY_DIR}/${RELATIVE_CURRENT_DIR}")
set(DYNAMIC_MODEL_LIST_H "${DYNAMIC_MODEL_LIST_OUT_DIR}/dynamic_model_list.h")
set(DYNAMIC_MODEL_LIST_C "${DYNAMIC_MODEL_LIST_OUT_DIR}/dynamic_model_list.c")
add_custom_command(
DEPENDS
${GEN_DYNAMIC_MODEL_LIST} ${MODEL_LIST_UTILS} ${DYNAMIC_MODEL_HEADERS}
OUTPUT
${DYNAMIC_MODEL_LIST_H} ${DYNAMIC_MODEL_LIST_C}
COMMAND
${NODEJS} ${GEN_DYNAMIC_MODEL_LIST}
${DYNAMIC_MODEL_LIST_H}
${DYNAMIC_MODEL_HEADERS}
COMMENT
"Generating dynamic model list"
VERBATIM
)
set(DYNAMIC_ANIMATED_MODEL_LIST_H "${DYNAMIC_MODEL_LIST_OUT_DIR}/dynamic_animated_model_list.h")
set(DYNAMIC_ANIMATED_MODEL_LIST_C "${DYNAMIC_MODEL_LIST_OUT_DIR}/dynamic_animated_model_list.c")
add_custom_command(
DEPENDS
${GEN_DYNAMIC_ANIM_MODEL_LIST} ${MODEL_LIST_UTILS} ${DYNAMIC_ANIMATED_MODEL_HEADERS}
OUTPUT
${DYNAMIC_ANIMATED_MODEL_LIST_H} ${DYNAMIC_ANIMATED_MODEL_LIST_C}
COMMAND
${NODEJS} ${GEN_DYNAMIC_ANIM_MODEL_LIST}
${DYNAMIC_ANIMATED_MODEL_LIST_H}
${DYNAMIC_ANIMATED_MODEL_HEADERS}
COMMENT
"Generating dynamic animated model list"
VERBATIM
)
add_custom_target(
model_lists
DEPENDS
${MODEL_GENERATED_FILES}
${DYNAMIC_MODEL_LIST_H}
${DYNAMIC_MODEL_LIST_C}
${DYNAMIC_ANIMATED_MODEL_LIST_H}
${DYNAMIC_ANIMATED_MODEL_LIST_C}
)

View file

@ -186,6 +186,8 @@ set(INSTRUMENT_BANKS
weapons/physcannon/energy_sing_loop4.ins weapons/physcannon/energy_sing_loop4.ins
) )
set(JSOX "${PROJECT_SOURCE_DIR}/tools/sound/jsox.js")
# TODO: remove # TODO: remove
function(_add_sound_transform_command_direct SOUND_NAME ARGS OUTPUT_LIST) function(_add_sound_transform_command_direct SOUND_NAME ARGS OUTPUT_LIST)
set(INPUT_FILE "${PAK_SOUND_DIR}/${SOUND_NAME}.wav") set(INPUT_FILE "${PAK_SOUND_DIR}/${SOUND_NAME}.wav")
@ -251,7 +253,7 @@ function(_add_sound_transform_command_sox SOUND_SCRIPT INPUT_FILE OUTPUT_FILE)
COMMAND COMMAND
${CMAKE_COMMAND} -E make_directory ${OUTPUT_DIR} ${CMAKE_COMMAND} -E make_directory ${OUTPUT_DIR}
COMMAND COMMAND
${Python3_EXECUTABLE} ${CONVERT_ASSET} ${SOX} ${INPUT_FILE} ${SOUND_SCRIPT} ${OUTPUT_FILE} ${PYTHON3} ${CONVERT_ASSET} ${SOX} ${INPUT_FILE} ${SOUND_SCRIPT} ${OUTPUT_FILE}
COMMENT COMMENT
"Transforming $<PATH:RELATIVE_PATH,${INPUT_FILE},${PROJECT_SOURCE_DIR}>" "Transforming $<PATH:RELATIVE_PATH,${INPUT_FILE},${PROJECT_SOURCE_DIR}>"
VERBATIM VERBATIM
@ -373,6 +375,8 @@ add_custom_target(
## Sound table generation ## ## Sound table generation ##
############################ ############################
set(GEN_SOUND_IDS "${PROJECT_SOURCE_DIR}/tools/sound/generate_sound_ids.js")
set(SOUND_TABLE_DEPENDENCIES ${SOUNDS_CONVERTED}) set(SOUND_TABLE_DEPENDENCIES ${SOUNDS_CONVERTED})
set(SOUND_TABLE_INPUTS "") set(SOUND_TABLE_INPUTS "")

View file

@ -43,6 +43,8 @@ set(EXPORT_SCRIPTS
${PROJECT_SOURCE_DIR}/tools/level_scripts/yaml_loader.lua ${PROJECT_SOURCE_DIR}/tools/level_scripts/yaml_loader.lua
) )
set(GEN_LEVEL_LIST "${PROJECT_SOURCE_DIR}/tools/models/generate_level_list.js")
function(_add_level_export_command LEVEL_NAME OUTPUT_VARIABLE) function(_add_level_export_command LEVEL_NAME OUTPUT_VARIABLE)
set(INPUT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${LEVEL_NAME}/${LEVEL_NAME}.blend") set(INPUT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${LEVEL_NAME}/${LEVEL_NAME}.blend")
set(OUTPUT_FILE "${CMAKE_BINARY_DIR}/${RELATIVE_CURRENT_DIR}/${LEVEL_NAME}/${LEVEL_NAME}.fbx") set(OUTPUT_FILE "${CMAKE_BINARY_DIR}/${RELATIVE_CURRENT_DIR}/${LEVEL_NAME}/${LEVEL_NAME}.fbx")
@ -144,7 +146,7 @@ set(LEVEL_LIST "${LEVEL_LIST_OUT_DIR}/level_list.h")
add_custom_command( add_custom_command(
DEPENDS DEPENDS
${GEN_LEVEL_LIST} ${LEVEL_GENERATED_HEADERS} ${GEN_LEVEL_LIST} ${MODEL_LIST_UTILS} ${LEVEL_GENERATED_HEADERS}
OUTPUT OUTPUT
${LEVEL_LIST} ${LEVEL_LIST}
COMMAND COMMAND

View file

@ -13,7 +13,7 @@ add_dependencies(portal
# TODO: rework when including models in compilation # TODO: rework when including models in compilation
level_list level_list
models model_lists
) )
############### ###############
@ -159,10 +159,7 @@ target_sources(portal PRIVATE
sk64/skelatool_animator.c sk64/skelatool_animator.c
sk64/skelatool_armature.c sk64/skelatool_armature.c
util/assert.c util/assert.c
util/dynamic_animated_asset_data.c
util/dynamic_asset_data.c
util/dynamic_asset_loader.c util/dynamic_asset_loader.c
util/linked_list.c
util/memory.c util/memory.c
util/profile.c util/profile.c
util/rom.c util/rom.c

View file

@ -117,7 +117,7 @@ void levelLoad(int index) {
return; return;
} }
struct LevelMetadata* metadata = &gLevelList[index]; struct LevelMetadata* metadata = &gLevels[index];
void* memory = malloc(metadata->segmentRomEnd - metadata->segmentRomStart); void* memory = malloc(metadata->segmentRomEnd - metadata->segmentRomStart);
romCopy(metadata->segmentRomStart, memory, metadata->segmentRomEnd - metadata->segmentRomStart); romCopy(metadata->segmentRomStart, memory, metadata->segmentRomEnd - metadata->segmentRomStart);

View file

@ -1,5 +1,5 @@
#ifndef __LEVEL_LIST_H__ #ifndef __LEVELS_H__
#define __LEVEL_LIST_H__ #define __LEVELS_H__
#include "physics/collision_object.h" #include "physics/collision_object.h"
#include "level_definition.h" #include "level_definition.h"

View file

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

View file

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

View file

@ -6,9 +6,6 @@
#include "../build/assets/models/dynamic_model_list.h" #include "../build/assets/models/dynamic_model_list.h"
#include "../build/assets/models/dynamic_animated_model_list.h" #include "../build/assets/models/dynamic_animated_model_list.h"
extern struct DynamicAssetModel gDynamicModels[];
extern struct DynamicAnimatedAssetModel gDynamicAnimatedModels[];
Gfx* gLoadedModels[DYNAMIC_MODEL_COUNT]; Gfx* gLoadedModels[DYNAMIC_MODEL_COUNT];
u32 gModelPointerOffset[DYNAMIC_MODEL_COUNT]; u32 gModelPointerOffset[DYNAMIC_MODEL_COUNT];

View file

@ -3,27 +3,11 @@
#include <ultra64.h> #include <ultra64.h>
#include "dynamic_asset_model.h"
#include "../sk64/skelatool_armature.h" #include "../sk64/skelatool_armature.h"
#include "../sk64/skelatool_clip.h" #include "../sk64/skelatool_clip.h"
struct DynamicAssetModel {
void* addressStart;
void* addressEnd;
void* segmentStart;
Gfx* model;
char* name;
};
struct DynamicAnimatedAssetModel {
void* addressStart;
void* addressEnd;
void* segmentStart;
struct SKArmatureDefinition* armature;
struct SKAnimationClip** clips;
short clipCount;
char* name;
};
struct SKArmatureWithAnimations { struct SKArmatureWithAnimations {
struct SKArmatureDefinition* armature; struct SKArmatureDefinition* armature;
struct SKAnimationClip** clips; struct SKAnimationClip** clips;

View file

@ -0,0 +1,27 @@
#ifndef __DYNAMIC_ASSET_MODEL_H__
#define __DYNAMIC_ASSET_MODEL_H__
#include <ultra64.h>
#include "../sk64/skelatool_armature.h"
#include "../sk64/skelatool_clip.h"
struct DynamicAssetModel {
void* addressStart;
void* addressEnd;
void* segmentStart;
Gfx* model;
char* name;
};
struct DynamicAnimatedAssetModel {
void* addressStart;
void* addressEnd;
void* segmentStart;
struct SKArmatureDefinition* armature;
struct SKAnimationClip** clips;
short clipCount;
char* name;
};
#endif

View file

@ -1,104 +0,0 @@
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 generateArmatureName(outputLocation, headerLocation) {
const relative = path.relative(path.dirname(outputLocation), headerLocation).slice(0, -2);
return relative.replace(InvalidTokenCharacter, '_') + '_armature';
}
function generateClipsName(outputLocation, headerLocation) {
const relative = path.relative(path.dirname(outputLocation), headerLocation).slice(0, -2);
return relative.replace(InvalidTokenCharacter, '_') + '_clips';
}
function generateClipCountName(outputLocation, headerLocation) {
const relative = path.relative(path.dirname(outputLocation), headerLocation).slice(0, -2);
return relative.replace(InvalidTokenCharacter, '_').toUpperCase() + '_CLIP_COUNT';
}
function generateDynamicModelName(outputLocation, headerLocation, index) {
const relative = path.relative(path.dirname(outputLocation), headerLocation).slice(0, -2);
return relative.replace(InvalidTokenCharacter, '_') + '_DYNAMIC_ANIMATED_MODEL';
}
function generateMetadata(outputLocation, headerLocation) {
const segmentName = getSegmentName(headerLocation);
return ` {
_${segmentName}_geoSegmentRomStart,
_${segmentName}_geoSegmentRomEnd,
_${segmentName}_geoSegmentStart,
&${generateArmatureName(outputLocation, headerLocation)},
${generateClipsName(outputLocation, headerLocation)},
${generateClipCountName(outputLocation, headerLocation)},
"${segmentName}",
},`;
}
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 DynamicAnimatedAssetModel gDynamicAnimatedModels[] = {
${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_ANIMATED_MODEL_LIST_DATA_H__
#define __DYNAMIC_ANIMATED_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_ANIMATED_MODEL_LIST_DEFINITION_H__
#define __DYNAMIC_ANIMATED_MODEL_LIST_DEFINITION_H__
#define DYNAMIC_ANIMATED_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

@ -1,92 +0,0 @@
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)},
"${segmentName}",
},`;
}
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

@ -1,69 +0,0 @@
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 generateLevelName(outputLocation, headerLocation) {
const relative = path.relative(path.dirname(outputLocation), headerLocation).slice(0, -2);
return relative.replace(InvalidTokenCharacter, '_') + '_level';
}
function generateMetadata(outputLocation, headerLocation) {
const segmentName = getSegmentName(headerLocation);
return ` {
&${generateLevelName(outputLocation, headerLocation)},
_${segmentName}_geoSegmentRomStart,
_${segmentName}_geoSegmentRomEnd,
_${segmentName}_geoSegmentStart,
},`;
}
function generateExterns(headerLocation) {
const segmentName = getSegmentName(headerLocation);
return `
extern char _${segmentName}_geoSegmentRomStart[];
extern char _${segmentName}_geoSegmentRomEnd[];
extern char _${segmentName}_geoSegmentStart[];
`;
}
function generateLevelList(outputLocation, headerLocations) {
return `struct LevelMetadata gLevelList[] = {
${headerLocations.map(headerLocation => generateMetadata(outputLocation, headerLocation)).join('\n')}
};
`;
}
function generateData(outputLocation, headerLocations) {
return `#ifndef __BUILD_LEVEL_LIST_H__
#define __BUILD_LEVEL_LIST_H__
#include "levels/level_metadata.h"
${headerLocations.map(headerLocation => generateInclude(outputLocation, headerLocation)).join('\n')}
#define LEVEL_COUNT ${headerLocations.length}
${headerLocations.map(generateExterns).join('\n')}
${generateLevelList(outputLocation, headerLocations)}
#endif
`;
}
const output = process.argv[2];
fs.writeFileSync(output, generateData(output, process.argv.slice(3)));

View file

@ -1,6 +0,0 @@
const fs = require('fs');
const path = require('path');
for (const wav of fs.readdirSync(process.argv[2])) {
fs.writeFileSync(path.join(process.argv[3], wav.slice(0, -4)) + '.sox', process.argv[4]);
}

View file

@ -0,0 +1,36 @@
const fs = require('fs');
const path = require('path');
const util = require('./model_list_utils');
function generateModelListEntry(outputPath, modelHeader) {
const modelName = util.generateModelName(modelHeader);
return ` {
_${modelName}_geoSegmentRomStart,
_${modelName}_geoSegmentRomEnd,
_${modelName}_geoSegmentStart,
&${util.generateRelativeModelName(outputPath, modelHeader, '_armature')},
${util.generateRelativeModelName(outputPath, modelHeader, '_clips')},
${util.generateRelativeModelName(outputPath, modelHeader, '_clip_count').toUpperCase()},
"${modelName}",
},`;
}
const [outputHeaderFile, ...modelHeaders] = process.argv.slice(2);
const { dir: outputDir, name: outputName } = path.parse(outputHeaderFile);
const outputSourceFile = `${outputDir}/${outputName}.c`
const config = {
modelHeaders,
modelGroup: "dynamic_animated_model",
modelType: "DynamicAnimatedAssetModel",
listEntryGenerator: generateModelListEntry
};
fs.writeFileSync(
outputHeaderFile,
util.generateHeader(outputHeaderFile, config)
);
fs.writeFileSync(
outputSourceFile,
util.generateData(outputSourceFile, config)
);

View file

@ -0,0 +1,34 @@
const fs = require('fs');
const path = require('path');
const util = require('./model_list_utils');
function generateModelListEntry(outputPath, modelHeader) {
const modelName = util.generateModelName(modelHeader);
return ` {
_${modelName}_geoSegmentRomStart,
_${modelName}_geoSegmentRomEnd,
_${modelName}_geoSegmentStart,
${util.generateRelativeModelName(outputPath, modelHeader, '_model_gfx')},
"${modelName}",
},`;
}
const [outputHeaderFile, ...modelHeaders] = process.argv.slice(2);
const { dir: outputDir, name: outputName } = path.parse(outputHeaderFile);
const outputSourceFile = `${outputDir}/${outputName}.c`
const config = {
modelHeaders,
modelGroup: "dynamic_model",
modelType: "DynamicAssetModel",
listEntryGenerator: generateModelListEntry
};
fs.writeFileSync(
outputHeaderFile,
util.generateHeader(outputHeaderFile, config)
);
fs.writeFileSync(
outputSourceFile,
util.generateData(outputSourceFile, config)
);

View file

@ -0,0 +1,42 @@
const fs = require('fs');
const util = require('./model_list_utils');
function generateLevelListEntry(outputPath, levelHeader) {
const levelName = util.generateModelName(levelHeader);
return ` {
&${util.generateRelativeModelName(outputPath, levelHeader, '_level')},
_${levelName}_geoSegmentRomStart,
_${levelName}_geoSegmentRomEnd,
_${levelName}_geoSegmentStart,
},`;
}
function generateHeader(outputPath, config) {
const { modelHeaders, modelGroup } = config;
return util.wrapWithIncludeGuard(outputPath,
`#include "levels/level_metadata.h"
${util.generateIncludes(outputPath, modelHeaders)}
${util.generateCount(modelGroup, modelHeaders)}
${util.generateExterns(modelHeaders)}
${util.generateModelList(outputPath, config)}`
);
}
const [outputHeaderFile, ...modelHeaders] = process.argv.slice(2);
const config = {
modelHeaders,
modelGroup: "level",
modelType: "LevelMetadata",
listEntryGenerator: generateLevelListEntry
};
fs.writeFileSync(
outputHeaderFile,
generateHeader(outputHeaderFile, config)
);

View file

@ -0,0 +1,132 @@
const path = require('path');
const INVALID_TOKEN_CHARACTER = /[^A-Za-z0-9_]/gim;
// Common
function sanitize(s) {
return s.replace(INVALID_TOKEN_CHARACTER, '_');
}
function wrapWithIncludeGuard(outputPath, content) {
const outputName = path.basename(outputPath);
const includeGuardName = `__${sanitize(outputName).toUpperCase()}__`;
return `#ifndef ${includeGuardName}
#define ${includeGuardName}
${content}
#endif
`;
}
function generateRelativeModelName(outputPath, modelHeader, suffix='') {
const outputDirName = path.dirname(outputPath);
const { dir: headerDirName, name: headerName } = path.parse(modelHeader);
const relative = path.join(
path.relative(outputDirName, headerDirName),
headerName
);
return sanitize(relative + suffix);
}
function generateModelName(modelHeader) {
return generateRelativeModelName(modelHeader, modelHeader);
}
function generateListDefinition(modelGroup, modelType) {
const groupPascalCase = modelGroup
.split('_')
.map(s => s && s.replace(/^(\w)/g, m => m.toUpperCase()))
.join('');
const listName = `g${groupPascalCase}s`;
return `struct ${modelType} ${listName}[]`;
}
// Header generation
function generateCount(modelGroup, modelHeaders) {
return `#define ${modelGroup.toUpperCase()}_COUNT ${modelHeaders.length}`;
}
function generateModelIndices(outputPath, modelHeaders, modelGroup) {
return modelHeaders.map((modelHeader, index) => {
const modelName = generateRelativeModelName(outputPath, modelHeader, `_${modelGroup}`);
return `#define ${modelName.toUpperCase()} ${index}`;
}).join('\n');
}
function generateListExtern(modelGroup, modelType) {
return `extern ${generateListDefinition(modelGroup, modelType)};`;
}
function generateHeader(outputPath, config) {
const { modelHeaders, modelGroup, modelType } = config;
return wrapWithIncludeGuard(outputPath,
`#include "util/dynamic_asset_model.h"
${generateCount(modelGroup, modelHeaders)}
${generateModelIndices(outputPath, modelHeaders, modelGroup)}
${generateListExtern(modelGroup, modelType)}`
);
}
// Data generation
function generateIncludes(outputPath, modelHeaders) {
return modelHeaders.map(modelHeader => {
const relativePath = path.relative(path.dirname(outputPath), modelHeader);
return `#include "${relativePath}"`;
}).join('\n');
}
function generateExterns(modelHeaders) {
return modelHeaders.map(modelHeader => {
const modelName = generateModelName(modelHeader);
return `extern char _${modelName}_geoSegmentRomStart[];
extern char _${modelName}_geoSegmentRomEnd[];
extern char _${modelName}_geoSegmentStart[];`;
}).join('\n\n');
}
function generateModelList(outputPath, config) {
const { modelHeaders, modelGroup, modelType, listEntryGenerator } = config;
return `${generateListDefinition(modelGroup, modelType)} = {
${modelHeaders.map(modelHeader => listEntryGenerator(outputPath, modelHeader)).join('\n')}
};`
}
function generateData(outputPath, config) {
const { name } = path.parse(outputPath);
const { modelHeaders } = config;
return `#include "${name}.h"
${generateIncludes(outputPath, modelHeaders)}
${generateExterns(modelHeaders)}
${generateModelList(outputPath, config)}
`;
}
module.exports = {
wrapWithIncludeGuard,
generateRelativeModelName,
generateModelName,
generateCount,
generateHeader,
generateIncludes,
generateExterns,
generateModelList,
generateData
};