Compare commits

...

13 Commits

Author SHA1 Message Date
097c8c00f9 Fixed flickering
Some checks failed
Build Dusk / run-tests (push) Successful in 1m42s
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-dolphin (push) Has been cancelled
2026-02-06 13:31:15 -06:00
aa5b41fe31 First texture rendering (if broken) 2026-02-06 12:48:49 -06:00
0d56859d94 Alpha textures 2026-02-05 23:28:26 -06:00
1af5f238e4 RGBA textures 2026-02-05 21:57:56 -06:00
dd697d5650 Emu vs Real! 2026-02-05 08:43:49 -06:00
5cf299a1c7 Mesh working, lua broken 2026-02-05 08:21:19 -06:00
67bf825cc9 Camera 2026-02-05 00:42:04 -06:00
56e1696cd4 Asset loading 2026-02-04 21:52:14 -06:00
d955fb6430 Debugging functions. 2026-02-04 18:32:20 -06:00
dd910a31aa Actually compiled 2026-02-04 17:44:53 -06:00
708c4d0ec3 "Improved" 2026-02-04 15:54:09 -06:00
ad13d6c6a1 SDL2 example builds. 2026-02-04 11:17:25 -06:00
1c32158142 DOlphin progress 2026-02-04 10:16:16 -06:00
54 changed files with 1328 additions and 211 deletions

5
.ci/dolphin/Dockerfile Normal file
View File

@@ -0,0 +1,5 @@
FROM devkitpro/devkitppc
RUN apt update && \
apt install -y python3 python3-pip python3-polib python3-pil python3-dotenv python3-pyqt5 python3-opengl && \
dkp-pacman -S --needed --noconfirm gamecube-sdl2 ppc-liblzma ppc-libzip

12
.ci/dolphin/build-gamecube.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
docker build -t myapp:latest -f .ci/dolphin/Dockerfile .
docker run -it -v ./:/workdir myapp:latest /bin/bash -c ' \
export PATH="$DEVKITPPC/bin:$PATH" && \
cd /workdir && \
rm -rf build-gamecube && \
mkdir -p build-gamecube && \
cmake -S. -Bbuild-gamecube -DDUSK_TARGET_SYSTEM=gamecube -DCMAKE_TOOLCHAIN_FILE="$DEVKITPRO/cmake/GameCube.cmake" && \
cd build-gamecube && \
make VERBOSE=1
'
# docker run -it -v ./:/workdir myapp:latest /bin/bash

13
.ci/dolphin/build-wii.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
docker build -t myapp:latest -f .ci/dolphin/Dockerfile .
docker run -it -v ./:/workdir myapp:latest /bin/bash -c ' \
export PATH="$DEVKITPPC/bin:$PATH" && \
cd /workdir && \
rm -rf build-wii && \
mkdir -p build-wii && \
cmake -S. -Bbuild-wii -DDUSK_TARGET_SYSTEM=wii -DCMAKE_TOOLCHAIN_FILE="$DEVKITPRO/cmake/Wii.cmake" && \
cd build-wii && \
make VERBOSE=1 && \
mv ./Dusk.dol ./boot.dol
'
# docker run -it -v ./:/workdir myapp:latest /bin/bash

View File

@@ -71,4 +71,17 @@ jobs:
with:
name: dusk-psp
path: build/gitea/
if-no-files-found: error
if-no-files-found: error
build-dolphin:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Install dependencies
run: |
apt-get update
apt-get install -y docker.io
- name: Docker test
run: |
./.ci/dolphin/build-gamecube.sh

5
.gitignore vendored
View File

@@ -101,4 +101,7 @@ yarn-error.log
yarn.lock
.editor
.venv
.venv
/build2
/build*

View File

@@ -4,18 +4,13 @@
# https://opensource.org/licenses/MIT
# Setup
cmake_minimum_required(VERSION 3.13)
cmake_minimum_required(VERSION 3.18)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
option(ENABLE_TESTS "Enable tests" ON)
# Set target system
if(NOT DEFINED DUSK_TARGET_SYSTEM)
set(DUSK_TARGET_SYSTEM "linux")
# set(DUSK_TARGET_SYSTEM "psp")
endif()
option(ENABLE_TESTS "Enable tests" OFF)
# Prep cache
set(DUSK_CACHE_TARGET "dusk-target")
@@ -42,28 +37,48 @@ file(MAKE_DIRECTORY ${DUSK_GENERATED_HEADERS_DIR})
# Find packages
find_package(Python3 COMPONENTS Interpreter REQUIRED)
# Set target system
# message(FATAL_ERROR "DISABLED FOR NOW2 ${DUSK_TARGET_SYSTEM}")
if(NOT DEFINED DUSK_TARGET_SYSTEM)
set(DUSK_TARGET_SYSTEM "linux")
# set(DUSK_TARGET_SYSTEM "psp")
endif()
# Toolchains
if(DUSK_TARGET_SYSTEM STREQUAL "psp")
find_package(pspsdk REQUIRED)
endif()
if(DUSK_TARGET_SYSTEM STREQUAL "gamecube" OR DUSK_TARGET_SYSTEM STREQUAL "wii")
# Override to make library and binary be the same.
set(DUSK_LIBRARY_TARGET_NAME "${DUSK_LIBRARY_TARGET_NAME}.elf" CACHE INTERNAL ${DUSK_CACHE_TARGET})
endif()
# Init Project.
project(${DUSK_LIBRARY_TARGET_NAME}
VERSION 1.0.0
LANGUAGES C
LANGUAGES C CXX
)
# MainLibrary
add_library(${DUSK_LIBRARY_TARGET_NAME})
# Either, create library and binary separately (used for tests), or make them
# one in the same so all code is in the binary.
if(ENABLE_TESTS)
# MainLibrary
add_library(${DUSK_LIBRARY_TARGET_NAME} STATIC)
# Binary Executable
add_executable(${DUSK_BINARY_TARGET_NAME})
# Binary Executable
add_executable(${DUSK_BINARY_TARGET_NAME} ${DUSK_SOURCES_DIR}/null.c)
# Link library to binary and test
target_link_libraries(${DUSK_BINARY_TARGET_NAME}
PUBLIC
${DUSK_LIBRARY_TARGET_NAME}
)
# Link library to binary
target_link_libraries(${DUSK_BINARY_TARGET_NAME}
PUBLIC
${DUSK_LIBRARY_TARGET_NAME}
)
else()
set(DUSK_BINARY_TARGET_NAME "${DUSK_LIBRARY_TARGET_NAME}" CACHE INTERNAL ${DUSK_CACHE_TARGET})
add_executable(${DUSK_BINARY_TARGET_NAME} ${DUSK_SOURCES_DIR}/null.c)
endif()
# Add tools
add_subdirectory(tools)
@@ -77,8 +92,10 @@ if(DUSK_TARGET_SYSTEM STREQUAL "linux")
find_package(OpenGL REQUIRED)
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
SDL2
pthread
OpenGL::GL
GL
m
)
elseif(DUSK_TARGET_SYSTEM STREQUAL "psp")
@@ -87,6 +104,7 @@ elseif(DUSK_TARGET_SYSTEM STREQUAL "psp")
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
${SDL2_LIBRARIES}
SDL2
pthread
OpenGL::GL
zip
bz2
@@ -94,18 +112,61 @@ elseif(DUSK_TARGET_SYSTEM STREQUAL "psp")
mbedtls
mbedcrypto
lzma
m
)
target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
${SDL2_INCLUDE_DIRS}
)
elseif(DUSK_TARGET_SYSTEM STREQUAL "gamecube" OR DUSK_TARGET_SYSTEM STREQUAL "wii")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fno-exceptions")
configure_file(opengl.pc.in opengl.pc @ONLY)
find_package(PkgConfig REQUIRED)
pkg_check_modules(zip IMPORTED_TARGET libzip)
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DOLPHIN
)
# Custom flags for cglm
set(CGLM_SHARED OFF CACHE BOOL "Build cglm shared" FORCE)
set(CGLM_STATIC ON CACHE BOOL "Build cglm static" FORCE)
find_package(cglm REQUIRED)
# Compile lua
include(FetchContent)
FetchContent_Declare(
liblua
URL https://www.lua.org/ftp/lua-5.5.0.tar.gz
)
FetchContent_MakeAvailable(liblua)
set(LUA_SRC_DIR "${liblua_SOURCE_DIR}/src")
set(LUA_C_FILES
lapi.c lauxlib.c lbaselib.c lcode.c lcorolib.c lctype.c ldblib.c ldebug.c
ldo.c ldump.c lfunc.c lgc.c linit.c liolib.c llex.c lmathlib.c lmem.c
loadlib.c lobject.c lopcodes.c loslib.c lparser.c lstate.c lstring.c
lstrlib.c ltable.c ltablib.c ltm.c lundump.c lutf8lib.c lvm.c lzio.c
)
list(TRANSFORM LUA_C_FILES PREPEND "${LUA_SRC_DIR}/")
add_library(liblua STATIC ${LUA_C_FILES})
target_include_directories(liblua PUBLIC "${LUA_SRC_DIR}")
target_compile_definitions(liblua PRIVATE LUA_USE_C89)
add_library(lua::lua ALIAS liblua)
set(Lua_FOUND TRUE CACHE BOOL "Lua found" FORCE)
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
cglm
liblua
m
fat
PkgConfig::zip
)
endif()
# Force turn tests off for now
set(ENABLE_TESTS OFF CACHE BOOL "Enable tests" FORCE)
# Add code
add_subdirectory(src)
add_subdirectory(${DUSK_SOURCES_DIR})
# Handle tests
if(ENABLE_TESTS)
@@ -141,4 +202,12 @@ if(DUSK_TARGET_SYSTEM STREQUAL "psp")
PSAR_PATH ${DUSK_BUILD_DIR}/dusk.dsk
VERSION 01.00
)
elseif(DUSK_TARGET_SYSTEM STREQUAL "gamecube" OR DUSK_TARGET_SYSTEM STREQUAL "wii")
set(DUSK_BINARY_TARGET_NAME_DOL "${DUSK_BUILD_DIR}/Dusk.dol")
add_custom_command(TARGET ${DUSK_BINARY_TARGET_NAME} POST_BUILD
COMMAND elf2dol
"$<TARGET_FILE:${DUSK_BINARY_TARGET_NAME}>"
"${DUSK_BINARY_TARGET_NAME_DOL}"
COMMENT "Generating ${DUSK_BINARY_TARGET_NAME_DOL} from ${DUSK_BINARY_TARGET_NAME}"
)
endif()

View File

@@ -8,7 +8,7 @@
#include "uiframe.h"
#include "display/spritebatch.h"
#include "assert/assert.h"
#include <math.h>
#include "util/math.h"
void uiFrameDraw(
const float_t x,

View File

@@ -16,6 +16,21 @@ if PLATFORM == "psp" then
inputBind("lstick_down", INPUT_ACTION_DOWN)
inputBind("lstick_left", INPUT_ACTION_LEFT)
inputBind("lstick_right", INPUT_ACTION_RIGHT)
elseif PLATFORM == "gamecube" then
-- inputBind("start", INPUT_ACTION_RAGEQUIT)
-- inputBind("dpad_up", INPUT_ACTION_UP)
-- inputBind("dpad_down", INPUT_ACTION_DOWN)
-- inputBind("dpad_left", INPUT_ACTION_LEFT)
-- inputBind("dpad_right", INPUT_ACTION_RIGHT)
-- inputBind("button_b", INPUT_ACTION_CANCEL)
-- inputBind("button_a", INPUT_ACTION_ACCEPT)
-- inputBind("button_start", INPUT_ACTION_RAGEQUIT)
-- inputBind("lstick_up", INPUT_ACTION_UP)
-- inputBind("lstick_down", INPUT_ACTION_DOWN)
-- inputBind("lstick_left", INPUT_ACTION_LEFT)
-- inputBind("lstick_right", INPUT_ACTION_RIGHT)
else
if INPUT_KEYBOARD then
inputBind("w", INPUT_ACTION_UP)
@@ -37,5 +52,5 @@ else
end
end
localeSet(DUSK_LOCALE_EN_US)
-- localeSet(DUSK_LOCALE_EN_US)
sceneSet('scene/initial.dsf')

View File

@@ -8,37 +8,59 @@ module('time')
module('map')
module('glm')
screenSetBackground(colorBlack())
mapLoad('map/testmap/testmap.dmf')
camera = cameraCreate(CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC)
screenSetBackground(colorLime())
mapCamera = cameraCreate()
text = "Hello, Dusk!"
text = "Hello World"
x = -35
y = 0
function sceneDispose()
end
function sceneUpdate()
end
function sceneRender()
-- Map Test
mapCamera.position = vec3(50, 50, 50)
cameraPushMatrix(mapCamera)
mapCamera.position = vec3(300, 300, 300)
mapRender()
textDraw(x, y, text, colorBlue())
spriteBatchFlush()
cameraPopMatrix()
end
-- UI Test
cameraPushMatrix(camera)
camera.bottom = screenGetHeight()
camera.right = screenGetWidth()
width, height = textMeasure(text)
x = (screenGetWidth() - width)
x = math.sin(TIME.time * 2) * (x / 2) + (x / 2)
y = (screenGetHeight() - height) / 2
y = math.cos(TIME.time * 3) * (y) + (y)
textDraw(x, y, text, colorMagenta())
-- screenSetBackground(colorBlack())
-- mapLoad('map/testmap/testmap.dmf')
-- camera = cameraCreate(CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC)
-- mapCamera = cameraCreate()
cameraPopMatrix()
end
-- text = "Hello World"
-- function sceneDispose()
-- end
-- function sceneUpdate()
-- end
-- function sceneRender()
-- -- Map Test
-- mapCamera.position = vec3(300, 300, 300)
-- cameraPushMatrix(mapCamera)
-- mapRender()
-- cameraPopMatrix()
-- -- UI Test
-- cameraPushMatrix(camera)
-- camera.bottom = screenGetHeight()
-- camera.right = screenGetWidth()
-- width, height = textMeasure(text)
-- x = (screenGetWidth() - width)
-- x = math.sin(TIME.time * 2) * (x / 2) + (x / 2)
-- y = (screenGetHeight() - height) / 2
-- y = math.cos(TIME.time * 3) * (y) + (y)
-- textDraw(x, y, text, colorMagenta())
-- cameraPopMatrix()
-- end

View File

@@ -3,4 +3,5 @@
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
add_asset(TILESET minogram.png type=PALETTIZED tileWidth=6 tileHeight=10 columns=16 rows=6)# Fixes PSP rendering
add_asset(TILESET minogram.png type=ALPHA tileWidth=6 tileHeight=10 columns=16 rows=6)# Fixes PSP rendering
# add_asset(TILESET minogram.png type=PALETTIZED tileWidth=6 tileHeight=10 columns=16 rows=6)# Fixes PSP rendering

View File

@@ -4,6 +4,7 @@
# https://opensource.org/licenses/MIT
include(FetchContent)
FetchContent_Declare(
cglm
GIT_REPOSITORY https://github.com/recp/cglm.git

10
opengl.pc.in Normal file
View File

@@ -0,0 +1,10 @@
prefix=@CMAKE_INSTALL_PREFIX@
libdir=${prefix}/lib
includedir=${prefix}/include
Name: OpenGL
Description: OpenGL (without GLX) library and headers.
Version: @PROJECT_VERSION@
Libs.private: -lstdc++ -logc -lm
Libs: -L${libdir} -lopengx
Cflags: -I${includedir}

View File

@@ -3,29 +3,31 @@
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
find_package(cglm REQUIRED)
find_package(libzip REQUIRED)
find_package(Lua REQUIRED)
if(NOT cglm_FOUND)
find_package(cglm REQUIRED)
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC cglm)
endif()
if(Lua_FOUND AND NOT TARGET Lua::Lua)
add_library(Lua::Lua INTERFACE IMPORTED)
set_target_properties(
Lua::Lua
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${LUA_LIBRARIES}"
)
if(NOT libzip_FOUND)
find_package(libzip REQUIRED)
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC zip)
endif()
if(NOT Lua_FOUND)
find_package(Lua REQUIRED)
if(Lua_FOUND AND NOT TARGET Lua::Lua)
add_library(Lua::Lua INTERFACE IMPORTED)
set_target_properties(
Lua::Lua
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${LUA_LIBRARIES}"
)
endif()
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC Lua::Lua)
endif()
# Libs
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
m
cglm
zip
pthread
Lua::Lua
)
# Includes
target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
@@ -63,7 +65,10 @@ add_subdirectory(map)
add_subdirectory(scene)
add_subdirectory(script)
add_subdirectory(story)
add_subdirectory(thread)
add_subdirectory(time)
add_subdirectory(ui)
add_subdirectory(util)
add_subdirectory(util)
if(DUSK_TARGET_SYSTEM STREQUAL "linux" OR DUSK_TARGET_SYSTEM STREQUAL "psp")
add_subdirectory(thread)
endif()

View File

@@ -37,6 +37,7 @@
line,
message
);
debugFlush();
abort();
}

View File

@@ -12,10 +12,66 @@
#include "asset/assettype.h"
#include "engine/engine.h"
#include "debug/debug.h"
#include "util/string.h"
errorret_t assetInit(void) {
memoryZero(&ASSET, sizeof(asset_t));
#if DOLPHIN
// Init FAT driver.
if(!fatInitDefault()) errorThrow("Failed to initialize FAT filesystem.");
char_t **dolphinSearchPath = (char_t **)ASSET_DOLPHIN_PATHS;
char_t foundPath[FILENAME_MAX];
foundPath[0] = '\0';
do {
// Try open dir
DIR *pdir = opendir(*dolphinSearchPath);
if(pdir == NULL) continue;
// Scan if file is present
while(true) {
struct dirent* pent = readdir(pdir);
if(pent == NULL) break;
if(stringCompareInsensitive(pent->d_name, ASSET_FILE) != 0) {
continue;
}
// Copy out filename
snprintf(
foundPath,
FILENAME_MAX,
"%s/%s",
*dolphinSearchPath,
ASSET_FILE
);
break;
}
// Close dir.
closedir(pdir);
// Did we find the file here?
if(foundPath[0] != '\0') break;
} while(*(++dolphinSearchPath) != NULL);
if(foundPath[0] != '\0') {
}
// Did we find the asset file?
if(foundPath[0] == '\0') {
errorThrow("Failed to find asset file on FAT filesystem.");
}
ASSET.zip = zip_open(foundPath, ZIP_RDONLY, NULL);
if(ASSET.zip == NULL) {
errorThrow("Failed to open asset file on FAT filesystem.");
}
errorOk();
#endif
// Engine may have been provided the launch path
if(ENGINE.argc > 0) {
// Get the directory of the executable
@@ -177,20 +233,42 @@ errorret_t assetLoad(const char_t *filename, void *output) {
assertStrLenMax(filename, FILENAME_MAX, "Filename too long.");
assertNotNull(output, "Output pointer cannot be NULL.");
// Get file size of the asset.
zip_stat_t st;
zip_stat_init(&st);
if(!zip_stat(ASSET.zip, filename, 0, &st) == 0) {
errorThrow("Failed to stat asset file: %s", filename);
}
// Minimum file size.
zip_int64_t fileSize = (zip_int64_t)st.size;
if(fileSize < sizeof(assetheader_t)) {
errorThrow("Asset file too small to contain header: %s", filename);
}
// Try to open the file
zip_file_t *file = zip_fopen(ASSET.zip, filename, 0);
if(file == NULL) {
errorThrow("Failed to open asset file: %s", filename);
}
// Read the header.
zip_int64_t bytesRemaining = fileSize;
assetheader_t header;
memoryZero(&header, sizeof(assetheader_t));
zip_int64_t bytesRead = zip_fread(file, &header, sizeof(assetheader_t));
if(bytesRead != sizeof(assetheader_t)) {
zip_int64_t bytesRead = zip_fread(
file,
&header,
(zip_uint64_t)sizeof(assetheader_t)
);
if((size_t)bytesRead != sizeof(assetheader_t)) {
zip_fclose(file);
errorThrow("Failed to read asset header for: %s", filename);
}
bytesRemaining -= (zip_uint64_t)bytesRead;
assertTrue(sizeof(assetheader_t) == ASSET_HEADER_SIZE, "Asset header size mismatch.");
assertTrue(bytesRead == ASSET_HEADER_SIZE, "Asset header read size mismatch.");
// Find the asset type based on the header
const assettypedef_t *def = NULL;
@@ -220,20 +298,44 @@ errorret_t assetLoad(const char_t *filename, void *output) {
switch(def->loadStrategy) {
case ASSET_LOAD_STRAT_ENTIRE:
assertNotNull(def->entire, "Asset load function cannot be NULL.");
void *data = memoryAllocate(def->dataSize);
bytesRead = zip_fread(file, data, def->dataSize);
if(bytesRead == 0 || bytesRead > def->dataSize) {
// Must have more to read
if(bytesRemaining <= 0) {
zip_fclose(file);
errorThrow("No data remaining to read for asset: %s", filename);
}
if(bytesRemaining > def->dataSize) {
zip_fclose(file);
errorThrow(
"Asset file has too much data remaining after header: %s",
filename
);
}
// Create space to read the entire asset data
void *data = memoryAllocate(bytesRemaining);
if(!data) {
zip_fclose(file);
errorThrow("Failed to allocate memory for asset data of file: %s", filename);
}
// Read in the asset data.
bytesRead = zip_fread(file, data, bytesRemaining);
if(bytesRead == 0 || bytesRead > bytesRemaining) {
memoryFree(data);
zip_fclose(file);
errorThrow("Failed to read asset data for file: %s", filename);
}
bytesRemaining -= bytesRead;
// Close the file now we have the data
zip_fclose(file);
// Pass to the asset type loader
errorret_t ret = def->entire(data, output);
memoryFree(data);
errorChain(ret);
break;

View File

@@ -9,27 +9,54 @@
#include "error/error.h"
#include "assettype.h"
#if ASSET_TYPE == wad
#if PSP
#define ASSET_PBP_READ_PBP_FROM_HOST 0
#define ASSET_PBP_SIGNATURE_SIZE 4
#define ASSET_PBP_SIGNATURE "\0PBP"
#if PSP
#define ASSET_PBP_READ_PBP_FROM_HOST 0
#define ASSET_PBP_SIGNATURE_SIZE 4
#define ASSET_PBP_SIGNATURE "\0PBP"
typedef struct {
char_t signature[ASSET_PBP_SIGNATURE_SIZE];
uint32_t version;
uint32_t sfoOffset;
uint32_t icon0Offset;
uint32_t icon1Offset;
uint32_t pic0Offset;
uint32_t pic1Offset;
uint32_t snd0Offset;
uint32_t pspOffset;
uint32_t psarOffset;
} assetpbp_t;
#endif
#else
#error "Unsupported ASSET_TYPE"
typedef struct {
char_t signature[ASSET_PBP_SIGNATURE_SIZE];
uint32_t version;
uint32_t sfoOffset;
uint32_t icon0Offset;
uint32_t icon1Offset;
uint32_t pic0Offset;
uint32_t pic1Offset;
uint32_t snd0Offset;
uint32_t pspOffset;
uint32_t psarOffset;
} assetpbp_t;
#elif DOLPHIN
#include <fat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
static const char_t *ASSET_DOLPHIN_PATHS[] = {
"/",
"/Dusk",
"/dusk",
"/DUSK",
"/apps",
"/apps/Dusk",
"/apps/dusk",
"/apps/DUSK",
".",
"./",
"./Dusk",
"./dusk",
"./DUSK",
"./apps",
"./apps/Dusk",
"./apps/dusk",
"./apps/DUSK",
NULL
};
#endif
#define ASSET_FILE "dusk.dsk"

View File

@@ -9,6 +9,8 @@
#include "assert/assert.h"
#include "display/texture.h"
#include "debug/debug.h"
errorret_t assetAlphaImageLoad(void *data, void *output) {
assertNotNull(data, "Data pointer cannot be NULL.");
assertNotNull(output, "Output pointer cannot be NULL.");
@@ -16,6 +18,10 @@ errorret_t assetAlphaImageLoad(void *data, void *output) {
assetalphaimage_t *dataPtr = (assetalphaimage_t *)data;
texture_t *outputPtr = (texture_t *)output;
// Fix endian
dataPtr->width = le32toh(dataPtr->width);
dataPtr->height = le32toh(dataPtr->height);
textureInit(
outputPtr,
dataPtr->width,

View File

@@ -43,6 +43,13 @@ errorret_t assetLanguageInit(
errorThrow("Failed to read language asset header.");
}
// Fix the endianness of the header data.
for(uint32_t i = 0; i < LANG_KEY_COUNT; i++) {
lang->header.strings[i].chunk = le32toh(lang->header.strings[i].chunk);
lang->header.strings[i].offset = le32toh(lang->header.strings[i].offset);
lang->header.strings[i].length = le32toh(lang->header.strings[i].length);
}
lang->chunksOffset = zip_ftell(lang->zip);
if(lang->chunksOffset <= 0) {
zip_fclose(lang->zip);

View File

@@ -55,6 +55,9 @@ errorret_t assetMapChunkHandler(assetcustom_t custom) {
errorThrow("Failed to read chunk asset header.");
}
// Fix endianess if necessary
header.tileCount = le32toh(header.tileCount);
if(header.tileCount != CHUNK_TILE_COUNT) {
zip_fclose(custom.zipFile);
errorThrow(
@@ -107,6 +110,9 @@ errorret_t assetMapChunkHandler(assetcustom_t custom) {
errorThrow("Failed to read chunk model header.");
}
// Fix endianess if necessary
modelHeader.vertexCount = le32toh(modelHeader.vertexCount);
if(
vertexIndex + modelHeader.vertexCount >
CHUNK_VERTEX_COUNT_MAX

View File

@@ -15,6 +15,10 @@ errorret_t assetPaletteImageLoad(void *data, void *output) {
assetpaletteimage_t *assetData = (assetpaletteimage_t *)data;
texture_t *texture = (texture_t *)output;
// Fix endian
assetData->width = le32toh(assetData->width);
assetData->height = le32toh(assetData->height);
textureInit(
texture,

View File

@@ -9,7 +9,7 @@
#include "error/error.h"
#include "duskdefs.h"
#include <zip.h>
#include <lua.h>
#include "script/scriptcontext.h"
#define ASSET_SCRIPT_BUFFER_SIZE 1024

View File

@@ -6,13 +6,17 @@
*/
#include "debug.h"
#if DOLPHIN
#include "display/display.h"
static char_t DEBUG_ERROR_BUFFER[16*1024] = {0};
#endif
void debugPrint(const char_t *message, ...) {
va_list args;
va_start(args, message);
vprintf(message, args);
va_end(args);
fflush(stdout);
#if PSP
FILE *file = fopen("ms0:/PSP/GAME/Dusk/debug.log", "a");
@@ -22,5 +26,70 @@ void debugPrint(const char_t *message, ...) {
va_end(args);
fclose(file);
}
#elif DOLPHIN
// append to error buffer
size_t start = strlen(DEBUG_ERROR_BUFFER);
va_start(args, message);
vsnprintf(
DEBUG_ERROR_BUFFER + start,
sizeof(DEBUG_ERROR_BUFFER) - start,
message,
args
);
va_end(args);
#endif
}
void debugFlush() {
#if PSP
// No buffering, so nothing to flush
#elif DOLPHIN
// Either create graphics, or hijack the displays' graphics.
void *xfb = NULL;
GXRModeObj *rmode = NULL;
void *framebuffer;
if(DISPLAY.frameBuffer[0]) {
console_init(
DISPLAY.frameBuffer[0],
20,
20,
DISPLAY.screenMode->fbWidth,
DISPLAY.screenMode->xfbHeight,
DISPLAY.screenMode->fbWidth * VI_DISPLAY_PIX_SZ
);
} else {
VIDEO_Init();
rmode = VIDEO_GetPreferredMode(NULL);
framebuffer = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
console_init(
framebuffer,
20,
20,
rmode->fbWidth,
rmode->xfbHeight,
rmode->fbWidth*VI_DISPLAY_PIX_SZ
);
VIDEO_Configure(rmode);
VIDEO_SetNextFramebuffer(framebuffer);
VIDEO_SetBlack(FALSE);
VIDEO_Flush();
VIDEO_WaitVSync();
if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
}
// Printf
printf("SOB\n");
printf(DEBUG_ERROR_BUFFER);
printf("\nEOB.");
while(SYS_MainLoop()) {
VIDEO_WaitVSync();
}
#else
fflush(stdout);
#endif
}

View File

@@ -14,4 +14,9 @@
* @param message The message format string.
* @param ... Additional arguments for the format string.
*/
void debugPrint(const char_t *message, ...);
void debugPrint(const char_t *message, ...);
/**
* Flushes the debug output buffer.
*/
void debugFlush();

View File

@@ -38,6 +38,15 @@ elseif(DUSK_TARGET_SYSTEM STREQUAL "psp")
DISPLAY_HEIGHT=272
DISPLAY_SIZE_DYNAMIC=0
)
elseif(DUSK_TARGET_SYSTEM STREQUAL "gamecube" OR DUSK_TARGET_SYSTEM STREQUAL "wii")
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
DISPLAY_WINDOW_WIDTH_DEFAULT=640
DISPLAY_WINDOW_HEIGHT_DEFAULT=480
DISPLAY_WIDTH=640
DISPLAY_HEIGHT=480
DISPLAY_SIZE_DYNAMIC=0
)
endif()
dusk_run_python(

View File

@@ -48,6 +48,97 @@ void cameraInitOrthographic(camera_t *camera) {
void cameraPushMatrix(camera_t *camera) {
assertNotNull(camera, "Not a camera component");
#if DOLPHIN
Mtx44 guProjection;
Mtx guView;
Mtx modelView;
switch(camera->projType) {
case CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC:
guOrtho(
guProjection,
camera->orthographic.left,
camera->orthographic.right,
camera->orthographic.bottom,
camera->orthographic.top,
camera->nearClip,
camera->farClip
);
break;
case CAMERA_PROJECTION_TYPE_PERSPECTIVE:
guPerspective(
guProjection,
// FOV is in degrees.
camera->perspective.fov * (180.0f / GLM_PIf),
(float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND) /
(float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND),
camera->nearClip,
camera->farClip
);
break;
case CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED:
assertUnreachable("Flipped perspective not implemented on Dolphin");
break;
default:
assertUnreachable("Invalid camera projection type");
}
switch(camera->viewType) {
case CAMERA_VIEW_TYPE_LOOKAT:
guVector eye = {
camera->lookat.position[0],
camera->lookat.position[1],
camera->lookat.position[2]
};
guVector up = {
camera->lookat.up[0],
camera->lookat.up[1],
camera->lookat.up[2]
};
guVector look = {
camera->lookat.target[0],
camera->lookat.target[1],
camera->lookat.target[2]
};
guLookAt(guView, &eye, &up, &look);
break;
case CAMERA_VIEW_TYPE_MATRIX:
assertUnreachable("Matrix camera not implemented");
break;
case CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT:
assertUnreachable("Pixel perfect camera not implemented");
break;
case CAMERA_VIEW_TYPE_2D:
assertUnreachable("2D camera not implemented");
break;
default:
assertUnreachable("Invalid camera view type");
}
// Set Projection Matrix
GX_LoadProjectionMtx(
guProjection,
camera->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC ?
GX_ORTHOGRAPHIC :
GX_PERSPECTIVE
);
// Set view and model matrix. Dunno how I'll handle models but whatever.
guMtxIdentity(modelView);
guMtxTransApply(modelView, modelView, 0.0F, 0.0F, 0.0F);
guMtxConcat(guView,modelView,modelView);
GX_LoadPosMtxImm(modelView, GX_PNMTX0);
return;
#endif
mat4 projection;
mat4 view;

View File

@@ -15,10 +15,19 @@
#include "ui/ui.h"
#include "debug/debug.h"
#include "display/text.h"
#include "assert/assert.h"
#include "util/memory.h"
display_t DISPLAY;
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <math.h>
display_t DISPLAY = { 0 };
errorret_t displayInit(void) {
memoryZero(&DISPLAY, sizeof(DISPLAY));
#if DISPLAY_SDL2
uint32_t flags = SDL_INIT_VIDEO;
#if INPUT_GAMEPAD == 1
@@ -69,6 +78,68 @@ errorret_t displayInit(void) {
glEnableClientState(GL_COLOR_ARRAY);// To confirm: every frame on PSP?
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
#elif DOLPHIN
VIDEO_Init();
DISPLAY.screenMode = VIDEO_GetPreferredMode(NULL);
DISPLAY.frameBuffer[0] = MEM_K0_TO_K1(
SYS_AllocateFramebuffer(DISPLAY.screenMode)
);
DISPLAY.frameBuffer[1] = MEM_K0_TO_K1(
SYS_AllocateFramebuffer(DISPLAY.screenMode)
);
VIDEO_Configure(DISPLAY.screenMode);
VIDEO_SetNextFramebuffer(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer]);
// VIDEO_SetPostRetraceCallback(copy_buffers);
VIDEO_SetBlack(FALSE);
VIDEO_Flush();
VIDEO_WaitVSync();
if(DISPLAY.screenMode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync();
DISPLAY.fifoBuffer = memalign(32, DISPLAY_FIFO_SIZE);
memoryZero(DISPLAY.fifoBuffer, DISPLAY_FIFO_SIZE);
GX_Init(DISPLAY.fifoBuffer, DISPLAY_FIFO_SIZE);
// This seems to be mostly related to interlacing vs progressive
GX_SetViewport(
0, 0,
DISPLAY.screenMode->fbWidth, DISPLAY.screenMode->efbHeight,
0, 1
);
float_t yscale = GX_GetYScaleFactor(
DISPLAY.screenMode->efbHeight, DISPLAY.screenMode->xfbHeight
);
uint32_t xfbHeight = GX_SetDispCopyYScale(yscale);
GX_SetScissor(
0, 0,
DISPLAY.screenMode->fbWidth, DISPLAY.screenMode->efbHeight
);
GX_SetDispCopySrc(
0, 0,
DISPLAY.screenMode->fbWidth, DISPLAY.screenMode->efbHeight
);
GX_SetDispCopyDst(DISPLAY.screenMode->fbWidth, xfbHeight);
GX_SetCopyFilter(
DISPLAY.screenMode->aa,
DISPLAY.screenMode->sample_pattern,
GX_TRUE,
DISPLAY.screenMode->vfilter
);
GX_SetFieldMode(
DISPLAY.screenMode->field_rendering,
(
(DISPLAY.screenMode->viHeight == 2 * DISPLAY.screenMode->xfbHeight) ?
GX_ENABLE :
GX_DISABLE
)
);
// Setup cull modes
GX_SetCullMode(GX_CULL_NONE);
GX_CopyDisp(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer], GX_TRUE);
GX_SetDispCopyGamma(GX_GM_1_0);
#endif
quadInit();
@@ -110,6 +181,10 @@ errorret_t displayUpdate(void) {
}
SDL_GL_MakeCurrent(DISPLAY.window, DISPLAY.glContext);
#elif DOLPHIN
#endif
// Reset state
@@ -125,7 +200,7 @@ errorret_t displayUpdate(void) {
errorChain(sceneRender());
// Render UI
uiRender();
// uiRender();
// Finish up
screenUnbind();
@@ -133,12 +208,22 @@ errorret_t displayUpdate(void) {
#if DISPLAY_SDL2
SDL_GL_SwapWindow(DISPLAY.window);
#endif
GLenum err;
while((err = glGetError()) != GL_NO_ERROR) {
debugPrint("GL Error: %d\n", err);
}
GLenum err;
while((err = glGetError()) != GL_NO_ERROR) {
debugPrint("GL Error: %d\n", err);
}
#elif DOLPHIN
GX_DrawDone();
DISPLAY.whichFrameBuffer ^= 1;
GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
GX_SetColorUpdate(GX_TRUE);
GX_CopyDisp(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer], GX_TRUE);
VIDEO_SetNextFramebuffer(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer]);
VIDEO_Flush();
VIDEO_WaitVSync();
#endif
// For now, we just return an OK error.
errorOk();

View File

@@ -15,6 +15,13 @@ typedef struct {
#if DISPLAY_SDL2
SDL_Window *window;
SDL_GLContext glContext;
#elif DOLPHIN
void *frameBuffer[2];// Double-Bufferred
int whichFrameBuffer;
GXRModeObj *screenMode;
void *fifoBuffer;
#endif
} display_t;

View File

@@ -6,6 +6,7 @@
*/
#pragma once
#include "dusk.h"
#if DISPLAY_SDL2
#include <SDL2/SDL.h>
@@ -17,6 +18,9 @@
#ifndef DISPLAY_SIZE_DYNAMIC
#define DISPLAY_SIZE_DYNAMIC 1
#endif
#elif DOLPHIN
// Dolphin.
#define DISPLAY_FIFO_SIZE (256*1024)
#else
#error "Need to specify display backend."
#endif

View File

@@ -68,6 +68,13 @@ int32_t frameBufferGetWidth(const framebuffer_t *framebuffer) {
}
return framebuffer->texture.width;
#elif DOLPHIN
return DISPLAY.screenMode->fbWidth;
#else
#error "Unsupported DISPLAY_TYPE."
#endif
}
@@ -84,15 +91,19 @@ int32_t frameBufferGetHeight(const framebuffer_t *framebuffer) {
}
return framebuffer->texture.height;
#elif DOLPHIN
return DISPLAY.screenMode->efbHeight;
#else
#error "Unsupported DISPLAY_TYPE."
#endif
}
void frameBufferBind(const framebuffer_t *framebuffer) {
if(framebuffer == NULL) {
#if DISPLAY_SDL2
frameBufferBind(&FRAMEBUFFER_BACKBUFFER);
#endif
frameBufferBind(&FRAMEBUFFER_BACKBUFFER);
FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER;
return;
}
@@ -117,6 +128,18 @@ void frameBufferBind(const framebuffer_t *framebuffer) {
0, 0,
frameBufferGetWidth(framebuffer), frameBufferGetHeight(framebuffer)
);
#elif DOLPHIN
GX_InvVtxCache();
GX_InvalidateTexAll();
GX_SetViewport(
0, 0,
frameBufferGetWidth(framebuffer),
frameBufferGetHeight(framebuffer),
0, 1
);
#endif
FRAMEBUFFER_BOUND = framebuffer;
@@ -141,6 +164,12 @@ void frameBufferClear(uint8_t flags, color_t color) {
}
glClear(glFlags);
#elif DOLPHIN
GX_SetCopyClear(
(GXColor){ color.r, color.g, color.b, color.a },
GX_MAX_Z24
);
#endif
}

View File

@@ -16,6 +16,9 @@ typedef struct {
// OpenGL Framebuffer Object ID
GLuint id;
texture_t texture;
#elif DOLPHIN
// --- IGNORE ---
uint8_t id;
#else
#error "Framebuffers not implemented on this platform."
#endif

View File

@@ -9,6 +9,10 @@
#include "util/memory.h"
#include "assert/assert.h"
#if DOLPHIN
#include "display/texture.h"
#endif
void meshInit(
mesh_t *mesh,
const meshprimitivetype_t primitiveType,
@@ -69,6 +73,37 @@ void meshDraw(
0,
count
);
#elif DOLPHIN
// Prepare Vertex descriptor
DCFlushRange(
(void*)&mesh->vertices[offset],
sizeof(meshvertex_t) * count
);
GX_ClearVtxDesc();// Just clears so may be un-needed?
GX_SetVtxDesc(GX_VA_POS, GX_INDEX16);
GX_SetVtxDesc(GX_VA_CLR0, GX_INDEX16);
GX_SetVtxDesc(GX_VA_TEX0, GX_INDEX16);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
const u8 stride = (u8)sizeof(meshvertex_t);
GX_SetArray(GX_VA_POS, &mesh->vertices[offset].pos[0], stride);
GX_SetArray(GX_VA_CLR0, &mesh->vertices[offset].color, stride);
GX_SetArray(GX_VA_TEX0, &mesh->vertices[offset].uv[0], stride);
textureDolphinUploadTEV();
GX_Begin(mesh->primitiveType, GX_VTXFMT0, (uint16_t)count);
for(u16 i = 0; i < (u16)count; ++i) {
GX_Position1x16(i);
GX_Color1x16(i);
GX_TexCoord1x16(i);
}
GX_End();
#endif
}

View File

@@ -12,6 +12,10 @@ typedef enum {
MESH_PRIMITIVE_TRIANGLES = GL_TRIANGLES,
MESH_PRIMITIVE_LINES = GL_LINES,
MESH_PRIMITIVE_POINTS = GL_POINTS,
#elif DOLPHIN
MESH_PRIMITIVE_TRIANGLES = GX_TRIANGLES,
MESH_PRIMITIVE_LINES = GX_LINES,
MESH_PRIMITIVE_POINTS = GX_POINTS,
#endif
} meshprimitivetype_t;
@@ -19,11 +23,9 @@ typedef enum {
#define MESH_VERTEX_POS_SIZE 3
typedef struct {
#if DISPLAY_SDL2
color4b_t color;
GLfloat uv[MESH_VERTEX_UV_SIZE];
GLfloat pos[MESH_VERTEX_POS_SIZE];
#endif
color_t color;
float uv[MESH_VERTEX_UV_SIZE];
float pos[MESH_VERTEX_POS_SIZE];
} meshvertex_t;
typedef struct {

View File

@@ -10,13 +10,13 @@
mesh_t QUAD_MESH_SIMPLE;
meshvertex_t QUAD_MESH_SIMPLE_VERTICES[QUAD_VERTEX_COUNT] = {
{ COLOR_WHITE_4B, { 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } },
{ COLOR_WHITE_4B, { 1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
{ COLOR_WHITE_4B, { 1.0f, 1.0f }, { 1.0f, 1.0f, 0.0f } },
{ COLOR_WHITE_4B, { 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } },
{ COLOR_WHITE_4B, { 1.0f, 1.0f }, { 1.0f, 1.0f, 0.0f } },
{ COLOR_WHITE_4B, { 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f } }
{ .color = COLOR_WHITE_4B, .uv = { 0.0f, 0.0f }, .pos = { 0.0f, 0.0f, 0.0f } },
{ .color = COLOR_WHITE_4B, .uv = { 1.0f, 0.0f }, .pos = { 1.0f, 0.0f, 0.0f } },
{ .color = COLOR_WHITE_4B, .uv = { 1.0f, 1.0f }, .pos = { 1.0f, 1.0f, 0.0f } },
{ .color = COLOR_WHITE_4B, .uv = { 0.0f, 0.0f }, .pos = { 0.0f, 0.0f, 0.0f } },
{ .color = COLOR_WHITE_4B, .uv = { 1.0f, 1.0f }, .pos = { 1.0f, 1.0f, 0.0f } },
{ .color = COLOR_WHITE_4B, .uv = { 0.0f, 1.0f }, .pos = { 0.0f, 1.0f, 0.0f } }
};
void quadInit() {
@@ -44,38 +44,48 @@ void quadBuffer(
assertNotNull(vertices, "Vertices cannot be NULL");
// First triangle
vertices[0] = (meshvertex_t) {
{ color.r, color.g, color.b, color.a }, // Color
{ u0, v0 }, // UV
{ minX, minY, z } // Position
};
vertices[1] = (meshvertex_t) {
{ color.r, color.g, color.b, color.a }, // Color
{ u1, v0 }, // UV
{ maxX, minY, z } // Position
};
vertices[2] = (meshvertex_t) {
{ color.r, color.g, color.b, color.a }, // Color
{ u1, v1 }, // UV
{ maxX, maxY, z } // Position
};
vertices[0].color = color;
vertices[0].uv[0] = u0;
vertices[0].uv[1] = v1;
vertices[0].pos[0] = minX;
vertices[0].pos[1] = maxY;
vertices[0].pos[2] = z;
vertices[2].color = color;
vertices[2].uv[0] = u0;
vertices[2].uv[1] = v0;
vertices[2].pos[0] = minX;
vertices[2].pos[1] = minY;
vertices[2].pos[2] = z;
vertices[1].color = color;
vertices[1].uv[0] = u1;
vertices[1].uv[1] = v0;
vertices[1].pos[0] = maxX;
vertices[1].pos[1] = minY;
vertices[1].pos[2] = z;
// Second triangle
vertices[3] = (meshvertex_t) {
{ color.r, color.g, color.b, color.a }, // Color
{ u0, v0 }, // UV
{ minX, minY, z } // Position
};
vertices[4] = (meshvertex_t) {
{ color.r, color.g, color.b, color.a }, // Color
{ u1, v1 }, // UV
{ maxX, maxY, z } // Position
};
vertices[5] = (meshvertex_t) {
{ color.r, color.g, color.b, color.a }, // Color
{ u0, v1 }, // UV
{ minX, maxY, z } // Position
};
vertices[3].color = color;
vertices[3].uv[0] = u0;
vertices[3].uv[1] = v1;
vertices[3].pos[0] = minX;
vertices[3].pos[1] = maxY;
vertices[3].pos[2] = z;
vertices[5].color = color;
vertices[5].uv[0] = u1;
vertices[5].uv[1] = v0;
vertices[5].pos[0] = maxX;
vertices[5].pos[1] = minY;
vertices[5].pos[2] = z;
vertices[4].color = color;
vertices[4].uv[0] = u1;
vertices[4].uv[1] = v1;
vertices[4].pos[0] = maxX;
vertices[4].pos[1] = maxY;
vertices[4].pos[2] = z;
}
void quadBuffer3D(
@@ -93,36 +103,46 @@ void quadBuffer3D(
assertNotNull(uvMax, "UV Max vector cannot be NULL");
// First triangle
vertices[0] = (meshvertex_t) {
{ color.r, color.g, color.b, color.a }, // Color
{ uvMin[0], uvMin[1] }, // UV
{ min[0], min[1], min[2] } // Position
};
vertices[1] = (meshvertex_t) {
{ color.r, color.g, color.b, color.a }, // Color
{ uvMax[0], uvMin[1] }, // UV
{ max[0], min[1], min[2] } // Position
};
vertices[2] = (meshvertex_t) {
{ color.r, color.g, color.b, color.a }, // Color
{ uvMax[0], uvMax[1] }, // UV
{ max[0], max[1], min[2] } // Position
};
vertices[0].color = color;
vertices[0].uv[0] = uvMin[0];
vertices[0].uv[1] = uvMin[1];
vertices[0].pos[0] = min[0];
vertices[0].pos[1] = min[1];
vertices[0].pos[2] = min[2];
vertices[1].color = color;
vertices[1].uv[0] = uvMax[0];
vertices[1].uv[1] = uvMin[1];
vertices[1].pos[0] = max[0];
vertices[1].pos[1] = min[1];
vertices[1].pos[2] = min[2];
vertices[2].color = color;
vertices[2].uv[0] = uvMax[0];
vertices[2].uv[1] = uvMax[1];
vertices[2].pos[0] = max[0];
vertices[2].pos[1] = max[1];
vertices[2].pos[2] = min[2];
// Second triangle
vertices[3] = (meshvertex_t) {
{ color.r, color.g, color.b, color.a }, // Color
{ uvMin[0], uvMin[1] }, // UV
{ min[0], min[1], min[2] } // Position
};
vertices[4] = (meshvertex_t) {
{ color.r, color.g, color.b, color.a }, // Color
{ uvMax[0], uvMax[1] }, // UV
{ max[0], max[1], min[2] } // Position
};
vertices[5] = (meshvertex_t) {
{ color.r, color.g, color.b, color.a }, // Color
{ uvMin[0], uvMax[1] }, // UV
{ min[0], max[1], min[2] } // Position
};
vertices[3].color = color;
vertices[3].uv[0] = uvMin[0];
vertices[3].uv[1] = uvMin[1];
vertices[3].pos[0] = min[0];
vertices[3].pos[1] = min[1];
vertices[3].pos[2] = min[2];
vertices[4].color = color;
vertices[4].uv[0] = uvMax[0];
vertices[4].uv[1] = uvMax[1];
vertices[4].pos[0] = max[0];
vertices[4].pos[1] = max[1];
vertices[4].pos[2] = min[2];
vertices[5].color = color;
vertices[5].uv[0] = uvMin[0];
vertices[5].uv[1] = uvMax[1];
vertices[5].pos[0] = min[0];
vertices[5].pos[1] = max[1];
vertices[5].pos[2] = min[2];
}

View File

@@ -9,9 +9,12 @@
#include "assert/assert.h"
#include "util/memory.h"
meshvertex_t SPRITEBATCH_VERTICES[SPRITEBATCH_VERTEX_COUNT];
spritebatch_t SPRITEBATCH;
void spriteBatchInit() {
memoryZero(&SPRITEBATCH, sizeof(spritebatch_t));
SPRITEBATCH.spriteCount = 0;
SPRITEBATCH.currentTexture = NULL;
@@ -19,7 +22,7 @@ void spriteBatchInit() {
&SPRITEBATCH.mesh,
MESH_PRIMITIVE_TRIANGLES,
SPRITEBATCH_VERTEX_COUNT,
&SPRITEBATCH.vertices[0]
&SPRITEBATCH_VERTICES[0]
);
}
@@ -45,7 +48,7 @@ void spriteBatchPush(
}
quadBuffer(
&SPRITEBATCH.vertices[SPRITEBATCH.spriteCount * QUAD_VERTEX_COUNT],
&SPRITEBATCH_VERTICES[SPRITEBATCH.spriteCount * QUAD_VERTEX_COUNT],
minX, minY, maxX, maxY,
color,
u0, v0, u1, v1
@@ -72,7 +75,7 @@ void spriteBatchPush3D(
}
quadBuffer3D(
&SPRITEBATCH.vertices[SPRITEBATCH.spriteCount * QUAD_VERTEX_COUNT],
&SPRITEBATCH_VERTICES[SPRITEBATCH.spriteCount * QUAD_VERTEX_COUNT],
min, max, color, uv0, uv1
);

View File

@@ -9,7 +9,7 @@
#include "display/mesh/quad.h"
#include "display/texture.h"
#define SPRITEBATCH_SPRITES_MAX 1
#define SPRITEBATCH_SPRITES_MAX 32
#define SPRITEBATCH_VERTEX_COUNT (SPRITEBATCH_SPRITES_MAX * QUAD_VERTEX_COUNT)
@@ -17,9 +17,11 @@ typedef struct {
mesh_t mesh;
int32_t spriteCount;
texture_t *currentTexture;
meshvertex_t vertices[SPRITEBATCH_VERTEX_COUNT];
} spritebatch_t;
// Have to define these seperately because of alignment in certain platforms.
// (Looking at you Dolphin)/
extern meshvertex_t SPRITEBATCH_VERTICES[SPRITEBATCH_VERTEX_COUNT];
extern spritebatch_t SPRITEBATCH;
/**

View File

@@ -144,10 +144,148 @@ void textureInit(
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
texture->ready = true;
#elif DOLPHIN
texture->format = format;
switch(format) {
case TEXTURE_FORMAT_RGBA:
assertTrue(
(width % 4) == 0 && (height % 4) == 0,
"RGB5A3 requires w/h multiple of 4 (or pad)"
);
// Convert to RGB5A3 format
size_t rgbaSize = width * height * sizeof(u16);
texture->rgba = (u16*)memalign(32, rgbaSize);
assertNotNull(texture->rgba, "Failed to allocate texture RGBA data");
for(uint32_t y = 0; y < height; ++y) {
for(uint32_t x = 0; x < width; ++x) {
const int src = y * width + x;
const int tileX = x >> 2;
const int tileY = y >> 2;
const int tilesPerRow = width >> 2;
const int tileIndex = tileY * tilesPerRow + tileX;
const int tileBaseWords = tileIndex * 16;
const int inTile = ((y & 3) << 2) + (x & 3);
const int dest = tileBaseWords + inTile;
color4b_t col = data.rgba.colors[src];
u16 outCol;
if(col.a < 255) {
// 0AAA RRRR GGGG BBBB
outCol = (
(0u << 15) |
((u16)(col.a >> 5) << 12) |
((u16)(col.r >> 4) << 8) |
((u16)(col.g >> 4) << 4) |
((u16)(col.b >> 4) << 0)
);
} else {
// 1RRRR RRGG GGGB BBBB
outCol = (
(1u << 15) |
((u16)(col.r >> 3) << 10) |
((u16)(col.g >> 3) << 5) |
((u16)(col.b >> 3) << 0)
);
}
texture->rgba[dest] = outCol;
}
}
DCFlushRange(texture->rgba, rgbaSize);
GX_InitTexObj(
&texture->texObj,
texture->rgba,
width, height,
GX_TF_RGB5A3,
GX_CLAMP, GX_CLAMP,
GX_FALSE
);
DCFlushRange(texture->rgba, rgbaSize);
GX_InvalidateTexAll();
GX_InitTexObjLOD(
&texture->texObj,
GX_NEAR, GX_NEAR,
0.0f, 0.0f, 0.0f,
GX_FALSE,
GX_FALSE,
GX_ANISO_1
);
break;
case TEXTURE_FORMAT_ALPHA:
assertTrue(
(width % 4) == 0 && (height % 4) == 0,
"GX_TF_I8 requires w/h multiple of 4 (or pad)"
);
// 1 byte per pixel (I8), GX expects 4x4 tiled layout
const size_t alphaSize = (size_t)width * (size_t)height;
texture->alpha = (u8*)memalign(32, alphaSize);
assertNotNull(texture->alpha, "Failed to allocate alpha texture data");
const u32 tilesPerRow = ((u32)width) >> 3; // /8
for (u32 y = 0; y < (u32)height; ++y) {
const u32 tileY = y >> 2; // /4
const u32 inTileY = (y & 3) << 3; // (y%4)*8
for (u32 x = 0; x < (u32)width; ++x) {
const u32 srcI = y * (u32)width + x;
const u8 srcA = data.alpha.data[srcI]; // linear input
const u32 tileX = x >> 3; // /8
const u32 tileIndex = tileY * tilesPerRow + tileX;
const u32 tileBase = tileIndex * 32; // 8*4*1 = 32 bytes per tile
const u32 inTile = inTileY + (x & 7); // (y%4)*8 + (x%8)
texture->alpha[tileBase + inTile] = 0xFF - srcA;// Fixes inverted alpha.
}
}
// Flush CPU cache so GX sees the swizzled I8 texture data
DCFlushRange(texture->alpha, alphaSize);
// Initialize GX texture object with swizzled data
GX_InitTexObj(
&texture->texObj,
texture->alpha,
width, height,
GX_TF_I8,
GX_CLAMP, GX_CLAMP,
GX_FALSE
);
GX_InitTexObjLOD(
&texture->texObj,
GX_NEAR, GX_NEAR,
0.0f, 0.0f, 0.0f,
GX_FALSE,
GX_FALSE,
GX_ANISO_1
);
break;
default:
assertUnreachable("Unsupported texture format for Dolphin");
break;
}
texture->ready = true;
#endif
}
void textureBind(const texture_t *texture) {
void textureBind(texture_t *texture) {
if(TEXTURE_BOUND == texture) return;
if(texture == NULL) {
@@ -158,10 +296,7 @@ void textureBind(const texture_t *texture) {
return;
}
assertTrue(
texture->id != 0,
"Texture ID must not be 0"
);
assertTrue(texture->ready, "Texture ID must be ready");
assertTrue(
texture->width > 0 && texture->height > 0,
"Texture width and height must be greater than 0"
@@ -171,15 +306,143 @@ void textureBind(const texture_t *texture) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture->id);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
#elif DOLPHIN
GX_LoadTexObj(&texture->texObj, GX_TEXMAP0);
#endif
TEXTURE_BOUND = texture;
}
void textureDispose(texture_t *texture) {
assertNotNull(texture, "Texture cannot be NULL");
assertTrue(texture->id != 0, "Texture ID must not be 0");
assertTrue(texture->ready, "Texture ID must be ready");
if(TEXTURE_BOUND == texture) {
textureBind(NULL);
}
#if DISPLAY_SDL2
glDeleteTextures(1, &texture->id);
#elif DOLPHIN
switch(texture->format) {
case TEXTURE_FORMAT_RGBA:
free(texture->rgba);
break;
case TEXTURE_FORMAT_ALPHA:
free(texture->alpha);
break;
default:
break;
}
memoryZero(texture, sizeof(texture_t));
#endif
}
}
#if DOLPHIN
void textureDolphinUploadTEV() {
// Add channel for vertex color
GX_SetNumChans(1);
GX_SetChanCtrl(
GX_COLOR0A0,// Store in color channel 0
GX_DISABLE,// Lighting disabled
GX_SRC_REG,// Ambient color?
GX_SRC_VTX,// Material color?
GX_LIGHTNULL,// Light Mask
GX_DF_NONE,// Diffuse function
GX_AF_NONE// Attenuation function
);
if(!TEXTURE_BOUND) {
GX_SetNumTexGens(0);
GX_SetNumTevStages(1);
GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
return;
}
// One set of UVs
GX_SetNumTexGens(1);
GX_SetTexCoordGen(
GX_TEXCOORD0,
GX_TG_MTX2x4,
GX_TG_TEX0,
GX_IDENTITY
);
// Basically the shader setup
switch(TEXTURE_BOUND->format) {
case TEXTURE_FORMAT_RGBA:
// One TEV stage: vertex color * texture color
GX_SetNumTevStages(1);
GX_SetTevOrder(
GX_TEVSTAGE0,
GX_TEXCOORD0,
GX_TEXMAP0,
GX_COLOR0A0
);
GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
break;
case TEXTURE_FORMAT_ALPHA:
// One TEV stage: vertex color * texture color
GX_SetNumTevStages(1);
GX_SetTevOrder(
GX_TEVSTAGE0,
GX_TEXCOORD0,
GX_TEXMAP0,
GX_COLOR0A0
);
// Color = vertex color
GX_SetTevColorIn(
GX_TEVSTAGE0,
GX_CC_RASC,
GX_CC_ZERO,
GX_CC_ZERO,
GX_CC_ZERO
);
GX_SetTevColorOp(
GX_TEVSTAGE0,
GX_TEV_ADD,
GX_TB_ZERO,
GX_CS_SCALE_1,
GX_TRUE,
GX_TEVPREV
);
// Alpha = vertex alpha * I8 intensity
GX_SetTevAlphaIn(
GX_TEVSTAGE0,
GX_CA_RASA,
GX_CA_ZERO,
GX_CA_TEXA,
GX_CA_ZERO
);
GX_SetTevAlphaOp(
GX_TEVSTAGE0,
GX_TEV_ADD,
GX_TB_ZERO,
GX_CS_SCALE_1,
GX_TRUE,
GX_TEVPREV
);
GX_SetBlendMode(
GX_BM_BLEND,
GX_BL_SRCALPHA,
GX_BL_INVSRCALPHA,
GX_LO_CLEAR
);
GX_SetColorUpdate(GX_TRUE);
GX_SetAlphaUpdate(GX_TRUE);
break;
default:
assertUnreachable("Unknown texture format in meshDraw");
break;
}
}
#endif

View File

@@ -14,14 +14,26 @@ typedef enum {
TEXTURE_FORMAT_RGBA = GL_RGBA,
TEXTURE_FORMAT_ALPHA = GL_ALPHA,
TEXTURE_FORMAT_PALETTE = GL_COLOR_INDEX8_EXT,
#elif DOLPHIN
TEXTURE_FORMAT_RGBA = GX_TF_RGBA8,
TEXTURE_FORMAT_ALPHA = GX_TF_A8,
TEXTURE_FORMAT_PALETTE = GX_TF_CI8,
#endif
} textureformat_t;
typedef struct {
#if DISPLAY_SDL2
GLuint id;
#elif DOLPHIN
GXTexObj texObj;
textureformat_t format;
union {
u16 *rgba;
u8 *alpha;
};
#endif
bool_t ready;
int32_t width;
int32_t height;
} texture_t;
@@ -65,11 +77,18 @@ void textureInit(
*
* @param texture The texture to bind.
*/
void textureBind(const texture_t *texture);
void textureBind(texture_t *texture);
/**
* Disposes a texture.
*
* @param texture The texture to dispose.
*/
void textureDispose(texture_t *texture);
void textureDispose(texture_t *texture);
#if DOLPHIN
/**
* Uploads the TEV settings for the currently bound texture.
*/
void textureDolphinUploadTEV();
#endif

View File

@@ -29,6 +29,15 @@
#include <psphprm.h>
#endif
#if DOLPHIN
#include <ogcsys.h>
#include <gccore.h>
#include <malloc.h>
#include <sys/endian.h>
#else
#include <endian.h>
#endif
typedef bool bool_t;
typedef int int_t;
typedef float float_t;

View File

@@ -18,6 +18,7 @@
#include "script/scriptmanager.h"
#include "debug/debug.h"
#include "item/backpack.h"
#include "assert/assert.h"
engine_t ENGINE;
@@ -37,7 +38,6 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
errorChain(uiInit());
errorChain(mapInit());
errorChain(sceneInit());
backpackInit();
// Run the initial script.
@@ -50,6 +50,10 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
}
errorret_t engineUpdate(void) {
#if DOLPHIN
ENGINE.running = SYS_MainLoop();
#endif
timeUpdate();
inputUpdate();

View File

@@ -19,12 +19,19 @@ if(DUSK_TARGET_SYSTEM STREQUAL "linux")
INPUT_MOUSE=1
INPUT_GAMEPAD=1
)
elseif(DUSK_TARGET_SYSTEM STREQUAL "psp")
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
INPUT_SDL2=1
INPUT_GAMEPAD=1
)
elseif(DUSK_TARGET_SYSTEM STREQUAL "gamecube" OR DUSK_TARGET_SYSTEM STREQUAL "wii")
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
INPUT_GAMEPAD=1
)
endif()
# CSV

View File

@@ -11,6 +11,7 @@
#include "util/string.h"
#include "util/math.h"
#include "time/time.h"
#include "debug/debug.h"
input_t INPUT;
@@ -23,7 +24,13 @@ void inputInit(void) {
INPUT.actions[i].currentValue = 0.0f;
}
INPUT.deadzone = 0.2f;
#if INPUT_GAMEPAD == 1
INPUT.deadzone = 0.2f;
#endif
#if DOLPHIN
PAD_Init();
#endif
eventInit(
&INPUT.eventPressed, INPUT.pressedListeners, INPUT_LISTENER_PRESSED_MAX
@@ -47,6 +54,19 @@ void inputUpdate(void) {
#if INPUT_KEYBOARD == 1
INPUT.keyboardState = SDL_GetKeyboardState(NULL);
#endif
#elif DOLPHIN
PAD_ScanPads();
for(uint8_t i = 0; i < INPUT_PAD_COUNT; i++) {
INPUT.padState[i] = PAD_ButtonsDown(i);
INPUT.pads[i][INPUT_GAMEPAD_AXIS_LEFT_X] = INPUT_AXIS_FLOAT(PAD_StickX(i));
INPUT.pads[i][INPUT_GAMEPAD_AXIS_LEFT_Y] = INPUT_AXIS_FLOAT(PAD_StickY(i));
INPUT.pads[i][INPUT_GAMEPAD_AXIS_C_X] = INPUT_AXIS_FLOAT(PAD_SubStickX(i));
INPUT.pads[i][INPUT_GAMEPAD_AXIS_C_Y] = INPUT_AXIS_FLOAT(PAD_SubStickY(i));
INPUT.pads[i][INPUT_GAMEPAD_AXIS_TRIGGER_LEFT] = INPUT_AXIS_FLOAT(PAD_TriggerL(i));
INPUT.pads[i][INPUT_GAMEPAD_AXIS_TRIGGER_RIGHT] = INPUT_AXIS_FLOAT(PAD_TriggerR(i));
}
#endif
// Reset all actions
@@ -69,7 +89,7 @@ void inputUpdate(void) {
// For each button...
inputbuttondata_t *cur = &INPUT_BUTTON_DATA[0];
do {
while(cur->name) {
cur->lastVal = cur->curVal;
cur->curVal = inputButtonGetValue(cur->button);
@@ -95,7 +115,7 @@ void inputUpdate(void) {
#endif
cur++;
} while(cur->name);
}
// Do we need to fire off events?
#if TIME_FIXED == 0

View File

@@ -10,6 +10,11 @@
#include "inputaction.h"
#include "event/event.h"
#if DOLPHIN
#define INPUT_PAD_COUNT PAD_CHANMAX
#define INPUT_AXIS_FLOAT(value) ((float_t)(value) / 128.0f)
#endif
#define INPUT_LISTENER_PRESSED_MAX 16
#define INPUT_LISTENER_RELEASED_MAX INPUT_LISTENER_PRESSED_MAX
@@ -37,6 +42,11 @@ typedef struct {
#if INPUT_KEYBOARD == 1
const uint8_t *keyboardState;
#endif
#elif DOLPHIN
int padState[INPUT_PAD_COUNT];
float_t pads[INPUT_PAD_COUNT][INPUT_GAMEPAD_AXIS_COUNT];
#endif
} input_t;

View File

@@ -31,7 +31,6 @@ inputbuttondata_t INPUT_BUTTON_DATA[] = {
{ .name = "lstick_negative_x", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = SDL_CONTROLLER_AXIS_LEFTX, .positive = false } } },
{ .name = "lstick_positive_y", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = SDL_CONTROLLER_AXIS_LEFTY, .positive = true } } },
{ .name = "lstick_negative_y", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = SDL_CONTROLLER_AXIS_LEFTY, .positive = false } } },
#else
#endif
#endif
@@ -172,6 +171,61 @@ inputbuttondata_t INPUT_BUTTON_DATA[] = {
{ .name = "kp_enter", { .type = INPUT_BUTTON_TYPE_KEYBOARD, .scancode = SDL_SCANCODE_KP_ENTER } },
{ .name = "kp_equals", { .type = INPUT_BUTTON_TYPE_KEYBOARD, .scancode = SDL_SCANCODE_KP_EQUALS } },
#endif
#elif DOLPHIN
#if WII
#if INPUT_GAMEPAD == 1
{ .name = "a", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_A } },
{ .name = "b", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_B } },
{ .name = "x", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_X } },
{ .name = "y", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_Y } },
{ .name = "start", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_START } },
{ .name = "dpad_up", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_UP } },
{ .name = "dpad_down", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_DOWN } },
{ .name = "dpad_left", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_LEFT } },
{ .name = "dpad_right", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_RIGHT } },
{ .name = "l", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_TRIGGER_L } },
{ .name = "r", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_TRIGGER_R } },
{ .name = "z", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_TRIGGER_Z } },
{ .name = "menu", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_MENU } },
{ .name = "lstick_positive_x", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_X, .positive = true } } },
{ .name = "lstick_negative_x", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_X, .positive = false } } },
{ .name = "lstick_positive_y", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_Y, .positive = true } } },
{ .name = "lstick_negative_y", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_Y, .positive = false } } },
{ .name = "cstick_positive_x", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_X, .positive = true } } },
{ .name = "cstick_negative_x", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_X, .positive = false } } },
{ .name = "cstick_positive_y", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_Y, .positive = true } } },
{ .name = "cstick_negative_y", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_Y, .positive = false } } },
{ .name = "ltrigger", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_TRIGGER_LEFT, .positive = true } } },
{ .name = "rtrigger", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_TRIGGER_RIGHT, .positive = true } } },
#endif
#elif GAMECUBE
#if INPUT_GAMEPAD == 1
{ .name = "a", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_A } },
{ .name = "b", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_B } },
{ .name = "x", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_X } },
{ .name = "y", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_Y } },
{ .name = "start", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_START } },
{ .name = "dpad_up", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_UP } },
{ .name = "dpad_down", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_DOWN } },
{ .name = "dpad_left", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_LEFT } },
{ .name = "dpad_right", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_RIGHT } },
{ .name = "l", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_TRIGGER_L } },
{ .name = "r", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_TRIGGER_R } },
{ .name = "z", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_TRIGGER_Z } },
{ .name = "menu", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_MENU } },
{ .name = "lstick_positive_x", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_X, .positive = true } } },
{ .name = "lstick_negative_x", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_X, .positive = false } } },
{ .name = "lstick_positive_y", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_Y, .positive = true } } },
{ .name = "lstick_negative_y", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_Y, .positive = false } } },
{ .name = "cstick_positive_x", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_X, .positive = true } } },
{ .name = "cstick_negative_x", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_X, .positive = false } } },
{ .name = "cstick_positive_y", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_Y, .positive = true } } },
{ .name = "cstick_negative_y", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_Y, .positive = false } } },
{ .name = "ltrigger", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_TRIGGER_LEFT, .positive = true } } },
{ .name = "rtrigger", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_TRIGGER_RIGHT, .positive = true } } },
#endif
#endif
#endif
{ .name = NULL }
@@ -197,9 +251,9 @@ float_t inputButtonGetValue(const inputbutton_t button) {
case INPUT_BUTTON_TYPE_KEYBOARD: {
#if INPUT_SDL2 == 1
return INPUT.keyboardState[button.scancode] ? 1.0f : 0.0f;
#else
return 0.0f;
#endif
return 0.0f;
}
#endif
@@ -209,19 +263,29 @@ float_t inputButtonGetValue(const inputbutton_t button) {
if(SDL_GameControllerGetButton(INPUT.controller, button.gpButton)) {
return 1.0f;
}
#elif DOLPHIN
if(INPUT.padState[0] & button.gpButton) return 1.0f;
#endif
return 0.0f;
}
case INPUT_BUTTON_TYPE_GAMEPAD_AXIS: {
float_t value = 0.0f;
#if INPUT_SDL2 == 1
Sint16 axis = SDL_GameControllerGetAxis(INPUT.controller, button.gpAxis.axis);
if(!button.gpAxis.positive) axis = -axis;
float_t value = (float_t)axis / 32767.0f;
// Deadzone
if(value < INPUT.deadzone) return 0.0f;
return value;
value = (float_t)axis / 32767.0f;
#elif DOLPHIN
value = INPUT.pads[0][button.gpAxis.axis];
#endif
if(!button.gpAxis.positive) value = -value;
if(value >= INPUT.deadzone) return value;
return 0.0f;
}
#endif

View File

@@ -10,6 +10,8 @@
#if INPUT_SDL2 == 1
#include <SDL2/SDL.h>
#elif DOLPHIN
#else
#error "No input backend defined"
#endif
@@ -26,6 +28,18 @@
#if INPUT_SDL2 == 1
typedef SDL_GameControllerButton inputgamepadbutton_t;
typedef SDL_GameControllerAxis inputgamepadaxis_t;
#elif DOLPHIN == 1
typedef u16 inputgamepadbutton_t;
typedef enum {
INPUT_GAMEPAD_AXIS_LEFT_X,
INPUT_GAMEPAD_AXIS_LEFT_Y,
INPUT_GAMEPAD_AXIS_C_X,
INPUT_GAMEPAD_AXIS_C_Y,
INPUT_GAMEPAD_AXIS_TRIGGER_LEFT,
INPUT_GAMEPAD_AXIS_TRIGGER_RIGHT,
INPUT_GAMEPAD_AXIS_COUNT
} inputgamepadaxis_t;
#endif
#endif
@@ -35,6 +49,7 @@ typedef enum {
#if INPUT_KEYBOARD == 1
INPUT_BUTTON_TYPE_KEYBOARD,
#endif
#if INPUT_GAMEPAD == 1
INPUT_BUTTON_TYPE_GAMEPAD,
INPUT_BUTTON_TYPE_GAMEPAD_AXIS,

View File

@@ -14,7 +14,6 @@ localemanager_t LOCALE;
errorret_t localeManagerInit() {
memoryZero(&LOCALE, sizeof(localemanager_t));
// errorChain(localeManagerSetLocale(DUSK_LOCALE_EN_US));
errorOk();
}

View File

@@ -6,7 +6,6 @@
*/
#include "engine/engine.h"
#include "asset/asset.h"
#include "util/string.h"
#include "input/input.h"

11
src/null.c Normal file
View File

@@ -0,0 +1,11 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
// Empty C file for annoying platforms.
void nothing(void) {
}

View File

@@ -76,7 +76,6 @@ errorret_t sceneSet(const char_t *script) {
// Create a new script context.
errorChain(scriptContextInit(&SCENE.scriptContext));
errorChain(scriptContextExecFile(&SCENE.scriptContext, script));
errorOk();
}
@@ -87,6 +86,7 @@ void sceneDispose(void) {
const char_t *strErr = lua_tostring(SCENE.scriptContext.luaState, -1);
lua_pop(SCENE.scriptContext.luaState, 1);
debugPrint("Failed to call function '%s': %s\n", "sceneDispose", strErr);
debugFlush();
}
} else {
lua_pop(SCENE.scriptContext.luaState, 1);

View File

@@ -37,6 +37,7 @@ int moduleSysPrint(lua_State *L) {
luaL_pushresult(&b);
const char *msg = lua_tostring(L, -1);
debugPrint("%s\n", msg);
debugFlush();
return 0; // no values returned to Lua
}

View File

@@ -45,7 +45,7 @@ errorret_t scriptContextExec(scriptcontext_t *context, const char_t *script) {
if(luaL_dostring(context->luaState, script) != LUA_OK) {
const char_t *strErr = lua_tostring(context->luaState, -1);
lua_pop(context->luaState, 1);
errorThrow("Failed to execute Lua: ", strErr);
errorThrow("Failed to execute Lua: %s", strErr);
}
errorOk();

View File

@@ -21,4 +21,8 @@ elseif(DUSK_TARGET_SYSTEM STREQUAL "psp")
PUBLIC
THREAD_PTHREAD=1
)
elseif(DUSK_TARGET_SYSTEM STREQUAL "gamecube" OR DUSK_TARGET_SYSTEM STREQUAL "wii")
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
)
endif()

View File

@@ -21,4 +21,9 @@ elseif(DUSK_TARGET_SYSTEM STREQUAL "psp")
PUBLIC
TIME_FIXED=1
)
elseif(DUSK_TARGET_SYSTEM STREQUAL "gamecube" OR DUSK_TARGET_SYSTEM STREQUAL "wii")
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
TIME_FIXED=1
)
endif()

View File

@@ -11,6 +11,8 @@
#include "display/spritebatch.h"
#include "display/screen.h"
#include "debug/debug.h"
ui_t UI;
errorret_t uiInit(void) {

View File

@@ -27,7 +27,6 @@ void memoryFree(void *ptr) {
MEMORY_POINTERS_IN_USE--;
}
void memoryCopy(void *dest, const void *src, const size_t size) {
assertNotNull(dest, "Cannot copy to NULL memory.");
assertNotNull(src, "Cannot copy from NULL memory.");