McRogueFace/src/3d/MeshLayer.h

173 lines
6 KiB
C++

// MeshLayer.h - Static 3D geometry layer for Viewport3D
// Supports terrain generation from HeightMap and height-based texture mapping
#pragma once
#include "Common.h"
#include "Math3D.h"
#include <memory>
#include <string>
#include <vector>
#include <libtcod.h> // For TCOD_heightmap_t
namespace mcrf {
// =============================================================================
// MeshVertex - Vertex format matching Viewport3D's shader attributes
// =============================================================================
struct MeshVertex {
vec3 position; // 12 bytes
vec2 texcoord; // 8 bytes
vec3 normal; // 12 bytes
vec4 color; // 16 bytes (RGBA as floats 0-1)
// Total: 48 bytes per vertex
MeshVertex()
: position(0, 0, 0)
, texcoord(0, 0)
, normal(0, 1, 0)
, color(1, 1, 1, 1)
{}
MeshVertex(const vec3& pos, const vec2& uv, const vec3& norm, const vec4& col)
: position(pos), texcoord(uv), normal(norm), color(col)
{}
};
// =============================================================================
// TextureRange - Height-based texture selection from sprite sheet
// =============================================================================
struct TextureRange {
float minHeight; // Minimum normalized height (0-1)
float maxHeight; // Maximum normalized height (0-1)
int spriteIndex; // Index into sprite sheet
TextureRange() : minHeight(0), maxHeight(1), spriteIndex(0) {}
TextureRange(float min, float max, int index)
: minHeight(min), maxHeight(max), spriteIndex(index) {}
};
// =============================================================================
// MeshLayer - Container for static 3D geometry
// =============================================================================
class MeshLayer {
public:
MeshLayer();
MeshLayer(const std::string& name, int zIndex = 0);
~MeshLayer();
// No copy, allow move
MeshLayer(const MeshLayer&) = delete;
MeshLayer& operator=(const MeshLayer&) = delete;
MeshLayer(MeshLayer&& other) noexcept;
MeshLayer& operator=(MeshLayer&& other) noexcept;
// =========================================================================
// Core Properties
// =========================================================================
const std::string& getName() const { return name_; }
void setName(const std::string& name) { name_ = name; }
int getZIndex() const { return zIndex_; }
void setZIndex(int z) { zIndex_ = z; }
bool isVisible() const { return visible_; }
void setVisible(bool v) { visible_ = v; }
// Texture (sprite sheet for height-based mapping)
void setTexture(sf::Texture* tex) { texture_ = tex; }
sf::Texture* getTexture() const { return texture_; }
// Sprite sheet configuration (for texture ranges)
void setSpriteSheetLayout(int tilesPerRow, int tilesPerCol);
// =========================================================================
// Mesh Generation
// =========================================================================
/// Build terrain mesh from HeightMap
/// @param heightmap libtcod heightmap pointer
/// @param yScale Vertical exaggeration factor
/// @param cellSize World-space size of each grid cell
void buildFromHeightmap(TCOD_heightmap_t* heightmap, float yScale, float cellSize);
/// Build a flat plane (for floors, water, etc.)
/// @param width World-space width (X axis)
/// @param depth World-space depth (Z axis)
/// @param y World-space height
void buildPlane(float width, float depth, float y = 0.0f);
/// Apply height-based texture ranges
/// Updates vertex UVs based on stored height data
void applyTextureRanges(const std::vector<TextureRange>& ranges);
/// Apply per-vertex colors from RGB heightmaps
/// Each heightmap provides one color channel (values 0-1 map to intensity)
/// @param rMap Red channel heightmap (must match terrain dimensions)
/// @param gMap Green channel heightmap
/// @param bMap Blue channel heightmap
void applyColorMap(TCOD_heightmap_t* rMap, TCOD_heightmap_t* gMap, TCOD_heightmap_t* bMap);
/// Clear all geometry
void clear();
// =========================================================================
// GPU Upload and Rendering
// =========================================================================
/// Upload vertex data to GPU
/// Call after modifying vertices or when dirty_ flag is set
void uploadToGPU();
/// Render this layer
/// @param model Model transformation matrix
/// @param view View matrix from camera
/// @param projection Projection matrix from camera
void render(const mat4& model, const mat4& view, const mat4& projection);
/// Get model matrix (identity by default, override for positioned layers)
mat4 getModelMatrix() const { return modelMatrix_; }
void setModelMatrix(const mat4& m) { modelMatrix_ = m; }
// =========================================================================
// Statistics
// =========================================================================
size_t getVertexCount() const { return vertices_.size(); }
bool isDirty() const { return dirty_; }
private:
// Identity
std::string name_;
int zIndex_ = 0;
bool visible_ = true;
// Geometry data (CPU side)
std::vector<MeshVertex> vertices_;
std::vector<float> heightData_; // Original heights for texture range re-application
int heightmapWidth_ = 0;
int heightmapHeight_ = 0;
// GPU resources
unsigned int vbo_ = 0;
bool dirty_ = false; // Needs GPU re-upload
// Texture
sf::Texture* texture_ = nullptr; // Not owned
int tilesPerRow_ = 1;
int tilesPerCol_ = 1;
// Transform
mat4 modelMatrix_ = mat4::identity();
// Helper methods
void cleanupGPU();
vec3 computeFaceNormal(const vec3& v0, const vec3& v1, const vec3& v2);
void computeVertexNormals();
};
} // namespace mcrf