Implement runtime texture loading

This commit is contained in:
Daniel Ramírez 2025-05-21 03:23:47 +02:00
parent f22b9e1bb6
commit cb1d89065f
26 changed files with 590 additions and 272 deletions

View File

@ -5,9 +5,30 @@
MeshFilter::MeshFilter(const FString &path) { MeshFilter::MeshFilter(const FString &path) {
// Load the mesh from the file // Load the mesh from the file
auto vertices = Utils::File::ReadObjFile(path); auto [shapes, materials] = Utils::File::ReadObjFile(path);
mesh = LoadResource<Mesh>(vertices); TVector<PtrShr<Texture>> textures;
for (const auto &material : materials) {
auto texture_binary = Utils::File::ReadBinaryFile<unsigned int>(
path.substr(0, path.find_last_of("/"))
.append("/")
.append(material.diffuse_texname.substr(
0, material.diffuse_texname.find_last_of(".")))
.append(".img.bin"));
// auto palette_binary = Utils::File::ReadBinaryFile<unsigned short>(
// path.substr(0, path.find_last_of("/"))
// .append("/")
// .append(material.diffuse_texname.substr(
// 0, material.diffuse_texname.find_last_of(".")))
// .append(".pal.bin"));
auto palette_binary = TVector<unsigned short>();
textures.push_back(LoadResource<Texture>(texture_binary, palette_binary));
}
mesh = LoadResource<Mesh>(shapes, materials, textures);
} }
PtrUnq<MeshFilter> MeshFilter::Initialize(const FString &path) { PtrUnq<MeshFilter> MeshFilter::Initialize(const FString &path) {

View File

@ -44,12 +44,19 @@ PtrUnq<Engine> &Engine::GetInstance() {
} }
void Engine::run() { void Engine::run() {
// TEST ------------------------ Renderer::GetInstance();
// TEST ------------------------
// Create a MeshInstance3D // Create a MeshInstance3D
PtrShr<MeshInstance3D> meshInstance = PtrShr<MeshInstance3D> meshInstance =
NewNode<MeshInstance3D>("meshes/mococo/mococo.obj"); NewNode<MeshInstance3D>("meshes/mococo/mococo.obj");
// PtrShr<MeshInstance3D> meshIPnstance =
// NewNode<MeshInstance3D>("meshes/cube/cube-tex.obj");
// PtrShr<MeshInstance3D> meshInstance =
// NewNode<MeshInstance3D>("meshes/quad/quad.obj");
// ----------------------------- // -----------------------------
while (pmMainLoop()) { while (pmMainLoop()) {
@ -63,6 +70,9 @@ void Engine::processInput() { Input::Update(); }
void Engine::update() { void Engine::update() {
// Update // Update
if (Input::IsButtonDown(EButton::A)) {
iprintf("A button pressed\n");
}
} }
void Engine::render() { void Engine::render() {
@ -84,8 +94,7 @@ void Engine::render() {
Engine::Engine() { Engine::Engine() {
// Initialize nitroFS // Initialize nitroFS
char nitroFSPath[32]; char nitroFSPath[] = {"assets"};
strcpy(nitroFSPath, "assets");
char *nitroFSPathptr = nitroFSPath; char *nitroFSPathptr = nitroFSPath;
if (!nitroFSInit(&nitroFSPathptr)) if (!nitroFSInit(&nitroFSPathptr))
exit(-1); exit(-1);

View File

@ -7,6 +7,8 @@
#include <nds.h> #include <nds.h>
#include <stdio.h> #include <stdio.h>
#include "input/input.h"
PtrUnq<Renderer> Renderer::Instance; PtrUnq<Renderer> Renderer::Instance;
PtrUnq<Renderer> &Renderer::GetInstance() { PtrUnq<Renderer> &Renderer::GetInstance() {
@ -16,34 +18,45 @@ PtrUnq<Renderer> &Renderer::GetInstance() {
return Instance; return Instance;
} }
// TEST -------------------
float rotation = 45.f;
// TEST -------------------
void Renderer::beginFrame() { void Renderer::beginFrame() {
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
glLoadIdentity(); glLoadIdentity();
gluPerspective(70, 256.0 / 192.0, 0.1, 100); gluPerspective(70, 256.0 / 192.0, 0.1, 100);
// glPolyFmt(POLY_ALPHA(31) | POLY_CULL_BACK);
// glMatrixMode(GL_TEXTURE);
// glLoadIdentity();
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glPushMatrix(); // Save the current matrix state
glPolyFmt(POLY_ALPHA(31) | POLY_CULL_BACK);
glLoadIdentity(); // Load the identity matrix glLoadIdentity(); // Load the identity matrix
// TEST -------------------
if (Input::IsButtonHeld(EButton::LEFT))
rotation++;
if (Input::IsButtonHeld(EButton::RIGHT))
rotation--;
// TEST -------------------
// Render scene // Render scene
glTranslatef(0.f, 0.f, -4.f); // Move the camera back glTranslatef(0.f, -.75f, -2.f); // Move the camera back
glRotatef(45, 1, 1, 0); // Rotation glRotatef(rotation, 1, 1, 0); // Rotation
} }
void Renderer::render(const Transform &transform, void Renderer::render(const Transform &transform,
const MeshFilter &meshFilter) { const MeshFilter &meshFilter) {
// drawCube(1.0f); // Draw a cube glPolyFmt(POLY_ALPHA(31) | POLY_CULL_BACK);
glBindTexture(0, meshFilter.getMesh()->getTextures()[0]->getId());
glBegin(GL_TRIANGLES); glBegin(GL_TRIANGLES);
for (const auto &shapes : meshFilter.getMesh()->getVertices()) { glColor3b(255, 255, 255);
for (const auto &vertex : shapes) { for (const auto &shape : meshFilter.getMesh()->getShapes()) {
glColor3f(1.0, 0.0, 0.0); // Red for (const auto &vertex : shape.vertices) {
glVertex3f(vertex.position.x, vertex.position.y, vertex.position.z); // glNormal3f(vertex.normal.x, vertex.normal.y, vertex.normal.z);
glNormal3f(vertex.normal.x, vertex.normal.y, vertex.normal.z);
glTexCoord2f(vertex.texCoords.x, vertex.texCoords.y); glTexCoord2f(vertex.texCoords.x, vertex.texCoords.y);
glVertex3f(vertex.position.x, vertex.position.y, vertex.position.z);
} }
} }
@ -110,19 +123,30 @@ void Renderer::drawCube(float size) {
Renderer::Renderer() { Renderer::Renderer() {
// Initialize the video subsystem // Initialize the video subsystem
lcdMainOnTop(); // Set the main screen on top lcdMainOnTop(); // Set the main screen on top
videoSetMode(MODE_0_3D); // Set 3D rendering mode videoSetMode(MODE_0_3D); // Set 3D rendering mode
videoSetModeSub(MODE_5_2D); // Set 2D rendering mode for the sub-screen videoSetModeSub(MODE_5_2D); // Set 2D rendering mode for the sub-screen
vramSetBankA(VRAM_A_MAIN_BG); // Allocate VRAM for textures vramSetBankB(VRAM_B_TEXTURE);
vramSetBankC(VRAM_C_TEXTURE);
vramSetBankD(VRAM_D_TEXTURE);
vramSetBankE(VRAM_E_TEX_PALETTE);
vramSetBankF(VRAM_F_LCD);
// vramSetBankG(VRAM_G_TEX_PALETTE);
// Initialize the 3D engine // Initialize the 3D engine
glInit(); glInit();
glEnable(GL_TEXTURE_2D);
glViewport(0, 0, 255, 191); // Set viewport
glEnable(GL_ANTIALIAS); glEnable(GL_ANTIALIAS);
glClearColor(0, 0, 0, 31); // Set clear color (black) glClearColor(0, 0, 0, 31); // Set clear color (black)
glClearPolyID(63); // Set default polygon ID glClearPolyID(63); // Set default polygon ID
glClearDepth(GL_MAX_DEPTH); // Set clear depth glClearDepth(GL_MAX_DEPTH); // Set clear depth
glViewport(0, 0, 255, 191); // Set viewport glEnable(GL_BLEND);
vramSetBankA(VRAM_A_TEXTURE); // Allocate VRAM for textures
vramSetBankB(VRAM_B_LCD);
// Debug // Debug
consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 23, 2, false, true);
consoleDemoInit(); consoleDemoInit();
} }

View File

@ -4,20 +4,20 @@
#include <stdio.h> #include <stdio.h>
// Static members // Static members
u32 Input::KeysHeldState = 0; u32 Input::ButtonsHeldState = 0;
u32 Input::KeysDownState = 0; u32 Input::ButtonsDownState = 0;
u32 Input::KeysUpState = 0; u32 Input::ButtonsUpState = 0;
void Input::Update() { void Input::Update() {
// Update key states // Update key states
scanKeys(); scanKeys();
KeysHeldState = keysHeld(); ButtonsHeldState = keysHeld();
KeysDownState = keysDown(); ButtonsDownState = keysDown();
KeysUpState = keysUp(); ButtonsUpState = keysUp();
} }
bool Input::IsKeyHeld(EKey key) { return KeysHeldState & key; } bool Input::IsButtonHeld(EButton key) { return ButtonsHeldState & key; }
bool Input::IsKeyDown(EKey key) { return KeysDownState & key; } bool Input::IsButtonDown(EButton key) { return ButtonsDownState & key; }
bool Input::IsKeyUp(EKey key) { return KeysUpState & key; } bool Input::IsButtonUp(EButton key) { return ButtonsUpState & key; }

View File

@ -1,8 +1,11 @@
#include "resources/mesh.h" #include "resources/mesh.h"
Mesh Mesh::Load(const TVector<TVector<FVertex>> &vertices) { Mesh Mesh::Load(const TVector<FShape> &shapes,
return std::move(Mesh(vertices)); const TVector<FMaterial> &materials,
const TVector<PtrShr<Texture>> &textures) {
return std::move(Mesh(shapes, materials, textures));
} }
Mesh::Mesh(const TVector<TVector<FVertex>> &vertices) Mesh::Mesh(const TVector<FShape> &shapes, const TVector<FMaterial> &materials,
: Super(), vertices(vertices) {} const TVector<PtrShr<Texture>> &textures)
: Super(), shapes(shapes), materials(materials), textures(textures) {}

View File

@ -0,0 +1,27 @@
#include "resources/texture.h"
#include <nds/arm9/videoGL.h>
Texture::~Texture() {
// TODO: Check what the hell is happening here
// glBindTexture(0, id);
// glColorTableEXT(0, 0, 0, 0, 0, nullptr);
// glDeleteTextures(1, &id);
// iprintf("Texture %d deleted\n", id);
}
Texture Texture::Load(TVector<unsigned int> &data,
TVector<unsigned short> &palette) {
return Texture(data, palette);
}
Texture::Texture(TVector<unsigned int> &data, TVector<unsigned short> &palette)
: Super() {
glGenTextures(1, &id);
glBindTexture(0, id);
// TODO: Manage different texture formats
glTexImage2D(0, 0, GL_RGBA, TEXTURE_SIZE_128, TEXTURE_SIZE_128, 0,
TEXGEN_TEXCOORD, (u8 *)data.data());
// glColorTableEXT(0, 0, 32, 0, 0, (u16 *)palette.data());
}

View File

@ -2,6 +2,7 @@
#define TINYOBJLOADER_IMPLEMENTATION #define TINYOBJLOADER_IMPLEMENTATION
#include "tiny_obj_loader.h" #include "tiny_obj_loader.h"
#include <algorithm>
namespace Utils::File { namespace Utils::File {
@ -11,15 +12,13 @@ FString ReadTextFile(const FString &path) {
FString result; FString result;
FILE *f = fopen(AssetPath(path).c_str(), "rb"); FILE *f = fopen(AssetPath(path).c_str(), "rb");
if (!f) { if (!f) {
iprintf("No se pudo abrir el archivo\n"); // TODO: iprintf("No se pudo abrir el archivo\n");
} else { } else {
iprintf("Archivo abierto\n");
fseek(f, 0, SEEK_END); fseek(f, 0, SEEK_END);
size_t size = ftell(f); size_t size = ftell(f);
rewind(f); rewind(f);
TVector<uint8_t> buffer(size); TVector<u8> buffer(size);
fread(buffer.data(), 1, size, f); fread(buffer.data(), 1, size, f);
fclose(f); fclose(f);
@ -30,28 +29,29 @@ FString ReadTextFile(const FString &path) {
return std::move(result); return std::move(result);
} }
TVector<TVector<FVertex>> ReadObjFile(const FString &path) { TTuple<TVector<FShape>, TVector<FMaterial>> ReadObjFile(const FString &path) {
FString nitroPath = AssetPath(path);
FString baseFolder =
nitroPath.substr(0, nitroPath.find_last_of("/")).append("/");
tinyobj::attrib_t attrib; tinyobj::attrib_t attrib;
TVector<tinyobj::shape_t> shapes; TVector<tinyobj::shape_t> shapes;
TVector<tinyobj::material_t> materials; TVector<tinyobj::material_t> materials;
FString err; FString err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err,
AssetPath(path).c_str(), nitroPath.c_str(), baseFolder.c_str(), true);
AssetPath(path + "/..").c_str(), true);
if (ret) if (!ret) {
iprintf("Cargado correctamente\n"); // TODO: print error
else return {TVector<FShape>(), TVector<FMaterial>()};
iprintf("Error al cargar el archivo: %s\n", err.c_str()); }
iprintf("Numero de materiales: %d\n", materials.size()); TVector<FShape> shape_vertices;
TVector<TVector<FVertex>> vertices;
// Loop over shapes // Loop over shapes
for (size_t s = 0; s < shapes.size(); s++) { for (size_t s = 0; s < shapes.size(); s++) {
vertices.push_back({}); shape_vertices.push_back({});
// Loop over faces(polygon) // Loop over faces(polygon)
size_t index_offset = 0; size_t index_offset = 0;
for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) { for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) {
@ -88,20 +88,23 @@ TVector<TVector<FVertex>> ReadObjFile(const FString &path) {
tinyobj::real_t ty = tinyobj::real_t ty =
attrib.texcoords[2 * size_t(idx.texcoord_index) + 1]; attrib.texcoords[2 * size_t(idx.texcoord_index) + 1];
// I lost 3 days bacause of this :')
ty = 1.0f - std::clamp(ty, 0.0f, 1.0f);
texCoords = FVector2(tx, ty); texCoords = FVector2(tx, ty);
} }
vertices[s].push_back(FVertex(positions, normals, texCoords)); shape_vertices[s].vertices.push_back(
FVertex(positions, normals, texCoords));
} }
index_offset += fv; index_offset += fv;
// per-face material // per-face material
// shapes[s].mesh.material_ids[f]; // shape_materials.push_back(materials[shapes[s].mesh.material_ids[f]]);
} }
} }
return std::move(vertices); return {shape_vertices, materials};
} }
} // namespace Utils::File } // namespace Utils::File

View File

@ -23,6 +23,7 @@ THE SOFTWARE.
*/ */
// //
// xrbDS 0.1 : Change TINYOBJ_SSCANF_BUFFER_SIZE to 512
// version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124) // version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124)
// version 1.0.5 : Ignore `Tr` when `d` exists in MTL(#43) // version 1.0.5 : Ignore `Tr` when `d` exists in MTL(#43)
// version 1.0.4 : Support multiple filenames for 'mtllib'(#112) // version 1.0.4 : Support multiple filenames for 'mtllib'(#112)
@ -98,15 +99,15 @@ namespace tinyobj {
// cube_left | cube_right // cube_left | cube_right
#ifdef TINYOBJLOADER_USE_DOUBLE #ifdef TINYOBJLOADER_USE_DOUBLE
//#pragma message "using double" // #pragma message "using double"
typedef double real_t; typedef double real_t;
#else #else
//#pragma message "using float" // #pragma message "using float"
typedef float real_t; typedef float real_t;
#endif #endif
typedef enum { typedef enum {
TEXTURE_TYPE_NONE, // default TEXTURE_TYPE_NONE, // default
TEXTURE_TYPE_SPHERE, TEXTURE_TYPE_SPHERE,
TEXTURE_TYPE_CUBE_TOP, TEXTURE_TYPE_CUBE_TOP,
TEXTURE_TYPE_CUBE_BOTTOM, TEXTURE_TYPE_CUBE_BOTTOM,
@ -118,18 +119,18 @@ typedef enum {
typedef struct { typedef struct {
texture_type_t type; // -type (default TEXTURE_TYPE_NONE) texture_type_t type; // -type (default TEXTURE_TYPE_NONE)
real_t sharpness; // -boost (default 1.0?) real_t sharpness; // -boost (default 1.0?)
real_t brightness; // base_value in -mm option (default 0) real_t brightness; // base_value in -mm option (default 0)
real_t contrast; // gain_value in -mm option (default 1) real_t contrast; // gain_value in -mm option (default 1)
real_t origin_offset[3]; // -o u [v [w]] (default 0 0 0) real_t origin_offset[3]; // -o u [v [w]] (default 0 0 0)
real_t scale[3]; // -s u [v [w]] (default 1 1 1) real_t scale[3]; // -s u [v [w]] (default 1 1 1)
real_t turbulence[3]; // -t u [v [w]] (default 0 0 0) real_t turbulence[3]; // -t u [v [w]] (default 0 0 0)
// int texture_resolution; // -texres resolution (default = ?) TODO // int texture_resolution; // -texres resolution (default = ?) TODO
bool clamp; // -clamp (default false) bool clamp; // -clamp (default false)
char imfchan; // -imfchan (the default for bump is 'l' and for decal is 'm') char imfchan; // -imfchan (the default for bump is 'l' and for decal is 'm')
bool blendu; // -blendu (default on) bool blendu; // -blendu (default on)
bool blendv; // -blendv (default on) bool blendv; // -blendv (default on)
real_t bump_multiplier; // -bm (for bump maps only, default 1.0) real_t bump_multiplier; // -bm (for bump maps only, default 1.0)
} texture_option_t; } texture_option_t;
typedef struct { typedef struct {
@ -141,20 +142,20 @@ typedef struct {
real_t transmittance[3]; real_t transmittance[3];
real_t emission[3]; real_t emission[3];
real_t shininess; real_t shininess;
real_t ior; // index of refraction real_t ior; // index of refraction
real_t dissolve; // 1 == opaque; 0 == fully transparent real_t dissolve; // 1 == opaque; 0 == fully transparent
// illumination model (see http://www.fileformat.info/format/material/) // illumination model (see http://www.fileformat.info/format/material/)
int illum; int illum;
int dummy; // Suppress padding warning. int dummy; // Suppress padding warning.
std::string ambient_texname; // map_Ka std::string ambient_texname; // map_Ka
std::string diffuse_texname; // map_Kd std::string diffuse_texname; // map_Kd
std::string specular_texname; // map_Ks std::string specular_texname; // map_Ks
std::string specular_highlight_texname; // map_Ns std::string specular_highlight_texname; // map_Ns
std::string bump_texname; // map_bump, bump std::string bump_texname; // map_bump, bump
std::string displacement_texname; // disp std::string displacement_texname; // disp
std::string alpha_texname; // map_d std::string alpha_texname; // map_d
texture_option_t ambient_texopt; texture_option_t ambient_texopt;
texture_option_t diffuse_texopt; texture_option_t diffuse_texopt;
@ -166,20 +167,20 @@ typedef struct {
// PBR extension // PBR extension
// http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr
real_t roughness; // [0, 1] default 0 real_t roughness; // [0, 1] default 0
real_t metallic; // [0, 1] default 0 real_t metallic; // [0, 1] default 0
real_t sheen; // [0, 1] default 0 real_t sheen; // [0, 1] default 0
real_t clearcoat_thickness; // [0, 1] default 0 real_t clearcoat_thickness; // [0, 1] default 0
real_t clearcoat_roughness; // [0, 1] default 0 real_t clearcoat_roughness; // [0, 1] default 0
real_t anisotropy; // aniso. [0, 1] default 0 real_t anisotropy; // aniso. [0, 1] default 0
real_t anisotropy_rotation; // anisor. [0, 1] default 0 real_t anisotropy_rotation; // anisor. [0, 1] default 0
real_t pad0; real_t pad0;
real_t pad1; real_t pad1;
std::string roughness_texname; // map_Pr std::string roughness_texname; // map_Pr
std::string metallic_texname; // map_Pm std::string metallic_texname; // map_Pm
std::string sheen_texname; // map_Ps std::string sheen_texname; // map_Ps
std::string emissive_texname; // map_Ke std::string emissive_texname; // map_Ke
std::string normal_texname; // norm. For normal mapping. std::string normal_texname; // norm. For normal mapping.
texture_option_t roughness_texopt; texture_option_t roughness_texopt;
texture_option_t metallic_texopt; texture_option_t metallic_texopt;
@ -210,11 +211,11 @@ typedef struct {
typedef struct { typedef struct {
std::vector<index_t> indices; std::vector<index_t> indices;
std::vector<unsigned char> num_face_vertices; // The number of vertices per std::vector<unsigned char> num_face_vertices; // The number of vertices per
// face. 3 = polygon, 4 = quad, // face. 3 = polygon, 4 = quad,
// ... Up to 255. // ... Up to 255.
std::vector<int> material_ids; // per-face material ID std::vector<int> material_ids; // per-face material ID
std::vector<tag_t> tags; // SubD tag std::vector<tag_t> tags; // SubD tag
} mesh_t; } mesh_t;
typedef struct { typedef struct {
@ -224,9 +225,9 @@ typedef struct {
// Vertex attributes // Vertex attributes
typedef struct { typedef struct {
std::vector<real_t> vertices; // 'v' std::vector<real_t> vertices; // 'v'
std::vector<real_t> normals; // 'vn' std::vector<real_t> normals; // 'vn'
std::vector<real_t> texcoords; // 'vt' std::vector<real_t> texcoords; // 'vt'
} attrib_t; } attrib_t;
typedef struct callback_t_ { typedef struct callback_t_ {
@ -254,18 +255,12 @@ typedef struct callback_t_ {
void (*object_cb)(void *user_data, const char *name); void (*object_cb)(void *user_data, const char *name);
callback_t_() callback_t_()
: vertex_cb(NULL), : vertex_cb(NULL), normal_cb(NULL), texcoord_cb(NULL), index_cb(NULL),
normal_cb(NULL), usemtl_cb(NULL), mtllib_cb(NULL), group_cb(NULL), object_cb(NULL) {}
texcoord_cb(NULL),
index_cb(NULL),
usemtl_cb(NULL),
mtllib_cb(NULL),
group_cb(NULL),
object_cb(NULL) {}
} callback_t; } callback_t;
class MaterialReader { class MaterialReader {
public: public:
MaterialReader() {} MaterialReader() {}
virtual ~MaterialReader(); virtual ~MaterialReader();
@ -276,7 +271,7 @@ class MaterialReader {
}; };
class MaterialFileReader : public MaterialReader { class MaterialFileReader : public MaterialReader {
public: public:
explicit MaterialFileReader(const std::string &mtl_basedir) explicit MaterialFileReader(const std::string &mtl_basedir)
: m_mtlBaseDir(mtl_basedir) {} : m_mtlBaseDir(mtl_basedir) {}
virtual ~MaterialFileReader() {} virtual ~MaterialFileReader() {}
@ -284,12 +279,12 @@ class MaterialFileReader : public MaterialReader {
std::vector<material_t> *materials, std::vector<material_t> *materials,
std::map<std::string, int> *matMap, std::string *err); std::map<std::string, int> *matMap, std::string *err);
private: private:
std::string m_mtlBaseDir; std::string m_mtlBaseDir;
}; };
class MaterialStreamReader : public MaterialReader { class MaterialStreamReader : public MaterialReader {
public: public:
explicit MaterialStreamReader(std::istream &inStream) explicit MaterialStreamReader(std::istream &inStream)
: m_inStream(inStream) {} : m_inStream(inStream) {}
virtual ~MaterialStreamReader() {} virtual ~MaterialStreamReader() {}
@ -297,7 +292,7 @@ class MaterialStreamReader : public MaterialReader {
std::vector<material_t> *materials, std::vector<material_t> *materials,
std::map<std::string, int> *matMap, std::string *err); std::map<std::string, int> *matMap, std::string *err);
private: private:
std::istream &m_inStream; std::istream &m_inStream;
}; };
@ -341,9 +336,9 @@ void LoadMtl(std::map<std::string, int> *material_map,
std::vector<material_t> *materials, std::istream *inStream, std::vector<material_t> *materials, std::istream *inStream,
std::string *warning); std::string *warning);
} // namespace tinyobj } // namespace tinyobj
#endif // TINY_OBJ_LOADER_H_ #endif // TINY_OBJ_LOADER_H_
#ifdef TINYOBJLOADER_IMPLEMENTATION #ifdef TINYOBJLOADER_IMPLEMENTATION
#include <cassert> #include <cassert>
@ -361,7 +356,7 @@ namespace tinyobj {
MaterialReader::~MaterialReader() {} MaterialReader::~MaterialReader() {}
#define TINYOBJ_SSCANF_BUFFER_SIZE (4096) #define TINYOBJ_SSCANF_BUFFER_SIZE (512)
struct vertex_index { struct vertex_index {
int v_idx, vt_idx, vn_idx; int v_idx, vt_idx, vn_idx;
@ -401,31 +396,35 @@ static std::istream &safeGetline(std::istream &is, std::string &t) {
for (;;) { for (;;) {
int c = sb->sbumpc(); int c = sb->sbumpc();
switch (c) { switch (c) {
case '\n': case '\n':
return is; return is;
case '\r': case '\r':
if (sb->sgetc() == '\n') sb->sbumpc(); if (sb->sgetc() == '\n')
return is; sb->sbumpc();
case EOF: return is;
// Also handle the case when the last line has no line ending case EOF:
if (t.empty()) is.setstate(std::ios::eofbit); // Also handle the case when the last line has no line ending
return is; if (t.empty())
default: is.setstate(std::ios::eofbit);
t += static_cast<char>(c); return is;
default:
t += static_cast<char>(c);
} }
} }
} }
#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) #define IS_SPACE(x) (((x) == ' ') || ((x) == '\t'))
#define IS_DIGIT(x) \ #define IS_DIGIT(x) \
(static_cast<unsigned int>((x) - '0') < static_cast<unsigned int>(10)) (static_cast<unsigned int>((x) - '0') < static_cast<unsigned int>(10))
#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) #define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0'))
// Make index zero-base, and also support relative index. // Make index zero-base, and also support relative index.
static inline int fixIndex(int idx, int n) { static inline int fixIndex(int idx, int n) {
if (idx > 0) return idx - 1; if (idx > 0)
if (idx == 0) return 0; return idx - 1;
return n + idx; // negative value = relative if (idx == 0)
return 0;
return n + idx; // negative value = relative
} }
static inline std::string parseString(const char **token) { static inline std::string parseString(const char **token) {
@ -521,9 +520,11 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) {
} }
// We must make sure we actually got something. // We must make sure we actually got something.
if (read == 0) goto fail; if (read == 0)
goto fail;
// We allow numbers of form "#", "###" etc. // We allow numbers of form "#", "###" etc.
if (!end_not_reached) goto assemble; if (!end_not_reached)
goto assemble;
// Read the decimal part. // Read the decimal part.
if (*curr == '.') { if (*curr == '.') {
@ -548,7 +549,8 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) {
goto assemble; goto assemble;
} }
if (!end_not_reached) goto assemble; if (!end_not_reached)
goto assemble;
// Read the exponent part. // Read the exponent part.
if (*curr == 'e' || *curr == 'E') { if (*curr == 'e' || *curr == 'E') {
@ -574,13 +576,14 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) {
end_not_reached = (curr != s_end); end_not_reached = (curr != s_end);
} }
exponent *= (exp_sign == '+' ? 1 : -1); exponent *= (exp_sign == '+' ? 1 : -1);
if (read == 0) goto fail; if (read == 0)
goto fail;
} }
assemble: assemble:
*result = *result = (sign == '+' ? 1 : -1) *
(sign == '+' ? 1 : -1) * (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent)
(exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent) : mantissa); : mantissa);
return true; return true;
fail: fail:
return false; return false;
@ -597,16 +600,16 @@ static inline real_t parseReal(const char **token, double default_value = 0.0) {
} }
static inline void parseReal2(real_t *x, real_t *y, const char **token, static inline void parseReal2(real_t *x, real_t *y, const char **token,
const double default_x = 0.0, const double default_x = 0.0,
const double default_y = 0.0) { const double default_y = 0.0) {
(*x) = parseReal(token, default_x); (*x) = parseReal(token, default_x);
(*y) = parseReal(token, default_y); (*y) = parseReal(token, default_y);
} }
static inline void parseReal3(real_t *x, real_t *y, real_t *z, const char **token, static inline void parseReal3(real_t *x, real_t *y, real_t *z,
const double default_x = 0.0, const char **token, const double default_x = 0.0,
const double default_y = 0.0, const double default_y = 0.0,
const double default_z = 0.0) { const double default_z = 0.0) {
(*x) = parseReal(token, default_x); (*x) = parseReal(token, default_x);
(*y) = parseReal(token, default_y); (*y) = parseReal(token, default_y);
(*z) = parseReal(token, default_z); (*z) = parseReal(token, default_z);
@ -638,8 +641,9 @@ static inline bool parseOnOff(const char **token, bool default_value = true) {
return ret; return ret;
} }
static inline texture_type_t parseTextureType( static inline texture_type_t
const char **token, texture_type_t default_value = TEXTURE_TYPE_NONE) { parseTextureType(const char **token,
texture_type_t default_value = TEXTURE_TYPE_NONE) {
(*token) += strspn((*token), " \t"); (*token) += strspn((*token), " \t");
const char *end = (*token) + strcspn((*token), " \t\r"); const char *end = (*token) + strcspn((*token), " \t\r");
texture_type_t ty = default_value; texture_type_t ty = default_value;
@ -715,7 +719,7 @@ static vertex_index parseTriple(const char **token, int vsize, int vnsize,
} }
// i/j/k // i/j/k
(*token)++; // skip '/' (*token)++; // skip '/'
vi.vn_idx = fixIndex(atoi((*token)), vnsize); vi.vn_idx = fixIndex(atoi((*token)), vnsize);
(*token) += strcspn((*token), "/ \t\r"); (*token) += strcspn((*token), "/ \t\r");
return vi; return vi;
@ -723,7 +727,7 @@ static vertex_index parseTriple(const char **token, int vsize, int vnsize,
// Parse raw triples: i, i/j/k, i//k, i/j // Parse raw triples: i, i/j/k, i//k, i/j
static vertex_index parseRawTriple(const char **token) { static vertex_index parseRawTriple(const char **token) {
vertex_index vi(static_cast<int>(0)); // 0 is an invalid index in OBJ vertex_index vi(static_cast<int>(0)); // 0 is an invalid index in OBJ
vi.v_idx = atoi((*token)); vi.v_idx = atoi((*token));
(*token) += strcspn((*token), "/ \t\r"); (*token) += strcspn((*token), "/ \t\r");
@ -748,7 +752,7 @@ static vertex_index parseRawTriple(const char **token) {
} }
// i/j/k // i/j/k
(*token)++; // skip '/' (*token)++; // skip '/'
vi.vn_idx = atoi((*token)); vi.vn_idx = atoi((*token));
(*token) += strcspn((*token), "/ \t\r"); (*token) += strcspn((*token), "/ \t\r");
return vi; return vi;
@ -785,7 +789,7 @@ static bool ParseTextureNameAndOption(std::string *texname,
texopt->turbulence[2] = 0.0f; texopt->turbulence[2] = 0.0f;
texopt->type = TEXTURE_TYPE_NONE; texopt->type = TEXTURE_TYPE_NONE;
const char *token = linebuf; // Assume line ends with NULL const char *token = linebuf; // Assume line ends with NULL
while (!IS_NEW_LINE((*token))) { while (!IS_NEW_LINE((*token))) {
if ((0 == strncmp(token, "-blendu", 7)) && IS_SPACE((token[7]))) { if ((0 == strncmp(token, "-blendu", 7)) && IS_SPACE((token[7]))) {
@ -806,15 +810,15 @@ static bool ParseTextureNameAndOption(std::string *texname,
} else if ((0 == strncmp(token, "-o", 2)) && IS_SPACE((token[2]))) { } else if ((0 == strncmp(token, "-o", 2)) && IS_SPACE((token[2]))) {
token += 3; token += 3;
parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]), parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]),
&(texopt->origin_offset[2]), &token); &(texopt->origin_offset[2]), &token);
} else if ((0 == strncmp(token, "-s", 2)) && IS_SPACE((token[2]))) { } else if ((0 == strncmp(token, "-s", 2)) && IS_SPACE((token[2]))) {
token += 3; token += 3;
parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]), parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]),
&token, 1.0, 1.0, 1.0); &token, 1.0, 1.0, 1.0);
} else if ((0 == strncmp(token, "-t", 2)) && IS_SPACE((token[2]))) { } else if ((0 == strncmp(token, "-t", 2)) && IS_SPACE((token[2]))) {
token += 3; token += 3;
parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]), parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]),
&(texopt->turbulence[2]), &token); &(texopt->turbulence[2]), &token);
} else if ((0 == strncmp(token, "-type", 5)) && IS_SPACE((token[5]))) { } else if ((0 == strncmp(token, "-type", 5)) && IS_SPACE((token[5]))) {
token += 5; token += 5;
texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE); texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE);
@ -822,7 +826,7 @@ static bool ParseTextureNameAndOption(std::string *texname,
token += 9; token += 9;
token += strspn(token, " \t"); token += strspn(token, " \t");
const char *end = token + strcspn(token, " \t\r"); const char *end = token + strcspn(token, " \t\r");
if ((end - token) == 1) { // Assume one char for -imfchan if ((end - token) == 1) { // Assume one char for -imfchan
texopt->imfchan = (*token); texopt->imfchan = (*token);
} }
token = end; token = end;
@ -831,12 +835,12 @@ static bool ParseTextureNameAndOption(std::string *texname,
parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0); parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0);
} else { } else {
// Assume texture filename // Assume texture filename
token += strspn(token, " \t"); // skip space token += strspn(token, " \t"); // skip space
size_t len = strcspn(token, " \t\r"); // untile next space size_t len = strcspn(token, " \t\r"); // untile next space
texture_name = std::string(token, token + len); texture_name = std::string(token, token + len);
token += len; token += len;
token += strspn(token, " \t"); // skip space token += strspn(token, " \t"); // skip space
found_texname = true; found_texname = true;
} }
@ -887,10 +891,11 @@ static void InitMaterial(material_t *material) {
material->unknown_parameter.clear(); material->unknown_parameter.clear();
} }
static bool exportFaceGroupToShape( static bool
shape_t *shape, const std::vector<std::vector<vertex_index> > &faceGroup, exportFaceGroupToShape(shape_t *shape,
const std::vector<tag_t> &tags, const int material_id, const std::vector<std::vector<vertex_index>> &faceGroup,
const std::string &name, bool triangulate) { const std::vector<tag_t> &tags, const int material_id,
const std::string &name, bool triangulate) {
if (faceGroup.empty()) { if (faceGroup.empty()) {
return false; return false;
} }
@ -940,7 +945,7 @@ static bool exportFaceGroupToShape(
shape->mesh.num_face_vertices.push_back( shape->mesh.num_face_vertices.push_back(
static_cast<unsigned char>(npolys)); static_cast<unsigned char>(npolys));
shape->mesh.material_ids.push_back(material_id); // per face shape->mesh.material_ids.push_back(material_id); // per face
} }
} }
@ -1004,9 +1009,11 @@ void LoadMtl(std::map<std::string, int> *material_map,
token += strspn(token, " \t"); token += strspn(token, " \t");
assert(token); assert(token);
if (token[0] == '\0') continue; // empty line if (token[0] == '\0')
continue; // empty line
if (token[0] == '#') continue; // comment line if (token[0] == '#')
continue; // comment line
// new mtl // new mtl
if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) {
@ -1305,7 +1312,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
token += 5; token += 5;
ParseTextureNameAndOption( ParseTextureNameAndOption(
&(material.normal_texname), &(material.normal_texopt), token, &(material.normal_texname), &(material.normal_texopt), token,
/* is_bump */ false); // @fixme { is_bump will be true? } /* is_bump */ false); // @fixme { is_bump will be true? }
continue; continue;
} }
@ -1431,7 +1438,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
std::vector<real_t> vn; std::vector<real_t> vn;
std::vector<real_t> vt; std::vector<real_t> vt;
std::vector<tag_t> tags; std::vector<tag_t> tags;
std::vector<std::vector<vertex_index> > faceGroup; std::vector<std::vector<vertex_index>> faceGroup;
std::string name; std::string name;
// material // material
@ -1464,9 +1471,11 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
token += strspn(token, " \t"); token += strspn(token, " \t");
assert(token); assert(token);
if (token[0] == '\0') continue; // empty line if (token[0] == '\0')
continue; // empty line
if (token[0] == '#') continue; // comment line if (token[0] == '#')
continue; // comment line
// vertex // vertex
if (token[0] == 'v' && IS_SPACE((token[1]))) { if (token[0] == 'v' && IS_SPACE((token[1]))) {
@ -1564,9 +1573,8 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
if (filenames.empty()) { if (filenames.empty()) {
if (err) { if (err) {
(*err) += (*err) += "WARN: Looks like empty filename for mtllib. Use default "
"WARN: Looks like empty filename for mtllib. Use default " "material. \n";
"material. \n";
} }
} else { } else {
bool found = false; bool found = false;
@ -1575,7 +1583,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
bool ok = (*readMatFn)(filenames[s].c_str(), materials, bool ok = (*readMatFn)(filenames[s].c_str(), materials,
&material_map, &err_mtl); &material_map, &err_mtl);
if (err && (!err_mtl.empty())) { if (err && (!err_mtl.empty())) {
(*err) += err_mtl; // This should be warn message. (*err) += err_mtl; // This should be warn message.
} }
if (ok) { if (ok) {
@ -1586,9 +1594,8 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
if (!found) { if (!found) {
if (err) { if (err) {
(*err) += (*err) += "WARN: Failed to load material file(s). Use default "
"WARN: Failed to load material file(s). Use default " "material.\n";
"material.\n";
} }
} }
} }
@ -1617,7 +1624,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
while (!IS_NEW_LINE(token[0])) { while (!IS_NEW_LINE(token[0])) {
std::string str = parseString(&token); std::string str = parseString(&token);
names.push_back(str); names.push_back(str);
token += strspn(token, " \t\r"); // skip tag token += strspn(token, " \t\r"); // skip tag
} }
assert(names.size() > 0); assert(names.size() > 0);
@ -1716,7 +1723,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
if (ret || shape.mesh.indices.size()) { if (ret || shape.mesh.indices.size()) {
shapes->push_back(shape); shapes->push_back(shape);
} }
faceGroup.clear(); // for safety faceGroup.clear(); // for safety
if (err) { if (err) {
(*err) += errss.str(); (*err) += errss.str();
@ -1737,7 +1744,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
// material // material
std::map<std::string, int> material_map; std::map<std::string, int> material_map;
int material_id = -1; // -1 = invalid int material_id = -1; // -1 = invalid
std::vector<index_t> indices; std::vector<index_t> indices;
std::vector<material_t> materials; std::vector<material_t> materials;
@ -1770,14 +1777,16 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
token += strspn(token, " \t"); token += strspn(token, " \t");
assert(token); assert(token);
if (token[0] == '\0') continue; // empty line if (token[0] == '\0')
continue; // empty line
if (token[0] == '#') continue; // comment line if (token[0] == '#')
continue; // comment line
// vertex // vertex
if (token[0] == 'v' && IS_SPACE((token[1]))) { if (token[0] == 'v' && IS_SPACE((token[1]))) {
token += 2; token += 2;
real_t x, y, z, w; // w is optional. default = 1.0 real_t x, y, z, w; // w is optional. default = 1.0
parseV(&x, &y, &z, &w, &token); parseV(&x, &y, &z, &w, &token);
if (callback.vertex_cb) { if (callback.vertex_cb) {
callback.vertex_cb(user_data, x, y, z, w); callback.vertex_cb(user_data, x, y, z, w);
@ -1799,7 +1808,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
// texcoord // texcoord
if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
token += 3; token += 3;
real_t x, y, z; // y and z are optional. default = 0.0 real_t x, y, z; // y and z are optional. default = 0.0
parseReal3(&x, &y, &z, &token); parseReal3(&x, &y, &z, &token);
if (callback.texcoord_cb) { if (callback.texcoord_cb) {
callback.texcoord_cb(user_data, x, y, z); callback.texcoord_cb(user_data, x, y, z);
@ -1873,9 +1882,8 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
if (filenames.empty()) { if (filenames.empty()) {
if (err) { if (err) {
(*err) += (*err) += "WARN: Looks like empty filename for mtllib. Use default "
"WARN: Looks like empty filename for mtllib. Use default " "material. \n";
"material. \n";
} }
} else { } else {
bool found = false; bool found = false;
@ -1884,7 +1892,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
bool ok = (*readMatFn)(filenames[s].c_str(), &materials, bool ok = (*readMatFn)(filenames[s].c_str(), &materials,
&material_map, &err_mtl); &material_map, &err_mtl);
if (err && (!err_mtl.empty())) { if (err && (!err_mtl.empty())) {
(*err) += err_mtl; // This should be warn message. (*err) += err_mtl; // This should be warn message.
} }
if (ok) { if (ok) {
@ -1895,9 +1903,8 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
if (!found) { if (!found) {
if (err) { if (err) {
(*err) += (*err) += "WARN: Failed to load material file(s). Use default "
"WARN: Failed to load material file(s). Use default " "material.\n";
"material.\n";
} }
} else { } else {
if (callback.mtllib_cb) { if (callback.mtllib_cb) {
@ -1918,7 +1925,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
while (!IS_NEW_LINE(token[0])) { while (!IS_NEW_LINE(token[0])) {
std::string str = parseString(&token); std::string str = parseString(&token);
names.push_back(str); names.push_back(str);
token += strspn(token, " \t\r"); // skip tag token += strspn(token, " \t\r"); // skip tag
} }
assert(names.size() > 0); assert(names.size() > 0);
@ -1967,7 +1974,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
continue; continue;
} }
#if 0 // @todo #if 0 // @todo
if (token[0] == 't' && IS_SPACE(token[1])) { if (token[0] == 't' && IS_SPACE(token[1])) {
tag_t tag; tag_t tag;
@ -2024,6 +2031,6 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
return true; return true;
} }
} // namespace tinyobj } // namespace tinyobj
#endif #endif

View File

@ -19,9 +19,11 @@
#include <bits/unique_ptr.h> #include <bits/unique_ptr.h>
#include <bits/shared_ptr.h> #include <bits/shared_ptr.h>
#include <calico/types.h> #include <calico/types.h>
#include <tiny_obj_loader.h>
#include <array> #include <array>
#include <vector> #include <vector>
#include <string> #include <string>
#include <tuple>
///////////////////////////////////// /////////////////////////////////////
// MEMORY // MEMORY
@ -71,6 +73,17 @@ template <typename T> using TVector = std::vector<T>;
* @tparam S The size of the array. * @tparam S The size of the array.
*/ */
template <typename T, size_t S> using TArray = std::array<T, S>; template <typename T, size_t S> using TArray = std::array<T, S>;
/**
* @brief Alias template for creating a std::tuple with a variadic list of
* types.
*
* This alias simplifies the usage of std::tuple by allowing the user to write
* TTuple<T...> instead of std::tuple<T...>.
*
* @tparam T... Variadic template parameter pack representing the types to be
* included in the tuple.
*/
template <typename... T> using TTuple = std::tuple<T...>;
///////////////////////////////////// /////////////////////////////////////
// BASIC TYPES // BASIC TYPES
@ -141,4 +154,10 @@ using FVector3 = glm::vec3;
*/ */
using FVector3Int = glm::ivec3; using FVector3Int = glm::ivec3;
/////////////////////////////////////
// SHAPES
/////////////////////////////////////
using FMaterial = tinyobj::material_t;
#endif // XRBDS_CORE_TYPES_H #endif // XRBDS_CORE_TYPES_H

View File

@ -6,7 +6,7 @@
* *
* This header defines the Input class, which provides static methods to query * This header defines the Input class, which provides static methods to query
* the state of keys (held, pressed, or released) on the system. It also defines * the state of keys (held, pressed, or released) on the system. It also defines
* the EKey enumeration for representing individual keys. * the EButton enumeration for representing individual keys.
* *
* The Input class is designed as a utility class and cannot be instantiated. * The Input class is designed as a utility class and cannot be instantiated.
* It maintains internal states for keys and provides methods to update and * It maintains internal states for keys and provides methods to update and
@ -14,8 +14,8 @@
* *
* Usage: * Usage:
* - Call Input::Update() periodically to refresh the key states. * - Call Input::Update() periodically to refresh the key states.
* - Use Input::IsKeyHeld(), Input::IsKeyDown(), or Input::IsKeyUp() to query * - Use Input::IsButtonHeld(), Input::IsButtonDown(), or Input::IsButtonUp() to
* the state of specific keys. * query the state of specific keys.
* *
* @author Daniel Ramirez Morilla * @author Daniel Ramirez Morilla
* @date 2025-04-19 * @date 2025-04-19
@ -33,7 +33,7 @@
#include <nds/input.h> #include <nds/input.h>
/** /**
* @enum EKey * @enum EButton
* @brief Represents the keys available on the NDS platform. * @brief Represents the keys available on the NDS platform.
* *
* This enumeration defines constants for each key on the NDS system, mapping * This enumeration defines constants for each key on the NDS system, mapping
@ -44,7 +44,7 @@
* *
* Example usage: * Example usage:
* @code * @code
* if (Input::IsKeyDown(EKey::A)) { * if (Input::IsButtonDown(EButton::A)) {
* // Handle A button press * // Handle A button press
* } * }
* @endcode * @endcode
@ -52,7 +52,7 @@
* @note These key codes are specific to the NDS platform and rely on the * @note These key codes are specific to the NDS platform and rely on the
* platform's key definitions. * platform's key definitions.
*/ */
enum EKey : u16 { enum EButton : u16 {
A = KEY_A, A = KEY_A,
B = KEY_B, B = KEY_B,
X = KEY_X, X = KEY_X,
@ -70,9 +70,9 @@ enum EKey : u16 {
/** /**
* @class Input * @class Input
* @brief Provides static methods to handle input states for keys. * @brief Provides static methods to handle input states for buttons.
* *
* The Input class is a utility class that allows querying the state of keys * The Input class is a utility class that allows querying the state of buttons
* (held, pressed, or released) using static methods. It is not meant to be * (held, pressed, or released) using static methods. It is not meant to be
* instantiated. * instantiated.
*/ */
@ -81,46 +81,47 @@ public:
/** /**
* @brief Updates the internal state of the input system. * @brief Updates the internal state of the input system.
* *
* This method should be called periodically to refresh the key states. * This method should be called periodically to refresh the button states.
*/ */
static void Update(); static void Update();
/** /**
* @brief Checks if a specific key is currently being held down. * @brief Checks if a specific button is currently being held down.
* @param key The key to check. * @param button The button to check.
* @return True if the key is held down, false otherwise. * @return True if the button is held down, false otherwise.
*/ */
static bool IsKeyHeld(EKey key); static bool IsButtonHeld(EButton button);
/** /**
* @brief Checks if a specific key was pressed in the current update cycle. * @brief Checks if a specific button was pressed in the current update cycle.
* @param key The key to check. * @param button The button to check.
* @return True if the key was pressed, false otherwise. * @return True if the button was pressed, false otherwise.
*/ */
static bool IsKeyDown(EKey key); static bool IsButtonDown(EButton button);
/** /**
* @brief Checks if a specific key was released in the current update cycle. * @brief Checks if a specific button was released in the current update
* @param key The key to check. * cycle.
* @return True if the key was released, false otherwise. * @param button The button to check.
* @return True if the button was released, false otherwise.
*/ */
static bool IsKeyUp(EKey key); static bool IsButtonUp(EButton button);
private: private:
/** /**
* @brief Stores the state of keys currently being held down. * @brief Stores the state of buttons currently being held down.
*/ */
static u32 KeysHeldState; static u32 ButtonsHeldState;
/** /**
* @brief Stores the state of keys pressed in the current update cycle. * @brief Stores the state of buttons pressed in the current update cycle.
*/ */
static u32 KeysDownState; static u32 ButtonsDownState;
/** /**
* @brief Stores the state of keys released in the current update cycle. * @brief Stores the state of buttons released in the current update cycle.
*/ */
static u32 KeysUpState; static u32 ButtonsUpState;
/** /**
* @brief Deleted constructor to prevent instantiation of the Input class. * @brief Deleted constructor to prevent instantiation of the Input class.

View File

@ -3,6 +3,7 @@
#include "resource.h" #include "resource.h"
#include "core/types.h" #include "core/types.h"
#include "resources/texture.h"
#include <vector> #include <vector>
@ -15,19 +16,30 @@ struct FVertex {
: position(pos), normal(norm), texCoords(tex) {} : position(pos), normal(norm), texCoords(tex) {}
}; };
struct FShape {
TVector<FVertex> vertices;
};
class Mesh : public Resource { class Mesh : public Resource {
using Super = Resource; using Super = Resource;
public: public:
static Mesh Load(const TVector<TVector<FVertex>> &vertices); static Mesh Load(const TVector<FShape> &shapes,
const TVector<FMaterial> &materials,
const TVector<PtrShr<Texture>> &textures);
TVector<TVector<FVertex>> getVertices() const { return vertices; } const TVector<FShape> &getShapes() const { return shapes; }
const TVector<FMaterial> &getMaterials() const { return materials; }
const TVector<PtrShr<Texture>> &getTextures() const { return textures; }
private: private:
TVector<TVector<FVertex>> vertices; TVector<FShape> shapes;
TVector<FMaterial> materials;
TVector<PtrShr<Texture>> textures;
Mesh() = default; Mesh() = default;
Mesh(const TVector<TVector<FVertex>> &vertices); Mesh(const TVector<FShape> &shapes, const TVector<FMaterial> &materials,
const TVector<PtrShr<Texture>> &textures);
}; };
#endif // XRBDS_RESOURCES_MESH_H #endif // XRBDS_RESOURCES_MESH_H

View File

@ -0,0 +1,46 @@
#ifndef XRBDS_RESOURCES_TEXTURE_H
#define XRBDS_RESOURCES_TEXTURE_H
#include "resource.h"
#include "core/types.h"
/**
* @class Texture
* @brief Represents a texture resource that includes pixel data and a color
* palette.
*
* The Texture class is derived from the Resource class and provides
* functionality to load and manage texture data and its associated palette. It
* also provides access to the texture's unique identifier.
*/
class Texture : public Resource {
using Super = Resource;
public:
virtual ~Texture();
/**
* @brief Loads a texture from the given pixel data and palette.
*
* @param data A reference to a vector containing the texture's pixel data.
* @param palette A reference to a vector containing the texture's color
* palette.
* @return A Texture object initialized with the provided data and palette.
*/
static Texture Load(TVector<unsigned int> &data,
TVector<unsigned short> &palette);
/**
* @brief Retrieves the unique identifier of the texture.
*
* @return The unique identifier of the texture as an unsigned 8-bit integer.
*/
const u8 getId() { return id; }
private:
Texture(TVector<unsigned int> &data, TVector<unsigned short> &palette);
int id;
};
#endif // XRBDS_RESOURCES_TEXTURE_H

View File

@ -4,8 +4,14 @@
#include "core/types.h" #include "core/types.h"
#include "resources/mesh.h" #include "resources/mesh.h"
struct FObjData {
TVector<TVector<FVertex>> vertices;
};
namespace Utils::File { namespace Utils::File {
FString AssetPath(const FString &path);
/** /**
* @brief Reads the contents of a text file and returns it as a string. * @brief Reads the contents of a text file and returns it as a string.
* *
@ -20,13 +26,38 @@ FString ReadTextFile(const FString &path);
* vector of vertices. * vector of vertices.
* *
* @param path The file path to the OBJ file as an FString. * @param path The file path to the OBJ file as an FString.
* @return A TVector of TVector of FVertex, where each inner vector represents a * @return A TVector of TVector of FVertex, where each inner vector represents
* group of vertices. * a group of vertices.
* *
* @note Ensure the file at the specified path exists and is in a valid OBJ * @note Ensure the file at the specified path exists and is in a valid OBJ
* format. * format.
*/ */
TVector<TVector<FVertex>> ReadObjFile(const FString &path); TTuple<TVector<FShape>, TVector<FMaterial>> ReadObjFile(const FString &path);
/**
* @brief Reads a binary file and returns its contents as a TVector of type T.
*
* @tparam T The type of data to read from the binary file.
* @param path The file path to the binary file to be read.
* @return TVector<T> A vector containing the contents of the binary file.
*/
template <typename T> TVector<T> ReadBinaryFile(const FString &path) {
FILE *file = fopen(AssetPath(path).c_str(), "rb");
if (!file) {
// TODO: iprintf("Error abriendo: %s\n", path.c_str());
return {};
}
fseek(file, 0, SEEK_END);
size_t size = ftell(file);
rewind(file);
TVector<T> buffer(size);
fread(buffer.data(), 1, size, file);
fclose(file);
return buffer;
}
} // namespace Utils::File } // namespace Utils::File

View File

@ -0,0 +1,98 @@
# cube-tex.obj
# Import into Blender with Y-forward, Z-up
#
# Vertices: Faces:
# f-------g +-------+
# /. /| /. 5 /| 3 back
# / . / | / . / |
# e-------h | 2 +-------+ 1|
# | b . .|. c z right | . . .|. +
# | . | / | /y | . 4 | /
# |. |/ |/ |. |/
# a-------d +---- x +-------+
# 6
# bottom
# Material defined in separate file.
mtllib cube.mtl
g cube
# Vertices
v 0.0 0.0 0.0 # 1 a
v 0.0 1.0 0.0 # 2 b
v 1.0 1.0 0.0 # 3 c
v 1.0 0.0 0.0 # 4 d
v 0.0 0.0 1.0 # 5 e
v 0.0 1.0 1.0 # 6 f
v 1.0 1.0 1.0 # 7 g
v 1.0 0.0 1.0 # 8 h
# Normal vectors
# One for each face. Shared by all vertices in that face.
vn 1.0 0.0 0.0 # 1 cghd
vn -1.0 0.0 0.0 # 2 aefb
vn 0.0 1.0 0.0 # 3 gcbf
vn 0.0 -1.0 0.0 # 4 dhea
vn 0.0 0.0 1.0 # 5 hgfe
vn 0.0 0.0 -1.0 # 6 cdab
# Texture
# (u,v) coordinate into texture map image, ranging from 0.0 - 1.0.
# +---f---g---+---+
# | | 5 | | |
# f---e---h---g---+
# | 2 | 4 | 1 | | v
# b---a---d---c---+ |
# | | 6 | | | |
# +---b---c---+---+ +---- u
# | | 3 | | |
# +---f---g---+---+
vt 0.25 1.00 # 1 f(5) = f for face 5
vt 0.50 1.00 # 2 g(5)
vt 0 0.75 # 3 f(2)
vt 0.25 0.75 # 4 e(2,4,5)
vt 0.50 0.75 # 5 h(1,4,5)
vt 0.75 0.75 # 6 g(1)
vt 0 0.50 # 7 b(2)
vt 0.25 0.50 # 8 a(2,4,6)
vt 0.50 0.50 # 9 d(1,4,6)
vt 0.75 0.50 # 10 c(1)
vt 0.25 0.25 # 11 b(3,6)
vt 0.50 0.25 # 12 c(3,6)
vt 0.25 0 # 13 f(3)
vt 0.50 0 # 14 g(3)
# Define material for the following faces
usemtl texture
# Faces v/vt/vn
# 3-------2
# | - |
# | # | Each face = 2 triangles (ccw)
# | - | = 1-2-3 + 1-3-4
# 4-------1
# Face 1: cghd = cgh + chd
f 3/10/1 7/6/1 8/5/1
f 3/10/1 8/5/1 4/9/1
# Face 2: aefb = aef + afb
f 1/8/2 5/4/2 6/3/2
f 1/8/2 6/3/2 2/7/2
# Face 3: gcbf = gcb + gbf
f 7/14/3 3/12/3 2/11/3
f 7/14/3 2/11/3 6/13/3
# Face 4: dhea = dhe + dea
f 4/9/4 8/5/4 5/4/4
f 4/9/4 5/4/4 1/8/4
# Face 5: hgfe = hgf + hfe
f 8/5/5 7/2/5 6/1/5
f 8/5/5 6/1/5 5/4/5
# Face 6: cdab = cda + cab
f 3/12/6 4/9/6 1/8/6
f 3/12/6 1/8/6 2/11/6

View File

@ -0,0 +1,7 @@
newmtl texture
Ka 0.0 0.0 0.0
Kd 0.5 0.5 0.5
Ks 0.0 0.0 0.0
Ns 10.0
illum 2
map_Kd texture.png

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,6 @@
Model: mococo.obj
Author: pyr0xene
License: CC BY-NC 4.0 (https://creativecommons.org/licenses/by-nc/4.0/)
Source: https://sketchfab.com/3d-models/mococo-2c53699f4fe647beb52a7926821f5d9c
This model is used under the terms of the Creative Commons Attribution-NonCommercial license.

View File

@ -1,46 +0,0 @@
# Blender v2.76 (sub 0) OBJ File: ''
# www.blender.org
mtllib cube.mtl
o Cube
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -0.999999
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vt 1.000000 0.333333
vt 1.000000 0.666667
vt 0.666667 0.666667
vt 0.666667 0.333333
vt 0.666667 0.000000
vt 0.000000 0.333333
vt 0.000000 0.000000
vt 0.333333 0.000000
vt 0.333333 1.000000
vt 0.000000 1.000000
vt 0.000000 0.666667
vt 0.333333 0.333333
vt 0.333333 0.666667
vt 1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 1.000000 0.000000 0.000000
vn -0.000000 0.000000 1.000000
vn -1.000000 -0.000000 -0.000000
vn 0.000000 0.000000 -1.000000
usemtl Material
s off
f 2/1/1 3/2/1 4/3/1
f 8/1/2 7/4/2 6/5/2
f 5/6/3 6/7/3 2/8/3
f 6/8/4 7/5/4 3/4/4
f 3/9/5 7/10/5 8/11/5
f 1/12/6 4/13/6 8/11/6
f 1/4/1 2/1/1 4/3/1
f 5/14/2 8/1/2 6/5/2
f 1/12/3 5/6/3 2/8/3
f 2/12/4 6/8/4 3/4/4
f 4/13/5 3/9/5 8/11/5
f 5/6/6 1/12/6 8/11/6

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View File

@ -0,0 +1,7 @@
newmtl texture
Ka 0.0 0.0 0.0
Kd 0.5 0.5 0.5
Ks 0.0 0.0 0.0
Ns 10.0
illum 2
map_Kd texture.png

View File

@ -0,0 +1,15 @@
# Blender v2.57 (sub 0) OBJ File: ''
# www.blender.org
mtllib quad.mtl
o Cube_Cube.001
v -1.000000 1.000000 0.000000
v 1.000000 1.000000 0.000000
v -1.000000 -1.000000 0.000000
v 1.000000 -1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
s off
f 4/4 3/3 1/2
f 2/1 4/4 1/2

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB