preparing doodad rendering, rendering cubes in place, need to fix alignment rotation as it is wildy off

This commit is contained in:
Natsirt867
2026-01-06 23:13:47 -06:00
parent 84f2f7470f
commit 482f92b5c9
13 changed files with 255 additions and 37 deletions

View File

@@ -36,6 +36,7 @@ add_executable(BLPExtractor
src/renderer/texture.c
src/renderer/texture.h
src/renderer/render_types.h
src/renderer/render_types.c
)
# static C runtime /MTd (debug mode StormLibDUS.lib)

View File

@@ -3,14 +3,21 @@ out vec4 FragColor;
in vec2 TexCoord;
in vec4 VertexColour;
uniform sampler2D texSampler;
uniform vec4 u_overrideColour;
void main() {
// sample the texture
vec4 texColor = texture(texSampler, TexCoord);
if (u_overrideColour.a > 0.0) {
FragColor = u_overrideColour;
return;
}
if (texColor.a < 0.1) discard;
// sample the texture
vec4 texColour = texture(texSampler, TexCoord) * VertexColour;
if (texColour.a < 0.1) discard;
//FragColor = vec4(0.6, 0.0, 1.0, 1.0);
FragColor = texColor * VertexColour;
FragColor = texColour;
}

View File

@@ -7,12 +7,24 @@ layout(location = 3) in vec4 vertex_colour;
out vec2 TexCoord;
out vec4 VertexColour;
uniform mat4 matrix; // must match "matrix" in C code
uniform mat4 view; // must match "view" in C code
uniform mat4 proj; // must match "proj" in C code
// TODO: change c code to match with u_<uniformName>
// also types really matter for shaders!!
uniform mat4 matrix;
uniform mat4 view;
uniform mat4 proj;
uniform vec3 u_lightDir;
uniform float u_lightInfluence; // 0.0 indoors, 1.0 outdoors
void main() {
gl_Position = proj * view * matrix * vec4(vertex_position, 1.0);
// max 0.3 so the shadowed side isn't pure black (ambient light)
float diffuse = max(dot(normalize(vertex_normal), normalize(u_lightDir)), 0.3);
float intensity = mix(1.0, diffuse, u_lightInfluence);
vec3 lighting = intensity * vertex_colour.rgb;
VertexColour = vec4(lighting, vertex_colour.a);
TexCoord = vertex_tex;
VertexColour = vertex_colour;
}

View File

@@ -377,14 +377,37 @@ void init() {
}
}
/*
GLfloat points [] = {
0.0f, 0.5f, -1.0f,
0.4f, -0.5f, -1.0f,
-0.4f, -0.5f, -1.0f,
//-0.4f, -0.5f, 0.0f,
//-0.4f, 0.5f, 0.0f,
//0.4f, -0.5f, 0.0f,
// front face
-0.5f, -0.5f, 0.5f, // bottom-left front
0.5f, -0.5f, 0.5f, // bottom-right front
0.5f, 0.5f, 0.5f, // top-right front
-0.5f, 0.5f, 0.5f, // top-left front
// back face
-0.5f, -0.5f, -0.5f, // bottom-left back
0.5f, -0.5f, -0.5f, // bottom-right back
0.5f, 0.5f, -0.5f, // top-right back
-0.5f, 0.5f, -0.5f // top-left back
};
GLfloat cube_points [] = {
// front face
-0.5f, -0.5f, 0.5f, // bottom-left front
0.5f, -0.5f, 0.5f, // bottom-right front
0.5f, 0.5f, 0.5f, // top-right front
-0.5f, 0.5f, 0.5f, // top-left front
// back face
-0.5f, -0.5f, -0.5f, // bottom-left back
0.5f, -0.5f, -0.5f, // bottom-right back
0.5f, 0.5f, -0.5f, // top-right back
-0.5f, 0.5f, -0.5f // top-left back
};
unsigned int cube_indices[] = {
0,1, 1,2, 2,3, 3,0, // Front Face
4,5, 5,6, 6,7, 7,4, // Back Face
0,4, 1,5, 2,6, 3,7 // Connecting Lines
};
GLfloat colours[] = {
@@ -400,6 +423,23 @@ void init() {
0.5f, 0.0f, 0.0f, 1.0f // fourth column
};
GLuint cubeVAO, cubeVBO, cubeEBO;
glGenVertexArrays(1, &cubeVAO);
glGenBuffers(1, &cubeVBO);
glGenBuffers(1, &cubeEBO);
glBindVertexArray(cubeVAO);
glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(cube_points), cube_points, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cubeEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cube_indices), cube_indices, GL_STATIC_DRAW);
glBindVertexArray(0);
GLuint points_vbo = 0;
glGenBuffers(1, &points_vbo);
glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
@@ -436,7 +476,7 @@ void init() {
mat4_t rotation_matrix_x = mat4_make_rotation_x(rotation_x);
mat4_t rotation_matrix_y = mat4_make_rotation_y(rotation_y);
mat4_t rotation_matrix_z = mat4_make_rotation_z(rotation_z);
*/
char *vertex_shader = ("../../shaders/wmo.vert");
char *fragment_shader = ("../../shaders/wmo.frag");
GLuint shader_programme = create_shader_program_from_files(vertex_shader, fragment_shader);
@@ -447,12 +487,14 @@ void init() {
int matrix_location = glGetUniformLocation(shader_programme, "matrix");
glUseProgram(shader_programme);
/*
float matrix[16] = {
1.0f, 0.0f, 0.0f, 0.0f, // first column
0.0f, 1.0f, 0.0f, 0.0f, // second column
0.0f, 0.0f, 1.0f, 0.0f, // third column
0.5f, 0.0f, 0.0f, 1.0f // fourth column
};
};*/
//glUniformMatrix4fv(matrix_location, 1, GL_FALSE, matrix);
// assigns an initial colour to the fragment shader
// can change uniform variables with glUniform() but only affects shader
@@ -552,7 +594,34 @@ void init() {
glUniformMatrix4fv(view_mat_location, 1, GL_FALSE, view_mat.m);
glUniformMatrix4fv(proj_mat_location, 1, GL_FALSE, projection.m);
GLint override_loc = glGetUniformLocation(shader_programme, "u_overrideColour");
float lightDir[3] = { 0.2f, 1.0f, 0.4f };
GLint light_location = glGetUniformLocation(shader_programme, "u_lightDir");
glUniform3fv(light_location, 1, lightDir);
GLint light_influence_location = glGetUniformLocation(shader_programme, "u_lightInfluence");
glUniform1f(light_influence_location, 0.0f);
SMODoodadDef* doodads = (SMODoodadDef*)wmo_root_data.modd_data_ptr;
char* names = (char*)wmo_root_data.modn_data_ptr;
size_t num_doodads = wmo_root_data.modd_size / sizeof(SMODoodadDef);
print_all(shader_programme);
for (int i = 0; i < num_doodads; i++) {
uint32_t name_index = doodads[i].ref.name_index;
if (name_index >= wmo_root_data.modn_size) {
log_error("Doodad %lu has invalid name offset: %u (MODN size: %lu)",
i, name_index, wmo_root_data.modn_size);
continue;
}
char* modelName = names + name_index;
log_info("Doodad %lu: %s at (%f, %f, %f) [Scale: %.2f]",
i, modelName, doodads[i].position.x, doodads[i].position.y, doodads[i].position.z, doodads[i].scale);
}
glUseProgram(shader_programme);
glBindVertexArray(cubeVAO);
// call `glPolygonMode` before rendering if you want to use wire-frame rendering mode
// Loop until the user closes the window
while(!glfwWindowShouldClose(window)) {
@@ -617,13 +686,24 @@ void init() {
cam_up.x, cam_up.y, cam_up.z
);
// Render here
// wipe the drawing surface clear
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Update viewport size for scaling
glViewport(0, 0, g_fb_width, g_fb_height);
glUseProgram(shader_programme);
glUniform3fv(light_location, 1, lightDir);
glUniform4f(override_loc, 0.0f, 0.0f, 0.0f, 0.0f);
for (size_t i = 0; i < wmo_root_data.group_count; i++) {
if (meshes[i].is_outdoor) {
// enable sun
glUniform1f(light_influence_location, 1.0f);
} else {
// disable sun (use baked lighting)
glUniform1f(light_influence_location, 0.0f);
}
if (meshes[i].VAO != 0) {
//glActiveTexture(GL_TEXTURE0);
//glBindTexture(GL_TEXTURE_2D, meshes[i].textureID);
@@ -632,6 +712,49 @@ void init() {
}
}
glUseProgram(shader_programme);
glBindVertexArray(cubeVAO);
glUniform4f(override_loc, 1.0f, 0.0f, 0.0f, 1.0f);
glUniform1f(light_influence_location, 0.0f);
// draw doodads as cubes
if (wmo_root_data.modd_data_ptr) {
doodads = (SMODoodadDef*)wmo_root_data.modd_data_ptr;
num_doodads = wmo_root_data.modd_size / sizeof(SMODoodadDef);
for (size_t i = 0; i < num_doodads; i++) {
SMODoodadDef* d = &doodads[i];
// scale
mat4_t mat_scale = mat4_make_scale(d->scale, d->scale, d->scale);
// rotate
C4Quaternion q = {
d->orientation.x,
d->orientation.y,
d->orientation.z,
d->orientation.w
};
mat4_t mat_rot = mat4_from_quaternion(
q
);
// translate
mat4_t mat_trans = mat4_make_translation(d->position.x, d->position.y, d->position.z);
// combine (order: T * R * S)
mat4_t model = mat4_identity();
model = mat4_mul_mat4(mat_rot, mat_scale);
model = mat4_mul_mat4(mat_trans, model);
glUniformMatrix4fv(matrix_location, 1, GL_FALSE, model.m);
// draw lines (24 indices)
// (void*)0 is the offset into the EBO bound earlier
glDrawElements(GL_LINES, 24, GL_UNSIGNED_INT, (void*)0);
}
}
glBindVertexArray(0);
// update other events like input handling
glfwPollEvents();

View File

@@ -2,8 +2,8 @@
// Created by Tristan on 12/10/2025.
//
#include "matrix.h"
#include <math.h>
#include "matrix.h"
mat4_t mat4_identity(void) {
mat4_t m = {
@@ -154,6 +154,46 @@ mat4_t mat4_make_quaternion(const GLfloat* q) {
return m;
}
mat4_t mat4_from_quaternion(C4Quaternion q) {
mat4_t m;
// WoW - x, y, z, w
float x = q.x;
float y = q.y;
float z = q.z;
float w = q.w;
float x2 = x * x;
float y2 = y * y;
float z2 = z * z;
// col 1
m.m[0] = 1.0f - (2.0f * y2) - (2.0f * z2);
m.m[1] = (2.0f * x * y) + (2.0f * w * z);
m.m[2] = (2 * x * z) - (2 * w * y);
m.m[3] = 0.0f;
// col 2
m.m[4] = (2.0f * x * y) - (2.0f * w * z);
m.m[5] = 1.0f - (2 * x2) - (2 * z2);
m.m[6] = (2.0f * y * z) + (2.0f * w * x);
m.m[7] = 0.0f;
// col 3
m.m[8] = (2.0f * x * z) + (2.0f * w * y);
m.m[9] = (2.0f * y * z) - (2.0f * w * x);
m.m[10] = 1.0f - (2.0f * x2) - (2.0 * y2);
m.m[11] = 0.0f;
// col 4
m.m[12] = 0.0f;
m.m[13] = 0.0f;
m.m[14] = 0.0f;
m.m[15] = 1.0f;
return m;
}
mat4_t mat4_look_at(float eyex, float eyey, float eyez,
float targetx, float targety, float targetz,
float upx, float upy, float upz) {

View File

@@ -1,11 +1,12 @@
#ifndef MATRIX_H
#define MATRIX_H
#include "render_types.h"
#include "glad/glad.h"
#define M_PI 3.14159265358979323846
#define ONE_DEG_IN_RAD (2.0 * M_PI) / 360.0 // 0.017444444
#ifndef TO_RAD
#define TO_RAD(deg) ((deg) * 3.14159265358979323846)
#define TO_RAD(deg) ((deg) * M_PI)
#endif
typedef struct {
@@ -22,6 +23,7 @@ mat4_t mat4_make_rotation_z(GLfloat m);
mat4_t mat4_make_scale(GLfloat sx, GLfloat sy, GLfloat sz);
mat4_t mat4_make_translation(GLfloat tx, GLfloat ty, GLfloat tz);
mat4_t mat4_make_quaternion(const GLfloat* q, GLfloat* m);
mat4_t mat4_from_quaternion(C4Quaternion q);
mat4_t mat4_make_perspective(float fov_rad, float aspect, float znear, float zfar);
mat4_t mat4_mul_mat4(mat4_t a, mat4_t b);
mat4_t mat4_identity(void);

View File

@@ -30,6 +30,7 @@ GroupMesh create_mesh_from_group(const WMOGroupData* group, const WMORootData* r
// size_t num_colours = (group->mocv_data_ptr) ? group->mocv_size / 4 : 0;
int is_outdoor = !group->header.flags.isIndoor;
mesh.is_outdoor = is_outdoor;
int has_mocv = group->header.flags.vertexColors;
for (size_t i = 0; i < num_vertices; i++) {
@@ -63,7 +64,8 @@ GroupMesh create_mesh_from_group(const WMOGroupData* group, const WMORootData* r
vertices[i].colour.b = colours[i].r;
vertices[i].colour.g = colours[i].g;
vertices[i].colour.r = colours[i].b;
vertices[i].colour.a = colours[i].a;
// set alpha to 255 instead of colours[i].a because only supporting one texture at this moment
vertices[i].colour.a = 255;
} else {
vertices[i].colour = (CImVector){ .b = 255, .g = 255, .r = 255, .a = 255 };
}

View File

@@ -20,6 +20,7 @@ typedef struct {
GLuint VAO, VBO, EBO;
RenderBatch* batches;
size_t batchCount;
int is_outdoor;
} GroupMesh;
GroupMesh create_mesh_from_group(const WMOGroupData* group, const WMORootData* root_data);

View File

@@ -0,0 +1,6 @@
//
// Created by Tristan on 1/6/2026.
//
#include "render_types.h"

View File

@@ -7,4 +7,11 @@
// TODO: move common types out of mesh.h/wmo_structures.h into here for header simplicity when linking :)
typedef struct {
float x;
float y;
float z;
float w;
} C4Quaternion;
#endif //RENDER_TYPES_H

View File

@@ -32,7 +32,7 @@ GLuint texture_load_from_blp_memory(const uint8_t* file_data, size_t file_size)
case 1: // DXT3
glFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
break;
case 7:
case 7: // DXT5
glFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
break;
default:
@@ -55,14 +55,15 @@ GLuint texture_load_from_blp_memory(const uint8_t* file_data, size_t file_size)
int blockSize = (glFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT || glFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
for (int i = 0; i < 16; i++) {
if (h->mipOffset[i] == 0 || (width == 0 && height == 0)) break;
if (h->mipOffset[i] == 0 || (width == 0 && height == 0))
break;
const uint8_t* dataPtr = file_data + h->mipOffset[i];
const uint8_t* data_ptr = file_data + h->mipOffset[i];
uint32_t mipSize = ((width + 3) / 4) * ((height + 3) / 4) * blockSize;
if (mipSize == 0)
mipSize = blockSize;
glCompressedTexImage2D(GL_TEXTURE_2D, i, glFormat, width, height, 0, mipSize, dataPtr);
glCompressedTexImage2D(GL_TEXTURE_2D, i, glFormat, width, height, 0, mipSize, data_ptr);
width /= 2;
height /= 2;

View File

@@ -94,18 +94,6 @@ int create_dir_recursive(const char *full_path) {
return 0;
}
// TODO: might not need this honestly
/*
WMOData load_mver_data() {
WMOChunkHeader mver_header;
WMOData result = {NULL, 0};
DWORD bytesRead = 0;
// magic;
// size;
// version;
// return;
}*/
WMOData load_wmo_data(ArchiveManager *archives, const char *wmoFileName) {
HANDLE hFile = get_file_in_archives(archives, wmoFileName);
WMOData result = {NULL, 0};

View File

@@ -6,6 +6,7 @@
#define WMO_STRUCTS_H
#include "StormLib.h"
#include "glad/glad.h"
#include "../renderer/render_types.h"
/* ---------- Enums ---------- */
typedef enum
@@ -110,6 +111,9 @@ typedef struct {
uint32_t runTimeData[4]; // 0x30: 16 bytes of runtime data
} SMOMaterial;
// MODD
// MOPT
typedef struct {
uint16_t startVertex;
@@ -222,6 +226,30 @@ typedef struct {
uint32_t unk; // 0x40: Unused 20740?
} SMOGroupHeader;
// MODD
typedef struct {
union {
uint32_t header;
struct {
uint32_t name_index : 24; // Offset into MODN chunk (filename '\0' terminated)
uint32_t flags : 8; //
} ref;
};
C3Vector position; // (X, Z, -Y)
C4Quaternion orientation; // (X, Y, Z, W)
float scale; // scale factor (1.0 normal size)
CImVector colour; // Vertex color lighting for the model (B,G,R,A) https://wowdev.wiki/WMO#MODD_chunk
} SMODoodadDef;
#pragma pack(pop)
// MODS
typedef struct {
char name[20]; // Set name (e.g "Set_$DefaultGlobal")
uint32_t firstInstance; // index of first doodad instance in this set, into MODD directly
uint32_t count; // number of doodad instances in this set
uint32_t unused;
} SMODoodadSet;
typedef struct {
uint16_t indices;
} MOVIChunk;