mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Initial commit: wowee native WoW 3.3.5a client
This commit is contained in:
commit
ce6cb8f38e
147 changed files with 32347 additions and 0 deletions
52
include/rendering/camera.hpp
Normal file
52
include/rendering/camera.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
struct Ray {
|
||||
glm::vec3 origin;
|
||||
glm::vec3 direction;
|
||||
};
|
||||
|
||||
class Camera {
|
||||
public:
|
||||
Camera();
|
||||
|
||||
void setPosition(const glm::vec3& pos) { position = pos; updateViewMatrix(); }
|
||||
void setRotation(float yaw, float pitch) { this->yaw = yaw; this->pitch = pitch; updateViewMatrix(); }
|
||||
void setAspectRatio(float aspect) { aspectRatio = aspect; updateProjectionMatrix(); }
|
||||
void setFov(float fov) { this->fov = fov; updateProjectionMatrix(); }
|
||||
|
||||
const glm::vec3& getPosition() const { return position; }
|
||||
const glm::mat4& getViewMatrix() const { return viewMatrix; }
|
||||
const glm::mat4& getProjectionMatrix() const { return projectionMatrix; }
|
||||
glm::mat4 getViewProjectionMatrix() const { return projectionMatrix * viewMatrix; }
|
||||
float getAspectRatio() const { return aspectRatio; }
|
||||
|
||||
glm::vec3 getForward() const;
|
||||
glm::vec3 getRight() const;
|
||||
glm::vec3 getUp() const;
|
||||
|
||||
Ray screenToWorldRay(float screenX, float screenY, float screenW, float screenH) const;
|
||||
|
||||
private:
|
||||
void updateViewMatrix();
|
||||
void updateProjectionMatrix();
|
||||
|
||||
glm::vec3 position = glm::vec3(0.0f);
|
||||
float yaw = 0.0f;
|
||||
float pitch = 0.0f;
|
||||
float fov = 45.0f;
|
||||
float aspectRatio = 16.0f / 9.0f;
|
||||
float nearPlane = 0.1f;
|
||||
float farPlane = 200000.0f; // Large draw distance for terrain visibility
|
||||
|
||||
glm::mat4 viewMatrix = glm::mat4(1.0f);
|
||||
glm::mat4 projectionMatrix = glm::mat4(1.0f);
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
130
include/rendering/camera_controller.hpp
Normal file
130
include/rendering/camera_controller.hpp
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
#pragma once
|
||||
|
||||
#include "rendering/camera.hpp"
|
||||
#include "core/input.hpp"
|
||||
#include <SDL2/SDL.h>
|
||||
#include <functional>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class TerrainManager;
|
||||
class WMORenderer;
|
||||
class WaterRenderer;
|
||||
|
||||
class CameraController {
|
||||
public:
|
||||
CameraController(Camera* camera);
|
||||
|
||||
void update(float deltaTime);
|
||||
void processMouseMotion(const SDL_MouseMotionEvent& event);
|
||||
void processMouseButton(const SDL_MouseButtonEvent& event);
|
||||
|
||||
void setMovementSpeed(float speed) { movementSpeed = speed; }
|
||||
void setMouseSensitivity(float sensitivity) { mouseSensitivity = sensitivity; }
|
||||
void setEnabled(bool enabled) { this->enabled = enabled; }
|
||||
void setTerrainManager(TerrainManager* tm) { terrainManager = tm; }
|
||||
void setWMORenderer(WMORenderer* wmo) { wmoRenderer = wmo; }
|
||||
void setWaterRenderer(WaterRenderer* wr) { waterRenderer = wr; }
|
||||
|
||||
void processMouseWheel(float delta);
|
||||
void setFollowTarget(glm::vec3* target);
|
||||
|
||||
void reset();
|
||||
|
||||
float getMovementSpeed() const { return movementSpeed; }
|
||||
bool isMoving() const;
|
||||
float getYaw() const { return yaw; }
|
||||
bool isThirdPerson() const { return thirdPerson; }
|
||||
bool isGrounded() const { return grounded; }
|
||||
bool isJumping() const { return !grounded && verticalVelocity > 0.0f; }
|
||||
bool isFalling() const { return !grounded && verticalVelocity <= 0.0f; }
|
||||
bool isSprinting() const;
|
||||
bool isRightMouseHeld() const { return rightMouseDown; }
|
||||
bool isSitting() const { return sitting; }
|
||||
bool isSwimming() const { return swimming; }
|
||||
const glm::vec3* getFollowTarget() const { return followTarget; }
|
||||
|
||||
// Movement callback for sending opcodes to server
|
||||
using MovementCallback = std::function<void(uint32_t opcode)>;
|
||||
void setMovementCallback(MovementCallback cb) { movementCallback = std::move(cb); }
|
||||
void setUseWoWSpeed(bool use) { useWoWSpeed = use; }
|
||||
|
||||
private:
|
||||
Camera* camera;
|
||||
TerrainManager* terrainManager = nullptr;
|
||||
WMORenderer* wmoRenderer = nullptr;
|
||||
WaterRenderer* waterRenderer = nullptr;
|
||||
|
||||
// Stored rotation (avoids lossy forward-vector round-trip)
|
||||
float yaw = 180.0f;
|
||||
float pitch = -30.0f;
|
||||
|
||||
// Movement settings
|
||||
float movementSpeed = 50.0f;
|
||||
float sprintMultiplier = 3.0f;
|
||||
float slowMultiplier = 0.3f;
|
||||
|
||||
// Mouse settings
|
||||
float mouseSensitivity = 0.2f;
|
||||
bool mouseButtonDown = false;
|
||||
bool leftMouseDown = false;
|
||||
bool rightMouseDown = false;
|
||||
|
||||
// Third-person orbit camera
|
||||
bool thirdPerson = false;
|
||||
float orbitDistance = 15.0f;
|
||||
float minOrbitDistance = 3.0f;
|
||||
float maxOrbitDistance = 50.0f;
|
||||
float zoomSpeed = 2.0f;
|
||||
glm::vec3* followTarget = nullptr;
|
||||
|
||||
// Gravity / grounding
|
||||
float verticalVelocity = 0.0f;
|
||||
bool grounded = false;
|
||||
float eyeHeight = 5.0f;
|
||||
float lastGroundZ = 0.0f; // Last known ground height (fallback when no terrain)
|
||||
static constexpr float GRAVITY = -30.0f;
|
||||
static constexpr float JUMP_VELOCITY = 15.0f;
|
||||
|
||||
// Swimming
|
||||
bool swimming = false;
|
||||
bool wasSwimming = false;
|
||||
static constexpr float SWIM_SPEED_FACTOR = 0.67f;
|
||||
static constexpr float SWIM_GRAVITY = -5.0f;
|
||||
static constexpr float SWIM_BUOYANCY = 8.0f;
|
||||
static constexpr float SWIM_SINK_SPEED = -3.0f;
|
||||
static constexpr float WATER_SURFACE_OFFSET = 1.5f;
|
||||
|
||||
// State
|
||||
bool enabled = true;
|
||||
bool sitting = false;
|
||||
bool xKeyWasDown = false;
|
||||
|
||||
// Movement state tracking (for sending opcodes on state change)
|
||||
bool wasMovingForward = false;
|
||||
bool wasMovingBackward = false;
|
||||
bool wasStrafingLeft = false;
|
||||
bool wasStrafingRight = false;
|
||||
bool wasJumping = false;
|
||||
bool wasFalling = false;
|
||||
|
||||
// Movement callback
|
||||
MovementCallback movementCallback;
|
||||
|
||||
// WoW-correct speeds
|
||||
bool useWoWSpeed = false;
|
||||
static constexpr float WOW_RUN_SPEED = 7.0f;
|
||||
static constexpr float WOW_WALK_SPEED = 2.5f;
|
||||
static constexpr float WOW_BACK_SPEED = 4.5f;
|
||||
static constexpr float WOW_GRAVITY = -19.29f;
|
||||
static constexpr float WOW_JUMP_VELOCITY = 7.96f;
|
||||
|
||||
// Default spawn position (in front of Stormwind gate)
|
||||
glm::vec3 defaultPosition = glm::vec3(-8900.0f, -170.0f, 150.0f);
|
||||
float defaultYaw = 0.0f; // Look north toward Stormwind gate
|
||||
float defaultPitch = -5.0f;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
101
include/rendering/celestial.hpp
Normal file
101
include/rendering/celestial.hpp
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Shader;
|
||||
class Camera;
|
||||
|
||||
/**
|
||||
* Celestial body renderer
|
||||
*
|
||||
* Renders sun and moon that move across the sky based on time of day.
|
||||
* Sun rises at dawn, sets at dusk. Moon is visible at night.
|
||||
*/
|
||||
class Celestial {
|
||||
public:
|
||||
Celestial();
|
||||
~Celestial();
|
||||
|
||||
bool initialize();
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Render celestial bodies (sun and moon)
|
||||
* @param camera Camera for view matrix
|
||||
* @param timeOfDay Time of day in hours (0-24)
|
||||
*/
|
||||
void render(const Camera& camera, float timeOfDay);
|
||||
|
||||
/**
|
||||
* Enable/disable celestial rendering
|
||||
*/
|
||||
void setEnabled(bool enabled) { renderingEnabled = enabled; }
|
||||
bool isEnabled() const { return renderingEnabled; }
|
||||
|
||||
/**
|
||||
* Update celestial bodies (for moon phase cycling)
|
||||
*/
|
||||
void update(float deltaTime);
|
||||
|
||||
/**
|
||||
* Set moon phase (0.0 = new moon, 0.25 = first quarter, 0.5 = full, 0.75 = last quarter, 1.0 = new)
|
||||
*/
|
||||
void setMoonPhase(float phase);
|
||||
float getMoonPhase() const { return moonPhase; }
|
||||
|
||||
/**
|
||||
* Enable/disable automatic moon phase cycling
|
||||
*/
|
||||
void setMoonPhaseCycling(bool enabled) { moonPhaseCycling = enabled; }
|
||||
bool isMoonPhaseCycling() const { return moonPhaseCycling; }
|
||||
|
||||
/**
|
||||
* Get sun position in world space
|
||||
*/
|
||||
glm::vec3 getSunPosition(float timeOfDay) const;
|
||||
|
||||
/**
|
||||
* Get moon position in world space
|
||||
*/
|
||||
glm::vec3 getMoonPosition(float timeOfDay) const;
|
||||
|
||||
/**
|
||||
* Get sun color (changes with time of day)
|
||||
*/
|
||||
glm::vec3 getSunColor(float timeOfDay) const;
|
||||
|
||||
/**
|
||||
* Get sun intensity (0-1, fades at dawn/dusk)
|
||||
*/
|
||||
float getSunIntensity(float timeOfDay) const;
|
||||
|
||||
private:
|
||||
void createCelestialQuad();
|
||||
void destroyCelestialQuad();
|
||||
|
||||
void renderSun(const Camera& camera, float timeOfDay);
|
||||
void renderMoon(const Camera& camera, float timeOfDay);
|
||||
|
||||
float calculateCelestialAngle(float timeOfDay, float riseTime, float setTime) const;
|
||||
|
||||
std::unique_ptr<Shader> celestialShader;
|
||||
|
||||
uint32_t vao = 0;
|
||||
uint32_t vbo = 0;
|
||||
uint32_t ebo = 0;
|
||||
|
||||
bool renderingEnabled = true;
|
||||
|
||||
// Moon phase system
|
||||
float moonPhase = 0.5f; // 0.0-1.0 (0=new, 0.5=full)
|
||||
bool moonPhaseCycling = true;
|
||||
float moonPhaseTimer = 0.0f;
|
||||
static constexpr float MOON_CYCLE_DURATION = 240.0f; // 4 minutes for full cycle
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
180
include/rendering/character_renderer.hpp
Normal file
180
include/rendering/character_renderer.hpp
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
#pragma once
|
||||
|
||||
#include "pipeline/m2_loader.hpp"
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline { class AssetManager; }
|
||||
namespace rendering {
|
||||
|
||||
// Forward declarations
|
||||
class Shader;
|
||||
class Texture;
|
||||
class Camera;
|
||||
|
||||
// Weapon attached to a character instance at a bone attachment point
|
||||
struct WeaponAttachment {
|
||||
uint32_t weaponModelId;
|
||||
uint32_t weaponInstanceId;
|
||||
uint32_t attachmentId; // 1=RightHand, 2=LeftHand
|
||||
uint16_t boneIndex;
|
||||
glm::vec3 offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Character renderer for M2 models with skeletal animation
|
||||
*
|
||||
* Features:
|
||||
* - Skeletal animation with bone transformations
|
||||
* - Keyframe interpolation (linear position/scale, slerp rotation)
|
||||
* - Vertex skinning (GPU-accelerated)
|
||||
* - Texture loading from BLP via AssetManager
|
||||
*/
|
||||
class CharacterRenderer {
|
||||
public:
|
||||
CharacterRenderer();
|
||||
~CharacterRenderer();
|
||||
|
||||
bool initialize();
|
||||
void shutdown();
|
||||
|
||||
void setAssetManager(pipeline::AssetManager* am) { assetManager = am; }
|
||||
|
||||
bool loadModel(const pipeline::M2Model& model, uint32_t id);
|
||||
|
||||
uint32_t createInstance(uint32_t modelId, const glm::vec3& position,
|
||||
const glm::vec3& rotation = glm::vec3(0.0f),
|
||||
float scale = 1.0f);
|
||||
|
||||
void playAnimation(uint32_t instanceId, uint32_t animationId, bool loop = true);
|
||||
|
||||
void update(float deltaTime);
|
||||
|
||||
void render(const Camera& camera, const glm::mat4& view, const glm::mat4& projection);
|
||||
|
||||
void setInstancePosition(uint32_t instanceId, const glm::vec3& position);
|
||||
void setInstanceRotation(uint32_t instanceId, const glm::vec3& rotation);
|
||||
void setActiveGeosets(uint32_t instanceId, const std::unordered_set<uint16_t>& geosets);
|
||||
void removeInstance(uint32_t instanceId);
|
||||
|
||||
/** Attach a weapon model to a character instance at the given attachment point. */
|
||||
bool attachWeapon(uint32_t charInstanceId, uint32_t attachmentId,
|
||||
const pipeline::M2Model& weaponModel, uint32_t weaponModelId,
|
||||
const std::string& texturePath);
|
||||
|
||||
/** Detach a weapon from the given attachment point. */
|
||||
void detachWeapon(uint32_t charInstanceId, uint32_t attachmentId);
|
||||
|
||||
size_t getInstanceCount() const { return instances.size(); }
|
||||
|
||||
private:
|
||||
// GPU representation of M2 model
|
||||
struct M2ModelGPU {
|
||||
uint32_t vao = 0;
|
||||
uint32_t vbo = 0;
|
||||
uint32_t ebo = 0;
|
||||
|
||||
pipeline::M2Model data; // Original model data
|
||||
std::vector<glm::mat4> bindPose; // Inverse bind pose matrices
|
||||
|
||||
// Textures loaded from BLP (indexed by texture array position)
|
||||
std::vector<GLuint> textureIds;
|
||||
};
|
||||
|
||||
// Character instance
|
||||
struct CharacterInstance {
|
||||
uint32_t id;
|
||||
uint32_t modelId;
|
||||
|
||||
glm::vec3 position;
|
||||
glm::vec3 rotation;
|
||||
float scale;
|
||||
|
||||
// Animation state
|
||||
uint32_t currentAnimationId = 0;
|
||||
int currentSequenceIndex = -1; // Index into M2Model::sequences
|
||||
float animationTime = 0.0f;
|
||||
bool animationLoop = true;
|
||||
std::vector<glm::mat4> boneMatrices; // Current bone transforms
|
||||
|
||||
// Geoset visibility — which submesh IDs to render
|
||||
// Empty = render all (for non-character models)
|
||||
std::unordered_set<uint16_t> activeGeosets;
|
||||
|
||||
// Weapon attachments (weapons parented to this instance's bones)
|
||||
std::vector<WeaponAttachment> weaponAttachments;
|
||||
|
||||
// Override model matrix (used for weapon instances positioned by parent bone)
|
||||
bool hasOverrideModelMatrix = false;
|
||||
glm::mat4 overrideModelMatrix{1.0f};
|
||||
};
|
||||
|
||||
void setupModelBuffers(M2ModelGPU& gpuModel);
|
||||
void calculateBindPose(M2ModelGPU& gpuModel);
|
||||
void updateAnimation(CharacterInstance& instance, float deltaTime);
|
||||
void calculateBoneMatrices(CharacterInstance& instance);
|
||||
glm::mat4 getBoneTransform(const pipeline::M2Bone& bone, float time, int sequenceIndex);
|
||||
glm::mat4 getModelMatrix(const CharacterInstance& instance) const;
|
||||
|
||||
// Keyframe interpolation helpers
|
||||
static int findKeyframeIndex(const std::vector<uint32_t>& timestamps, float time);
|
||||
static glm::vec3 interpolateVec3(const pipeline::M2AnimationTrack& track,
|
||||
int seqIdx, float time, const glm::vec3& defaultVal);
|
||||
static glm::quat interpolateQuat(const pipeline::M2AnimationTrack& track,
|
||||
int seqIdx, float time);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Build a composited character skin texture by alpha-blending overlay
|
||||
* layers (e.g. underwear) onto a base skin BLP. Each overlay is placed
|
||||
* at the correct CharComponentTextureSections region based on its
|
||||
* filename (pelvis, torso, etc.). Returns the resulting GL texture ID.
|
||||
*/
|
||||
GLuint compositeTextures(const std::vector<std::string>& layerPaths);
|
||||
|
||||
/**
|
||||
* Build a composited character skin with explicit region-based equipment overlays.
|
||||
* @param basePath Body skin texture path
|
||||
* @param baseLayers Underwear overlay paths (placed by filename keyword)
|
||||
* @param regionLayers Pairs of (region_index, blp_path) for equipment textures
|
||||
* @return GL texture ID of the composited result
|
||||
*/
|
||||
GLuint compositeWithRegions(const std::string& basePath,
|
||||
const std::vector<std::string>& baseLayers,
|
||||
const std::vector<std::pair<int, std::string>>& regionLayers);
|
||||
|
||||
/** Load a BLP texture from MPQ and return the GL texture ID (cached). */
|
||||
GLuint loadTexture(const std::string& path);
|
||||
|
||||
/** Replace a loaded model's texture at the given slot with a new GL texture. */
|
||||
void setModelTexture(uint32_t modelId, uint32_t textureSlot, GLuint textureId);
|
||||
|
||||
/** Reset a model's texture slot back to white fallback. */
|
||||
void resetModelTexture(uint32_t modelId, uint32_t textureSlot);
|
||||
|
||||
|
||||
private:
|
||||
std::unique_ptr<Shader> characterShader;
|
||||
pipeline::AssetManager* assetManager = nullptr;
|
||||
|
||||
// Texture cache
|
||||
std::unordered_map<std::string, GLuint> textureCache;
|
||||
GLuint whiteTexture = 0;
|
||||
|
||||
std::unordered_map<uint32_t, M2ModelGPU> models;
|
||||
std::unordered_map<uint32_t, CharacterInstance> instances;
|
||||
|
||||
uint32_t nextInstanceId = 1;
|
||||
|
||||
// Maximum bones supported (GPU uniform limit)
|
||||
static constexpr int MAX_BONES = 200;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
95
include/rendering/clouds.hpp
Normal file
95
include/rendering/clouds.hpp
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class Shader;
|
||||
|
||||
/**
|
||||
* @brief Renders procedural animated clouds on a sky dome
|
||||
*
|
||||
* Features:
|
||||
* - Procedural cloud generation using multiple noise layers
|
||||
* - Two cloud layers at different altitudes
|
||||
* - Animated wind movement
|
||||
* - Time-of-day color tinting (orange at sunrise/sunset)
|
||||
* - Transparency and soft edges
|
||||
*/
|
||||
class Clouds {
|
||||
public:
|
||||
Clouds();
|
||||
~Clouds();
|
||||
|
||||
/**
|
||||
* @brief Initialize cloud system (generate mesh and shaders)
|
||||
* @return true if initialization succeeded
|
||||
*/
|
||||
bool initialize();
|
||||
|
||||
/**
|
||||
* @brief Render clouds
|
||||
* @param camera The camera to render from
|
||||
* @param timeOfDay Current time (0-24 hours)
|
||||
*/
|
||||
void render(const Camera& camera, float timeOfDay);
|
||||
|
||||
/**
|
||||
* @brief Update cloud animation
|
||||
* @param deltaTime Time since last frame
|
||||
*/
|
||||
void update(float deltaTime);
|
||||
|
||||
/**
|
||||
* @brief Enable or disable cloud rendering
|
||||
*/
|
||||
void setEnabled(bool enabled) { this->enabled = enabled; }
|
||||
bool isEnabled() const { return enabled; }
|
||||
|
||||
/**
|
||||
* @brief Set cloud density (0.0 = clear, 1.0 = overcast)
|
||||
*/
|
||||
void setDensity(float density);
|
||||
float getDensity() const { return density; }
|
||||
|
||||
/**
|
||||
* @brief Set wind speed multiplier
|
||||
*/
|
||||
void setWindSpeed(float speed) { windSpeed = speed; }
|
||||
float getWindSpeed() const { return windSpeed; }
|
||||
|
||||
private:
|
||||
void generateMesh();
|
||||
void cleanup();
|
||||
glm::vec3 getCloudColor(float timeOfDay) const;
|
||||
|
||||
// OpenGL objects
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0;
|
||||
GLuint ebo = 0;
|
||||
std::unique_ptr<Shader> shader;
|
||||
|
||||
// Mesh data
|
||||
std::vector<glm::vec3> vertices;
|
||||
std::vector<unsigned int> indices;
|
||||
int triangleCount = 0;
|
||||
|
||||
// Cloud parameters
|
||||
bool enabled = true;
|
||||
float density = 0.5f; // Cloud coverage
|
||||
float windSpeed = 1.0f;
|
||||
float windOffset = 0.0f; // Accumulated wind movement
|
||||
|
||||
// Mesh generation parameters
|
||||
static constexpr int SEGMENTS = 32; // Horizontal segments
|
||||
static constexpr int RINGS = 8; // Vertical rings (only upper hemisphere)
|
||||
static constexpr float RADIUS = 900.0f; // Slightly smaller than skybox
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
88
include/rendering/frustum.hpp
Normal file
88
include/rendering/frustum.hpp
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <array>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
/**
|
||||
* Frustum plane
|
||||
*/
|
||||
struct Plane {
|
||||
glm::vec3 normal;
|
||||
float distance;
|
||||
|
||||
Plane() : normal(0.0f), distance(0.0f) {}
|
||||
Plane(const glm::vec3& n, float d) : normal(n), distance(d) {}
|
||||
|
||||
/**
|
||||
* Calculate signed distance from point to plane
|
||||
* Positive = in front, Negative = behind
|
||||
*/
|
||||
float distanceToPoint(const glm::vec3& point) const {
|
||||
return glm::dot(normal, point) + distance;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* View frustum for culling
|
||||
*
|
||||
* Six planes: left, right, bottom, top, near, far
|
||||
*/
|
||||
class Frustum {
|
||||
public:
|
||||
enum Side {
|
||||
LEFT = 0,
|
||||
RIGHT,
|
||||
BOTTOM,
|
||||
TOP,
|
||||
NEAR,
|
||||
FAR
|
||||
};
|
||||
|
||||
Frustum() = default;
|
||||
|
||||
/**
|
||||
* Extract frustum planes from view-projection matrix
|
||||
* @param viewProjection Combined view * projection matrix
|
||||
*/
|
||||
void extractFromMatrix(const glm::mat4& viewProjection);
|
||||
|
||||
/**
|
||||
* Test if point is inside frustum
|
||||
*/
|
||||
bool containsPoint(const glm::vec3& point) const;
|
||||
|
||||
/**
|
||||
* Test if sphere is inside or intersecting frustum
|
||||
* @param center Sphere center
|
||||
* @param radius Sphere radius
|
||||
* @return true if sphere is visible (fully or partially inside)
|
||||
*/
|
||||
bool intersectsSphere(const glm::vec3& center, float radius) const;
|
||||
|
||||
/**
|
||||
* Test if axis-aligned bounding box intersects frustum
|
||||
* @param min Box minimum corner
|
||||
* @param max Box maximum corner
|
||||
* @return true if box is visible (fully or partially inside)
|
||||
*/
|
||||
bool intersectsAABB(const glm::vec3& min, const glm::vec3& max) const;
|
||||
|
||||
/**
|
||||
* Get frustum plane
|
||||
*/
|
||||
const Plane& getPlane(Side side) const { return planes[side]; }
|
||||
|
||||
private:
|
||||
std::array<Plane, 6> planes;
|
||||
|
||||
/**
|
||||
* Normalize plane (ensure unit length normal)
|
||||
*/
|
||||
void normalizePlane(Plane& plane);
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
85
include/rendering/lens_flare.hpp
Normal file
85
include/rendering/lens_flare.hpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class Shader;
|
||||
|
||||
/**
|
||||
* @brief Renders lens flare effect when looking at the sun
|
||||
*
|
||||
* Features:
|
||||
* - Multiple flare elements (ghosts) along sun-to-center axis
|
||||
* - Sun glow at sun position
|
||||
* - Colored flare elements (chromatic aberration simulation)
|
||||
* - Intensity based on sun visibility and angle
|
||||
* - Additive blending for realistic light artifacts
|
||||
*/
|
||||
class LensFlare {
|
||||
public:
|
||||
LensFlare();
|
||||
~LensFlare();
|
||||
|
||||
/**
|
||||
* @brief Initialize lens flare system
|
||||
* @return true if initialization succeeded
|
||||
*/
|
||||
bool initialize();
|
||||
|
||||
/**
|
||||
* @brief Render lens flare effect
|
||||
* @param camera The camera to render from
|
||||
* @param sunPosition World-space sun position
|
||||
* @param timeOfDay Current time (0-24 hours)
|
||||
*/
|
||||
void render(const Camera& camera, const glm::vec3& sunPosition, float timeOfDay);
|
||||
|
||||
/**
|
||||
* @brief Enable or disable lens flare rendering
|
||||
*/
|
||||
void setEnabled(bool enabled) { this->enabled = enabled; }
|
||||
bool isEnabled() const { return enabled; }
|
||||
|
||||
/**
|
||||
* @brief Set flare intensity multiplier
|
||||
*/
|
||||
void setIntensity(float intensity);
|
||||
float getIntensity() const { return intensityMultiplier; }
|
||||
|
||||
private:
|
||||
struct FlareElement {
|
||||
float position; // Position along sun-center axis (-1 to 1, 0 = center)
|
||||
float size; // Size in screen space
|
||||
glm::vec3 color; // RGB color
|
||||
float brightness; // Brightness multiplier
|
||||
};
|
||||
|
||||
void generateFlareElements();
|
||||
void cleanup();
|
||||
float calculateSunVisibility(const Camera& camera, const glm::vec3& sunPosition) const;
|
||||
glm::vec2 worldToScreen(const Camera& camera, const glm::vec3& worldPos) const;
|
||||
|
||||
// OpenGL objects
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0;
|
||||
std::unique_ptr<Shader> shader;
|
||||
|
||||
// Flare elements
|
||||
std::vector<FlareElement> flareElements;
|
||||
|
||||
// Parameters
|
||||
bool enabled = true;
|
||||
float intensityMultiplier = 1.0f;
|
||||
|
||||
// Quad vertices for rendering flare sprites
|
||||
static constexpr int VERTICES_PER_QUAD = 6;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
105
include/rendering/lightning.hpp
Normal file
105
include/rendering/lightning.hpp
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
// Forward declarations
|
||||
class Shader;
|
||||
class Camera;
|
||||
|
||||
/**
|
||||
* Lightning system for thunder storm effects
|
||||
*
|
||||
* Features:
|
||||
* - Random lightning strikes during rain
|
||||
* - Screen flash effect
|
||||
* - Procedural lightning bolts with branches
|
||||
* - Thunder timing (light then sound delay)
|
||||
* - Intensity scaling with weather
|
||||
*/
|
||||
class Lightning {
|
||||
public:
|
||||
Lightning();
|
||||
~Lightning();
|
||||
|
||||
bool initialize();
|
||||
void shutdown();
|
||||
|
||||
void update(float deltaTime, const Camera& camera);
|
||||
void render(const Camera& camera, const glm::mat4& view, const glm::mat4& projection);
|
||||
|
||||
// Control
|
||||
void setEnabled(bool enabled);
|
||||
bool isEnabled() const { return enabled; }
|
||||
|
||||
void setIntensity(float intensity); // 0.0 - 1.0 (affects frequency)
|
||||
float getIntensity() const { return intensity; }
|
||||
|
||||
// Trigger manual strike (for testing or scripted events)
|
||||
void triggerStrike(const glm::vec3& position);
|
||||
|
||||
private:
|
||||
struct LightningBolt {
|
||||
glm::vec3 startPos;
|
||||
glm::vec3 endPos;
|
||||
float lifetime;
|
||||
float maxLifetime;
|
||||
std::vector<glm::vec3> segments; // Bolt path
|
||||
std::vector<glm::vec3> branches; // Branch points
|
||||
float brightness;
|
||||
bool active;
|
||||
};
|
||||
|
||||
struct Flash {
|
||||
float intensity; // 0.0 - 1.0
|
||||
float lifetime;
|
||||
float maxLifetime;
|
||||
bool active;
|
||||
};
|
||||
|
||||
void generateLightningBolt(LightningBolt& bolt);
|
||||
void generateBoltSegments(const glm::vec3& start, const glm::vec3& end,
|
||||
std::vector<glm::vec3>& segments, int depth = 0);
|
||||
void updateBolts(float deltaTime);
|
||||
void updateFlash(float deltaTime);
|
||||
void spawnRandomStrike(const glm::vec3& cameraPos);
|
||||
|
||||
void renderBolts(const glm::mat4& viewProj);
|
||||
void renderFlash();
|
||||
|
||||
bool enabled = true;
|
||||
float intensity = 0.5f; // Strike frequency multiplier
|
||||
|
||||
// Timing
|
||||
float strikeTimer = 0.0f;
|
||||
float nextStrikeTime = 0.0f;
|
||||
|
||||
// Active effects
|
||||
std::vector<LightningBolt> bolts;
|
||||
Flash flash;
|
||||
|
||||
// Rendering
|
||||
std::unique_ptr<Shader> boltShader;
|
||||
std::unique_ptr<Shader> flashShader;
|
||||
unsigned int boltVAO = 0;
|
||||
unsigned int boltVBO = 0;
|
||||
unsigned int flashVAO = 0;
|
||||
unsigned int flashVBO = 0;
|
||||
|
||||
// Configuration
|
||||
static constexpr int MAX_BOLTS = 3;
|
||||
static constexpr float MIN_STRIKE_INTERVAL = 2.0f;
|
||||
static constexpr float MAX_STRIKE_INTERVAL = 8.0f;
|
||||
static constexpr float BOLT_LIFETIME = 0.15f; // Quick flash
|
||||
static constexpr float FLASH_LIFETIME = 0.3f;
|
||||
static constexpr float STRIKE_DISTANCE = 200.0f; // From camera
|
||||
static constexpr int MAX_SEGMENTS = 64;
|
||||
static constexpr float BRANCH_PROBABILITY = 0.3f;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
145
include/rendering/m2_renderer.hpp
Normal file
145
include/rendering/m2_renderer.hpp
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
#pragma once
|
||||
|
||||
#include "pipeline/m2_loader.hpp"
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace wowee {
|
||||
|
||||
namespace pipeline {
|
||||
class AssetManager;
|
||||
}
|
||||
|
||||
namespace rendering {
|
||||
|
||||
class Shader;
|
||||
class Camera;
|
||||
|
||||
/**
|
||||
* GPU representation of an M2 model
|
||||
*/
|
||||
struct M2ModelGPU {
|
||||
struct BatchGPU {
|
||||
GLuint texture = 0;
|
||||
uint32_t indexStart = 0; // offset in indices (not bytes)
|
||||
uint32_t indexCount = 0;
|
||||
bool hasAlpha = false;
|
||||
};
|
||||
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0;
|
||||
GLuint ebo = 0;
|
||||
uint32_t indexCount = 0;
|
||||
uint32_t vertexCount = 0;
|
||||
std::vector<BatchGPU> batches;
|
||||
|
||||
glm::vec3 boundMin;
|
||||
glm::vec3 boundMax;
|
||||
float boundRadius = 0.0f;
|
||||
|
||||
std::string name;
|
||||
|
||||
bool isValid() const { return vao != 0 && indexCount > 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Instance of an M2 model in the world
|
||||
*/
|
||||
struct M2Instance {
|
||||
uint32_t id = 0; // Unique instance ID
|
||||
uint32_t modelId;
|
||||
glm::vec3 position;
|
||||
glm::vec3 rotation; // Euler angles in degrees
|
||||
float scale;
|
||||
glm::mat4 modelMatrix;
|
||||
|
||||
void updateModelMatrix();
|
||||
};
|
||||
|
||||
/**
|
||||
* M2 Model Renderer
|
||||
*
|
||||
* Handles rendering of M2 models (doodads like trees, rocks, bushes)
|
||||
*/
|
||||
class M2Renderer {
|
||||
public:
|
||||
M2Renderer();
|
||||
~M2Renderer();
|
||||
|
||||
bool initialize(pipeline::AssetManager* assets);
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Load an M2 model to GPU
|
||||
* @param model Parsed M2 model data
|
||||
* @param modelId Unique ID for this model
|
||||
* @return True if successful
|
||||
*/
|
||||
bool loadModel(const pipeline::M2Model& model, uint32_t modelId);
|
||||
|
||||
/**
|
||||
* Create an instance of a loaded model
|
||||
* @param modelId ID of the loaded model
|
||||
* @param position World position
|
||||
* @param rotation Rotation in degrees (x, y, z)
|
||||
* @param scale Scale factor (1.0 = normal)
|
||||
* @return Instance ID
|
||||
*/
|
||||
uint32_t createInstance(uint32_t modelId, const glm::vec3& position,
|
||||
const glm::vec3& rotation = glm::vec3(0.0f),
|
||||
float scale = 1.0f);
|
||||
|
||||
/**
|
||||
* Create an instance with a pre-computed model matrix
|
||||
* Used for WMO doodads where the full transform is computed externally
|
||||
*/
|
||||
uint32_t createInstanceWithMatrix(uint32_t modelId, const glm::mat4& modelMatrix,
|
||||
const glm::vec3& position);
|
||||
|
||||
/**
|
||||
* Render all visible instances
|
||||
*/
|
||||
void render(const Camera& camera, const glm::mat4& view, const glm::mat4& projection);
|
||||
|
||||
/**
|
||||
* Remove a specific instance by ID
|
||||
* @param instanceId Instance ID returned by createInstance()
|
||||
*/
|
||||
void removeInstance(uint32_t instanceId);
|
||||
|
||||
/**
|
||||
* Clear all models and instances
|
||||
*/
|
||||
void clear();
|
||||
|
||||
// Stats
|
||||
uint32_t getModelCount() const { return static_cast<uint32_t>(models.size()); }
|
||||
uint32_t getInstanceCount() const { return static_cast<uint32_t>(instances.size()); }
|
||||
uint32_t getTotalTriangleCount() const;
|
||||
uint32_t getDrawCallCount() const { return lastDrawCallCount; }
|
||||
|
||||
private:
|
||||
pipeline::AssetManager* assetManager = nullptr;
|
||||
std::unique_ptr<Shader> shader;
|
||||
|
||||
std::unordered_map<uint32_t, M2ModelGPU> models;
|
||||
std::vector<M2Instance> instances;
|
||||
|
||||
uint32_t nextInstanceId = 1;
|
||||
uint32_t lastDrawCallCount = 0;
|
||||
|
||||
GLuint loadTexture(const std::string& path);
|
||||
std::unordered_map<std::string, GLuint> textureCache;
|
||||
GLuint whiteTexture = 0;
|
||||
|
||||
// Lighting uniforms
|
||||
glm::vec3 lightDir = glm::vec3(0.5f, 0.5f, 1.0f);
|
||||
glm::vec3 ambientColor = glm::vec3(0.4f, 0.4f, 0.45f);
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
32
include/rendering/material.hpp
Normal file
32
include/rendering/material.hpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Shader;
|
||||
class Texture;
|
||||
|
||||
class Material {
|
||||
public:
|
||||
Material() = default;
|
||||
~Material() = default;
|
||||
|
||||
void setShader(std::shared_ptr<Shader> shader) { this->shader = shader; }
|
||||
void setTexture(std::shared_ptr<Texture> texture) { this->texture = texture; }
|
||||
void setColor(const glm::vec4& color) { this->color = color; }
|
||||
|
||||
std::shared_ptr<Shader> getShader() const { return shader; }
|
||||
std::shared_ptr<Texture> getTexture() const { return texture; }
|
||||
const glm::vec4& getColor() const { return color; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Shader> shader;
|
||||
std::shared_ptr<Texture> texture;
|
||||
glm::vec4 color = glm::vec4(1.0f);
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
33
include/rendering/mesh.hpp
Normal file
33
include/rendering/mesh.hpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
struct Vertex {
|
||||
glm::vec3 position;
|
||||
glm::vec3 normal;
|
||||
glm::vec2 texCoord;
|
||||
};
|
||||
|
||||
class Mesh {
|
||||
public:
|
||||
Mesh() = default;
|
||||
~Mesh();
|
||||
|
||||
void create(const std::vector<Vertex>& vertices, const std::vector<uint32_t>& indices);
|
||||
void destroy();
|
||||
void draw() const;
|
||||
|
||||
private:
|
||||
GLuint VAO = 0;
|
||||
GLuint VBO = 0;
|
||||
GLuint EBO = 0;
|
||||
size_t indexCount = 0;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
54
include/rendering/minimap.hpp
Normal file
54
include/rendering/minimap.hpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Shader;
|
||||
class Camera;
|
||||
class TerrainRenderer;
|
||||
|
||||
class Minimap {
|
||||
public:
|
||||
Minimap();
|
||||
~Minimap();
|
||||
|
||||
bool initialize(int size = 200);
|
||||
void shutdown();
|
||||
|
||||
void setTerrainRenderer(TerrainRenderer* tr) { terrainRenderer = tr; }
|
||||
|
||||
void render(const Camera& playerCamera, int screenWidth, int screenHeight);
|
||||
|
||||
void setEnabled(bool enabled) { this->enabled = enabled; }
|
||||
bool isEnabled() const { return enabled; }
|
||||
void toggle() { enabled = !enabled; }
|
||||
|
||||
void setViewRadius(float radius) { viewRadius = radius; }
|
||||
|
||||
private:
|
||||
void renderTerrainToFBO(const Camera& playerCamera);
|
||||
void renderQuad(int screenWidth, int screenHeight);
|
||||
|
||||
TerrainRenderer* terrainRenderer = nullptr;
|
||||
|
||||
// FBO for offscreen rendering
|
||||
GLuint fbo = 0;
|
||||
GLuint fboTexture = 0;
|
||||
GLuint fboDepth = 0;
|
||||
|
||||
// Screen quad
|
||||
GLuint quadVAO = 0;
|
||||
GLuint quadVBO = 0;
|
||||
std::unique_ptr<Shader> quadShader;
|
||||
|
||||
int mapSize = 200;
|
||||
float viewRadius = 500.0f;
|
||||
bool enabled = false;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
100
include/rendering/performance_hud.hpp
Normal file
100
include/rendering/performance_hud.hpp
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <deque>
|
||||
|
||||
namespace wowee {
|
||||
|
||||
namespace rendering {
|
||||
class Renderer;
|
||||
class Camera;
|
||||
}
|
||||
|
||||
namespace rendering {
|
||||
|
||||
/**
|
||||
* Performance HUD for displaying real-time statistics
|
||||
*
|
||||
* Shows FPS, frame time, rendering stats, and terrain info
|
||||
*/
|
||||
class PerformanceHUD {
|
||||
public:
|
||||
PerformanceHUD();
|
||||
~PerformanceHUD();
|
||||
|
||||
/**
|
||||
* Update HUD with latest frame time
|
||||
* @param deltaTime Time since last frame in seconds
|
||||
*/
|
||||
void update(float deltaTime);
|
||||
|
||||
/**
|
||||
* Render HUD using ImGui
|
||||
* @param renderer Renderer for accessing stats
|
||||
* @param camera Camera for position info
|
||||
*/
|
||||
void render(const Renderer* renderer, const Camera* camera);
|
||||
|
||||
/**
|
||||
* Enable/disable HUD display
|
||||
*/
|
||||
void setEnabled(bool enabled) { this->enabled = enabled; }
|
||||
bool isEnabled() const { return enabled; }
|
||||
|
||||
/**
|
||||
* Toggle HUD visibility
|
||||
*/
|
||||
void toggle() { enabled = !enabled; }
|
||||
|
||||
/**
|
||||
* Set HUD position
|
||||
*/
|
||||
enum class Position {
|
||||
TOP_LEFT,
|
||||
TOP_RIGHT,
|
||||
BOTTOM_LEFT,
|
||||
BOTTOM_RIGHT
|
||||
};
|
||||
void setPosition(Position pos) { position = pos; }
|
||||
|
||||
/**
|
||||
* Enable/disable specific sections
|
||||
*/
|
||||
void setShowFPS(bool show) { showFPS = show; }
|
||||
void setShowRenderer(bool show) { showRenderer = show; }
|
||||
void setShowTerrain(bool show) { showTerrain = show; }
|
||||
void setShowCamera(bool show) { showCamera = show; }
|
||||
void setShowControls(bool show) { showControls = show; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Calculate average FPS from frame time history
|
||||
*/
|
||||
void calculateFPS();
|
||||
|
||||
bool enabled = true; // Enabled by default, press F1 to toggle
|
||||
Position position = Position::TOP_LEFT;
|
||||
|
||||
// Section visibility
|
||||
bool showFPS = true;
|
||||
bool showRenderer = true;
|
||||
bool showTerrain = true;
|
||||
bool showCamera = true;
|
||||
bool showControls = true;
|
||||
|
||||
// FPS tracking
|
||||
std::deque<float> frameTimeHistory;
|
||||
static constexpr size_t MAX_FRAME_HISTORY = 120; // 2 seconds at 60 FPS
|
||||
float currentFPS = 0.0f;
|
||||
float averageFPS = 0.0f;
|
||||
float minFPS = 0.0f;
|
||||
float maxFPS = 0.0f;
|
||||
float frameTime = 0.0f;
|
||||
|
||||
// Update timing
|
||||
float updateTimer = 0.0f;
|
||||
static constexpr float UPDATE_INTERVAL = 0.1f; // Update stats every 0.1s
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
174
include/rendering/renderer.hpp
Normal file
174
include/rendering/renderer.hpp
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace core { class Window; }
|
||||
namespace game { class World; class ZoneManager; }
|
||||
namespace audio { class MusicManager; }
|
||||
namespace pipeline { class AssetManager; }
|
||||
|
||||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class CameraController;
|
||||
class Scene;
|
||||
class TerrainRenderer;
|
||||
class TerrainManager;
|
||||
class PerformanceHUD;
|
||||
class WaterRenderer;
|
||||
class Skybox;
|
||||
class Celestial;
|
||||
class StarField;
|
||||
class Clouds;
|
||||
class LensFlare;
|
||||
class Weather;
|
||||
class SwimEffects;
|
||||
class CharacterRenderer;
|
||||
class WMORenderer;
|
||||
class M2Renderer;
|
||||
class Minimap;
|
||||
|
||||
class Renderer {
|
||||
public:
|
||||
Renderer();
|
||||
~Renderer();
|
||||
|
||||
bool initialize(core::Window* window);
|
||||
void shutdown();
|
||||
|
||||
void beginFrame();
|
||||
void endFrame();
|
||||
|
||||
void renderWorld(game::World* world);
|
||||
|
||||
/**
|
||||
* Update renderer (camera, etc.)
|
||||
*/
|
||||
void update(float deltaTime);
|
||||
|
||||
/**
|
||||
* Load test terrain for debugging
|
||||
* @param assetManager Asset manager to load terrain data
|
||||
* @param adtPath Path to ADT file (e.g., "World\\Maps\\Azeroth\\Azeroth_32_49.adt")
|
||||
*/
|
||||
bool loadTestTerrain(pipeline::AssetManager* assetManager, const std::string& adtPath);
|
||||
|
||||
/**
|
||||
* Enable/disable terrain rendering
|
||||
*/
|
||||
void setTerrainEnabled(bool enabled) { terrainEnabled = enabled; }
|
||||
|
||||
/**
|
||||
* Enable/disable wireframe mode
|
||||
*/
|
||||
void setWireframeMode(bool enabled);
|
||||
|
||||
/**
|
||||
* Load terrain tiles around position
|
||||
* @param mapName Map name (e.g., "Azeroth", "Kalimdor")
|
||||
* @param centerX Center tile X coordinate
|
||||
* @param centerY Center tile Y coordinate
|
||||
* @param radius Load radius in tiles
|
||||
*/
|
||||
bool loadTerrainArea(const std::string& mapName, int centerX, int centerY, int radius = 1);
|
||||
|
||||
/**
|
||||
* Enable/disable terrain streaming
|
||||
*/
|
||||
void setTerrainStreaming(bool enabled);
|
||||
|
||||
/**
|
||||
* Render performance HUD
|
||||
*/
|
||||
void renderHUD();
|
||||
|
||||
Camera* getCamera() { return camera.get(); }
|
||||
CameraController* getCameraController() { return cameraController.get(); }
|
||||
Scene* getScene() { return scene.get(); }
|
||||
TerrainRenderer* getTerrainRenderer() const { return terrainRenderer.get(); }
|
||||
TerrainManager* getTerrainManager() const { return terrainManager.get(); }
|
||||
PerformanceHUD* getPerformanceHUD() { return performanceHUD.get(); }
|
||||
WaterRenderer* getWaterRenderer() const { return waterRenderer.get(); }
|
||||
Skybox* getSkybox() const { return skybox.get(); }
|
||||
Celestial* getCelestial() const { return celestial.get(); }
|
||||
StarField* getStarField() const { return starField.get(); }
|
||||
Clouds* getClouds() const { return clouds.get(); }
|
||||
LensFlare* getLensFlare() const { return lensFlare.get(); }
|
||||
Weather* getWeather() const { return weather.get(); }
|
||||
CharacterRenderer* getCharacterRenderer() const { return characterRenderer.get(); }
|
||||
WMORenderer* getWMORenderer() const { return wmoRenderer.get(); }
|
||||
M2Renderer* getM2Renderer() const { return m2Renderer.get(); }
|
||||
Minimap* getMinimap() const { return minimap.get(); }
|
||||
const std::string& getCurrentZoneName() const { return currentZoneName; }
|
||||
|
||||
// Third-person character follow
|
||||
void setCharacterFollow(uint32_t instanceId);
|
||||
glm::vec3& getCharacterPosition() { return characterPosition; }
|
||||
uint32_t getCharacterInstanceId() const { return characterInstanceId; }
|
||||
float getCharacterYaw() const { return characterYaw; }
|
||||
|
||||
// Emote support
|
||||
void playEmote(const std::string& emoteName);
|
||||
void cancelEmote();
|
||||
bool isEmoteActive() const { return emoteActive; }
|
||||
static std::string getEmoteText(const std::string& emoteName);
|
||||
|
||||
// Targeting support
|
||||
void setTargetPosition(const glm::vec3* pos);
|
||||
bool isMoving() const;
|
||||
|
||||
private:
|
||||
core::Window* window = nullptr;
|
||||
std::unique_ptr<Camera> camera;
|
||||
std::unique_ptr<CameraController> cameraController;
|
||||
std::unique_ptr<Scene> scene;
|
||||
std::unique_ptr<TerrainRenderer> terrainRenderer;
|
||||
std::unique_ptr<TerrainManager> terrainManager;
|
||||
std::unique_ptr<PerformanceHUD> performanceHUD;
|
||||
std::unique_ptr<WaterRenderer> waterRenderer;
|
||||
std::unique_ptr<Skybox> skybox;
|
||||
std::unique_ptr<Celestial> celestial;
|
||||
std::unique_ptr<StarField> starField;
|
||||
std::unique_ptr<Clouds> clouds;
|
||||
std::unique_ptr<LensFlare> lensFlare;
|
||||
std::unique_ptr<Weather> weather;
|
||||
std::unique_ptr<SwimEffects> swimEffects;
|
||||
std::unique_ptr<CharacterRenderer> characterRenderer;
|
||||
std::unique_ptr<WMORenderer> wmoRenderer;
|
||||
std::unique_ptr<M2Renderer> m2Renderer;
|
||||
std::unique_ptr<Minimap> minimap;
|
||||
std::unique_ptr<audio::MusicManager> musicManager;
|
||||
std::unique_ptr<game::ZoneManager> zoneManager;
|
||||
|
||||
pipeline::AssetManager* cachedAssetManager = nullptr;
|
||||
uint32_t currentZoneId = 0;
|
||||
std::string currentZoneName;
|
||||
|
||||
// Third-person character state
|
||||
glm::vec3 characterPosition = glm::vec3(0.0f);
|
||||
uint32_t characterInstanceId = 0;
|
||||
float characterYaw = 0.0f;
|
||||
|
||||
// Character animation state
|
||||
enum class CharAnimState { IDLE, WALK, RUN, JUMP_START, JUMP_MID, JUMP_END, SIT_DOWN, SITTING, EMOTE, SWIM_IDLE, SWIM };
|
||||
CharAnimState charAnimState = CharAnimState::IDLE;
|
||||
void updateCharacterAnimation();
|
||||
|
||||
// Emote state
|
||||
bool emoteActive = false;
|
||||
uint32_t emoteAnimId = 0;
|
||||
bool emoteLoop = false;
|
||||
|
||||
// Target facing
|
||||
const glm::vec3* targetPosition = nullptr;
|
||||
|
||||
bool terrainEnabled = true;
|
||||
bool terrainLoaded = false;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
27
include/rendering/scene.hpp
Normal file
27
include/rendering/scene.hpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Mesh;
|
||||
|
||||
class Scene {
|
||||
public:
|
||||
Scene() = default;
|
||||
~Scene() = default;
|
||||
|
||||
void addMesh(std::shared_ptr<Mesh> mesh);
|
||||
void removeMesh(std::shared_ptr<Mesh> mesh);
|
||||
void clear();
|
||||
|
||||
const std::vector<std::shared_ptr<Mesh>>& getMeshes() const { return meshes; }
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Mesh>> meshes;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
41
include/rendering/shader.hpp
Normal file
41
include/rendering/shader.hpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Shader {
|
||||
public:
|
||||
Shader() = default;
|
||||
~Shader();
|
||||
|
||||
bool loadFromFile(const std::string& vertexPath, const std::string& fragmentPath);
|
||||
bool loadFromSource(const std::string& vertexSource, const std::string& fragmentSource);
|
||||
|
||||
void use() const;
|
||||
void unuse() const;
|
||||
|
||||
void setUniform(const std::string& name, int value);
|
||||
void setUniform(const std::string& name, float value);
|
||||
void setUniform(const std::string& name, const glm::vec2& value);
|
||||
void setUniform(const std::string& name, const glm::vec3& value);
|
||||
void setUniform(const std::string& name, const glm::vec4& value);
|
||||
void setUniform(const std::string& name, const glm::mat3& value);
|
||||
void setUniform(const std::string& name, const glm::mat4& value);
|
||||
|
||||
GLuint getProgram() const { return program; }
|
||||
|
||||
private:
|
||||
bool compile(const std::string& vertexSource, const std::string& fragmentSource);
|
||||
GLint getUniformLocation(const std::string& name) const;
|
||||
|
||||
GLuint program = 0;
|
||||
GLuint vertexShader = 0;
|
||||
GLuint fragmentShader = 0;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
83
include/rendering/skybox.hpp
Normal file
83
include/rendering/skybox.hpp
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Shader;
|
||||
class Camera;
|
||||
|
||||
/**
|
||||
* Skybox renderer
|
||||
*
|
||||
* Renders an atmospheric sky dome with gradient colors.
|
||||
* The sky uses a dome/sphere approach for realistic appearance.
|
||||
*/
|
||||
class Skybox {
|
||||
public:
|
||||
Skybox();
|
||||
~Skybox();
|
||||
|
||||
bool initialize();
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Render the skybox
|
||||
* @param camera Camera for view matrix (position is ignored for skybox)
|
||||
* @param timeOfDay Time of day in hours (0-24), affects sky color
|
||||
*/
|
||||
void render(const Camera& camera, float timeOfDay = 12.0f);
|
||||
|
||||
/**
|
||||
* Enable/disable skybox rendering
|
||||
*/
|
||||
void setEnabled(bool enabled) { renderingEnabled = enabled; }
|
||||
bool isEnabled() const { return renderingEnabled; }
|
||||
|
||||
/**
|
||||
* Set time of day (0-24 hours)
|
||||
* 0 = midnight, 6 = dawn, 12 = noon, 18 = dusk, 24 = midnight
|
||||
*/
|
||||
void setTimeOfDay(float time);
|
||||
float getTimeOfDay() const { return timeOfDay; }
|
||||
|
||||
/**
|
||||
* Enable/disable time progression
|
||||
*/
|
||||
void setTimeProgression(bool enabled) { timeProgressionEnabled = enabled; }
|
||||
bool isTimeProgressionEnabled() const { return timeProgressionEnabled; }
|
||||
|
||||
/**
|
||||
* Update time progression
|
||||
*/
|
||||
void update(float deltaTime);
|
||||
|
||||
/**
|
||||
* Get horizon color for fog (public for fog system)
|
||||
*/
|
||||
glm::vec3 getHorizonColor(float time) const;
|
||||
|
||||
private:
|
||||
void createSkyDome();
|
||||
void destroySkyDome();
|
||||
|
||||
glm::vec3 getSkyColor(float altitude, float time) const;
|
||||
glm::vec3 getZenithColor(float time) const;
|
||||
|
||||
std::unique_ptr<Shader> skyShader;
|
||||
|
||||
uint32_t vao = 0;
|
||||
uint32_t vbo = 0;
|
||||
uint32_t ebo = 0;
|
||||
int indexCount = 0;
|
||||
|
||||
float timeOfDay = 12.0f; // Default: noon
|
||||
float timeSpeed = 1.0f; // 1.0 = 1 hour per real second
|
||||
bool timeProgressionEnabled = false;
|
||||
bool renderingEnabled = true;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
76
include/rendering/starfield.hpp
Normal file
76
include/rendering/starfield.hpp
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Shader;
|
||||
class Camera;
|
||||
|
||||
/**
|
||||
* Star field renderer
|
||||
*
|
||||
* Renders a field of stars across the night sky.
|
||||
* Stars fade in at dusk and out at dawn.
|
||||
*/
|
||||
class StarField {
|
||||
public:
|
||||
StarField();
|
||||
~StarField();
|
||||
|
||||
bool initialize();
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Render the star field
|
||||
* @param camera Camera for view matrix
|
||||
* @param timeOfDay Time of day in hours (0-24)
|
||||
*/
|
||||
void render(const Camera& camera, float timeOfDay);
|
||||
|
||||
/**
|
||||
* Update star twinkle animation
|
||||
*/
|
||||
void update(float deltaTime);
|
||||
|
||||
/**
|
||||
* Enable/disable star rendering
|
||||
*/
|
||||
void setEnabled(bool enabled) { renderingEnabled = enabled; }
|
||||
bool isEnabled() const { return renderingEnabled; }
|
||||
|
||||
/**
|
||||
* Get number of stars
|
||||
*/
|
||||
int getStarCount() const { return starCount; }
|
||||
|
||||
private:
|
||||
void generateStars();
|
||||
void createStarBuffers();
|
||||
void destroyStarBuffers();
|
||||
|
||||
float getStarIntensity(float timeOfDay) const;
|
||||
|
||||
std::unique_ptr<Shader> starShader;
|
||||
|
||||
struct Star {
|
||||
glm::vec3 position;
|
||||
float brightness; // 0.3 to 1.0
|
||||
float twinklePhase; // 0 to 2π for animation
|
||||
};
|
||||
|
||||
std::vector<Star> stars;
|
||||
int starCount = 1000;
|
||||
|
||||
uint32_t vao = 0;
|
||||
uint32_t vbo = 0;
|
||||
|
||||
float twinkleTime = 0.0f;
|
||||
bool renderingEnabled = true;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
59
include/rendering/swim_effects.hpp
Normal file
59
include/rendering/swim_effects.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class CameraController;
|
||||
class WaterRenderer;
|
||||
class Shader;
|
||||
|
||||
class SwimEffects {
|
||||
public:
|
||||
SwimEffects();
|
||||
~SwimEffects();
|
||||
|
||||
bool initialize();
|
||||
void shutdown();
|
||||
void update(const Camera& camera, const CameraController& cc,
|
||||
const WaterRenderer& water, float deltaTime);
|
||||
void render(const Camera& camera);
|
||||
|
||||
private:
|
||||
struct Particle {
|
||||
glm::vec3 position;
|
||||
glm::vec3 velocity;
|
||||
float lifetime;
|
||||
float maxLifetime;
|
||||
float size;
|
||||
float alpha;
|
||||
};
|
||||
|
||||
static constexpr int MAX_RIPPLE_PARTICLES = 200;
|
||||
static constexpr int MAX_BUBBLE_PARTICLES = 150;
|
||||
|
||||
std::vector<Particle> ripples;
|
||||
std::vector<Particle> bubbles;
|
||||
|
||||
GLuint rippleVAO = 0, rippleVBO = 0;
|
||||
GLuint bubbleVAO = 0, bubbleVBO = 0;
|
||||
std::unique_ptr<Shader> rippleShader;
|
||||
std::unique_ptr<Shader> bubbleShader;
|
||||
|
||||
std::vector<float> rippleVertexData;
|
||||
std::vector<float> bubbleVertexData;
|
||||
|
||||
float rippleSpawnAccum = 0.0f;
|
||||
float bubbleSpawnAccum = 0.0f;
|
||||
|
||||
void spawnRipple(const glm::vec3& pos, const glm::vec3& moveDir, float waterH);
|
||||
void spawnBubble(const glm::vec3& pos, float waterH);
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
270
include/rendering/terrain_manager.hpp
Normal file
270
include/rendering/terrain_manager.hpp
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
#pragma once
|
||||
|
||||
#include "pipeline/adt_loader.hpp"
|
||||
#include "pipeline/terrain_mesh.hpp"
|
||||
#include "pipeline/m2_loader.hpp"
|
||||
#include "pipeline/wmo_loader.hpp"
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <queue>
|
||||
#include <condition_variable>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
|
||||
namespace pipeline { class AssetManager; }
|
||||
namespace rendering { class TerrainRenderer; class Camera; class WaterRenderer; class M2Renderer; class WMORenderer; }
|
||||
|
||||
namespace rendering {
|
||||
|
||||
/**
|
||||
* Terrain tile coordinates
|
||||
*/
|
||||
struct TileCoord {
|
||||
int x;
|
||||
int y;
|
||||
|
||||
bool operator==(const TileCoord& other) const {
|
||||
return x == other.x && y == other.y;
|
||||
}
|
||||
|
||||
struct Hash {
|
||||
size_t operator()(const TileCoord& coord) const {
|
||||
return std::hash<int>()(coord.x) ^ (std::hash<int>()(coord.y) << 1);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Loaded terrain tile data
|
||||
*/
|
||||
struct TerrainTile {
|
||||
TileCoord coord;
|
||||
pipeline::ADTTerrain terrain;
|
||||
pipeline::TerrainMesh mesh;
|
||||
bool loaded = false;
|
||||
|
||||
// Tile bounds in world coordinates
|
||||
float minX, minY, maxX, maxY;
|
||||
|
||||
// Instance IDs for cleanup on unload
|
||||
std::vector<uint32_t> wmoInstanceIds;
|
||||
std::vector<uint32_t> m2InstanceIds;
|
||||
std::vector<uint32_t> doodadUniqueIds; // For dedup cleanup on unload
|
||||
};
|
||||
|
||||
/**
|
||||
* Pre-processed tile data ready for GPU upload (produced by background thread)
|
||||
*/
|
||||
struct PendingTile {
|
||||
TileCoord coord;
|
||||
pipeline::ADTTerrain terrain;
|
||||
pipeline::TerrainMesh mesh;
|
||||
|
||||
// Pre-loaded M2 data
|
||||
struct M2Ready {
|
||||
uint32_t modelId;
|
||||
pipeline::M2Model model;
|
||||
std::string path;
|
||||
};
|
||||
std::vector<M2Ready> m2Models;
|
||||
|
||||
// M2 instance placement data (references modelId from m2Models)
|
||||
struct M2Placement {
|
||||
uint32_t modelId;
|
||||
uint32_t uniqueId;
|
||||
glm::vec3 position;
|
||||
glm::vec3 rotation;
|
||||
float scale;
|
||||
};
|
||||
std::vector<M2Placement> m2Placements;
|
||||
|
||||
// Pre-loaded WMO data
|
||||
struct WMOReady {
|
||||
uint32_t modelId;
|
||||
pipeline::WMOModel model;
|
||||
glm::vec3 position;
|
||||
glm::vec3 rotation;
|
||||
};
|
||||
std::vector<WMOReady> wmoModels;
|
||||
|
||||
// WMO doodad M2 models (M2s placed inside WMOs)
|
||||
struct WMODoodadReady {
|
||||
uint32_t modelId;
|
||||
pipeline::M2Model model;
|
||||
glm::vec3 worldPosition; // For frustum culling
|
||||
glm::mat4 modelMatrix; // Pre-computed world transform
|
||||
};
|
||||
std::vector<WMODoodadReady> wmoDoodads;
|
||||
};
|
||||
|
||||
/**
|
||||
* Terrain manager for multi-tile terrain streaming
|
||||
*
|
||||
* Handles loading and unloading terrain tiles based on camera position
|
||||
*/
|
||||
class TerrainManager {
|
||||
public:
|
||||
TerrainManager();
|
||||
~TerrainManager();
|
||||
|
||||
/**
|
||||
* Initialize terrain manager
|
||||
* @param assetManager Asset manager for loading files
|
||||
* @param terrainRenderer Terrain renderer for GPU upload
|
||||
*/
|
||||
bool initialize(pipeline::AssetManager* assetManager, TerrainRenderer* terrainRenderer);
|
||||
|
||||
/**
|
||||
* Update terrain streaming based on camera position
|
||||
* @param camera Current camera
|
||||
* @param deltaTime Time since last update
|
||||
*/
|
||||
void update(const Camera& camera, float deltaTime);
|
||||
|
||||
/**
|
||||
* Set map name
|
||||
* @param mapName Map name (e.g., "Azeroth", "Kalimdor")
|
||||
*/
|
||||
void setMapName(const std::string& mapName) { this->mapName = mapName; }
|
||||
|
||||
/**
|
||||
* Load a single tile
|
||||
* @param x Tile X coordinate (0-63)
|
||||
* @param y Tile Y coordinate (0-63)
|
||||
* @return true if loaded successfully
|
||||
*/
|
||||
bool loadTile(int x, int y);
|
||||
|
||||
/**
|
||||
* Unload a tile
|
||||
* @param x Tile X coordinate
|
||||
* @param y Tile Y coordinate
|
||||
*/
|
||||
void unloadTile(int x, int y);
|
||||
|
||||
/**
|
||||
* Unload all tiles
|
||||
*/
|
||||
void unloadAll();
|
||||
|
||||
/**
|
||||
* Set streaming parameters
|
||||
*/
|
||||
void setLoadRadius(int radius) { loadRadius = radius; }
|
||||
void setUnloadRadius(int radius) { unloadRadius = radius; }
|
||||
void setStreamingEnabled(bool enabled) { streamingEnabled = enabled; }
|
||||
void setWaterRenderer(WaterRenderer* renderer) { waterRenderer = renderer; }
|
||||
void setM2Renderer(M2Renderer* renderer) { m2Renderer = renderer; }
|
||||
void setWMORenderer(WMORenderer* renderer) { wmoRenderer = renderer; }
|
||||
|
||||
/**
|
||||
* Get terrain height at GL coordinates
|
||||
* @param glX GL X position
|
||||
* @param glY GL Y position
|
||||
* @return Height (GL Z) if terrain loaded at that position, empty otherwise
|
||||
*/
|
||||
std::optional<float> getHeightAt(float glX, float glY) const;
|
||||
|
||||
/**
|
||||
* Get statistics
|
||||
*/
|
||||
int getLoadedTileCount() const { return static_cast<int>(loadedTiles.size()); }
|
||||
TileCoord getCurrentTile() const { return currentTile; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Get tile coordinates from world position
|
||||
*/
|
||||
TileCoord worldToTile(float worldX, float worldY) const;
|
||||
|
||||
/**
|
||||
* Get world bounds for a tile
|
||||
*/
|
||||
void getTileBounds(const TileCoord& coord, float& minX, float& minY,
|
||||
float& maxX, float& maxY) const;
|
||||
|
||||
/**
|
||||
* Build ADT file path
|
||||
*/
|
||||
std::string getADTPath(const TileCoord& coord) const;
|
||||
|
||||
/**
|
||||
* Load tiles in radius around current tile
|
||||
*/
|
||||
void streamTiles();
|
||||
|
||||
/**
|
||||
* Background thread: prepare tile data (CPU work only, no OpenGL)
|
||||
*/
|
||||
std::unique_ptr<PendingTile> prepareTile(int x, int y);
|
||||
|
||||
/**
|
||||
* Main thread: upload prepared tile data to GPU
|
||||
*/
|
||||
void finalizeTile(std::unique_ptr<PendingTile> pending);
|
||||
|
||||
/**
|
||||
* Background worker thread loop
|
||||
*/
|
||||
void workerLoop();
|
||||
|
||||
/**
|
||||
* Main thread: poll for completed tiles and upload to GPU
|
||||
*/
|
||||
void processReadyTiles();
|
||||
|
||||
pipeline::AssetManager* assetManager = nullptr;
|
||||
TerrainRenderer* terrainRenderer = nullptr;
|
||||
WaterRenderer* waterRenderer = nullptr;
|
||||
M2Renderer* m2Renderer = nullptr;
|
||||
WMORenderer* wmoRenderer = nullptr;
|
||||
|
||||
std::string mapName = "Azeroth";
|
||||
|
||||
// Loaded tiles (keyed by coordinate)
|
||||
std::unordered_map<TileCoord, std::unique_ptr<TerrainTile>, TileCoord::Hash> loadedTiles;
|
||||
|
||||
// Tiles that failed to load (don't retry)
|
||||
std::unordered_map<TileCoord, bool, TileCoord::Hash> failedTiles;
|
||||
|
||||
// Current tile (where camera is)
|
||||
TileCoord currentTile = {-1, -1};
|
||||
TileCoord lastStreamTile = {-1, -1};
|
||||
|
||||
// Streaming parameters
|
||||
bool streamingEnabled = true;
|
||||
int loadRadius = 4; // Load tiles within this radius (9x9 grid, ~2133 units)
|
||||
int unloadRadius = 6; // Unload tiles beyond this radius (~3200 units, past far clip)
|
||||
float updateInterval = 0.1f; // Check streaming every 0.1 seconds
|
||||
float timeSinceLastUpdate = 0.0f;
|
||||
|
||||
// Tile size constants (WoW ADT specifications)
|
||||
// A tile (ADT) = 16x16 chunks = 533.33 units across
|
||||
// A chunk = 8x8 vertex quads = 33.33 units across
|
||||
static constexpr float TILE_SIZE = 533.33333f; // One tile = 533.33 units
|
||||
static constexpr float CHUNK_SIZE = 33.33333f; // One chunk = 33.33 units
|
||||
|
||||
// Background loading thread
|
||||
std::thread workerThread;
|
||||
std::mutex queueMutex;
|
||||
std::condition_variable queueCV;
|
||||
std::queue<TileCoord> loadQueue;
|
||||
std::queue<std::unique_ptr<PendingTile>> readyQueue;
|
||||
std::atomic<bool> workerRunning{false};
|
||||
|
||||
// Track tiles currently queued or being processed to avoid duplicates
|
||||
std::unordered_map<TileCoord, bool, TileCoord::Hash> pendingTiles;
|
||||
|
||||
// Dedup set for doodad placements across tile boundaries
|
||||
std::unordered_set<uint32_t> placedDoodadIds;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
193
include/rendering/terrain_renderer.hpp
Normal file
193
include/rendering/terrain_renderer.hpp
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
#pragma once
|
||||
|
||||
#include "pipeline/terrain_mesh.hpp"
|
||||
#include "rendering/shader.hpp"
|
||||
#include "rendering/texture.hpp"
|
||||
#include "rendering/camera.hpp"
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
namespace wowee {
|
||||
|
||||
// Forward declarations
|
||||
namespace pipeline { class AssetManager; }
|
||||
|
||||
namespace rendering {
|
||||
|
||||
class Frustum;
|
||||
|
||||
/**
|
||||
* GPU-side terrain chunk data
|
||||
*/
|
||||
struct TerrainChunkGPU {
|
||||
GLuint vao = 0; // Vertex array object
|
||||
GLuint vbo = 0; // Vertex buffer
|
||||
GLuint ibo = 0; // Index buffer
|
||||
uint32_t indexCount = 0; // Number of indices to draw
|
||||
|
||||
// Texture IDs for this chunk
|
||||
GLuint baseTexture = 0;
|
||||
std::vector<GLuint> layerTextures;
|
||||
std::vector<GLuint> alphaTextures;
|
||||
|
||||
// World position for culling
|
||||
float worldX = 0.0f;
|
||||
float worldY = 0.0f;
|
||||
float worldZ = 0.0f;
|
||||
|
||||
// Owning tile coordinates (for per-tile removal)
|
||||
int tileX = -1, tileY = -1;
|
||||
|
||||
// Bounding sphere for frustum culling
|
||||
float boundingSphereRadius = 0.0f;
|
||||
glm::vec3 boundingSphereCenter = glm::vec3(0.0f);
|
||||
|
||||
bool isValid() const { return vao != 0 && vbo != 0 && ibo != 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Terrain renderer
|
||||
*
|
||||
* Handles uploading terrain meshes to GPU and rendering them
|
||||
*/
|
||||
class TerrainRenderer {
|
||||
public:
|
||||
TerrainRenderer();
|
||||
~TerrainRenderer();
|
||||
|
||||
/**
|
||||
* Initialize terrain renderer
|
||||
* @param assetManager Asset manager for loading textures
|
||||
*/
|
||||
bool initialize(pipeline::AssetManager* assetManager);
|
||||
|
||||
/**
|
||||
* Shutdown and cleanup GPU resources
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Load terrain mesh and upload to GPU
|
||||
* @param mesh Terrain mesh to load
|
||||
* @param texturePaths Texture file paths from ADT
|
||||
* @param tileX Tile X coordinate for tracking ownership (-1 = untracked)
|
||||
* @param tileY Tile Y coordinate for tracking ownership (-1 = untracked)
|
||||
*/
|
||||
bool loadTerrain(const pipeline::TerrainMesh& mesh,
|
||||
const std::vector<std::string>& texturePaths,
|
||||
int tileX = -1, int tileY = -1);
|
||||
|
||||
/**
|
||||
* Remove all chunks belonging to a specific tile
|
||||
* @param tileX Tile X coordinate
|
||||
* @param tileY Tile Y coordinate
|
||||
*/
|
||||
void removeTile(int tileX, int tileY);
|
||||
|
||||
/**
|
||||
* Render loaded terrain
|
||||
* @param camera Camera for view/projection matrices
|
||||
*/
|
||||
void render(const Camera& camera);
|
||||
|
||||
/**
|
||||
* Clear all loaded terrain
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Set lighting parameters
|
||||
*/
|
||||
void setLighting(const float lightDir[3], const float lightColor[3],
|
||||
const float ambientColor[3]);
|
||||
|
||||
/**
|
||||
* Set fog parameters
|
||||
*/
|
||||
void setFog(const float fogColor[3], float fogStart, float fogEnd);
|
||||
|
||||
/**
|
||||
* Enable/disable wireframe rendering
|
||||
*/
|
||||
void setWireframe(bool enabled) { wireframe = enabled; }
|
||||
|
||||
/**
|
||||
* Enable/disable frustum culling
|
||||
*/
|
||||
void setFrustumCulling(bool enabled) { frustumCullingEnabled = enabled; }
|
||||
|
||||
/**
|
||||
* Enable/disable distance fog
|
||||
*/
|
||||
void setFogEnabled(bool enabled) { fogEnabled = enabled; }
|
||||
bool isFogEnabled() const { return fogEnabled; }
|
||||
|
||||
/**
|
||||
* Get statistics
|
||||
*/
|
||||
int getChunkCount() const { return static_cast<int>(chunks.size()); }
|
||||
int getRenderedChunkCount() const { return renderedChunks; }
|
||||
int getCulledChunkCount() const { return culledChunks; }
|
||||
int getTriangleCount() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Upload single chunk to GPU
|
||||
*/
|
||||
TerrainChunkGPU uploadChunk(const pipeline::ChunkMesh& chunk);
|
||||
|
||||
/**
|
||||
* Load texture from asset manager
|
||||
*/
|
||||
GLuint loadTexture(const std::string& path);
|
||||
|
||||
/**
|
||||
* Create alpha texture from raw alpha data
|
||||
*/
|
||||
GLuint createAlphaTexture(const std::vector<uint8_t>& alphaData);
|
||||
|
||||
/**
|
||||
* Check if chunk is in view frustum
|
||||
*/
|
||||
bool isChunkVisible(const TerrainChunkGPU& chunk, const Frustum& frustum);
|
||||
|
||||
/**
|
||||
* Calculate bounding sphere for chunk
|
||||
*/
|
||||
void calculateBoundingSphere(TerrainChunkGPU& chunk, const pipeline::ChunkMesh& meshChunk);
|
||||
|
||||
pipeline::AssetManager* assetManager = nullptr;
|
||||
std::unique_ptr<Shader> shader;
|
||||
|
||||
// Loaded terrain chunks
|
||||
std::vector<TerrainChunkGPU> chunks;
|
||||
|
||||
// Texture cache (path -> GL texture ID)
|
||||
std::unordered_map<std::string, GLuint> textureCache;
|
||||
|
||||
// Lighting parameters
|
||||
float lightDir[3] = {-0.5f, -1.0f, -0.5f};
|
||||
float lightColor[3] = {1.0f, 1.0f, 0.9f};
|
||||
float ambientColor[3] = {0.3f, 0.3f, 0.35f};
|
||||
|
||||
// Fog parameters
|
||||
float fogColor[3] = {0.5f, 0.6f, 0.7f};
|
||||
float fogStart = 400.0f;
|
||||
float fogEnd = 800.0f;
|
||||
|
||||
// Rendering state
|
||||
bool wireframe = false;
|
||||
bool frustumCullingEnabled = true;
|
||||
bool fogEnabled = true;
|
||||
int renderedChunks = 0;
|
||||
int culledChunks = 0;
|
||||
|
||||
// Default white texture (fallback)
|
||||
GLuint whiteTexture = 0;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
31
include/rendering/texture.hpp
Normal file
31
include/rendering/texture.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <GL/glew.h>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Texture {
|
||||
public:
|
||||
Texture() = default;
|
||||
~Texture();
|
||||
|
||||
bool loadFromFile(const std::string& path);
|
||||
bool loadFromMemory(const unsigned char* data, int width, int height, int channels);
|
||||
|
||||
void bind(GLuint unit = 0) const;
|
||||
void unbind() const;
|
||||
|
||||
GLuint getID() const { return textureID; }
|
||||
int getWidth() const { return width; }
|
||||
int getHeight() const { return height; }
|
||||
|
||||
private:
|
||||
GLuint textureID = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
123
include/rendering/water_renderer.hpp
Normal file
123
include/rendering/water_renderer.hpp
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
struct ADTTerrain;
|
||||
struct LiquidData;
|
||||
}
|
||||
|
||||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class Shader;
|
||||
|
||||
/**
|
||||
* Water surface for a single map chunk
|
||||
*/
|
||||
struct WaterSurface {
|
||||
glm::vec3 position; // World position
|
||||
float minHeight; // Minimum water height
|
||||
float maxHeight; // Maximum water height
|
||||
uint8_t liquidType; // 0=water, 1=ocean, 2=magma, 3=slime
|
||||
|
||||
// Owning tile coordinates (for per-tile removal)
|
||||
int tileX = -1, tileY = -1;
|
||||
|
||||
// Water layer dimensions within chunk (0-7 offset, 1-8 size)
|
||||
uint8_t xOffset = 0;
|
||||
uint8_t yOffset = 0;
|
||||
uint8_t width = 8; // Width in tiles (1-8)
|
||||
uint8_t height = 8; // Height in tiles (1-8)
|
||||
|
||||
// Height map for water surface ((width+1) x (height+1) vertices)
|
||||
std::vector<float> heights;
|
||||
|
||||
// Render mask (which tiles have water)
|
||||
std::vector<uint8_t> mask;
|
||||
|
||||
// Render data
|
||||
uint32_t vao = 0;
|
||||
uint32_t vbo = 0;
|
||||
uint32_t ebo = 0;
|
||||
int indexCount = 0;
|
||||
|
||||
bool hasHeightData() const { return !heights.empty(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Water renderer
|
||||
*
|
||||
* Renders water surfaces with transparency and animation.
|
||||
* Supports multiple liquid types (water, ocean, magma, slime).
|
||||
*/
|
||||
class WaterRenderer {
|
||||
public:
|
||||
WaterRenderer();
|
||||
~WaterRenderer();
|
||||
|
||||
bool initialize();
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Load water surfaces from ADT terrain
|
||||
* @param terrain The ADT terrain data
|
||||
* @param append If true, add to existing water instead of replacing
|
||||
* @param tileX Tile X coordinate for tracking ownership (-1 = untracked)
|
||||
* @param tileY Tile Y coordinate for tracking ownership (-1 = untracked)
|
||||
*/
|
||||
void loadFromTerrain(const pipeline::ADTTerrain& terrain, bool append = false,
|
||||
int tileX = -1, int tileY = -1);
|
||||
|
||||
/**
|
||||
* Remove all water surfaces belonging to a specific tile
|
||||
* @param tileX Tile X coordinate
|
||||
* @param tileY Tile Y coordinate
|
||||
*/
|
||||
void removeTile(int tileX, int tileY);
|
||||
|
||||
/**
|
||||
* Clear all water surfaces
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Render all water surfaces
|
||||
*/
|
||||
void render(const Camera& camera, float time);
|
||||
|
||||
/**
|
||||
* Enable/disable water rendering
|
||||
*/
|
||||
void setEnabled(bool enabled) { renderingEnabled = enabled; }
|
||||
bool isEnabled() const { return renderingEnabled; }
|
||||
|
||||
/**
|
||||
* Query the water height at a given world position.
|
||||
* Returns the highest water surface height at that XY, or nullopt if no water.
|
||||
*/
|
||||
std::optional<float> getWaterHeightAt(float glX, float glY) const;
|
||||
|
||||
/**
|
||||
* Get water surface count
|
||||
*/
|
||||
int getSurfaceCount() const { return static_cast<int>(surfaces.size()); }
|
||||
|
||||
private:
|
||||
void createWaterMesh(WaterSurface& surface);
|
||||
void destroyWaterMesh(WaterSurface& surface);
|
||||
|
||||
glm::vec4 getLiquidColor(uint8_t liquidType) const;
|
||||
float getLiquidAlpha(uint8_t liquidType) const;
|
||||
|
||||
std::unique_ptr<Shader> waterShader;
|
||||
std::vector<WaterSurface> surfaces;
|
||||
bool renderingEnabled = true;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
112
include/rendering/weather.hpp
Normal file
112
include/rendering/weather.hpp
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class Shader;
|
||||
|
||||
/**
|
||||
* @brief Weather particle system for rain and snow
|
||||
*
|
||||
* Features:
|
||||
* - Rain particles (fast vertical drops)
|
||||
* - Snow particles (slow floating flakes)
|
||||
* - Particle recycling for efficiency
|
||||
* - Camera-relative positioning (follows player)
|
||||
* - Adjustable intensity (light, medium, heavy)
|
||||
* - GPU instanced rendering
|
||||
*/
|
||||
class Weather {
|
||||
public:
|
||||
enum class Type {
|
||||
NONE,
|
||||
RAIN,
|
||||
SNOW
|
||||
};
|
||||
|
||||
Weather();
|
||||
~Weather();
|
||||
|
||||
/**
|
||||
* @brief Initialize weather system
|
||||
* @return true if initialization succeeded
|
||||
*/
|
||||
bool initialize();
|
||||
|
||||
/**
|
||||
* @brief Update weather particles
|
||||
* @param camera Camera for particle positioning
|
||||
* @param deltaTime Time since last frame
|
||||
*/
|
||||
void update(const Camera& camera, float deltaTime);
|
||||
|
||||
/**
|
||||
* @brief Render weather particles
|
||||
* @param camera Camera for rendering
|
||||
*/
|
||||
void render(const Camera& camera);
|
||||
|
||||
/**
|
||||
* @brief Set weather type
|
||||
*/
|
||||
void setWeatherType(Type type) { weatherType = type; }
|
||||
Type getWeatherType() const { return weatherType; }
|
||||
|
||||
/**
|
||||
* @brief Set weather intensity (0.0 = none, 1.0 = heavy)
|
||||
*/
|
||||
void setIntensity(float intensity);
|
||||
float getIntensity() const { return intensity; }
|
||||
|
||||
/**
|
||||
* @brief Enable or disable weather
|
||||
*/
|
||||
void setEnabled(bool enabled) { this->enabled = enabled; }
|
||||
bool isEnabled() const { return enabled; }
|
||||
|
||||
/**
|
||||
* @brief Get active particle count
|
||||
*/
|
||||
int getParticleCount() const;
|
||||
|
||||
private:
|
||||
struct Particle {
|
||||
glm::vec3 position;
|
||||
glm::vec3 velocity;
|
||||
float lifetime;
|
||||
float maxLifetime;
|
||||
};
|
||||
|
||||
void cleanup();
|
||||
void resetParticles(const Camera& camera);
|
||||
void updateParticle(Particle& particle, const Camera& camera, float deltaTime);
|
||||
glm::vec3 getRandomPosition(const glm::vec3& center) const;
|
||||
|
||||
// OpenGL objects
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0; // Instance buffer
|
||||
std::unique_ptr<Shader> shader;
|
||||
|
||||
// Particles
|
||||
std::vector<Particle> particles;
|
||||
std::vector<glm::vec3> particlePositions; // For rendering
|
||||
|
||||
// Weather parameters
|
||||
bool enabled = false;
|
||||
Type weatherType = Type::NONE;
|
||||
float intensity = 0.5f;
|
||||
|
||||
// Particle system parameters
|
||||
static constexpr int MAX_PARTICLES = 2000;
|
||||
static constexpr float SPAWN_VOLUME_SIZE = 100.0f; // Size of spawn area around camera
|
||||
static constexpr float SPAWN_HEIGHT = 80.0f; // Height above camera to spawn
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
262
include/rendering/wmo_renderer.hpp
Normal file
262
include/rendering/wmo_renderer.hpp
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
struct WMOModel;
|
||||
struct WMOGroup;
|
||||
class AssetManager;
|
||||
}
|
||||
|
||||
namespace rendering {
|
||||
|
||||
class Camera;
|
||||
class Shader;
|
||||
|
||||
/**
|
||||
* WMO (World Model Object) Renderer
|
||||
*
|
||||
* Renders buildings, dungeons, and large structures from WMO files.
|
||||
* Features:
|
||||
* - Multi-material rendering
|
||||
* - Batched rendering per group
|
||||
* - Frustum culling
|
||||
* - Portal visibility (future)
|
||||
* - Dynamic lighting support (future)
|
||||
*/
|
||||
class WMORenderer {
|
||||
public:
|
||||
WMORenderer();
|
||||
~WMORenderer();
|
||||
|
||||
/**
|
||||
* Initialize renderer and create shaders
|
||||
* @param assetManager Asset manager for loading textures (optional)
|
||||
*/
|
||||
bool initialize(pipeline::AssetManager* assetManager = nullptr);
|
||||
|
||||
/**
|
||||
* Cleanup GPU resources
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Load WMO model and create GPU resources
|
||||
* @param model WMO model with geometry data
|
||||
* @param id Unique identifier for this WMO instance
|
||||
* @return True if successful
|
||||
*/
|
||||
bool loadModel(const pipeline::WMOModel& model, uint32_t id);
|
||||
|
||||
/**
|
||||
* Unload WMO model and free GPU resources
|
||||
* @param id WMO model identifier
|
||||
*/
|
||||
void unloadModel(uint32_t id);
|
||||
|
||||
/**
|
||||
* Create a WMO instance in the world
|
||||
* @param modelId WMO model to instantiate
|
||||
* @param position World position
|
||||
* @param rotation Rotation (euler angles in radians)
|
||||
* @param scale Uniform scale
|
||||
* @return Instance ID
|
||||
*/
|
||||
uint32_t createInstance(uint32_t modelId, const glm::vec3& position,
|
||||
const glm::vec3& rotation = glm::vec3(0.0f),
|
||||
float scale = 1.0f);
|
||||
|
||||
/**
|
||||
* Remove WMO instance
|
||||
* @param instanceId Instance to remove
|
||||
*/
|
||||
void removeInstance(uint32_t instanceId);
|
||||
|
||||
/**
|
||||
* Remove all instances
|
||||
*/
|
||||
void clearInstances();
|
||||
|
||||
/**
|
||||
* Render all WMO instances
|
||||
* @param camera Camera for view/projection matrices
|
||||
* @param view View matrix
|
||||
* @param projection Projection matrix
|
||||
*/
|
||||
void render(const Camera& camera, const glm::mat4& view, const glm::mat4& projection);
|
||||
|
||||
/**
|
||||
* Get number of loaded models
|
||||
*/
|
||||
uint32_t getModelCount() const { return loadedModels.size(); }
|
||||
|
||||
/**
|
||||
* Get number of active instances
|
||||
*/
|
||||
uint32_t getInstanceCount() const { return instances.size(); }
|
||||
|
||||
/**
|
||||
* Get total triangle count (all instances)
|
||||
*/
|
||||
uint32_t getTotalTriangleCount() const;
|
||||
|
||||
/**
|
||||
* Get total draw call count (last frame)
|
||||
*/
|
||||
uint32_t getDrawCallCount() const { return lastDrawCalls; }
|
||||
|
||||
/**
|
||||
* Enable/disable wireframe rendering
|
||||
*/
|
||||
void setWireframeMode(bool enabled) { wireframeMode = enabled; }
|
||||
|
||||
/**
|
||||
* Enable/disable frustum culling
|
||||
*/
|
||||
void setFrustumCulling(bool enabled) { frustumCulling = enabled; }
|
||||
|
||||
/**
|
||||
* Get floor height at a GL position via ray-triangle intersection
|
||||
*/
|
||||
std::optional<float> getFloorHeight(float glX, float glY, float glZ) const;
|
||||
|
||||
/**
|
||||
* Check wall collision and adjust position
|
||||
* @param from Starting position
|
||||
* @param to Desired position
|
||||
* @param adjustedPos Output adjusted position (pushed away from walls)
|
||||
* @return true if collision occurred
|
||||
*/
|
||||
bool checkWallCollision(const glm::vec3& from, const glm::vec3& to, glm::vec3& adjustedPos) const;
|
||||
|
||||
/**
|
||||
* Check if a position is inside any WMO
|
||||
* @param outModelId If not null, receives the model ID of the WMO
|
||||
* @return true if inside a WMO
|
||||
*/
|
||||
bool isInsideWMO(float glX, float glY, float glZ, uint32_t* outModelId = nullptr) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* WMO group GPU resources
|
||||
*/
|
||||
struct GroupResources {
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0;
|
||||
GLuint ebo = 0;
|
||||
uint32_t indexCount = 0;
|
||||
uint32_t vertexCount = 0;
|
||||
glm::vec3 boundingBoxMin;
|
||||
glm::vec3 boundingBoxMax;
|
||||
|
||||
// Material batches (start index, count, material ID)
|
||||
struct Batch {
|
||||
uint32_t startIndex; // First index in EBO
|
||||
uint32_t indexCount; // Number of indices to draw
|
||||
uint8_t materialId; // Material/texture reference
|
||||
};
|
||||
std::vector<Batch> batches;
|
||||
|
||||
// Collision geometry (positions only, for floor raycasting)
|
||||
std::vector<glm::vec3> collisionVertices;
|
||||
std::vector<uint16_t> collisionIndices;
|
||||
};
|
||||
|
||||
/**
|
||||
* Loaded WMO model data
|
||||
*/
|
||||
struct ModelData {
|
||||
uint32_t id;
|
||||
std::vector<GroupResources> groups;
|
||||
glm::vec3 boundingBoxMin;
|
||||
glm::vec3 boundingBoxMax;
|
||||
|
||||
// Texture handles for this model (indexed by texture path order)
|
||||
std::vector<GLuint> textures;
|
||||
|
||||
// Material texture indices (materialId -> texture index)
|
||||
std::vector<uint32_t> materialTextureIndices;
|
||||
|
||||
// Material blend modes (materialId -> blendMode; 1 = alpha-test cutout)
|
||||
std::vector<uint32_t> materialBlendModes;
|
||||
|
||||
uint32_t getTotalTriangles() const {
|
||||
uint32_t total = 0;
|
||||
for (const auto& group : groups) {
|
||||
total += group.indexCount / 3;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* WMO instance in the world
|
||||
*/
|
||||
struct WMOInstance {
|
||||
uint32_t id;
|
||||
uint32_t modelId;
|
||||
glm::vec3 position;
|
||||
glm::vec3 rotation; // Euler angles (radians)
|
||||
float scale;
|
||||
glm::mat4 modelMatrix;
|
||||
|
||||
void updateModelMatrix();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create GPU resources for a WMO group
|
||||
*/
|
||||
bool createGroupResources(const pipeline::WMOGroup& group, GroupResources& resources);
|
||||
|
||||
/**
|
||||
* Render a single group
|
||||
*/
|
||||
void renderGroup(const GroupResources& group, const ModelData& model,
|
||||
const glm::mat4& modelMatrix,
|
||||
const glm::mat4& view, const glm::mat4& projection);
|
||||
|
||||
/**
|
||||
* Check if group is visible in frustum
|
||||
*/
|
||||
bool isGroupVisible(const GroupResources& group, const glm::mat4& modelMatrix,
|
||||
const Camera& camera) const;
|
||||
|
||||
/**
|
||||
* Load a texture from path
|
||||
*/
|
||||
GLuint loadTexture(const std::string& path);
|
||||
|
||||
// Shader
|
||||
std::unique_ptr<Shader> shader;
|
||||
|
||||
// Asset manager for loading textures
|
||||
pipeline::AssetManager* assetManager = nullptr;
|
||||
|
||||
// Texture cache (path -> texture ID)
|
||||
std::unordered_map<std::string, GLuint> textureCache;
|
||||
|
||||
// Default white texture
|
||||
GLuint whiteTexture = 0;
|
||||
|
||||
// Loaded models (modelId -> ModelData)
|
||||
std::unordered_map<uint32_t, ModelData> loadedModels;
|
||||
|
||||
// Active instances
|
||||
std::vector<WMOInstance> instances;
|
||||
uint32_t nextInstanceId = 1;
|
||||
|
||||
// Rendering state
|
||||
bool wireframeMode = false;
|
||||
bool frustumCulling = true;
|
||||
uint32_t lastDrawCalls = 0;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
Loading…
Add table
Add a link
Reference in a new issue