This commit is contained in:
Gabrieal Jimmy
2026-10-02 12:42:00 -05:00
commit 2c99305fb0
145 changed files with 6525 additions and 0 deletions

106
engine/include/hd2d/Model.h Normal file
View File

@@ -0,0 +1,106 @@
#pragma once
#include "hd2d/GL.h"
#include "hd2d/Texture.h"
#include <glm/glm.hpp>
#include <cmath>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
namespace hd2d {
// Material of a glTF primitive. Materials whose name starts with "foliage"
// get alpha testing, two-sided wrap lighting and sun translucency.
struct ModelMaterial {
std::string name;
const Texture* texture = nullptr; // base color (sRGB); null = white
glm::vec4 baseColor{1.0f};
bool alphaMask = false;
float alphaCutoff = 0.5f;
bool doubleSided = false;
bool foliage = false;
glm::vec3 emissive{0.0f}; // linear HDR (factor x strength)
const Texture* emissiveTexture = nullptr; // multiplies `emissive`; null = white
};
struct ModelPrimitive {
GLuint vbo = 0, ibo = 0;
GLsizei indexCount = 0;
const ModelMaterial* material = nullptr; // owned by the ModelLibrary
};
// Static mesh made of primitives (one per material). Vertex layout: position,
// normal, uv, color (rgb = tint/AO, a = wind weight). Built from one root
// node of a glTF file with the node's world transform baked in.
struct Model {
std::string name;
std::vector<ModelPrimitive> primitives;
glm::vec3 boundsMin{0.0f}, boundsMax{0.0f};
// Named attachment points from child empties of the glTF node (model space),
// e.g. "fire_0" on the bridge braziers.
std::unordered_map<std::string, glm::vec3> sockets;
};
// Loads a .glb/.gltf once (textures shared by all its models) and exposes each
// root node with a mesh as a named Model.
class ModelLibrary {
public:
ModelLibrary() = default;
ModelLibrary(const ModelLibrary&) = delete;
ModelLibrary& operator=(const ModelLibrary&) = delete;
~ModelLibrary();
bool load(const std::string& path);
const Model* get(const std::string& name) const;
private:
std::vector<std::unique_ptr<Texture>> textures_;
std::vector<std::unique_ptr<ModelMaterial>> materials_;
std::unordered_map<std::string, std::unique_ptr<Model>> models_;
};
// Per-instance placement: rotation about +Y, non-uniform scale, brightness tint.
struct ModelInstance {
glm::vec3 pos{0.0f};
float yaw = 0.0f;
glm::vec3 scale{1.0f};
float tint = 1.0f;
glm::vec3 toWorld(const glm::vec3& local) const { // matches mesh.vert
glm::vec3 p = local * scale;
float c = std::cos(yaw), s = std::sin(yaw);
return pos + glm::vec3(c * p.x + s * p.z, p.y, -s * p.x + c * p.z);
}
};
// Instanced draw of one model.
class ModelBatch {
public:
explicit ModelBatch(const Model* model = nullptr) : model(model) {}
ModelBatch(const ModelBatch&) = delete;
ModelBatch& operator=(const ModelBatch&) = delete;
~ModelBatch();
void upload(); // call after editing `instances`
// Draws primitive `i` (the renderer binds its material first).
void drawPrimitive(size_t i) const;
const Model* model = nullptr;
std::vector<ModelInstance> instances;
bool castShadows = true;
bool inReflections = true;
float windScale = 1.0f; // multiplies the per-vertex wind weight
// Strength of the contact shadow baked into the ground under each instance
// (see GroundOcclusion); 0 disables. Its size follows the model's footprint.
float groundOcclusion = 0.0f;
private:
std::vector<GLuint> vaos_;
GLuint instanceVbo_ = 0;
size_t capacity_ = 0;
GLsizei count_ = 0;
};
} // namespace hd2d