Include C test program in repo
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
/src/mario
|
/src/mario
|
||||||
/build/
|
/build/
|
||||||
/dist/
|
/dist/
|
||||||
|
/test/level.c
|
||||||
|
/test/level.h
|
||||||
*.z64
|
*.z64
|
||||||
*.so
|
*.so
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
default: all
|
default: test
|
||||||
|
|
||||||
CC := cc
|
CC := cc
|
||||||
CFLAGS := -g -Wall -fPIC
|
CFLAGS := -g -Wall -fPIC
|
||||||
LDFLAGS := -lm -shared
|
LDFLAGS := -lm -shared
|
||||||
|
|
||||||
SRC_DIRS := src src/engine src/game src/mario src/tools
|
SRC_DIRS := src src/engine src/game src/mario src/tools test
|
||||||
BUILD_DIR := build
|
BUILD_DIR := build
|
||||||
DIST_DIR := dist
|
DIST_DIR := dist
|
||||||
ALL_DIRS := $(addprefix $(BUILD_DIR)/,$(SRC_DIRS))
|
ALL_DIRS := $(addprefix $(BUILD_DIR)/,$(SRC_DIRS))
|
||||||
|
|
||||||
BIN_FILE := $(DIST_DIR)/libsm64.so
|
LIB_FILE := $(DIST_DIR)/libsm64.so
|
||||||
LIB_H_FILE := $(DIST_DIR)/include/libsm64.h
|
LIB_H_FILE := $(DIST_DIR)/include/libsm64.h
|
||||||
|
TEST_FILE := run-test
|
||||||
|
|
||||||
C_IMPORTED := src/mario/geo.inc.c src/mario/model.inc.c
|
C_IMPORTED := src/mario/geo.inc.c src/mario/model.inc.c
|
||||||
H_IMPORTED := $(C_IMPORTED:.c=.h)
|
H_IMPORTED := $(C_IMPORTED:.c=.h)
|
||||||
@@ -19,6 +20,7 @@ IMPORTED := $(C_IMPORTED) $(H_IMPORTED)
|
|||||||
C_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c)) $(C_IMPORTED)
|
C_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c)) $(C_IMPORTED)
|
||||||
O_FILES := $(foreach file,$(C_FILES),$(BUILD_DIR)/$(file:.c=.o))
|
O_FILES := $(foreach file,$(C_FILES),$(BUILD_DIR)/$(file:.c=.o))
|
||||||
DEP_FILES := $(O_FILES:.o=.d)
|
DEP_FILES := $(O_FILES:.o=.d)
|
||||||
|
TEST_OBJS := build/test/context.o build/test/level.o build/test/main.o
|
||||||
|
|
||||||
DUMMY != mkdir -p src/mario
|
DUMMY != mkdir -p src/mario
|
||||||
DUMMY != mkdir -p $(ALL_DIRS)
|
DUMMY != mkdir -p $(ALL_DIRS)
|
||||||
@@ -28,19 +30,32 @@ $(filter-out src/mario/geo.inc.c,$(IMPORTED)): src/mario/geo.inc.c
|
|||||||
src/mario/geo.inc.c: ./import-mario-geo.py
|
src/mario/geo.inc.c: ./import-mario-geo.py
|
||||||
./import-mario-geo.py
|
./import-mario-geo.py
|
||||||
|
|
||||||
|
test/level.c test/level.h: ./import-test-collision.py
|
||||||
|
./import-test-collision.py
|
||||||
|
|
||||||
|
test/main.c: test/level.h
|
||||||
|
|
||||||
$(BUILD_DIR)/%.o: %.c $(IMPORTED)
|
$(BUILD_DIR)/%.o: %.c $(IMPORTED)
|
||||||
@$(CC) $(CFLAGS) -MM -MP -MT $@ -MF $(BUILD_DIR)/$*.d $<
|
@$(CC) $(CFLAGS) -MM -MP -MT $@ -MF $(BUILD_DIR)/$*.d $<
|
||||||
$(CC) -c $(CFLAGS) -o $@ $<
|
$(CC) -c $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
$(BIN_FILE): $(O_FILES)
|
$(LIB_FILE): $(O_FILES)
|
||||||
$(CC) $(LDFLAGS) -o $@ $^
|
$(CC) $(LDFLAGS) -o $@ $^
|
||||||
|
|
||||||
$(LIB_H_FILE): src/libsm64.h
|
$(LIB_H_FILE): src/libsm64.h
|
||||||
cp -f $< $@
|
cp -f $< $@
|
||||||
|
|
||||||
all: $(BIN_FILE) $(LIB_H_FILE)
|
$(TEST_FILE): $(LIB_FILE) $(TEST_OBJS)
|
||||||
|
$(CC) -o $@ $(TEST_OBJS) $(LIB_FILE) -lGLEW -lGL -lSDL2 -lSDL2main -lm
|
||||||
|
|
||||||
|
lib: $(LIB_FILE)
|
||||||
|
|
||||||
|
test: $(TEST_FILE)
|
||||||
|
|
||||||
|
run: test
|
||||||
|
./$(TEST_FILE)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILD_DIR) $(DIST_DIR) src/mario
|
rm -rf $(BUILD_DIR) $(DIST_DIR) src/mario test/level.?
|
||||||
|
|
||||||
-include $(DEP_FILES)
|
-include $(DEP_FILES)
|
||||||
Executable
+58
@@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import requests
|
||||||
|
|
||||||
|
BOB_COLLISION_URL = "https://raw.githubusercontent.com/n64decomp/sm64/master/levels/bob/areas/1/collision.inc.c"
|
||||||
|
|
||||||
|
LEVEL_H = """#pragma once
|
||||||
|
|
||||||
|
#include "../src/libsm64.h"
|
||||||
|
|
||||||
|
extern const struct SM64Surface surfaces[];
|
||||||
|
extern const size_t surfaces_count;
|
||||||
|
"""
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("Downloading " + BOB_COLLISION_URL)
|
||||||
|
in_lines = requests.get(BOB_COLLISION_URL).text.splitlines()
|
||||||
|
|
||||||
|
verts = []
|
||||||
|
tris = []
|
||||||
|
mode = ""
|
||||||
|
|
||||||
|
for line in in_lines:
|
||||||
|
if not line.strip().startswith("COL_"):
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tokens = line.strip().replace("(", ",").replace(")", "").split(",")
|
||||||
|
|
||||||
|
if tokens[0] == "COL_VERTEX":
|
||||||
|
verts.append([ int(tokens[1]), int(tokens[2]), int(tokens[3]) ])
|
||||||
|
elif tokens[0] == "COL_TRI_INIT":
|
||||||
|
mode = tokens[1]
|
||||||
|
elif tokens[0] == "COL_TRI":
|
||||||
|
tris.append([ int(tokens[1]), int(tokens[2]), int(tokens[3]), mode ])
|
||||||
|
|
||||||
|
out_lines = []
|
||||||
|
|
||||||
|
for tri in tris:
|
||||||
|
out_lines.append("{%s,0,{{%s,%s,%s},{%s,%s,%s},{%s,%s,%s}}}"%(tri[3], \
|
||||||
|
verts[tri[0]][0], verts[tri[0]][1], verts[tri[0]][2], \
|
||||||
|
verts[tri[1]][0], verts[tri[1]][1], verts[tri[1]][2], \
|
||||||
|
verts[tri[2]][0], verts[tri[2]][1], verts[tri[2]][2]))
|
||||||
|
|
||||||
|
out_str = ",\n".join(out_lines)
|
||||||
|
out_str = "const struct SM64Surface surfaces[] = {\n" + out_str + "};\n\n"
|
||||||
|
out_str += "const size_t surfaces_count = sizeof( surfaces ) / sizeof( surfaces[0] );"
|
||||||
|
out_str = '#include "../src/include/surface_terrains.h"\n' + out_str
|
||||||
|
out_str = '#include "level.h"\n' + out_str
|
||||||
|
|
||||||
|
with open("test/level.c", "w") as file:
|
||||||
|
file.write(out_str)
|
||||||
|
|
||||||
|
with open("test/level.h", "w") as file:
|
||||||
|
file.write(LEVEL_H)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
+4
-4
@@ -1669,7 +1669,7 @@ void mario_update_hitbox_and_cap_model(struct MarioState *m) {
|
|||||||
* An unused and possibly a debug function. Z + another button input
|
* An unused and possibly a debug function. Z + another button input
|
||||||
* sets Mario with a different cap.
|
* sets Mario with a different cap.
|
||||||
*/
|
*/
|
||||||
static void debug_update_mario_cap(u16 button, s32 flags, u16 capTimer, u16 capMusic) {
|
//static void debug_update_mario_cap(u16 button, s32 flags, u16 capTimer, u16 capMusic) {
|
||||||
// // This checks for Z_TRIG instead of Z_DOWN flag
|
// // This checks for Z_TRIG instead of Z_DOWN flag
|
||||||
// // (which is also what other debug functions do),
|
// // (which is also what other debug functions do),
|
||||||
// // so likely debug behavior rather than unused behavior.
|
// // so likely debug behavior rather than unused behavior.
|
||||||
@@ -1683,7 +1683,7 @@ static void debug_update_mario_cap(u16 button, s32 flags, u16 capTimer, u16 capM
|
|||||||
|
|
||||||
// play_cap_music(capMusic);
|
// play_cap_music(capMusic);
|
||||||
// }
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
#ifdef VERSION_SH
|
#ifdef VERSION_SH
|
||||||
void func_sh_8025574C(void) {
|
void func_sh_8025574C(void) {
|
||||||
@@ -1793,8 +1793,8 @@ s32 execute_mario_action(UNUSED struct Object *o) {
|
|||||||
**************************************************/
|
**************************************************/
|
||||||
|
|
||||||
void init_mario(void) {
|
void init_mario(void) {
|
||||||
Vec3s capPos;
|
//Vec3s capPos;
|
||||||
struct Object *capObject;
|
//struct Object *capObject;
|
||||||
|
|
||||||
unused80339F10 = 0;
|
unused80339F10 = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -60,22 +60,22 @@ enum SaveOption { SAVE_OPT_SAVE_AND_CONTINUE = 1, SAVE_OPT_SAVE_AND_QUIT, SAVE_O
|
|||||||
|
|
||||||
static struct Object *sIntroWarpPipeObj;
|
static struct Object *sIntroWarpPipeObj;
|
||||||
static struct Object *sEndPeachObj;
|
static struct Object *sEndPeachObj;
|
||||||
static struct Object *sEndRightToadObj;
|
//static struct Object *sEndRightToadObj;
|
||||||
static struct Object *sEndLeftToadObj;
|
//static struct Object *sEndLeftToadObj;
|
||||||
static struct Object *sEndJumboStarObj;
|
static struct Object *sEndJumboStarObj;
|
||||||
static UNUSED s32 sUnused;
|
static UNUSED s32 sUnused;
|
||||||
static s16 sEndPeachAnimation;
|
static s16 sEndPeachAnimation;
|
||||||
static s16 sEndToadAnims[2];
|
//static s16 sEndToadAnims[2];
|
||||||
|
|
||||||
static Vp sEndCutsceneVp = { { { 640, 480, 511, 0 }, { 640, 480, 511, 0 } } };
|
static Vp sEndCutsceneVp = { { { 640, 480, 511, 0 }, { 640, 480, 511, 0 } } };
|
||||||
static struct CreditsEntry *sDispCreditsEntry = NULL;
|
//static struct CreditsEntry *sDispCreditsEntry = NULL;
|
||||||
|
|
||||||
// related to peach gfx?
|
// related to peach gfx?
|
||||||
static s8 D_8032CBE4 = 0;
|
static s8 D_8032CBE4 = 0;
|
||||||
static s8 D_8032CBE8 = 0;
|
//static s8 D_8032CBE8 = 0;
|
||||||
static s8 D_8032CBEC[7] = { 2, 3, 2, 1, 2, 3, 2 };
|
//static s8 D_8032CBEC[7] = { 2, 3, 2, 1, 2, 3, 2 };
|
||||||
|
|
||||||
static u8 sStarsNeededForDialog[] = { 1, 3, 8, 30, 50, 70 };
|
//static u8 sStarsNeededForDialog[] = { 1, 3, 8, 30, 50, 70 };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data for the jumbo star cutscene. It specifies the flight path after triple
|
* Data for the jumbo star cutscene. It specifies the flight path after triple
|
||||||
@@ -239,15 +239,15 @@ s32 geo_switch_peach_eyes(s32 run, struct GraphNode *node, UNUSED s32 a2) {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// unused
|
// unused
|
||||||
static void stub_is_textbox_active(u16 *a0) {
|
//static void stub_is_textbox_active(u16 *a0) {
|
||||||
// if (get_dialog_id() == -1) {
|
// if (get_dialog_id() == -1) {
|
||||||
// *a0 = 0;
|
// *a0 = 0;
|
||||||
// }
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get_star_collection_dialog: Determine what dialog should show when Mario
|
* get_star_collection_dialog: Determine what dialog should show when Mario
|
||||||
@@ -271,6 +271,7 @@ s32 get_star_collection_dialog(struct MarioState *m) {
|
|||||||
|
|
||||||
// m->prevNumStarsForDialog = m->numStars;
|
// m->prevNumStarsForDialog = m->numStars;
|
||||||
// return dialogID;
|
// return dialogID;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// save menu handler
|
// save menu handler
|
||||||
@@ -318,6 +319,7 @@ struct Object *spawn_obj_at_mario_rel_yaw(struct MarioState *m, s32 model, const
|
|||||||
// o->oPosZ = m->pos[2];
|
// o->oPosZ = m->pos[2];
|
||||||
|
|
||||||
// return o;
|
// return o;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -519,7 +521,7 @@ s32 act_reading_automatic_dialog(struct MarioState *m) {
|
|||||||
// }
|
// }
|
||||||
// // apply head turn
|
// // apply head turn
|
||||||
// vec3s_set(m->marioBodyState->headAngle, m->actionTimer, 0, 0);
|
// vec3s_set(m->marioBodyState->headAngle, m->actionTimer, 0, 0);
|
||||||
// return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 act_reading_sign(struct MarioState *m) {
|
s32 act_reading_sign(struct MarioState *m) {
|
||||||
@@ -559,7 +561,7 @@ s32 act_reading_sign(struct MarioState *m) {
|
|||||||
//
|
//
|
||||||
// vec3f_copy(marioObj->header.gfx.pos, m->pos);
|
// vec3f_copy(marioObj->header.gfx.pos, m->pos);
|
||||||
// vec3s_set(marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
|
// vec3s_set(marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
|
||||||
// return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 act_debug_free_move(struct MarioState *m) {
|
s32 act_debug_free_move(struct MarioState *m) {
|
||||||
@@ -842,7 +844,7 @@ s32 act_unlocking_key_door(struct MarioState *m) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// m->actionTimer++;
|
// m->actionTimer++;
|
||||||
// return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 act_unlocking_star_door(struct MarioState *m) {
|
s32 act_unlocking_star_door(struct MarioState *m) {
|
||||||
@@ -883,7 +885,7 @@ s32 act_unlocking_star_door(struct MarioState *m) {
|
|||||||
// update_mario_pos_for_anim(m);
|
// update_mario_pos_for_anim(m);
|
||||||
// stop_and_set_height_to_floor(m);
|
// stop_and_set_height_to_floor(m);
|
||||||
|
|
||||||
// return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 act_entering_star_door(struct MarioState *m) {
|
s32 act_entering_star_door(struct MarioState *m) {
|
||||||
@@ -1181,7 +1183,7 @@ s32 act_exit_land_save_dialog(struct MarioState *m) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// m->marioObj->header.gfx.angle[1] += 0x8000;
|
// m->marioObj->header.gfx.angle[1] += 0x8000;
|
||||||
// return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 act_death_exit(struct MarioState *m) {
|
s32 act_death_exit(struct MarioState *m) {
|
||||||
@@ -2011,21 +2013,21 @@ void generate_yellow_sparkles(s16 x, s16 y, s16 z, f32 radius) {
|
|||||||
|
|
||||||
// not sure what this does, returns the height of the floor.
|
// not sure what this does, returns the height of the floor.
|
||||||
// (animation related?)
|
// (animation related?)
|
||||||
static f32 end_obj_set_visual_pos(struct Object *o) {
|
// static f32 end_obj_set_visual_pos(struct Object *o) {
|
||||||
struct Surface *surf;
|
// struct Surface *surf;
|
||||||
Vec3s sp24;
|
// Vec3s sp24;
|
||||||
f32 sp20;
|
// f32 sp20;
|
||||||
f32 sp1C;
|
// f32 sp1C;
|
||||||
f32 sp18;
|
// f32 sp18;
|
||||||
|
//
|
||||||
find_mario_anim_flags_and_translation(o, o->header.gfx.angle[1], sp24);
|
// find_mario_anim_flags_and_translation(o, o->header.gfx.angle[1], sp24);
|
||||||
|
//
|
||||||
sp20 = o->header.gfx.pos[0] + sp24[0];
|
// sp20 = o->header.gfx.pos[0] + sp24[0];
|
||||||
sp1C = o->header.gfx.pos[1] + 10.0f;
|
// sp1C = o->header.gfx.pos[1] + 10.0f;
|
||||||
sp18 = o->header.gfx.pos[2] + sp24[2];
|
// sp18 = o->header.gfx.pos[2] + sp24[2];
|
||||||
|
//
|
||||||
return find_floor(sp20, sp1C, sp18, &surf);
|
// return find_floor(sp20, sp1C, sp18, &surf);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// make Mario fall and soften wing cap gravity
|
// make Mario fall and soften wing cap gravity
|
||||||
static void end_peach_cutscene_mario_falling(struct MarioState *m) {
|
static void end_peach_cutscene_mario_falling(struct MarioState *m) {
|
||||||
@@ -2308,13 +2310,13 @@ static void end_peach_cutscene_dialog_2(struct MarioState *m) {
|
|||||||
#undef TIMER_PEACH_KISS
|
#undef TIMER_PEACH_KISS
|
||||||
|
|
||||||
// blink twice then have half-shut eyes (see end_peach_cutscene_kiss_from_peach)
|
// blink twice then have half-shut eyes (see end_peach_cutscene_kiss_from_peach)
|
||||||
static u8 sMarioBlinkOverride[20] = {
|
// static u8 sMarioBlinkOverride[20] = {
|
||||||
MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_CLOSED, MARIO_EYES_CLOSED,
|
// MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_CLOSED, MARIO_EYES_CLOSED,
|
||||||
MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_OPEN, MARIO_EYES_OPEN,
|
// MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_OPEN, MARIO_EYES_OPEN,
|
||||||
MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_CLOSED, MARIO_EYES_CLOSED,
|
// MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_CLOSED, MARIO_EYES_CLOSED,
|
||||||
MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_OPEN, MARIO_EYES_OPEN,
|
// MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_OPEN, MARIO_EYES_OPEN,
|
||||||
MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_CLOSED, MARIO_EYES_CLOSED,
|
// MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_CLOSED, MARIO_EYES_CLOSED,
|
||||||
};
|
// };
|
||||||
|
|
||||||
static void end_peach_cutscene_kiss_from_peach(struct MarioState *m) {
|
static void end_peach_cutscene_kiss_from_peach(struct MarioState *m) {
|
||||||
// sEndPeachAnimation = 10;
|
// sEndPeachAnimation = 10;
|
||||||
@@ -2610,7 +2612,7 @@ static s32 act_credits_cutscene(struct MarioState *m) {
|
|||||||
//
|
//
|
||||||
// m->marioObj->header.gfx.angle[1] += (gCurrCreditsEntry->unk02 & 0xC0) << 8;
|
// m->marioObj->header.gfx.angle[1] += (gCurrCreditsEntry->unk02 & 0xC0) << 8;
|
||||||
//
|
//
|
||||||
// return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static s32 act_end_waving_cutscene(struct MarioState *m) {
|
static s32 act_end_waving_cutscene(struct MarioState *m) {
|
||||||
@@ -2648,7 +2650,7 @@ static s32 act_end_waving_cutscene(struct MarioState *m) {
|
|||||||
// level_trigger_warp(m, WARP_OP_CREDITS_END);
|
// level_trigger_warp(m, WARP_OP_CREDITS_END);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static s32 check_for_instant_quicksand(struct MarioState *m) {
|
static s32 check_for_instant_quicksand(struct MarioState *m) {
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ void bhv_toad_message_init(void) {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
static void star_door_unlock_spawn_particles(s16 angleOffset) {
|
//static void star_door_unlock_spawn_particles(s16 angleOffset) {
|
||||||
// struct Object *sparkleParticle = spawn_object(gCurrentObject, 0, bhvSparkleSpawn);
|
// struct Object *sparkleParticle = spawn_object(gCurrentObject, 0, bhvSparkleSpawn);
|
||||||
//
|
//
|
||||||
// sparkleParticle->oPosX +=
|
// sparkleParticle->oPosX +=
|
||||||
@@ -232,7 +232,7 @@ static void star_door_unlock_spawn_particles(s16 angleOffset) {
|
|||||||
// 100.0f * coss((gCurrentObject->oUnlockDoorStarTimer * 0x2800) + angleOffset);
|
// 100.0f * coss((gCurrentObject->oUnlockDoorStarTimer * 0x2800) + angleOffset);
|
||||||
// // Particles are spawned lower each frame
|
// // Particles are spawned lower each frame
|
||||||
// sparkleParticle->oPosY -= gCurrentObject->oUnlockDoorStarTimer * 10.0f;
|
// sparkleParticle->oPosY -= gCurrentObject->oUnlockDoorStarTimer * 10.0f;
|
||||||
}
|
//}
|
||||||
|
|
||||||
void bhv_unlock_door_star_init(void) {
|
void bhv_unlock_door_star_init(void) {
|
||||||
// gCurrentObject->oUnlockDoorStarState = UNLOCK_DOOR_STAR_RISING;
|
// gCurrentObject->oUnlockDoorStarState = UNLOCK_DOOR_STAR_RISING;
|
||||||
@@ -410,7 +410,7 @@ Gfx *geo_mario_tilt_torso(s32 callContext, struct GraphNode *node, UNUSED Mat4 *
|
|||||||
Gfx *geo_mario_head_rotation(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c) {
|
Gfx *geo_mario_head_rotation(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c) {
|
||||||
struct GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node;
|
struct GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node;
|
||||||
struct MarioBodyState *bodyState = &gBodyStates[asGenerated->parameter];
|
struct MarioBodyState *bodyState = &gBodyStates[asGenerated->parameter];
|
||||||
s32 action = bodyState->action;
|
// s32 action = bodyState->action;
|
||||||
|
|
||||||
if (callContext == GEO_CONTEXT_RENDER) {
|
if (callContext == GEO_CONTEXT_RENDER) {
|
||||||
struct GraphNodeRotation *rotNode = (struct GraphNodeRotation *) node->next;
|
struct GraphNodeRotation *rotNode = (struct GraphNodeRotation *) node->next;
|
||||||
@@ -653,4 +653,5 @@ Gfx *geo_mirror_mario_backface_culling(s32 callContext, struct GraphNode *node,
|
|||||||
// asGenerated->fnNode.node.flags = (asGenerated->fnNode.node.flags & 0xFF) | (LAYER_OPAQUE << 8);
|
// asGenerated->fnNode.node.flags = (asGenerated->fnNode.node.flags & 0xFF) | (LAYER_OPAQUE << 8);
|
||||||
// }
|
// }
|
||||||
// return gfx;
|
// return gfx;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ struct Object *hack_allocate_mario(void)
|
|||||||
*/
|
*/
|
||||||
void bhv_mario_update(void) {
|
void bhv_mario_update(void) {
|
||||||
u32 particleFlags = 0;
|
u32 particleFlags = 0;
|
||||||
s32 i;
|
// s32 i;
|
||||||
|
|
||||||
gCurrentObject = gMarioObject;
|
gCurrentObject = gMarioObject;
|
||||||
particleFlags = execute_mario_action(gCurrentObject);
|
particleFlags = execute_mario_action(gCurrentObject);
|
||||||
|
|||||||
+3
-3
@@ -27,8 +27,8 @@ enum MarioTextures
|
|||||||
|
|
||||||
#define NUM_USED_TEXTURES 11
|
#define NUM_USED_TEXTURES 11
|
||||||
|
|
||||||
static int mario_tex_offsets[NUM_USED_TEXTURES] = { 144, 4240, 6288, 8336, 10384, 12432, 14480, 16528, 30864, 32912, 37008 };
|
static const int mario_tex_offsets[NUM_USED_TEXTURES] = { 144, 4240, 6288, 8336, 10384, 12432, 14480, 16528, 30864, 32912, 37008 };
|
||||||
static int mario_tex_widths [NUM_USED_TEXTURES] = { 64, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 };
|
static const int mario_tex_widths [NUM_USED_TEXTURES] = { 64, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 };
|
||||||
static int mario_tex_heights[NUM_USED_TEXTURES] = { 32, 32, 32, 32, 32, 32, 32, 32, 32, 64, 64 };
|
static const int mario_tex_heights[NUM_USED_TEXTURES] = { 32, 32, 32, 32, 32, 32, 32, 32, 32, 64, 64 };
|
||||||
|
|
||||||
extern void load_mario_textures_from_rom( uint8_t *rom, uint8_t *outTexture );
|
extern void load_mario_textures_from_rom( uint8_t *rom, uint8_t *outTexture );
|
||||||
+161
@@ -0,0 +1,161 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
// A few choice functions and types from the cglm library
|
||||||
|
// https://github.com/recp/cglm
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
typedef float vec2[2];
|
||||||
|
typedef float vec3[3];
|
||||||
|
typedef float vec4[4];
|
||||||
|
typedef float mat4[4][4];
|
||||||
|
|
||||||
|
static void
|
||||||
|
glm_mat4_zero( mat4 m )
|
||||||
|
{
|
||||||
|
memset( m, 0, sizeof(mat4) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
glm_mat4_identity( mat4 m )
|
||||||
|
{
|
||||||
|
glm_mat4_zero( m );
|
||||||
|
m[0][0] = 1.0f;
|
||||||
|
m[1][1] = 1.0f;
|
||||||
|
m[2][2] = 1.0f;
|
||||||
|
m[3][3] = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
glm_vec4_scale(vec4 v, float s, vec4 dest) {
|
||||||
|
dest[0] = v[0] * s;
|
||||||
|
dest[1] = v[1] * s;
|
||||||
|
dest[2] = v[2] * s;
|
||||||
|
dest[3] = v[3] * s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
glm_vec4_add(vec4 a, vec4 b, vec4 dest) {
|
||||||
|
dest[0] = a[0] + b[0];
|
||||||
|
dest[1] = a[1] + b[1];
|
||||||
|
dest[2] = a[2] + b[2];
|
||||||
|
dest[3] = a[3] + b[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
glm_vec3_scale(vec3 v, float s, vec3 dest) {
|
||||||
|
dest[0] = v[0] * s;
|
||||||
|
dest[1] = v[1] * s;
|
||||||
|
dest[2] = v[2] * s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
glm_vec3_sub(vec3 a, vec3 b, vec3 dest) {
|
||||||
|
dest[0] = a[0] - b[0];
|
||||||
|
dest[1] = a[1] - b[1];
|
||||||
|
dest[2] = a[2] - b[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
static float
|
||||||
|
glm_vec3_dot(vec3 a, vec3 b) {
|
||||||
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
static float
|
||||||
|
glm_vec3_norm2(vec3 v) {
|
||||||
|
return glm_vec3_dot(v, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float
|
||||||
|
glm_vec3_norm(vec3 v) {
|
||||||
|
return sqrtf(glm_vec3_norm2(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
glm_vec3_normalize(vec3 v) {
|
||||||
|
float norm;
|
||||||
|
|
||||||
|
norm = glm_vec3_norm(v);
|
||||||
|
|
||||||
|
if (norm == 0.0f) {
|
||||||
|
v[0] = v[1] = v[2] = 0.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm_vec3_scale(v, 1.0f / norm, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
glm_vec3_cross(vec3 a, vec3 b, vec3 dest) {
|
||||||
|
/* (u2.v3 - u3.v2, u3.v1 - u1.v3, u1.v2 - u2.v1) */
|
||||||
|
dest[0] = a[1] * b[2] - a[2] * b[1];
|
||||||
|
dest[1] = a[2] * b[0] - a[0] * b[2];
|
||||||
|
dest[2] = a[0] * b[1] - a[1] * b[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
glm_vec3_crossn(vec3 a, vec3 b, vec3 dest) {
|
||||||
|
glm_vec3_cross(a, b, dest);
|
||||||
|
glm_vec3_normalize(dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
glm_perspective(float fovy,
|
||||||
|
float aspect,
|
||||||
|
float nearVal,
|
||||||
|
float farVal,
|
||||||
|
mat4 dest) {
|
||||||
|
float f, fn;
|
||||||
|
|
||||||
|
glm_mat4_zero(dest);
|
||||||
|
|
||||||
|
f = 1.0f / tanf(fovy * 0.5f);
|
||||||
|
fn = 1.0f / (nearVal - farVal);
|
||||||
|
|
||||||
|
dest[0][0] = f / aspect;
|
||||||
|
dest[1][1] = f;
|
||||||
|
dest[2][2] = (nearVal + farVal) * fn;
|
||||||
|
dest[2][3] =-1.0f;
|
||||||
|
dest[3][2] = 2.0f * nearVal * farVal * fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
glm_translate(mat4 m, vec3 v) {
|
||||||
|
vec4 v1, v2, v3;
|
||||||
|
|
||||||
|
glm_vec4_scale(m[0], v[0], v1);
|
||||||
|
glm_vec4_scale(m[1], v[1], v2);
|
||||||
|
glm_vec4_scale(m[2], v[2], v3);
|
||||||
|
|
||||||
|
glm_vec4_add(v1, m[3], m[3]);
|
||||||
|
glm_vec4_add(v2, m[3], m[3]);
|
||||||
|
glm_vec4_add(v3, m[3], m[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
glm_lookat(vec3 eye, vec3 center, vec3 up, mat4 dest) {
|
||||||
|
vec3 f, u, s;
|
||||||
|
|
||||||
|
glm_vec3_sub(center, eye, f);
|
||||||
|
glm_vec3_normalize(f);
|
||||||
|
|
||||||
|
glm_vec3_crossn(f, up, s);
|
||||||
|
glm_vec3_cross(s, f, u);
|
||||||
|
|
||||||
|
dest[0][0] = s[0];
|
||||||
|
dest[0][1] = u[0];
|
||||||
|
dest[0][2] =-f[0];
|
||||||
|
dest[1][0] = s[1];
|
||||||
|
dest[1][1] = u[1];
|
||||||
|
dest[1][2] =-f[1];
|
||||||
|
dest[2][0] = s[2];
|
||||||
|
dest[2][1] = u[2];
|
||||||
|
dest[2][2] =-f[2];
|
||||||
|
dest[3][0] =-glm_vec3_dot(s, eye);
|
||||||
|
dest[3][1] =-glm_vec3_dot(u, eye);
|
||||||
|
dest[3][2] = glm_vec3_dot(f, eye);
|
||||||
|
dest[0][3] = dest[1][3] = dest[2][3] = 0.0f;
|
||||||
|
dest[3][3] = 1.0f;
|
||||||
|
}
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
#include "context.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static SDL_Window *s_sdlWindow;
|
||||||
|
static SDL_GLContext s_sdlGlContext;
|
||||||
|
static SDL_GameController *s_controller;
|
||||||
|
static int s_windowWidth;
|
||||||
|
static int s_windowHeight;
|
||||||
|
|
||||||
|
void context_init( const char *title, int width, int height )
|
||||||
|
{
|
||||||
|
if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ) < 0 ) goto err;
|
||||||
|
|
||||||
|
SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 );
|
||||||
|
SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 4 );
|
||||||
|
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 4 );
|
||||||
|
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 1 );
|
||||||
|
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
|
||||||
|
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
|
||||||
|
SDL_GL_SetSwapInterval( 1 );
|
||||||
|
|
||||||
|
s_windowWidth = width;
|
||||||
|
s_windowHeight = height;
|
||||||
|
|
||||||
|
s_sdlWindow = SDL_CreateWindow(
|
||||||
|
title,
|
||||||
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||||
|
width, height,
|
||||||
|
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE
|
||||||
|
);
|
||||||
|
|
||||||
|
if( !s_sdlWindow ) goto err;
|
||||||
|
|
||||||
|
s_sdlGlContext = SDL_GL_CreateContext( s_sdlWindow );
|
||||||
|
|
||||||
|
if( !s_sdlGlContext ) goto err;
|
||||||
|
|
||||||
|
#ifndef __APPLE__
|
||||||
|
glewExperimental = GL_TRUE;
|
||||||
|
const GLenum glewInitResult = glewInit();
|
||||||
|
if( glewInitResult != GLEW_OK ) goto err;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
s_controller = NULL;
|
||||||
|
|
||||||
|
for( int i = 0; i < SDL_NumJoysticks(); ++i )
|
||||||
|
{
|
||||||
|
if( ! SDL_IsGameController( i ) ) continue;
|
||||||
|
s_controller = SDL_GameControllerOpen( i );
|
||||||
|
printf( "Using game controller: %s", SDL_GameControllerName( s_controller ) );
|
||||||
|
if( s_controller ) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
err:
|
||||||
|
SDL_GL_DeleteContext( s_sdlGlContext );
|
||||||
|
SDL_DestroyWindow( s_sdlWindow );
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_GameController *context_get_controller( void )
|
||||||
|
{
|
||||||
|
return s_controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool context_flip_frame_poll_events( void )
|
||||||
|
{
|
||||||
|
bool still_running = true;
|
||||||
|
|
||||||
|
SDL_GL_SwapWindow( s_sdlWindow );
|
||||||
|
|
||||||
|
SDL_Event event;
|
||||||
|
|
||||||
|
while( SDL_PollEvent( &event ) )
|
||||||
|
{
|
||||||
|
switch( event.type )
|
||||||
|
{
|
||||||
|
case SDL_QUIT:
|
||||||
|
still_running = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_KEYDOWN:
|
||||||
|
if( event.key.keysym.sym == SDLK_ESCAPE )
|
||||||
|
still_running = false;
|
||||||
|
|
||||||
|
case SDL_WINDOWEVENT:
|
||||||
|
if( event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED )
|
||||||
|
{
|
||||||
|
s_windowWidth = event.window.data1;
|
||||||
|
s_windowHeight = event.window.data2;
|
||||||
|
glViewport( 0, 0, s_windowWidth, s_windowHeight );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return still_running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void context_terminate( void )
|
||||||
|
{
|
||||||
|
if( !s_sdlWindow )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( s_controller )
|
||||||
|
SDL_GameControllerClose( s_controller );
|
||||||
|
|
||||||
|
SDL_GL_DeleteContext( s_sdlGlContext );
|
||||||
|
SDL_DestroyWindow( s_sdlWindow );
|
||||||
|
SDL_Quit();
|
||||||
|
|
||||||
|
s_sdlWindow = NULL;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
# include <OpenGL/gl3.h>
|
||||||
|
# include <SDL2/SDL.h>
|
||||||
|
#elif _WIN32
|
||||||
|
# define HAVE_M_PI
|
||||||
|
# include <GL/glew.h>
|
||||||
|
# include <SDL.h>
|
||||||
|
#else
|
||||||
|
# include <GL/glew.h>
|
||||||
|
# include <SDL2/SDL.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern void context_init( const char *title, int width, int height );
|
||||||
|
extern bool context_flip_frame_poll_events( void );
|
||||||
|
extern SDL_GameController *context_get_controller( void );
|
||||||
|
extern void context_terminate( void );
|
||||||
+450
@@ -0,0 +1,450 @@
|
|||||||
|
#define _CRT_SECURE_NO_WARNINGS 1 // for fopen
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "../src/libsm64.h"
|
||||||
|
|
||||||
|
#include "cglm.h"
|
||||||
|
#include "ns_clock.h"
|
||||||
|
#include "level.h"
|
||||||
|
#include "context.h"
|
||||||
|
|
||||||
|
static const int WINDOW_WIDTH = 1280;
|
||||||
|
static const int WINDOW_HEIGHT = 800;
|
||||||
|
|
||||||
|
typedef struct CollisionMesh
|
||||||
|
{
|
||||||
|
size_t num_vertices;
|
||||||
|
float *position;
|
||||||
|
float *normal;
|
||||||
|
uint16_t *index;
|
||||||
|
|
||||||
|
GLuint vao;
|
||||||
|
GLuint position_buffer;
|
||||||
|
GLuint normal_buffer;
|
||||||
|
}
|
||||||
|
CollisionMesh;
|
||||||
|
|
||||||
|
typedef struct MarioMesh
|
||||||
|
{
|
||||||
|
size_t num_vertices;
|
||||||
|
uint16_t *index;
|
||||||
|
|
||||||
|
GLuint vao;
|
||||||
|
GLuint position_buffer;
|
||||||
|
GLuint normal_buffer;
|
||||||
|
GLuint color_buffer;
|
||||||
|
GLuint uv_buffer;
|
||||||
|
}
|
||||||
|
MarioMesh;
|
||||||
|
|
||||||
|
typedef struct RenderState
|
||||||
|
{
|
||||||
|
CollisionMesh collision;
|
||||||
|
MarioMesh mario;
|
||||||
|
GLuint world_shader;
|
||||||
|
GLuint mario_shader;
|
||||||
|
GLuint mario_texture;
|
||||||
|
}
|
||||||
|
RenderState;
|
||||||
|
|
||||||
|
static const char *MARIO_SHADER =
|
||||||
|
"\n uniform mat4 view;"
|
||||||
|
"\n uniform mat4 projection;"
|
||||||
|
"\n uniform sampler2D marioTex;"
|
||||||
|
"\n "
|
||||||
|
"\n v2f vec3 v_color;"
|
||||||
|
"\n v2f vec3 v_normal;"
|
||||||
|
"\n v2f vec3 v_light;"
|
||||||
|
"\n v2f vec2 v_uv;"
|
||||||
|
"\n "
|
||||||
|
"\n #ifdef VERTEX"
|
||||||
|
"\n "
|
||||||
|
"\n layout(location = 0) in vec3 position;"
|
||||||
|
"\n layout(location = 1) in vec3 normal;"
|
||||||
|
"\n layout(location = 2) in vec3 color;"
|
||||||
|
"\n layout(location = 3) in vec2 uv;"
|
||||||
|
"\n "
|
||||||
|
"\n void main()"
|
||||||
|
"\n {"
|
||||||
|
"\n v_color = color;"
|
||||||
|
"\n v_normal = normal;"
|
||||||
|
"\n v_light = transpose( mat3( view )) * normalize( vec3( 1 ));"
|
||||||
|
"\n v_uv = uv;"
|
||||||
|
"\n "
|
||||||
|
"\n gl_Position = projection * view * vec4( position, 1. );"
|
||||||
|
"\n }"
|
||||||
|
"\n "
|
||||||
|
"\n #endif"
|
||||||
|
"\n #ifdef FRAGMENT"
|
||||||
|
"\n "
|
||||||
|
"\n out vec4 color;"
|
||||||
|
"\n "
|
||||||
|
"\n void main() "
|
||||||
|
"\n {"
|
||||||
|
"\n float light = .5 + .5 * clamp( dot( v_normal, v_light ), 0., 1. );"
|
||||||
|
"\n vec4 texColor = texture2D( marioTex, v_uv );"
|
||||||
|
"\n vec3 mainColor = mix( v_color, texColor.rgb, texColor.a ); // v_uv.x >= 0. ? texColor.a : 0. );"
|
||||||
|
"\n color = vec4( mainColor * light, 1 );"
|
||||||
|
"\n }"
|
||||||
|
"\n "
|
||||||
|
"\n #endif"
|
||||||
|
;
|
||||||
|
static const char *WORLD_SHADER =
|
||||||
|
"\n uniform mat4 model;"
|
||||||
|
"\n uniform mat4 view;"
|
||||||
|
"\n uniform mat4 projection;"
|
||||||
|
"\n uniform sampler2D tex;"
|
||||||
|
"\n "
|
||||||
|
"\n v2f vec3 v_normal;"
|
||||||
|
"\n v2f vec3 v_worldPos;"
|
||||||
|
"\n "
|
||||||
|
"\n #ifdef VERTEX"
|
||||||
|
"\n "
|
||||||
|
"\n layout(location = 0) in vec3 position;"
|
||||||
|
"\n layout(location = 1) in vec3 normal;"
|
||||||
|
"\n "
|
||||||
|
"\n void main()"
|
||||||
|
"\n {"
|
||||||
|
"\n v_normal = inverse(mat3(model)) * normal;"
|
||||||
|
"\n vec4 worldPos4 = model * vec4(position, 1.);"
|
||||||
|
"\n v_worldPos = worldPos4.xyz;"
|
||||||
|
"\n gl_Position = projection * view * worldPos4;"
|
||||||
|
"\n }"
|
||||||
|
"\n "
|
||||||
|
"\n #endif"
|
||||||
|
"\n #ifdef FRAGMENT"
|
||||||
|
"\n "
|
||||||
|
"\n vec3 tri( vec3 x )"
|
||||||
|
"\n {"
|
||||||
|
"\n return abs(x-floor(x)-.5);"
|
||||||
|
"\n } "
|
||||||
|
"\n float surfFunc( vec3 p )"
|
||||||
|
"\n {"
|
||||||
|
"\n float n = dot(tri(p*.15 + tri(p.yzx*.075)), vec3(.444));"
|
||||||
|
"\n p = p*1.5773 - n;"
|
||||||
|
"\n p.yz = vec2(p.y + p.z, p.z - p.y) * .866;"
|
||||||
|
"\n p.xz = vec2(p.x + p.z, p.z - p.x) * .866;"
|
||||||
|
"\n n += dot(tri(p*.225 + tri(p.yzx*.1125)), vec3(.222)); "
|
||||||
|
"\n return abs(n-.5)*1.9 + (1.-abs(sin(n*9.)))*.05;"
|
||||||
|
"\n }"
|
||||||
|
"\n "
|
||||||
|
"\n const vec3 light_x = vec3(-1.0, 0.4, 0.9);"
|
||||||
|
"\n "
|
||||||
|
"\n out vec4 color;"
|
||||||
|
"\n "
|
||||||
|
"\n void main() "
|
||||||
|
"\n {"
|
||||||
|
"\n float surfy = surfFunc( v_worldPos / 50. );"
|
||||||
|
"\n float brightness = smoothstep( .2, .3, surfy );"
|
||||||
|
"\n "
|
||||||
|
"\n color = vec4( (0.5 + 0.25 * brightness) * (.5+.5*v_normal), 1 );"
|
||||||
|
"\n }"
|
||||||
|
"\n "
|
||||||
|
"\n #endif"
|
||||||
|
;
|
||||||
|
|
||||||
|
uint8_t *utils_read_file_alloc( const char *path, size_t *fileLength )
|
||||||
|
{
|
||||||
|
FILE *f = fopen( path, "rb" );
|
||||||
|
|
||||||
|
if( !f ) return NULL;
|
||||||
|
|
||||||
|
fseek( f, 0, SEEK_END );
|
||||||
|
size_t length = (size_t)ftell( f );
|
||||||
|
rewind( f );
|
||||||
|
uint8_t *buffer = malloc( length + 1 );
|
||||||
|
fread( buffer, 1, length, f );
|
||||||
|
buffer[length] = 0;
|
||||||
|
fclose( f );
|
||||||
|
|
||||||
|
if( fileLength ) *fileLength = length;
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GLuint shader_compile( const char *shaderContents, size_t shaderContentsLength, GLenum shaderType )
|
||||||
|
{
|
||||||
|
const GLchar *shaderDefine = shaderType == GL_VERTEX_SHADER
|
||||||
|
? "\n#version 410\n#define VERTEX \n#define v2f out\n"
|
||||||
|
: "\n#version 410\n#define FRAGMENT\n#define v2f in \n";
|
||||||
|
|
||||||
|
const GLchar *shaderStrings[2] = { shaderDefine, shaderContents };
|
||||||
|
GLint shaderStringLengths[2] = { strlen( shaderDefine ), (GLint)shaderContentsLength };
|
||||||
|
|
||||||
|
GLuint shader = glCreateShader( shaderType );
|
||||||
|
glShaderSource( shader, 2, shaderStrings, shaderStringLengths );
|
||||||
|
glCompileShader( shader );
|
||||||
|
|
||||||
|
GLint isCompiled;
|
||||||
|
glGetShaderiv( shader, GL_COMPILE_STATUS, &isCompiled );
|
||||||
|
if( isCompiled == GL_FALSE )
|
||||||
|
{
|
||||||
|
GLint maxLength;
|
||||||
|
glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &maxLength );
|
||||||
|
char *log = (char*)malloc( maxLength );
|
||||||
|
glGetShaderInfoLog( shader, maxLength, &maxLength, log );
|
||||||
|
|
||||||
|
printf( "Error in shader: %s\n%s\n%s\n", log, shaderStrings[0], shaderStrings[1] );
|
||||||
|
exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GLuint shader_load( const char *shaderContents )
|
||||||
|
{
|
||||||
|
GLuint result;
|
||||||
|
GLuint vert = shader_compile( shaderContents, strlen( shaderContents ), GL_VERTEX_SHADER );
|
||||||
|
GLuint frag = shader_compile( shaderContents, strlen( shaderContents ), GL_FRAGMENT_SHADER );
|
||||||
|
|
||||||
|
GLuint ref = glCreateProgram();
|
||||||
|
glAttachShader( ref, vert );
|
||||||
|
glAttachShader( ref, frag );
|
||||||
|
glLinkProgram ( ref );
|
||||||
|
glDetachShader( ref, vert );
|
||||||
|
glDetachShader( ref, frag );
|
||||||
|
result = ref;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_collision_mesh( CollisionMesh *mesh )
|
||||||
|
{
|
||||||
|
mesh->num_vertices = 3 * surfaces_count;
|
||||||
|
mesh->position = malloc( sizeof( float ) * surfaces_count * 9 );
|
||||||
|
mesh->normal = malloc( sizeof( float ) * surfaces_count * 9 );
|
||||||
|
mesh->index = malloc( sizeof( uint16_t ) * surfaces_count * 3 );
|
||||||
|
|
||||||
|
for( size_t i = 0; i < surfaces_count; ++i )
|
||||||
|
{
|
||||||
|
const struct SM64Surface *surf = &surfaces[i];
|
||||||
|
|
||||||
|
float x1 = mesh->position[9*i+0] = surf->vertices[0][0];
|
||||||
|
float y1 = mesh->position[9*i+1] = surf->vertices[0][1];
|
||||||
|
float z1 = mesh->position[9*i+2] = surf->vertices[0][2];
|
||||||
|
float x2 = mesh->position[9*i+3] = surf->vertices[1][0];
|
||||||
|
float y2 = mesh->position[9*i+4] = surf->vertices[1][1];
|
||||||
|
float z2 = mesh->position[9*i+5] = surf->vertices[1][2];
|
||||||
|
float x3 = mesh->position[9*i+6] = surf->vertices[2][0];
|
||||||
|
float y3 = mesh->position[9*i+7] = surf->vertices[2][1];
|
||||||
|
float z3 = mesh->position[9*i+8] = surf->vertices[2][2];
|
||||||
|
|
||||||
|
float nx = (y2 - y1) * (z3 - z2) - (z2 - z1) * (y3 - y2);
|
||||||
|
float ny = (z2 - z1) * (x3 - x2) - (x2 - x1) * (z3 - z2);
|
||||||
|
float nz = (x2 - x1) * (y3 - y2) - (y2 - y1) * (x3 - x2);
|
||||||
|
float mag = sqrtf(nx * nx + ny * ny + nz * nz);
|
||||||
|
nx /= mag;
|
||||||
|
ny /= mag;
|
||||||
|
nz /= mag;
|
||||||
|
|
||||||
|
mesh->normal[9*i+0] = nx;
|
||||||
|
mesh->normal[9*i+1] = ny;
|
||||||
|
mesh->normal[9*i+2] = nz;
|
||||||
|
mesh->normal[9*i+3] = nx;
|
||||||
|
mesh->normal[9*i+4] = ny;
|
||||||
|
mesh->normal[9*i+5] = nz;
|
||||||
|
mesh->normal[9*i+6] = nx;
|
||||||
|
mesh->normal[9*i+7] = ny;
|
||||||
|
mesh->normal[9*i+8] = nz;
|
||||||
|
|
||||||
|
mesh->index[3*i+0] = 3*i+0;
|
||||||
|
mesh->index[3*i+1] = 3*i+1;
|
||||||
|
mesh->index[3*i+2] = 3*i+2;
|
||||||
|
}
|
||||||
|
|
||||||
|
glGenVertexArrays( 1, &mesh->vao );
|
||||||
|
glBindVertexArray( mesh->vao );
|
||||||
|
|
||||||
|
#define X( loc, buff, arr, type ) do { \
|
||||||
|
glGenBuffers( 1, &buff ); \
|
||||||
|
glBindBuffer( GL_ARRAY_BUFFER, buff ); \
|
||||||
|
glBufferData( GL_ARRAY_BUFFER, mesh->num_vertices*sizeof( type ), arr, GL_STATIC_DRAW ); \
|
||||||
|
glEnableVertexAttribArray( loc ); \
|
||||||
|
glVertexAttribPointer( loc, sizeof( type ) / sizeof( float ), GL_FLOAT, GL_FALSE, sizeof( type ), NULL ); \
|
||||||
|
} while( 0 )
|
||||||
|
|
||||||
|
X( 0, mesh->position_buffer, mesh->position, vec3 );
|
||||||
|
X( 1, mesh->normal_buffer, mesh->normal, vec3 );
|
||||||
|
|
||||||
|
#undef X
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_mario_mesh( MarioMesh *mesh, struct SM64MarioGeometryBuffers *marioGeo )
|
||||||
|
{
|
||||||
|
mesh->index = malloc( 3 * SM64_GEO_MAX_TRIANGLES * sizeof(uint16_t) );
|
||||||
|
for( int i = 0; i < 3 * SM64_GEO_MAX_TRIANGLES; ++i )
|
||||||
|
mesh->index[i] = i;
|
||||||
|
|
||||||
|
mesh->num_vertices = 3 * SM64_GEO_MAX_TRIANGLES;
|
||||||
|
|
||||||
|
glGenVertexArrays( 1, &mesh->vao );
|
||||||
|
glBindVertexArray( mesh->vao );
|
||||||
|
|
||||||
|
#define X( loc, buff, arr, type ) do { \
|
||||||
|
glGenBuffers( 1, &buff ); \
|
||||||
|
glBindBuffer( GL_ARRAY_BUFFER, buff ); \
|
||||||
|
glBufferData( GL_ARRAY_BUFFER, sizeof( type ) * 3 * SM64_GEO_MAX_TRIANGLES, arr, GL_DYNAMIC_DRAW ); \
|
||||||
|
glEnableVertexAttribArray( loc ); \
|
||||||
|
glVertexAttribPointer( loc, sizeof( type ) / sizeof( float ), GL_FLOAT, GL_FALSE, sizeof( type ), NULL ); \
|
||||||
|
} while( 0 )
|
||||||
|
|
||||||
|
X( 0, mesh->position_buffer, marioGeo->position, vec3 );
|
||||||
|
X( 1, mesh->normal_buffer, marioGeo->normal, vec3 );
|
||||||
|
X( 2, mesh->color_buffer, marioGeo->color, vec3 );
|
||||||
|
X( 3, mesh->uv_buffer, marioGeo->uv, vec2 );
|
||||||
|
|
||||||
|
#undef X
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_mario_mesh( MarioMesh *mesh, struct SM64MarioGeometryBuffers *marioGeo )
|
||||||
|
{
|
||||||
|
if( mesh->index == NULL )
|
||||||
|
load_mario_mesh( mesh, marioGeo );
|
||||||
|
|
||||||
|
mesh->num_vertices = 3 * marioGeo->numTrianglesUsed;
|
||||||
|
|
||||||
|
glBindBuffer( GL_ARRAY_BUFFER, mesh->position_buffer );
|
||||||
|
glBufferData( GL_ARRAY_BUFFER, sizeof( vec3 ) * 3 * SM64_GEO_MAX_TRIANGLES, marioGeo->position, GL_DYNAMIC_DRAW );
|
||||||
|
glBindBuffer( GL_ARRAY_BUFFER, mesh->normal_buffer );
|
||||||
|
glBufferData( GL_ARRAY_BUFFER, sizeof( vec3 ) * 3 * SM64_GEO_MAX_TRIANGLES, marioGeo->normal, GL_DYNAMIC_DRAW );
|
||||||
|
glBindBuffer( GL_ARRAY_BUFFER, mesh->color_buffer );
|
||||||
|
glBufferData( GL_ARRAY_BUFFER, sizeof( vec3 ) * 3 * SM64_GEO_MAX_TRIANGLES, marioGeo->color, GL_DYNAMIC_DRAW );
|
||||||
|
glBindBuffer( GL_ARRAY_BUFFER, mesh->uv_buffer );
|
||||||
|
glBufferData( GL_ARRAY_BUFFER, sizeof( vec2 ) * 3 * SM64_GEO_MAX_TRIANGLES, marioGeo->uv, GL_DYNAMIC_DRAW );
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_state_init( RenderState *renderState, uint8_t *marioTexture )
|
||||||
|
{
|
||||||
|
load_collision_mesh( &renderState->collision );
|
||||||
|
renderState->world_shader = shader_load( WORLD_SHADER );
|
||||||
|
renderState->mario_shader = shader_load( MARIO_SHADER );
|
||||||
|
|
||||||
|
glEnable( GL_CULL_FACE );
|
||||||
|
glCullFace( GL_BACK );
|
||||||
|
glClearColor( 0.2f, 0.2f, 0.2f, 1.0f );
|
||||||
|
glEnable( GL_DEPTH_TEST );
|
||||||
|
|
||||||
|
glGenTextures( 1, &renderState->mario_texture );
|
||||||
|
glBindTexture( GL_TEXTURE_2D, renderState->mario_texture );
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SM64_TEXTURE_WIDTH, SM64_TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, marioTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_draw( RenderState *renderState, const vec3 camPos, const struct SM64MarioState *marioState, struct SM64MarioGeometryBuffers *marioGeo )
|
||||||
|
{
|
||||||
|
update_mario_mesh( &renderState->mario, marioGeo );
|
||||||
|
|
||||||
|
mat4 model, view, projection;
|
||||||
|
glm_perspective( 45.0f, (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 100.0f, 20000.0f, projection );
|
||||||
|
glm_translate( view, (float*)camPos );
|
||||||
|
glm_lookat( (float*)camPos, (float*)marioState->position, (vec3){0,1,0}, view );
|
||||||
|
glm_mat4_identity( model );
|
||||||
|
|
||||||
|
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
|
||||||
|
|
||||||
|
glUseProgram( renderState->world_shader );
|
||||||
|
glBindVertexArray( renderState->collision.vao );
|
||||||
|
glUniformMatrix4fv( glGetUniformLocation( renderState->world_shader, "model" ), 1, GL_FALSE, (GLfloat*)model );
|
||||||
|
glUniformMatrix4fv( glGetUniformLocation( renderState->world_shader, "view" ), 1, GL_FALSE, (GLfloat*)view );
|
||||||
|
glUniformMatrix4fv( glGetUniformLocation( renderState->world_shader, "projection" ), 1, GL_FALSE, (GLfloat*)projection );
|
||||||
|
glDrawElements( GL_TRIANGLES, renderState->collision.num_vertices, GL_UNSIGNED_SHORT, renderState->collision.index );
|
||||||
|
|
||||||
|
glUseProgram( renderState->mario_shader );
|
||||||
|
glActiveTexture( GL_TEXTURE0 );
|
||||||
|
glBindTexture( GL_TEXTURE_2D, renderState->mario_texture );
|
||||||
|
glBindVertexArray( renderState->mario.vao );
|
||||||
|
glUniformMatrix4fv( glGetUniformLocation( renderState->mario_shader, "view" ), 1, GL_FALSE, (GLfloat*)view );
|
||||||
|
glUniformMatrix4fv( glGetUniformLocation( renderState->mario_shader, "projection" ), 1, GL_FALSE, (GLfloat*)projection );
|
||||||
|
glUniform1i( glGetUniformLocation( renderState->mario_shader, "marioTex" ), 0 );
|
||||||
|
glDrawElements( GL_TRIANGLES, renderState->mario.num_vertices, GL_UNSIGNED_SHORT, renderState->mario.index );
|
||||||
|
}
|
||||||
|
|
||||||
|
static float read_axis( int16_t val )
|
||||||
|
{
|
||||||
|
float result = (float)val / 32767.0f;
|
||||||
|
|
||||||
|
if( result < 0.2f && result > -0.2f )
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return result > 0.0f ? (result - 0.2f) / 0.8f : (result + 0.2f) / 0.8f;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main( void )
|
||||||
|
{
|
||||||
|
size_t romSize;
|
||||||
|
|
||||||
|
uint8_t *rom = utils_read_file_alloc( "baserom.us.z64", &romSize );
|
||||||
|
uint8_t *texture = malloc( 4 * SM64_TEXTURE_WIDTH * SM64_TEXTURE_HEIGHT );
|
||||||
|
|
||||||
|
sm64_global_init( rom, texture, NULL );
|
||||||
|
sm64_load_surfaces( 0, surfaces, surfaces_count );
|
||||||
|
sm64_mario_reset( 0, 1000, 0 );
|
||||||
|
|
||||||
|
free( rom );
|
||||||
|
|
||||||
|
RenderState renderState;
|
||||||
|
renderState.mario.index = NULL;
|
||||||
|
vec3 cameraPos = { 0, 0, 0 };
|
||||||
|
float cameraRot = 0.0f;
|
||||||
|
|
||||||
|
context_init( "libsm64", WINDOW_WIDTH, WINDOW_HEIGHT );
|
||||||
|
render_state_init( &renderState, texture );
|
||||||
|
|
||||||
|
struct timespec ts;
|
||||||
|
ts.tv_sec = 0;
|
||||||
|
ts.tv_nsec = 0;
|
||||||
|
|
||||||
|
struct SM64MarioInputs marioInputs;
|
||||||
|
struct SM64MarioState marioState;
|
||||||
|
struct SM64MarioGeometryBuffers marioGeometry;
|
||||||
|
|
||||||
|
marioGeometry.position = malloc( sizeof(float) * 9 * SM64_GEO_MAX_TRIANGLES );
|
||||||
|
marioGeometry.color = malloc( sizeof(float) * 9 * SM64_GEO_MAX_TRIANGLES );
|
||||||
|
marioGeometry.normal = malloc( sizeof(float) * 9 * SM64_GEO_MAX_TRIANGLES );
|
||||||
|
marioGeometry.uv = malloc( sizeof(float) * 6 * SM64_GEO_MAX_TRIANGLES );
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
uint64_t frameTopTime = ns_clock();
|
||||||
|
|
||||||
|
SDL_GameController *controller = context_get_controller();
|
||||||
|
float x_axis = read_axis( SDL_GameControllerGetAxis( controller, SDL_CONTROLLER_AXIS_LEFTX ));
|
||||||
|
float y_axis = read_axis( SDL_GameControllerGetAxis( controller, SDL_CONTROLLER_AXIS_LEFTY ));
|
||||||
|
float x0_axis = read_axis( SDL_GameControllerGetAxis( controller, SDL_CONTROLLER_AXIS_RIGHTX ));
|
||||||
|
|
||||||
|
cameraRot += 0.1f * x0_axis;
|
||||||
|
cameraPos[0] = marioState.position[0] + 1000.0f * cosf( cameraRot );
|
||||||
|
cameraPos[1] = marioState.position[1] + 200.0f;
|
||||||
|
cameraPos[2] = marioState.position[2] + 1000.0f * sinf( cameraRot );
|
||||||
|
|
||||||
|
marioInputs.buttonA = SDL_GameControllerGetButton( controller, 0 );
|
||||||
|
marioInputs.buttonB = SDL_GameControllerGetButton( controller, 2 );
|
||||||
|
marioInputs.buttonZ = SDL_GameControllerGetButton( controller, 9 );
|
||||||
|
marioInputs.camLookX = marioState.position[0] - cameraPos[0];
|
||||||
|
marioInputs.camLookZ = marioState.position[2] - cameraPos[2];
|
||||||
|
marioInputs.stickX = x_axis;
|
||||||
|
marioInputs.stickY = y_axis;
|
||||||
|
|
||||||
|
sm64_mario_tick( &marioInputs, &marioState, &marioGeometry );
|
||||||
|
|
||||||
|
render_draw( &renderState, cameraPos, &marioState, &marioGeometry );
|
||||||
|
|
||||||
|
ts.tv_nsec = 33333333 - (ns_clock() - frameTopTime);
|
||||||
|
nanosleep( &ts, &ts );
|
||||||
|
}
|
||||||
|
while( context_flip_frame_poll_events() );
|
||||||
|
|
||||||
|
sm64_global_terminate();
|
||||||
|
context_terminate();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
//
|
||||||
|
// From: http://roxlu.com/2014/047/high-resolution-timer-function-in-c-c--
|
||||||
|
/* ----------------------------------------------------------------------- */
|
||||||
|
/*
|
||||||
|
Easy embeddable cross-platform high resolution timer function. For each
|
||||||
|
platform we select the high resolution timer. You can call the 'ns()'
|
||||||
|
function in your file after embedding this.
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#if defined(__linux)
|
||||||
|
# define HAVE_POSIX_TIMER
|
||||||
|
# include <time.h>
|
||||||
|
# ifdef CLOCK_MONOTONIC
|
||||||
|
# define CLOCKID CLOCK_MONOTONIC
|
||||||
|
# else
|
||||||
|
# define CLOCKID CLOCK_REALTIME
|
||||||
|
# endif
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
# define HAVE_MACH_TIMER
|
||||||
|
# include <mach/mach_time.h>
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
# include <windows.h>
|
||||||
|
#endif
|
||||||
|
static uint64_t ns_clock() {
|
||||||
|
static uint64_t is_init = 0;
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
static mach_timebase_info_data_t info;
|
||||||
|
if (0 == is_init) {
|
||||||
|
mach_timebase_info(&info);
|
||||||
|
is_init = 1;
|
||||||
|
}
|
||||||
|
uint64_t now;
|
||||||
|
now = mach_absolute_time();
|
||||||
|
now *= info.numer;
|
||||||
|
now /= info.denom;
|
||||||
|
return now;
|
||||||
|
#elif defined(__linux)
|
||||||
|
static struct timespec linux_rate;
|
||||||
|
if (0 == is_init) {
|
||||||
|
clock_getres(CLOCKID, &linux_rate);
|
||||||
|
is_init = 1;
|
||||||
|
}
|
||||||
|
uint64_t now;
|
||||||
|
struct timespec spec;
|
||||||
|
clock_gettime(CLOCKID, &spec);
|
||||||
|
now = spec.tv_sec * 1.0e9 + spec.tv_nsec;
|
||||||
|
return now;
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
static LARGE_INTEGER win_frequency;
|
||||||
|
if (0 == is_init) {
|
||||||
|
QueryPerformanceFrequency(&win_frequency);
|
||||||
|
is_init = 1;
|
||||||
|
}
|
||||||
|
LARGE_INTEGER now;
|
||||||
|
QueryPerformanceCounter(&now);
|
||||||
|
return (uint64_t) ((1e9 * now.QuadPart) / win_frequency.QuadPart);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user