backport studio custom levels (missing textures for now)

This commit is contained in:
Dominicentek 2024-10-02 22:31:19 +02:00
parent d70ad90457
commit 2c7b4775d2
19 changed files with 153 additions and 308 deletions

View file

@ -23,7 +23,11 @@ const GeoLayout sa_area_1[] = {
GEO_OPEN_NODE(),
GEO_CAMERA(CAMERA_MODE_CLOSE, 0, 0, 0, 0, -100, 0, geo_camera_main),
GEO_OPEN_NODE(),
GEO_BRANCH(1, sa_area_1_geo),
GEO_SWITCH_CASE(0, geo_switch_override_model),
GEO_OPEN_NODE(),
GEO_BRANCH(1, sa_area_1_geo),
GEO_DISPLAY_LIST(0, NULL),
GEO_CLOSE_NODE(),
GEO_RENDER_OBJ(),
GEO_ASM(ENVFX_MODE_NONE, geo_envfx_main),
GEO_CLOSE_NODE(),

View file

@ -22,7 +22,11 @@ const GeoLayout sa_area_2[] = {
GEO_OPEN_NODE(),
GEO_CAMERA(CAMERA_MODE_CLOSE, 0, 0, 0, 0, -100, 0, geo_camera_main),
GEO_OPEN_NODE(),
GEO_BRANCH(1, sa_area_2_geo),
GEO_SWITCH_CASE(0, geo_switch_override_model),
GEO_OPEN_NODE(),
GEO_BRANCH(1, sa_area_2_geo),
GEO_DISPLAY_LIST(0, NULL),
GEO_CLOSE_NODE(),
GEO_RENDER_OBJ(),
GEO_ASM(ENVFX_MODE_NONE, geo_envfx_main),
GEO_CLOSE_NODE(),

View file

@ -1,36 +0,0 @@
#include "src/game/envfx_snow.h"
#include "src/game/custom_level.h"
const GeoLayout sa_area_3_geo[] = {
GEO_NODE_START(),
GEO_OPEN_NODE(),
GEO_ASM(LAYER_OPAQUE, geo_custom_level),
GEO_CLOSE_NODE(),
GEO_RETURN(),
};
const GeoLayout sa_area_3[] = {
GEO_NODE_SCREEN_AREA(10, SCREEN_WIDTH/2, SCREEN_HEIGHT/2, SCREEN_WIDTH/2, SCREEN_HEIGHT/2),
GEO_OPEN_NODE(),
GEO_ZBUFFER(0),
GEO_OPEN_NODE(),
GEO_NODE_ORTHO(100.0000),
GEO_OPEN_NODE(),
GEO_BACKGROUND_DYN(geo_skybox_main),
GEO_CLOSE_NODE(),
GEO_CLOSE_NODE(),
GEO_ZBUFFER(1),
GEO_OPEN_NODE(),
GEO_CAMERA_FRUSTUM_WITH_FUNC(45.0000, 100, 30000, geo_camera_fov),
GEO_OPEN_NODE(),
GEO_CAMERA(CAMERA_MODE_CLOSE, 0, 0, 0, 0, -100, 0, geo_camera_main),
GEO_OPEN_NODE(),
GEO_BRANCH(1, sa_area_3_geo),
GEO_RENDER_OBJ(),
GEO_ASM(ENVFX_MODE_NONE, geo_envfx_main),
GEO_CLOSE_NODE(),
GEO_CLOSE_NODE(),
GEO_CLOSE_NODE(),
GEO_DISPLAY_LIST(LAYER_OPAQUE, sa_dl_material_revert_render_settings),
GEO_CLOSE_NODE(),
GEO_END(),
};

View file

@ -1,3 +0,0 @@
const MacroObject sa_area_3_macro_objs[] = {
MACRO_OBJECT_END(),
};

View file

@ -9,6 +9,8 @@
#include "game/screen_transition.h"
#include "game/paintings.h"
#include "saturn/imgui/saturn_imgui_machinima.h"
#include "make_const_nonconst.h"
#include "levels/sa/header.h"

View file

@ -1,3 +1,2 @@
#include "levels/sa/area_2/geo.inc.c"
#include "levels/sa/area_1/geo.inc.c"
#include "levels/sa/area_3/geo.inc.c"

View file

@ -4,5 +4,4 @@
#include "levels/sa/area_1/collision.inc.c"
#include "levels/sa/area_1/macro.inc.c"
#include "levels/sa/area_1/spline.inc.c"
#include "levels/sa/area_3/macro.inc.c"
#include "levels/sa/model.inc.c"

View file

@ -6,7 +6,6 @@
#include "dialog_ids.h"
#include "segment_symbols.h"
#include "level_commands.h"
#include "src/game/custom_level.h"
#include "game/level_update.h"
@ -69,18 +68,6 @@ const LevelScript level_sa_entry[] = {
/* Fast64 end persistent block [area commands] */
END_AREA(),
AREA(3, sa_area_3),
WARP_NODE(0x0A, LEVEL_BOB, 0x01, 0x0A, WARP_NO_CHECKPOINT),
WARP_NODE(0xF0, LEVEL_BOB, 0x01, 0x0A, WARP_NO_CHECKPOINT),
WARP_NODE(0xF1, LEVEL_BOB, 0x01, 0x0A, WARP_NO_CHECKPOINT),
OBJECT(MODEL_NONE, 0, 100, 0, 0, 0, 0, 0x000A0000, bhvSpinAirborneWarp),
MARIO_POS(0x03, 0, 100, 0, 0),
TERRAIN(custom_level_collision),
MACRO_OBJECTS(sa_area_3_macro_objs),
STOP_MUSIC(0),
TERRAIN_TYPE(TERRAIN_GRASS),
END_AREA(),
FREE_LEVEL_POOL(),
MARIO_POS(0x02, 180, 0, 200, 0),
CALL(0, lvl_init_or_update),

View file

@ -369,10 +369,15 @@ extern struct GraphNodeCamera *gCurGraphNodeCamera;
extern struct GraphNodeHeldObject *gCurGraphNodeHeldObject;
extern u16 gAreaUpdateCounter;
#ifdef __cplusplus
extern "C" {
#endif
extern struct GraphNode *gCurRootGraphNode;
extern struct GraphNode *gCurGraphNodeList[];
extern s16 gCurGraphNodeIndex;
#ifdef __cplusplus
}
#endif
extern Vec3f gVec3fZero;
extern Vec3s gVec3sZero;

View file

@ -612,6 +612,7 @@ static void level_cmd_set_terrain_data(void) {
data = segmented_to_virtual(CMD_GET(void *, 4));
size = get_area_terrain_size(data) * sizeof(Collision);
gAreas[sCurrAreaIndex].terrainData = alloc_only_pool_alloc(sLevelPool, size);
gAreas[sCurrAreaIndex].terrainDataOrig = gAreas[sCurrAreaIndex].terrainData;
memcpy(gAreas[sCurrAreaIndex].terrainData, data, size);
}
sCurrentCmd = CMD_NEXT;

View file

@ -66,7 +66,7 @@ struct Area
/*0x01*/ s8 flags; // Only has 1 flag: 0x01 = Is this the active area?
/*0x02*/ u16 terrainType; // default terrain of the level (set from level script cmd 0x31)
/*0x04*/ struct GraphNodeRoot *unk04; // geometry layout data
/*0x08*/ s16 *terrainData; // collision data (set from level script cmd 0x2E)
/*0x08*/ s16 *terrainData, *terrainDataOrig; // collision data (set from level script cmd 0x2E)
/*0x0C*/ s8 *surfaceRooms; // (set from level script cmd 0x2F)
/*0x10*/ s16 *macroObjects; // Macro Objects Ptr (set from level script cmd 0x39)
/*0x14*/ struct ObjectWarpNode *warpNodes;

View file

@ -1,122 +0,0 @@
#include "custom_level.h"
#include <string.h>
#include "include/types.h"
#include "include/surface_terrains.h"
Collision custom_level_collision[1024 * 64] = {
COL_INIT(),
COL_VERTEX_INIT(0),
COL_TRI_INIT(0, 0),
COL_TRI_STOP(),
COL_END(),
};
Gfx* custom_level_dl;
Gfx* geo_iter;
Vtx* vertices;
int* faces;
char** textures;
int vertex_iter = 0;
int face_iter = 0;
int used_vert = 0;
int tex_iter = 0;
Lights1 lights = gdSPDefLights1(0x7F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0x28, 0x28, 0x28);
void custom_level_new() {
if (vertices != NULL) {
free(vertices);
free(faces);
free(custom_level_dl);
for (int i = 0; i < tex_iter; i++) {
free(textures[i]);
}
free(textures);
geo_iter = NULL;
}
vertices = malloc(sizeof(Vtx) * 8192);
faces = malloc(sizeof(int) * 3 * 8192);
custom_level_dl = malloc(sizeof(Gfx) * 16384);
textures = (char**)malloc(sizeof(char*) * 128);
for (int i = 0; i < 128; i++) {
textures[i] = (char*)malloc(sizeof(char) * 256);
}
vertex_iter = 0;
face_iter = 0;
used_vert = 0;
tex_iter = 0;
geo_iter = custom_level_dl;
gDPPipeSync(geo_iter++);
gDPSetCombineLERP(geo_iter++, TEXEL0, 0, SHADE, 0, 0, 0, 0, ENVIRONMENT, TEXEL0, 0, SHADE, 0, 0, 0, 0, ENVIRONMENT);
gSPTexture(geo_iter++, 65535, 65535, 0, 0, 1);
gSPSetLights1(geo_iter++, lights);
custom_level_texture("");
}
void custom_level_vertex(float x, float y, float z, float u, float v) {
if (vertex_iter == 8192) return;
vertices[vertex_iter].v.ob[0] = x;
vertices[vertex_iter].v.ob[1] = y;
vertices[vertex_iter].v.ob[2] = z;
vertices[vertex_iter].v.flag = 0;
vertices[vertex_iter].v.tc[0] = u;
vertices[vertex_iter].v.tc[1] = v;
vertices[vertex_iter].v.cn[0] = 255;
vertices[vertex_iter].v.cn[1] = 255;
vertices[vertex_iter].v.cn[2] = 255;
vertices[vertex_iter].v.cn[3] = 255;
vertex_iter++;
}
void custom_level_face() {
int begin = used_vert;
int num_vert = vertex_iter - used_vert;
gSPVertex(geo_iter++, vertices + begin, num_vert, 0);
for (int i = 2; i < num_vert; i++) {
if (face_iter == 8192) break;
faces[face_iter * 3 + 0] = begin;
faces[face_iter * 3 + 1] = i - 1 + begin;
faces[face_iter * 3 + 2] = i + begin;
face_iter++;
gSP1Triangle(geo_iter++, 0, i - 1, i, 0);
}
used_vert = vertex_iter;
}
void custom_level_texture(char* texpath) {
memcpy(textures[tex_iter], texpath, strlen(texpath) + 1);
gDPSetTextureImage(geo_iter++, G_IM_FMT_RGBA, G_IM_SIZ_16b_LOAD_BLOCK, 1, textures[tex_iter]);
gDPSetTile(geo_iter++, G_IM_FMT_RGBA, G_IM_SIZ_16b_LOAD_BLOCK, 0, 0, 7, 0, G_TX_WRAP | G_TX_NOMIRROR, 5, 0, G_TX_WRAP | G_TX_NOMIRROR, 5, 0);
gDPLoadBlock(geo_iter++, 7, 0, 0, 1023, 256);
gDPSetTile(geo_iter++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 8, 0, 0, 0, G_TX_WRAP | G_TX_NOMIRROR, 5, 0, G_TX_WRAP | G_TX_NOMIRROR, 5, 0);
gDPSetTileSize(geo_iter++, 0, 0, 0, 124, 124);
tex_iter++;
}
void custom_level_finish() {
int iter = 0;
custom_level_collision[iter++] = TERRAIN_LOAD_VERTICES;
custom_level_collision[iter++] = vertex_iter;
int lowest = 0x7FFF;
for (int i = 0; i < vertex_iter; i++) {
for (int j = 0; j < 3; j++) {
if (lowest > vertices[i].v.ob[j]) lowest = vertices[i].v.ob[j];
custom_level_collision[iter++] = vertices[i].v.ob[j];
}
}
custom_level_collision[iter++] = 0;
custom_level_collision[iter++] = face_iter;
for (int i = 0; i < face_iter; i++) {
for (int j = 0; j < 3; j++) {
custom_level_collision[iter++] = faces[i * 3 + j];
}
}
custom_level_collision[iter++] = TERRAIN_LOAD_CONTINUE;
custom_level_collision[iter++] = TERRAIN_LOAD_END;
gSPEndDisplayList(geo_iter++);
}
Gfx *geo_custom_level(s32 sp, struct GraphNode* graphNode, UNUSED void* context) {
return custom_level_dl;
}

View file

@ -1,18 +0,0 @@
#ifndef CUSTOM_LEVEL_H
#define CUSTOM_LEVEL_H
#include "include/types.h"
#include "include/macros.h"
extern Collision custom_level_collision[];
extern Gfx* custom_level_dl;
void custom_level_new();
void custom_level_vertex(float x, float y, float z, float u, float v);
void custom_level_face();
void custom_level_texture(char* texture);
void custom_level_finish();
Gfx* geo_custom_level(s32 sp, struct GraphNode* graphNode, UNUSED void* context);
#endif

View file

@ -14,6 +14,7 @@
#include "saturn/saturn.h"
#include "saturn/saturn_animations.h"
#include "pc/configfile.h"
#include "saturn/imgui/saturn_imgui_machinima.h"
/**
* This file contains the code that processes the scene graph for rendering.
@ -1251,6 +1252,9 @@ static void geo_process_object_parent(struct GraphNodeObjectParent *node) {
if (node->node.children != NULL) {
geo_process_node_and_siblings(node->node.children);
}
if (override_level && override_level_geolayout) {
geo_process_node_and_siblings(override_level_geolayout);
}
}
/**

View file

@ -154,6 +154,7 @@ void OpenModelSelector() {
ImGui::BeginChild("###menu_model_selector", ImVec2(-FLT_MIN, 125), true);
for (int i = 0; i < model_list.size(); i++) {
Model model = model_list[i];
if (model.Type != "mario") continue;
if (model.Active) {
bool is_selected = DynOS_Opt_GetValue(String("dynos_pack_%d", i));

View file

@ -7,11 +7,13 @@
#include <map>
#include <fstream>
#include "engine/graph_node.h"
#include "saturn/libs/imgui/imgui.h"
#include "saturn/libs/imgui/imgui_internal.h"
#include "saturn/libs/imgui/imgui_impl_sdl.h"
#include "saturn/libs/imgui/imgui_impl_opengl3.h"
#include "saturn/saturn.h"
#include "saturn/saturn_models.h"
#include "saturn/saturn_textures.h"
#include "saturn/saturn_animation_ids.h"
#include "saturn/saturn_animations.h"
@ -42,7 +44,7 @@ extern "C" {
#include "src/game/interaction.h"
#include "include/behavior_data.h"
#include "game/object_helpers.h"
#include "game/custom_level.h"
#include "engine/surface_load.h"
}
using namespace std;
@ -191,92 +193,92 @@ int get_saturn_level_id(int level) {
}
}
std::vector<std::string> split(std::string input, char character) {
std::vector<std::string> tokens = {};
std::string token = "";
for (int i = 0; i < input.length(); i++) {
if (input[i] == '\r') continue;
if (input[i] == character) {
tokens.push_back(token);
token = "";
}
else token += input[i];
bool override_level = false;
bool custom_level_loaded = false;
struct GraphNode* override_level_geolayout;
Collision* override_level_collision;
Gfx* geo_switch_override_model(s32 callContext, struct GraphNode *node, UNUSED Mat4 *mtx) {
struct GraphNodeSwitchCase* switchCase = (struct GraphNodeSwitchCase*)node;
if (callContext == GEO_CONTEXT_RENDER) {
switchCase->selectedCase = override_level && override_level_geolayout;
}
tokens.push_back(token);
return tokens;
}
std::vector<std::vector<std::string>> tokenize(std::string input) {
std::vector<std::vector<std::string>> tokens = {};
auto lines = split(input, '\n');
for (auto line : lines) {
tokens.push_back(split(line, ' '));
}
return tokens;
return NULL;
}
int textureIndex = 0;
std::filesystem::path customlvl_texdir = std::filesystem::path(sys_user_path()) / "res" / "gfx" / "customlevel";
bool custom_level_flip_normals = false;
void parse_materials(char* data, std::map<std::string, filesystem::path>* materials) {
auto tokens = tokenize(std::string(data));
std::string matname = "";
for (auto line : tokens) {
if (line[0] == "newmtl") matname = line[1];
if (line[0] == "map_Kd" && matname != "") {
std::string path = std::to_string(textureIndex++) + ".png";
std::filesystem::path raw = std::filesystem::path(line[1]);
std::filesystem::path src = raw.is_absolute() ? raw : std::filesystem::path(custom_level_path).parent_path() / raw;
std::filesystem::path dst = customlvl_texdir / path;
std::filesystem::remove(dst);
std::filesystem::copy_file(src, dst);
materials->insert({ matname, "customlevel/" + path });
#define C0(pos, width) ((dl->words.w0 >> (pos)) & ((1U << width) - 1))
#define C1(pos, width) ((dl->words.w1 >> (pos)) & ((1U << width) - 1))
void append_collision_data(Gfx* dl, int* cur, int* nvt, std::map<void*, int>* off, std::vector<float>* vtx, std::vector<int>* tri) {
bool running = true;
while (running) {
int opcode = dl->words.w0 >> 24;
switch (opcode) {
case G_DL: {
append_collision_data((Gfx*)dl->words.w1, cur, nvt, off, vtx, tri);
} break;
case G_VTX: {
Vtx* verts = (Vtx*)dl->words.w1;
if (off->find(verts) == off->end()) {
off->insert({ verts, *nvt });
int num = C0(12, 8);
*nvt += num;
for (int i = 0; i < num; i++) {
vtx->push_back(verts[i].v.ob[0]);
vtx->push_back(verts[i].v.ob[1]);
vtx->push_back(verts[i].v.ob[2]);
}
}
*cur = (*off)[verts];
} break;
case G_TRI1: {
tri->push_back(C0(16, 8) / 2 + *cur);
tri->push_back(C0( 8, 8) / 2 + *cur);
tri->push_back(C0( 0, 8) / 2 + *cur);
} break;
case G_TRI2: {
tri->push_back(C0(16, 8) / 2 + *cur);
tri->push_back(C0( 8, 8) / 2 + *cur);
tri->push_back(C0( 0, 8) / 2 + *cur);
tri->push_back(C1(16, 8) / 2 + *cur);
tri->push_back(C1( 8, 8) / 2 + *cur);
tri->push_back(C1( 0, 8) / 2 + *cur);
} break;
case G_ENDDL: {
running = false;
} break;
}
dl++;
}
}
void parse_custom_level(char* data) {
auto tokens = tokenize(std::string(data));
textureIndex = 0;
if (std::filesystem::exists(customlvl_texdir)) std::filesystem::remove_all(customlvl_texdir);
std::filesystem::create_directories(customlvl_texdir);
custom_level_new();
std::vector<std::array<float, 3>> vertices = {};
std::vector<std::array<float, 2>> uv = {};
std::map<std::string, filesystem::path> materials = {};
for (auto line : tokens) {
if (line.size() == 0) continue;
if (line[0] == "mtllib") {
filesystem::path path = filesystem::absolute(std::filesystem::path(custom_level_dirname) / line[1]);
if (!filesystem::exists(path)) continue;
auto size = filesystem::file_size(path);
char* mtldata = (char*)malloc(size);
std::ifstream file = std::ifstream(path, std::ios::binary);
file.read(mtldata, size);
parse_materials(mtldata, &materials);
free(mtldata);
Collision* create_collision_mesh(struct GraphNode* node) {
if (node == NULL) return NULL;
if (node->type == GRAPH_NODE_TYPE_DISPLAY_LIST) {
struct GraphNodeDisplayList* dlnode = (struct GraphNodeDisplayList*)node;
Gfx* dl = (Gfx*)dlnode->displayList;
std::vector<float> vtx = {};
std::vector<int> tri = {};
std::map<void*, int> off = {};
int nvt = 0;
int cur = 0;
append_collision_data(dl, &cur, &nvt, &off, &vtx, &tri);
Collision* coll = (Collision*)malloc(sizeof(s16) * (6 + vtx.size() + tri.size()));
int ptr = 0;
coll[ptr++] = TERRAIN_LOAD_VERTICES;
coll[ptr++] = vtx.size() / 3;
for (int i = 0; i < vtx.size(); i++) {
coll[ptr++] = vtx[i];
}
if (line[0] == "v") vertices.push_back({ std::stof(line[1]), std::stof(line[2]), std::stof(line[3]) });
if (line[0] == "vt") uv.push_back({ std::stof(line[1]), std::stof(line[2]) });
if (line[0] == "usemtl") {
if (materials.find(line[1]) == materials.end()) continue;
custom_level_texture((char*)materials[line[1]].c_str());
}
if (line[0] == "f") {
for (int i = 1; i < line.size(); i++) {
int idx = i;
if (custom_level_flip_normals && idx == 1) idx = 3;
else if (custom_level_flip_normals && idx == 3) idx = 1;
auto indexes = split(line[idx], '/');
int v = std::stoi(indexes[0]) - 1;
int vt = std::stoi(indexes[1]) - 1;
custom_level_vertex(vertices[v][0] * custom_level_scale, vertices[v][1] * custom_level_scale, vertices[v][2] * custom_level_scale, uv[vt][0] * 1024, uv[vt][1] * 1024);
}
custom_level_face();
coll[ptr++] = SURFACE_DEFAULT;
coll[ptr++] = tri.size() / 3;
for (int i = 0; i < tri.size(); i++) {
coll[ptr++] = tri[i];
}
coll[ptr++] = TERRAIN_LOAD_CONTINUE;
coll[ptr++] = TERRAIN_LOAD_END;
return coll;
}
gfx_precache_textures();
custom_level_finish();
return create_collision_mesh(node->children);
}
struct AnimEntry {
@ -353,6 +355,45 @@ void imgui_machinima_quick_options() {
k_frame_keys.clear();
}
if (gCurrLevelNum == LEVEL_SA) {
if (ImGui::Checkbox("Load Level Model", &override_level)) {
gCurrentArea->terrainData =
override_level && override_level_collision ?
override_level_collision :
gAreas[gCurrAreaIndex].terrainDataOrig;
load_area_terrain(gCurrAreaIndex, gCurrentArea->terrainData, gCurrentArea->surfaceRooms, NULL);
}
if (override_level) {
Array<PackData*>& sDynosPacks = DynOS_Gfx_GetPacks();
ImGui::BeginChild("##level_model_select", ImVec2(0, 120), true);
for (Model& model : model_list) {
if (model.Type != "level") continue;
GfxData* gfx = DynOS_Gfx_LoadFromBinary(sDynosPacks[model.DynOSId]->mPath, "mario_geo");
GraphNode* geo = (GraphNode*)DynOS_Geo_GetGraphNode((*(gfx->mGeoLayouts.end() - 1))->mData, true);
bool selected = geo == override_level_geolayout;
if (ImGui::Selectable(model.Name.c_str(), selected)) {
if (override_level_collision) {
free(override_level_collision);
override_level_collision = NULL;
}
override_level_geolayout = geo;
override_level_collision = create_collision_mesh(geo);
gCurrentArea->terrainData = override_level_collision;
load_area_terrain(gCurrAreaIndex, gCurrentArea->terrainData, gCurrentArea->surfaceRooms, NULL);
}
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::TextDisabled("%s - by", model.Version.c_str());
ImGui::SameLine();
ImGui::Text("%s", model.Author.c_str());
ImGui::Text("%s", model.Description.c_str());
ImGui::EndTooltip();
}
}
ImGui::EndChild();
}
}
auto locations = saturn_get_locations();
bool do_save = false;
std::vector<std::string> forRemoval = {};
@ -465,38 +506,6 @@ void imgui_machinima_quick_options() {
ImGui::EndMenu();
}
}
UNSTABLE
if (ImGui::BeginMenu("(!) Custom Level")) {
bool in_custom_level = gCurrLevelNum == LEVEL_SA && gCurrAreaIndex == 3;
ImGui::PushItemWidth(80);
ImGui::InputFloat("Scale###cl_scale", &custom_level_scale);
ImGui::PopItemWidth();
if (!is_custom_level_loaded || in_custom_level) ImGui::BeginDisabled();
if (ImGui::Button("Load Level")) {
auto size = filesystem::file_size(custom_level_path);
char* data = (char*)malloc(size);
std::ifstream file = std::ifstream((char*)custom_level_path.c_str(), std::ios::binary);
file.read(data, size);
parse_custom_level(data);
free(data);
warp_to_level(0, 3);
}
if (!is_custom_level_loaded || in_custom_level) ImGui::EndDisabled();
ImGui::SameLine();
if (ImGui::Button("Load .obj")) {
auto selection = choose_file_dialog("Select a model", { "Wavefront Model (.obj)", "*.obj", "All Files", "*" }, false);
if (selection.size() != 0) {
filesystem::path path = selection[0];
is_custom_level_loaded = true;
custom_level_path = path.string();
custom_level_dirname = path.parent_path().string();
custom_level_filename = path.filename().string();
}
}
ImGui::Text(is_custom_level_loaded ? custom_level_filename.c_str() : "No model loaded!");
ImGui::EndMenu();
}
}
static char animSearchTerm[128];

View file

@ -8,16 +8,22 @@ extern float gravity;
extern bool enable_time_freeze;
extern int current_sanim_id;
extern bool override_level;
extern struct GraphNode* override_level_geolayout;
extern Collision* override_level_collision;
extern void anim_play_button();
extern void saturn_create_object(int, const BehaviorScript*, float, float, float, s16, s16, s16, int);
#ifdef __cplusplus
extern "C" {
#endif
void warp_to_level(int, int, int);
int get_saturn_level_id(int);
void smachinima_imgui_init(void);
void smachinima_imgui_controls(SDL_Event * event);
Gfx* geo_switch_override_model(s32 callContext, struct GraphNode *node, UNUSED Mat4 *mtx);
void imgui_machinima_animation_player(void);
void imgui_machinima_quick_options(void);

View file

@ -98,6 +98,8 @@ Model LoadModelData(std::string folderPath) {
model.Name = root["name"].asString();
model.Author = root["author"].asString();
model.Version = root["version"].asString();
if (root.isMember("type")) model.Type = root["type"].asString();
std::transform(model.Type.begin(), model.Type.end(), model.Type.begin(), [](unsigned char c){ return std::tolower(c); });
// Description (optional)
if (root.isMember("description")) {

View file

@ -24,6 +24,7 @@ class Model {
std::string Author;
std::string Version = "1.0.0"; // String, so version format can be anything
std::string Description;
std::string Type = "mario";
// Expressions
std::vector<Expression> Expressions;