Rendering¶
The rendering layer converts W3D data to GPU resources and handles visualization.
Overview¶
Located in src/render/, this layer provides:
- Mesh conversion and GPU upload
- Skeletal animation
- LOD management
- Camera control
- Debug visualization
HLodModel¶
hlod_model.hpp/cpp - Multi-LOD model representation.
Structure¶
graph TB
HM[HLodModel]
HM --> LOD0[LOD Level 0]
HM --> LOD1[LOD Level 1]
HM --> LODN[LOD Level N]
LOD0 --> M0A[Mesh A]
LOD0 --> M0B[Mesh B]
LOD1 --> M1A[Mesh A simplified]
HM --> SKEL[Skeleton]
HM --> ANIM[Animations]
LOD Selection¶
Automatic LOD based on screen size:
int HLodModel::selectLOD(float screenSize) const {
for (int i = 0; i < lodArrays.size(); i++) {
if (screenSize <= lodArrays[i].maxScreenSize) {
return i;
}
}
return lodArrays.size() - 1; // Lowest detail
}
Drawing¶
void HLodModel::draw(vk::CommandBuffer cmd, const Pipeline& pipeline) {
int lod = selectLOD(currentScreenSize);
for (const auto& mesh : lodArrays[lod].meshes) {
// Update bone matrices if skinned
if (mesh.isSkinned()) {
updateBoneBuffer(mesh);
}
mesh.draw(cmd, pipeline);
}
}
RenderableMesh¶
renderable_mesh.hpp/cpp - GPU mesh representation.
GPU Resources¶
class RenderableMesh {
Buffer vertexBuffer;
Buffer indexBuffer;
uint32_t indexCount;
// Material data
MaterialData material;
Texture* texture;
};
Vertex Format¶
struct Vertex {
glm::vec3 position; // 12 bytes
glm::vec3 normal; // 12 bytes
glm::vec2 texCoord; // 8 bytes
glm::uvec4 boneIds; // 16 bytes
glm::vec4 boneWeights; // 16 bytes
}; // Total: 64 bytes
Drawing¶
void RenderableMesh::draw(vk::CommandBuffer cmd) {
cmd.bindVertexBuffers(0, vertexBuffer.buffer(), {0});
cmd.bindIndexBuffer(indexBuffer.buffer(), 0, vk::IndexType::eUint32);
// Push material data
cmd.pushConstants(layout, vk::ShaderStageFlagBits::eFragment,
0, sizeof(MaterialData), &material);
cmd.drawIndexed(indexCount, 1, 0, 0, 0);
}
MeshConverter¶
mesh_converter.hpp/cpp - W3D to GPU conversion.
Conversion Process¶
graph LR
W3D[W3D Mesh] --> UNROLL[Unroll faces]
UNROLL --> VERTS[Build vertices]
VERTS --> UPLOAD[Upload to GPU]
UPLOAD --> RM[RenderableMesh]
Face Unrolling¶
W3D uses per-face UV indices, requiring vertex duplication:
std::vector<Vertex> unrollMesh(const Mesh& mesh) {
std::vector<Vertex> vertices;
for (const auto& tri : mesh.triangles) {
for (int i = 0; i < 3; i++) {
Vertex v;
v.position = mesh.vertices[tri.vertexIndices[i]];
v.normal = mesh.normals[tri.vertexIndices[i]];
// UV from per-face index
v.texCoord = mesh.texCoords[perFaceUVs[faceIdx][i]];
vertices.push_back(v);
}
}
return vertices;
}
Skeleton¶
skeleton.hpp/cpp - Bone pose computation.
Bone Matrix Calculation¶
glm::mat4 Skeleton::computeBoneMatrix(int boneIndex) {
const auto& bone = hierarchy.pivots[boneIndex];
// Local transform
glm::mat4 local = glm::translate(glm::mat4(1.0f), bone.translation)
* glm::mat4_cast(bone.rotation);
// Multiply by parent
if (bone.parentIndex != 0xFFFFFFFF) {
return computeBoneMatrix(bone.parentIndex) * local;
}
return local;
}
Pose Update¶
void Skeleton::updatePose(const Animation& anim, float time) {
for (const auto& channel : anim.channels) {
float value = interpolate(channel, time);
applyChannel(channel.pivot, channel.flags, value);
}
// Recompute all bone matrices
computeWorldMatrices();
}
AnimationPlayer¶
animation_player.hpp/cpp - Animation playback control.
Interface¶
class AnimationPlayer {
public:
void setAnimation(const Animation* anim);
void play();
void pause();
void stop();
void setSpeed(float speed);
void setLoop(bool loop);
void setFrame(float frame);
void update(float deltaTime);
float getCurrentFrame() const;
};
Playback Loop¶
void AnimationPlayer::update(float deltaTime) {
if (!playing || !animation) return;
currentTime += deltaTime * speed;
if (currentTime >= duration) {
if (loop) {
currentTime = fmod(currentTime, duration);
} else {
currentTime = duration;
playing = false;
}
}
}
BoneBuffer¶
bone_buffer.hpp/cpp - GPU bone matrix storage.
Buffer Layout¶
Update¶
void BoneBuffer::update(const Skeleton& skeleton) {
auto* data = buffer.map();
for (int i = 0; i < skeleton.boneCount(); i++) {
data->bones[i] = skeleton.getBoneMatrix(i);
}
buffer.unmap();
}
Camera¶
camera.hpp/cpp - Orbital camera implementation.
State¶
class Camera {
glm::vec3 target; // Focal point
float distance; // Distance from target
float azimuth; // Horizontal angle
float elevation; // Vertical angle
float fov; // Field of view
};
View Matrix¶
glm::mat4 Camera::viewMatrix() const {
glm::vec3 position = target + sphericalToCartesian(distance, azimuth, elevation);
return glm::lookAt(position, target, glm::vec3(0, 1, 0));
}
Mouse Input¶
void Camera::onMouseDrag(float dx, float dy) {
azimuth += dx * rotationSpeed;
elevation = glm::clamp(elevation + dy * rotationSpeed,
-89.0f, 89.0f);
}
void Camera::onScroll(float delta) {
distance *= (1.0f - delta * zoomSpeed);
distance = glm::clamp(distance, minDistance, maxDistance);
}
Texture¶
texture.hpp/cpp - Texture loading and management.
Texture Cache¶
class TextureManager {
std::unordered_map<std::string, Texture> cache;
public:
Texture* get(const std::string& name);
void preload(const std::vector<std::string>& names);
};
Supported Formats¶
| Format | Extension | Notes |
|---|---|---|
| TGA | .tga |
Most common |
| DDS | .dds |
Compressed |
SkeletonRenderer¶
skeleton_renderer.hpp/cpp - Debug bone visualization.
Renders bones as lines connecting parent to child:
void SkeletonRenderer::render(const Skeleton& skeleton) {
for (const auto& bone : skeleton.bones()) {
if (bone.parentIndex != ROOT) {
drawLine(bone.worldPosition,
skeleton.bones()[bone.parentIndex].worldPosition,
boneColor);
}
}
}
Material¶
material.hpp - Material data for shaders.