Files
libsm64/test/main.cpp
T
NepuShiro a499b4f032
Build libsm64 / Build libsm64 for linux (push) Successful in 31s
Build libsm64 / Build libsm64 for web (push) Successful in 32s
Build libsm64 / Build libsm64 for windows (push) Successful in 24s
Implement Rumble
2026-05-25 12:34:52 -05:00

300 lines
8.0 KiB
C++

#define _CRT_SECURE_NO_WARNINGS 1 // for fopen
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>
#include "../src/libsm64.h"
extern "C"
{
#define SDL_MAIN_HANDLED
#include "level.h"
#include "context.h"
#include "renderer.h"
#include "gl33core/gl33core_renderer.h"
#include "gl20/gl20_renderer.h"
}
#include "audio.h"
static SDL_Haptic *gHaptic = nullptr;
static void InitHaptics(SDL_GameController *controller)
{
if (controller == nullptr || gHaptic != nullptr)
{
return;
}
SDL_Joystick *joy = SDL_GameControllerGetJoystick(controller);
if (joy == nullptr)
{
return;
}
gHaptic = SDL_HapticOpenFromJoystick(joy);
if (gHaptic == nullptr)
{
return;
}
if (SDL_HapticRumbleInit(gHaptic) != 0)
{
SDL_HapticClose(gHaptic);
gHaptic = nullptr;
}
}
uint8_t *utils_read_file_alloc(const char *path, size_t *fileLength)
{
FILE *f = fopen(path, "rb");
if (!f) return nullptr;
fseek(f, 0, SEEK_END);
size_t length = (size_t)ftell(f);
rewind(f);
uint8_t *buffer = (uint8_t *)malloc(length + 1);
fread(buffer, 1, length, f);
buffer[length] = 0;
fclose(f);
if (fileLength) *fileLength = length;
return buffer;
}
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;
}
float lerp(float a, float b, float amount)
{
return a + (b - a) * amount;
}
static void DebugPrint(const char *message)
{
if (message == nullptr)
{
return;
}
printf("[SM64] %s\n", message);
fflush(stdout);
}
static void Rumble(int32_t marioId, int16_t level, int16_t time)
{
if (gHaptic == nullptr)
{
return;
}
float strength = (float)level;
Uint32 duration = (Uint32)time * 2;
SDL_HapticRumblePlay(gHaptic, strength, duration);
}
int main(void)
{
size_t romSize;
uint8_t *rom = utils_read_file_alloc("baserom.us.z64", &romSize);
if (rom == nullptr)
{
printf("\nFailed to read ROM file \"baserom.us.z64\"\n\n");
return 1;
}
uint8_t *texture = (uint8_t *)malloc(4 * SM64_TEXTURE_WIDTH * SM64_TEXTURE_HEIGHT);
sm64_global_terminate();
sm64_global_init(rom, texture);
sm64_audio_init(rom);
// sm64_register_debug_print_function(DebugPrint);
sm64_register_rumble_callback_function(Rumble);
sm64_static_surfaces_load(surfaces, surfaces_count);
int32_t marioId = sm64_mario_create(0, 1000, 0);
printf("MarioID: %i\n", marioId);
free(rom);
RenderState renderState;
renderState.mario.index = nullptr;
vec3 cameraPos = {0, 0, 0};
float cameraRot = 0.0f;
struct Renderer *renderer;
int major, minor;
#ifdef GL33_CORE
major = 3; minor = 3;
renderer = &gl33core_renderer;
#else
major = 2;
minor = 0;
renderer = &gl20_renderer;
#endif
context_init("libsm64", 1280, 720, major, minor);
renderer->init(&renderState, texture);
struct SM64MarioInputs marioInputs;
struct SM64MarioState marioState;
struct SM64MarioGeometryBuffers marioGeometry;
// interpolation
float lastPos[3], currPos[3];
float lastGeoPos[9 * SM64_GEO_MAX_TRIANGLES], currGeoPos[9 * SM64_GEO_MAX_TRIANGLES];
marioGeometry.position = (float *)malloc(sizeof(float) * 9 * SM64_GEO_MAX_TRIANGLES);
marioGeometry.color = (float *)malloc(sizeof(float) * 9 * SM64_GEO_MAX_TRIANGLES);
marioGeometry.normal = (float *)malloc(sizeof(float) * 9 * SM64_GEO_MAX_TRIANGLES);
marioGeometry.uv = (float *)malloc(sizeof(float) * 6 * SM64_GEO_MAX_TRIANGLES);
marioGeometry.numTrianglesUsed = 0;
float tick = 0;
uint32_t lastTicks = SDL_GetTicks();
audio_init();
sm64_play_music(0, 0x05 | 0x80, 0); // from decomp/include/seq_ids.h: SEQ_LEVEL_WATER | SEQ_VARIATION
do
{
float dt = (SDL_GetTicks() - lastTicks) / 1000.f;
lastTicks = SDL_GetTicks();
tick += dt;
SDL_GameController *controller = context_get_controller();
InitHaptics(controller);
float x_axis, y_axis, x0_axis;
if (!controller) // keyboard
{
const Uint8 *state = SDL_GetKeyboardState(nullptr);
float dir;
float spd = 0;
if (state[SDL_SCANCODE_UP] && state[SDL_SCANCODE_RIGHT])
{
dir = -M_PI * 0.25f;
spd = 1;
}
else if (state[SDL_SCANCODE_UP] && state[SDL_SCANCODE_LEFT])
{
dir = -M_PI * 0.75f;
spd = 1;
}
else if (state[SDL_SCANCODE_DOWN] && state[SDL_SCANCODE_RIGHT])
{
dir = M_PI * 0.25f;
spd = 1;
}
else if (state[SDL_SCANCODE_DOWN] && state[SDL_SCANCODE_LEFT])
{
dir = M_PI * 0.75f;
spd = 1;
}
else if (state[SDL_SCANCODE_UP])
{
dir = -M_PI * 0.5f;
spd = 1;
}
else if (state[SDL_SCANCODE_DOWN])
{
dir = M_PI * 0.5f;
spd = 1;
}
else if (state[SDL_SCANCODE_LEFT])
{
dir = M_PI;
spd = 1;
}
else if (state[SDL_SCANCODE_RIGHT])
{
dir = 0;
spd = 1;
}
x_axis = cosf(dir) * spd;
y_axis = sinf(dir) * spd;
x0_axis = state[SDL_SCANCODE_LSHIFT] ? 1 : state[SDL_SCANCODE_RSHIFT] ? -1 : 0;
marioInputs.buttonA = state[SDL_SCANCODE_X];
marioInputs.buttonB = state[SDL_SCANCODE_C];
marioInputs.buttonZ = state[SDL_SCANCODE_Z];
}
else
{
x_axis = read_axis(SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX));
y_axis = read_axis(SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY));
x0_axis = read_axis(SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX));
marioInputs.buttonA = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_A);
marioInputs.buttonB = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_X);
marioInputs.buttonZ = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
}
cameraRot += x0_axis * dt * 2;
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.camLookX = marioState.position[0] - cameraPos[0];
marioInputs.camLookZ = marioState.position[2] - cameraPos[2];
marioInputs.stickX = x_axis;
marioInputs.stickY = y_axis;
while (tick >= 1.f / 30)
{
memcpy(lastPos, currPos, sizeof(currPos));
memcpy(lastGeoPos, currGeoPos, sizeof(currGeoPos));
tick -= 1.f / 30;
sm64_mario_tick(marioId, &marioInputs, &marioState, &marioGeometry);
memcpy(currPos, marioState.position, sizeof(currPos));
memcpy(currGeoPos, marioGeometry.position, sizeof(currGeoPos));
}
for (int i = 0; i < 3; i++) marioState.position[i] = lerp(lastPos[i], currPos[i], tick / (1.f / 30));
for (int i = 0; i < marioGeometry.numTrianglesUsed * 9; i++) marioGeometry.position[i] = lerp(lastGeoPos[i], currGeoPos[i], tick / (1.f / 30));
renderer->draw(&renderState, cameraPos, &marioState, &marioGeometry);
}
while (context_flip_frame_poll_events());
sm64_stop_background_music(sm64_get_current_background_music());
sm64_global_terminate();
context_terminate();
if (gHaptic != nullptr)
{
SDL_HapticClose(gHaptic);
gHaptic = nullptr;
}
return 0;
}