From aa900bfc68027fa65c98e0ebdabfd01faa988c1f Mon Sep 17 00:00:00 2001 From: korenkonder Date: Sun, 21 Apr 2024 19:28:11 +0300 Subject: [PATCH] Compress some stuff to R10G10B10A2 --- res/ft_shaders.farc | Bin 130864 -> 130048 bytes src/DivaGL/mdl/disp_manager.cpp | 294 +++++++++++++---- src/DivaGL/mdl/disp_manager.hpp | 1 + src/DivaGL/object.cpp | 568 +++++++++++++++++++++++++------- src/DivaGL/object.hpp | 3 +- src/DivaGL/rob/rob.cpp | 142 ++++++-- 6 files changed, 804 insertions(+), 204 deletions(-) diff --git a/res/ft_shaders.farc b/res/ft_shaders.farc index 5db353ada95edc6a2721c8fb38b6f05a8f10bee6..6f00324f6f43b5a4daadb69478cd5624df9ca500 100644 GIT binary patch delta 29526 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

R{}=ir)s+A^jf4mS;VRfT*m*#DuyHUz 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.m.compression; GLsizei size_vertex = (GLsizei)mesh->size_vertex; obj_vertex_format vertex_format = mesh->vertex_format; @@ -333,7 +334,7 @@ namespace mdl { && i.morph_vertex_buffer == morph_vertex_buffer && i.morph_vertex_buffer_offset == morph_vertex_buffer_offset && i.index_buffer == index_buffer && i.vertex_format == vertex_format - && i.size_vertex == size_vertex + && i.size_vertex == size_vertex && i.compression == compression && !memcmp(i.vertex_attrib_buffer_binding, vertex_attrib_buffer_binding, sizeof(vertex_attrib_buffer_binding)) && !memcmp(i.texcoord_array, texcoord_array, sizeof(texcoord_array))) @@ -388,6 +389,7 @@ namespace mdl { vertex_array->alive_time = 60; vertex_array->vertex_format = vertex_format; vertex_array->size_vertex = size_vertex; + vertex_array->compression = compression; memcpy(&vertex_array->texcoord_array, texcoord_array, sizeof(texcoord_array)); memcpy(&vertex_array->vertex_attrib_buffer_binding, vertex_attrib_buffer_binding, sizeof(vertex_attrib_buffer_binding)); @@ -419,9 +421,24 @@ namespace mdl { vertex_array->vertex_attrib_array[NORMAL_INDEX] = true; } - glVertexAttribPointer(NORMAL_INDEX, - 3, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 12; + switch (compression) { + case 0: + default: + glVertexAttribPointer(NORMAL_INDEX, + 3, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 12; + break; + case 1: + glVertexAttribPointer(NORMAL_INDEX, + 3, GL_SHORT, GL_TRUE, size_vertex, (void*)offset); + offset += 8; + break; + case 2: + glVertexAttribPointer(NORMAL_INDEX, + 4, GL_INT_2_10_10_10_REV, GL_TRUE, size_vertex, (void*)offset); + offset += 4; + break; + } } else if (vertex_array->vertex_attrib_array[NORMAL_INDEX]) { glDisableVertexAttribArray(NORMAL_INDEX); @@ -434,16 +451,31 @@ namespace mdl { vertex_array->vertex_attrib_array[TANGENT_INDEX] = true; } - glVertexAttribPointer(TANGENT_INDEX, - 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 16; + switch (compression) { + case 0: + default: + glVertexAttribPointer(TANGENT_INDEX, + 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 16; + break; + case 1: + glVertexAttribPointer(TANGENT_INDEX, + 4, GL_SHORT, GL_TRUE, size_vertex, (void*)offset); + offset += 8; + break; + case 2: + glVertexAttribPointer(TANGENT_INDEX, + 4, GL_INT_2_10_10_10_REV, GL_TRUE, size_vertex, (void*)offset); + offset += 4; + break; + } } else if (vertex_array->vertex_attrib_array[TANGENT_INDEX]) { glDisableVertexAttribArray(TANGENT_INDEX); vertex_array->vertex_attrib_array[TANGENT_INDEX] = false; } - if (vertex_format & OBJ_VERTEX_BINORMAL) + if (!compression && (vertex_format & OBJ_VERTEX_BINORMAL)) offset += 12; for (int32_t i = 0; i < 2; i++) { @@ -462,19 +494,45 @@ namespace mdl { vertex_array->vertex_attrib_array[TEXCOORD0_INDEX + i] = true; } - glVertexAttribPointer(TEXCOORD0_INDEX + i, 2, GL_FLOAT, GL_FALSE, - size_vertex, (void*)(offset + 8ULL * texcoord_index)); + switch (compression) { + case 0: + default: + glVertexAttribPointer(TEXCOORD0_INDEX + i, 2, GL_FLOAT, GL_FALSE, + size_vertex, (void*)(offset + 8ULL * texcoord_index)); + break; + case 1: + case 2: + glVertexAttribPointer(TEXCOORD0_INDEX + i, 2, GL_HALF_FLOAT, GL_FALSE, + size_vertex, (void*)(offset + 4ULL * texcoord_index)); + break; + } } } - if (vertex_format & OBJ_VERTEX_TEXCOORD0) - offset += 8; - if (vertex_format & OBJ_VERTEX_TEXCOORD1) - offset += 8; - if (vertex_format & OBJ_VERTEX_TEXCOORD2) - offset += 8; - if (vertex_format & OBJ_VERTEX_TEXCOORD3) - offset += 8; + switch (compression) { + case 0: + default: + if (vertex_format & OBJ_VERTEX_TEXCOORD0) + offset += 8; + if (vertex_format & OBJ_VERTEX_TEXCOORD1) + offset += 8; + if (vertex_format & OBJ_VERTEX_TEXCOORD2) + offset += 8; + if (vertex_format & OBJ_VERTEX_TEXCOORD3) + offset += 8; + break; + case 1: + case 2: + if (vertex_format & OBJ_VERTEX_TEXCOORD0) + offset += 4; + if (vertex_format & OBJ_VERTEX_TEXCOORD1) + offset += 4; + if (vertex_format & OBJ_VERTEX_TEXCOORD2) + offset += 4; + if (vertex_format & OBJ_VERTEX_TEXCOORD3) + offset += 4; + break; + } if (vertex_format & OBJ_VERTEX_COLOR0) { if (!vertex_array->vertex_attrib_array[COLOR0_INDEX]) { @@ -482,16 +540,27 @@ namespace mdl { vertex_array->vertex_attrib_array[COLOR0_INDEX] = true; } - glVertexAttribPointer(COLOR0_INDEX, - 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 16; + switch (compression) { + case 0: + default: + glVertexAttribPointer(COLOR0_INDEX, + 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 16; + break; + case 1: + case 2: + glVertexAttribPointer(COLOR0_INDEX, + 4, GL_HALF_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 8; + break; + } } else if (vertex_array->vertex_attrib_array[COLOR0_INDEX]) { glDisableVertexAttribArray(COLOR0_INDEX); vertex_array->vertex_attrib_array[COLOR0_INDEX] = false; } - if (vertex_format & OBJ_VERTEX_COLOR1) + if (!compression && (vertex_format & OBJ_VERTEX_COLOR1)) offset += 16; if ((vertex_format & OBJ_VERTEX_BONE_DATA) == OBJ_VERTEX_BONE_DATA) { @@ -500,18 +569,44 @@ namespace mdl { vertex_array->vertex_attrib_array[BONE_WEIGHT_INDEX] = true; } - glVertexAttribPointer(BONE_WEIGHT_INDEX, - 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 16; + switch (compression) { + case 0: + default: + glVertexAttribPointer(BONE_WEIGHT_INDEX, + 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 16; + break; + case 1: + glVertexAttribPointer(BONE_WEIGHT_INDEX, + 4, GL_UNSIGNED_SHORT, GL_TRUE, size_vertex, (void*)offset); + offset += 8; + break; + case 2: + glVertexAttribPointer(BONE_WEIGHT_INDEX, + 4, GL_UNSIGNED_INT_2_10_10_10_REV, GL_TRUE, size_vertex, (void*)offset); + offset += 4; + break; + } if (!vertex_array->vertex_attrib_array[BONE_INDEX_INDEX]) { glEnableVertexAttribArray(BONE_INDEX_INDEX); vertex_array->vertex_attrib_array[BONE_INDEX_INDEX] = true; } - glVertexAttribIPointer(BONE_INDEX_INDEX, - 4, GL_SHORT, size_vertex, (void*)offset); - offset += 8; + switch (compression) { + case 0: + case 1: + default: + glVertexAttribIPointer(BONE_INDEX_INDEX, + 4, GL_UNSIGNED_SHORT, size_vertex, (void*)offset); + offset += 8; + break; + case 2: + glVertexAttribIPointer(BONE_INDEX_INDEX, + 4, GL_UNSIGNED_BYTE, size_vertex, (void*)offset); + offset += 4; + break; + } } else { if (vertex_array->vertex_attrib_array[BONE_WEIGHT_INDEX]) { @@ -525,7 +620,7 @@ namespace mdl { } } - if (vertex_format & OBJ_VERTEX_UNKNOWN) { + if (!compression && (vertex_format & OBJ_VERTEX_UNKNOWN)) { if (!vertex_array->vertex_attrib_array[UNKNOWN_INDEX]) { glEnableVertexAttribArray(UNKNOWN_INDEX); vertex_array->vertex_attrib_array[UNKNOWN_INDEX] = true; @@ -565,9 +660,24 @@ namespace mdl { vertex_array->vertex_attrib_array[MORPH_NORMAL_INDEX] = true; } - glVertexAttribPointer(MORPH_NORMAL_INDEX, - 3, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 12; + switch (compression) { + case 0: + default: + glVertexAttribPointer(MORPH_NORMAL_INDEX, + 3, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 12; + break; + case 1: + glVertexAttribPointer(MORPH_NORMAL_INDEX, + 3, GL_SHORT, GL_TRUE, size_vertex, (void*)offset); + offset += 8; + break; + case 2: + glVertexAttribPointer(MORPH_NORMAL_INDEX, + 3, GL_INT_2_10_10_10_REV, GL_TRUE, size_vertex, (void*)offset); + offset += 4; + break; + } } else if (vertex_array->vertex_attrib_array[MORPH_NORMAL_INDEX]) { glDisableVertexAttribArray(MORPH_NORMAL_INDEX); @@ -580,9 +690,24 @@ namespace mdl { vertex_array->vertex_attrib_array[MORPH_TANGENT_INDEX] = true; } - glVertexAttribPointer(MORPH_TANGENT_INDEX, - 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 16; + switch (compression) { + case 0: + default: + glVertexAttribPointer(MORPH_TANGENT_INDEX, + 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 16; + break; + case 1: + glVertexAttribPointer(MORPH_TANGENT_INDEX, + 4, GL_SHORT, GL_TRUE, size_vertex, (void*)offset); + offset += 8; + break; + case 2: + glVertexAttribPointer(MORPH_TANGENT_INDEX, + 4, GL_INT_2_10_10_10_REV, GL_TRUE, size_vertex, (void*)offset); + offset += 4; + break; + } } else if (vertex_array->vertex_attrib_array[MORPH_TANGENT_INDEX]) { glDisableVertexAttribArray(MORPH_TANGENT_INDEX); @@ -598,9 +723,20 @@ namespace mdl { vertex_array->vertex_attrib_array[MORPH_TEXCOORD0_INDEX] = true; } - glVertexAttribPointer(MORPH_TEXCOORD0_INDEX, - 2, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 8; + switch (compression) { + case 0: + default: + glVertexAttribPointer(MORPH_TEXCOORD0_INDEX, + 2, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 8; + break; + case 1: + case 2: + glVertexAttribPointer(MORPH_TEXCOORD0_INDEX, + 2, GL_HALF_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 4; + break; + } } else if (vertex_array->vertex_attrib_array[MORPH_TEXCOORD0_INDEX]) { glDisableVertexAttribArray(MORPH_TEXCOORD0_INDEX); @@ -613,19 +749,42 @@ namespace mdl { vertex_array->vertex_attrib_array[MORPH_TEXCOORD1_INDEX] = true; } - glVertexAttribPointer(MORPH_TEXCOORD1_INDEX, - 2, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 8; + switch (compression) { + case 0: + default: + glVertexAttribPointer(MORPH_TEXCOORD1_INDEX, + 2, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 8; + break; + case 1: + case 2: + glVertexAttribPointer(MORPH_TEXCOORD1_INDEX, + 2, GL_HALF_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 4; + break; + } } else if (vertex_array->vertex_attrib_array[MORPH_TEXCOORD1_INDEX]) { glDisableVertexAttribArray(MORPH_TEXCOORD1_INDEX); vertex_array->vertex_attrib_array[MORPH_TEXCOORD1_INDEX] = false; } - if (vertex_format & OBJ_VERTEX_TEXCOORD2) - offset += 8; - if (vertex_format & OBJ_VERTEX_TEXCOORD3) - offset += 8; + switch (compression) { + case 0: + default: + if (vertex_format & OBJ_VERTEX_TEXCOORD2) + offset += 8; + if (vertex_format & OBJ_VERTEX_TEXCOORD3) + offset += 8; + break; + case 1: + case 2: + if (vertex_format & OBJ_VERTEX_TEXCOORD2) + offset += 4; + if (vertex_format & OBJ_VERTEX_TEXCOORD3) + offset += 4; + break; + } if (vertex_format & OBJ_VERTEX_COLOR0) { uniform->arr[U_MORPH_COLOR] = 1; @@ -635,23 +794,47 @@ namespace mdl { vertex_array->vertex_attrib_array[MORPH_COLOR_INDEX] = true; } - glVertexAttribPointer(MORPH_COLOR_INDEX, - 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 16; + switch (compression) { + case 0: + default: + glVertexAttribPointer(MORPH_COLOR_INDEX, + 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 16; + break; + case 1: + case 2: + glVertexAttribPointer(MORPH_COLOR_INDEX, + 4, GL_HALF_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 8; + break; + } } else if (vertex_array->vertex_attrib_array[MORPH_COLOR_INDEX]) { glDisableVertexAttribArray(MORPH_COLOR_INDEX); vertex_array->vertex_attrib_array[MORPH_COLOR_INDEX] = false; } - if (vertex_format & OBJ_VERTEX_COLOR1) - offset += 16; + switch (compression) { + case 0: + default: + if (vertex_format & OBJ_VERTEX_COLOR1) + offset += 16; - if ((vertex_format & OBJ_VERTEX_BONE_DATA) == OBJ_VERTEX_BONE_DATA) - offset += 32; + if ((vertex_format & OBJ_VERTEX_BONE_DATA) == OBJ_VERTEX_BONE_DATA) + offset += 24; - if (vertex_format & OBJ_VERTEX_UNKNOWN) - offset += 16; + if (vertex_format & OBJ_VERTEX_UNKNOWN) + offset += 16; + break; + case 1: + if ((vertex_format & OBJ_VERTEX_BONE_DATA) == OBJ_VERTEX_BONE_DATA) + offset += 16; + break; + case 2: + if ((vertex_format & OBJ_VERTEX_BONE_DATA) == OBJ_VERTEX_BONE_DATA) + offset += 8; + break; + } } else { if (vertex_array->vertex_attrib_array[MORPH_POSITION_INDEX]) { @@ -2908,6 +3091,7 @@ static void object_data_get_vertex_attrib_buffer_bindings(const mdl::ObjSubMeshA GLuint vertex_buffer = args->vertex_buffer; GLuint morph_vertex_buffer = args->morph_vertex_buffer; + uint32_t compression = mesh->attrib.m.compression; GLsizei size_vertex = (GLsizei)mesh->size_vertex; obj_vertex_format vertex_format = mesh->vertex_format; @@ -2933,7 +3117,7 @@ static void object_data_get_vertex_attrib_buffer_bindings(const mdl::ObjSubMeshA vertex_attrib_buffer_binding[BONE_INDEX_INDEX] = vertex_buffer; } - if (vertex_format & OBJ_VERTEX_UNKNOWN) + if (!compression && (vertex_format & OBJ_VERTEX_UNKNOWN)) vertex_attrib_buffer_binding[UNKNOWN_INDEX] = vertex_buffer; if (args->morph_vertex_buffer) { diff --git a/src/DivaGL/mdl/disp_manager.hpp b/src/DivaGL/mdl/disp_manager.hpp index 9ecae66..945b00d 100644 --- a/src/DivaGL/mdl/disp_manager.hpp +++ b/src/DivaGL/mdl/disp_manager.hpp @@ -364,6 +364,7 @@ namespace mdl { bool vertex_attrib_array[16]; obj_vertex_format vertex_format; GLsizei size_vertex; + uint32_t compression; GLuint vertex_attrib_buffer_binding[16]; int32_t texcoord_array[2]; diff --git a/src/DivaGL/object.cpp b/src/DivaGL/object.cpp index b99526c..6ab6547 100644 --- a/src/DivaGL/object.cpp +++ b/src/DivaGL/object.cpp @@ -90,6 +90,8 @@ static void free_vertex_buffer(GLuint buffer); static void obj_vertex_add_bone_weight(vec4& bone_weight, vec4i16& bone_index, int16_t index, float_t weight); static void obj_vertex_validate_bone_data(vec4& bone_weight, vec4i16& bone_index); static uint32_t obj_vertex_format_get_vertex_size(obj_vertex_format format); +static uint32_t obj_vertex_format_get_vertex_size_comp1(obj_vertex_format format); +static uint32_t obj_vertex_format_get_vertex_size_comp2(obj_vertex_format format); obj_material_shader_lighting_type obj_material_shader_attrib::get_lighting_type() const { if (!m.is_lgt_diffuse && !m.is_lgt_specular) @@ -239,7 +241,20 @@ bool obj_mesh_vertex_buffer::load(obj_mesh* mesh, bool dynamic) { if (!mesh->num_vertex) return false; - uint32_t size_vertex = obj_vertex_format_get_vertex_size(mesh->vertex_format); + uint32_t size_vertex; + switch (mesh->attrib.m.compression) { + case 0: + default: + size_vertex = obj_vertex_format_get_vertex_size(mesh->vertex_format); + break; + case 1: + size_vertex = obj_vertex_format_get_vertex_size_comp1(mesh->vertex_format); + break; + case 2: + size_vertex = obj_vertex_format_get_vertex_size_comp2(mesh->vertex_format); + break; + } + mesh->size_vertex = size_vertex; void* vertex = force_malloc((size_t)size_vertex * mesh->num_vertex); @@ -284,141 +299,382 @@ void* obj_mesh_vertex_buffer::fill_data(void* data, obj_mesh* mesh) { uint32_t size_vertex = mesh->size_vertex; uint32_t num_vertex = mesh->num_vertex; size_t d = (size_t)data; - if (vertex_format & OBJ_VERTEX_POSITION) { - vec3* position = vtx.position; - for (uint32_t i = num_vertex; i; i--) { - *(vec3*)d = *position++; - d += size_vertex; + switch (mesh->attrib.m.compression) { + case 0: + default: + if (vertex_format & OBJ_VERTEX_POSITION) { + vec3* position = vtx.position; + for (uint32_t i = num_vertex; i; i--) { + *(vec3*)d = *position++; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 12; } - d -= (size_t)size_vertex * num_vertex; - d += 12; - } - if (vertex_format & OBJ_VERTEX_NORMAL) { - vec3* normal = vtx.normal; - for (uint32_t i = num_vertex; i; i--) { - *(vec3*)d = *normal++; - d += size_vertex; + if (vertex_format & OBJ_VERTEX_NORMAL) { + vec3* normal = vtx.normal; + for (uint32_t i = num_vertex; i; i--) { + *(vec3*)d = *normal++; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 12; } - d -= (size_t)size_vertex * num_vertex; - d += 12; - } - - if (vertex_format & OBJ_VERTEX_TANGENT) { - vec4* tangent = vtx.tangent; - for (uint32_t i = num_vertex; i; i--) { - *(vec4*)d = *tangent++; - d += size_vertex; - } - d -= (size_t)size_vertex * num_vertex; - d += 16; - } - if (vertex_format & OBJ_VERTEX_BINORMAL) { - vec3* binormal = vtx.binormal; - for (uint32_t i = num_vertex; i; i--) { - *(vec3*)d = *binormal++; - d += size_vertex; + if (vertex_format & OBJ_VERTEX_TANGENT) { + vec4* tangent = vtx.tangent; + for (uint32_t i = num_vertex; i; i--) { + *(vec4*)d = *tangent++; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 16; } - d -= (size_t)size_vertex * num_vertex; - d += 12; - } - if (vertex_format & OBJ_VERTEX_TEXCOORD0) { - vec2* texcoord0 = vtx.texcoord0; - for (uint32_t i = num_vertex; i; i--) { - *(vec2*)d = *texcoord0++; - d += size_vertex; + if (vertex_format & OBJ_VERTEX_BINORMAL) { + vec3* binormal = vtx.binormal; + for (uint32_t i = num_vertex; i; i--) { + *(vec3*)d = *binormal++; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 12; } - d -= (size_t)size_vertex * num_vertex; - d += 8; - } - - if (vertex_format & OBJ_VERTEX_TEXCOORD1) { - vec2* texcoord1 = vtx.texcoord1; - for (uint32_t i = num_vertex; i; i--) { - *(vec2*)d = *texcoord1++; - d += size_vertex; - } - d -= (size_t)size_vertex * num_vertex; - d += 8; - } - - if (vertex_format & OBJ_VERTEX_TEXCOORD2) { - vec2* texcoord2 = vtx.texcoord2; - for (uint32_t i = num_vertex; i; i--) { - *(vec2*)d = *texcoord2++; - d += size_vertex; - } - d -= (size_t)size_vertex * num_vertex; - d += 8; - } - - if (vertex_format & OBJ_VERTEX_TEXCOORD3) { - vec2* texcoord3 = vtx.texcoord3; - for (uint32_t i = num_vertex; i; i--) { - *(vec2*)d = *texcoord3++; - d += size_vertex; - } - d -= (size_t)size_vertex * num_vertex; - d += 8; - } - - if (vertex_format & OBJ_VERTEX_COLOR0) { - vec4* color0 = vtx.color0; - for (uint32_t i = num_vertex; i; i--) { - *(vec4*)d = *color0++; - d += size_vertex; - } - d -= (size_t)size_vertex * num_vertex; - d += 16; - } - - if (vertex_format & OBJ_VERTEX_COLOR1) { - vec4* color1 = vtx.color1; - for (uint32_t i = num_vertex; i; i--) { - *(vec4*)d = *color1++; - d += size_vertex; - } - d -= (size_t)size_vertex * num_vertex; - d += 16; - } - - if ((vertex_format & OBJ_VERTEX_BONE_DATA) == OBJ_VERTEX_BONE_DATA) { - vec4* bone_weight = vtx.bone_weight; - vec4* bone_index = vtx.bone_index; - for (uint32_t i = num_vertex; i; i--) { - vec4 _bone_weight = *bone_weight++; - int32_t bone_index_x = (int32_t)bone_index->x; - int32_t bone_index_y = (int32_t)bone_index->y; - int32_t bone_index_z = (int32_t)bone_index->z; - int32_t bone_index_w = (int32_t)bone_index->w; - bone_index++; - - vec4i16 _bone_index; - _bone_index.x = (int16_t)(bone_index_x >= 0 ? bone_index_x / 3 : -1); - _bone_index.y = (int16_t)(bone_index_y >= 0 ? bone_index_y / 3 : -1); - _bone_index.z = (int16_t)(bone_index_z >= 0 ? bone_index_z / 3 : -1); - _bone_index.w = (int16_t)(bone_index_w >= 0 ? bone_index_w / 3 : -1); - obj_vertex_validate_bone_data(_bone_weight, _bone_index); - - *(vec4*)d = _bone_weight; - *(vec4i16*)(d + 16) = _bone_index; - d += size_vertex; + if (vertex_format & OBJ_VERTEX_TEXCOORD0) { + vec2* texcoord0 = vtx.texcoord0; + for (uint32_t i = num_vertex; i; i--) { + *(vec2*)d = *texcoord0++; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 8; } - d -= (size_t)size_vertex * num_vertex; - d += 24; - } - if (vertex_format & OBJ_VERTEX_UNKNOWN) { - vec4* unknown = vtx.unknown; - for (uint32_t i = num_vertex; i; i--) { - *(vec4*)d = *unknown++; - d += size_vertex; + if (vertex_format & OBJ_VERTEX_TEXCOORD1) { + vec2* texcoord1 = vtx.texcoord1; + for (uint32_t i = num_vertex; i; i--) { + *(vec2*)d = *texcoord1++; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 8; } - d -= (size_t)size_vertex * num_vertex; - d += 16; + + if (vertex_format & OBJ_VERTEX_TEXCOORD2) { + vec2* texcoord2 = vtx.texcoord2; + for (uint32_t i = num_vertex; i; i--) { + *(vec2*)d = *texcoord2++; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 8; + } + + if (vertex_format & OBJ_VERTEX_TEXCOORD3) { + vec2* texcoord3 = vtx.texcoord3; + for (uint32_t i = num_vertex; i; i--) { + *(vec2*)d = *texcoord3++; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 8; + } + + if (vertex_format & OBJ_VERTEX_COLOR0) { + vec4* color0 = vtx.color0; + for (uint32_t i = num_vertex; i; i--) { + *(vec4*)d = *color0++; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 16; + } + + if (vertex_format & OBJ_VERTEX_COLOR1) { + vec4* color1 = vtx.color1; + for (uint32_t i = num_vertex; i; i--) { + *(vec4*)d = *color1++; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 16; + } + + if ((vertex_format & OBJ_VERTEX_BONE_DATA) == OBJ_VERTEX_BONE_DATA) { + vec4* bone_weight = vtx.bone_weight; + vec4* bone_index = vtx.bone_index; + for (uint32_t i = num_vertex; i; i--) { + vec4 _bone_weight = *bone_weight++; + + int32_t bone_index_x = (int32_t)bone_index->x; + int32_t bone_index_y = (int32_t)bone_index->y; + int32_t bone_index_z = (int32_t)bone_index->z; + int32_t bone_index_w = (int32_t)bone_index->w; + bone_index++; + + vec4i16 _bone_index; + _bone_index.x = (int16_t)(bone_index_x >= 0 ? bone_index_x / 3 : -1); + _bone_index.y = (int16_t)(bone_index_y >= 0 ? bone_index_y / 3 : -1); + _bone_index.z = (int16_t)(bone_index_z >= 0 ? bone_index_z / 3 : -1); + _bone_index.w = (int16_t)(bone_index_w >= 0 ? bone_index_w / 3 : -1); + obj_vertex_validate_bone_data(_bone_weight, _bone_index); + + *(vec4*)d = _bone_weight; + *(vec4i16*)(d + 16) = _bone_index; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 24; + } + + if (vertex_format & OBJ_VERTEX_UNKNOWN) { + vec4* unknown = vtx.unknown; + for (uint32_t i = num_vertex; i; i--) { + *(vec4*)d = *unknown++; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 16; + } + break; + case 1: + if (vertex_format & OBJ_VERTEX_POSITION) { + vec3* position = vtx.position; + for (uint32_t i = num_vertex; i; i--) { + *(vec3*)d = *position++; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 12; + } + + if (vertex_format & OBJ_VERTEX_NORMAL) { + vec3* normal = vtx.normal; + for (uint32_t i = num_vertex; i; i--) { + vec3_to_vec3i16(*normal++ * 32767.0f, *(vec3i16*)d); + *(int16_t*)(d + 6) = 0; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 8; + } + + if (vertex_format & OBJ_VERTEX_TANGENT) { + vec4* tangent = vtx.tangent; + for (uint32_t i = num_vertex; i; i--) { + vec4_to_vec4i16(*tangent++ * 32767.0f, *(vec4i16*)d); + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 8; + } + + if (vertex_format & OBJ_VERTEX_TEXCOORD0) { + vec2* texcoord0 = vtx.texcoord0; + for (uint32_t i = num_vertex; i; i--) { + vec2_to_vec2h(*texcoord0++, *(vec2h*)d); + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 4; + } + + if (vertex_format & OBJ_VERTEX_TEXCOORD1) { + vec2* texcoord1 = vtx.texcoord1; + for (uint32_t i = num_vertex; i; i--) { + vec2_to_vec2h(*texcoord1++, *(vec2h*)d); + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 4; + } + + if (vertex_format & OBJ_VERTEX_TEXCOORD2) { + vec2* texcoord2 = vtx.texcoord2; + for (uint32_t i = num_vertex; i; i--) { + vec2_to_vec2h(*texcoord2++, *(vec2h*)d); + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 4; + } + + if (vertex_format & OBJ_VERTEX_TEXCOORD3) { + vec2* texcoord3 = vtx.texcoord3; + for (uint32_t i = num_vertex; i; i--) { + vec2_to_vec2h(*texcoord3++, *(vec2h*)d); + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 4; + } + + if (vertex_format & OBJ_VERTEX_COLOR0) { + vec4* color0 = vtx.color0; + for (uint32_t i = num_vertex; i; i--) { + vec4_to_vec4h(*color0++, *(vec4h*)d); + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 8; + } + + if ((vertex_format & OBJ_VERTEX_BONE_DATA) == OBJ_VERTEX_BONE_DATA) { + vec4* bone_weight = vtx.bone_weight; + vec4* bone_index = vtx.bone_index; + for (uint32_t i = num_vertex; i; i--) { + vec4 _bone_weight = *bone_weight++; + + int32_t bone_index_x = (int32_t)bone_index->x; + int32_t bone_index_y = (int32_t)bone_index->y; + int32_t bone_index_z = (int32_t)bone_index->z; + int32_t bone_index_w = (int32_t)bone_index->w; + bone_index++; + + vec4i16 _bone_index; + _bone_index.x = (int16_t)(bone_index_x >= 0 ? bone_index_x / 3 : -1); + _bone_index.y = (int16_t)(bone_index_y >= 0 ? bone_index_y / 3 : -1); + _bone_index.z = (int16_t)(bone_index_z >= 0 ? bone_index_z / 3 : -1); + _bone_index.w = (int16_t)(bone_index_w >= 0 ? bone_index_w / 3 : -1); + obj_vertex_validate_bone_data(_bone_weight, _bone_index); + + vec4_to_vec4u16(_bone_weight * 65535.0f, *(vec4u16*)d); + *(vec4i16*)(d + 16) = _bone_index; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 16; + } + break; + case 2: + if (vertex_format & OBJ_VERTEX_POSITION) { + vec3* position = vtx.position; + for (uint32_t i = num_vertex; i; i--) { + *(vec3*)d = *position++; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 12; + } + + if (vertex_format & OBJ_VERTEX_NORMAL) { + vec3* normal = vtx.normal; + for (uint32_t i = num_vertex; i; i--) { + vec3i16 normal_int; + vec3_to_vec3i16(*normal++ * 511.0f, normal_int); + *(uint32_t*)d = (((uint32_t)0 & 0x03) << 30) + | (((uint32_t)normal_int.z & 0x3FF) << 20) + | (((uint32_t)normal_int.y & 0x3FF) << 10) + | ((uint32_t)normal_int.x & 0x3FF); + *(int16_t*)(d + 6) = 0; + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 4; + } + + if (vertex_format & OBJ_VERTEX_TANGENT) { + vec4* tangent = vtx.tangent; + for (uint32_t i = num_vertex; i; i--) { + vec4i16 tangent_int; + vec4_to_vec4i16(*tangent++ * 511.0f, tangent_int); + *(uint32_t*)d = (((uint32_t)clamp_def(tangent_int.w, -1, 1) & 0x03) << 30) + | (((uint32_t)tangent_int.z & 0x3FF) << 20) + | (((uint32_t)tangent_int.y & 0x3FF) << 10) + | ((uint32_t)tangent_int.x & 0x3FF); + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 4; + } + + if (vertex_format & OBJ_VERTEX_TEXCOORD0) { + vec2* texcoord0 = vtx.texcoord0; + for (uint32_t i = num_vertex; i; i--) { + vec2_to_vec2h(*texcoord0++, *(vec2h*)d); + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 4; + } + + if (vertex_format & OBJ_VERTEX_TEXCOORD1) { + vec2* texcoord1 = vtx.texcoord1; + for (uint32_t i = num_vertex; i; i--) { + vec2_to_vec2h(*texcoord1++, *(vec2h*)d); + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 4; + } + + if (vertex_format & OBJ_VERTEX_TEXCOORD2) { + vec2* texcoord2 = vtx.texcoord2; + for (uint32_t i = num_vertex; i; i--) { + vec2_to_vec2h(*texcoord2++, *(vec2h*)d); + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 4; + } + + if (vertex_format & OBJ_VERTEX_TEXCOORD3) { + vec2* texcoord3 = vtx.texcoord3; + for (uint32_t i = num_vertex; i; i--) { + vec2_to_vec2h(*texcoord3++, *(vec2h*)d); + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 4; + } + + if (vertex_format & OBJ_VERTEX_COLOR0) { + vec4* color0 = vtx.color0; + for (uint32_t i = num_vertex; i; i--) { + vec4_to_vec4h(*color0++, *(vec4h*)d); + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 8; + } + + if ((vertex_format & OBJ_VERTEX_BONE_DATA) == OBJ_VERTEX_BONE_DATA) { + vec4* bone_weight = vtx.bone_weight; + vec4* bone_index = vtx.bone_index; + for (uint32_t i = num_vertex; i; i--) { + vec4 _bone_weight = *bone_weight++; + + int32_t bone_index_x = (int32_t)bone_index->x; + int32_t bone_index_y = (int32_t)bone_index->y; + int32_t bone_index_z = (int32_t)bone_index->z; + int32_t bone_index_w = (int32_t)bone_index->w; + bone_index++; + + vec4i16 _bone_index; + _bone_index.x = (int16_t)(bone_index_x >= 0 ? bone_index_x / 3 : -1); + _bone_index.y = (int16_t)(bone_index_y >= 0 ? bone_index_y / 3 : -1); + _bone_index.z = (int16_t)(bone_index_z >= 0 ? bone_index_z / 3 : -1); + _bone_index.w = (int16_t)(bone_index_w >= 0 ? bone_index_w / 3 : -1); + obj_vertex_validate_bone_data(_bone_weight, _bone_index); + + vec4i16 bone_weight_int; + vec4_to_vec4i16(_bone_weight * 1023.0f, bone_weight_int); + *(uint32_t*)d = (((uint32_t)0 & 0x03) << 30) + | (((uint32_t)bone_weight_int.z & 0x3FF) << 20) + | (((uint32_t)bone_weight_int.y & 0x3FF) << 10) + | ((uint32_t)bone_weight_int.x & 0x3FF); + + vec4i bone_index; + vec4i16_to_vec4i(_bone_index, bone_index); + vec4i_to_vec4u8(bone_index, *(vec4u8*)(d + 4)); + d += size_vertex; + } + d -= (size_t)size_vertex * num_vertex; + d += 8; + } + break; } return (void*)((size_t)data + (size_t)size_vertex * num_vertex); @@ -556,7 +812,21 @@ bool obj_vertex_buffer::load(obj* obj) { if (!mesh.num_vertex) continue; - uint32_t size_vertex = obj_vertex_format_get_vertex_size(mesh.vertex_format); + mesh.attrib.m.compression = 0; + + uint32_t size_vertex; + switch (mesh.attrib.m.compression) { + case 0: + default: + size_vertex = obj_vertex_format_get_vertex_size(mesh.vertex_format); + break; + case 1: + size_vertex = obj_vertex_format_get_vertex_size_comp1(mesh.vertex_format); + break; + case 2: + size_vertex = obj_vertex_format_get_vertex_size_comp2(mesh.vertex_format); + break; + } mesh.size_vertex = size_vertex; buffer_size += (size_t)size_vertex * mesh.num_vertex; @@ -977,3 +1247,49 @@ inline static uint32_t obj_vertex_format_get_vertex_size(obj_vertex_format forma size += 16; return size; } + +inline static uint32_t obj_vertex_format_get_vertex_size_comp1(obj_vertex_format format) { + uint32_t size = 0; + if (format & OBJ_VERTEX_POSITION) + size += 12; + if (format & OBJ_VERTEX_NORMAL) + size += 8; + if (format & OBJ_VERTEX_TANGENT) + size += 8; + if (format & OBJ_VERTEX_TEXCOORD0) + size += 4; + if (format & OBJ_VERTEX_TEXCOORD1) + size += 4; + if (format & OBJ_VERTEX_TEXCOORD2) + size += 4; + if (format & OBJ_VERTEX_TEXCOORD3) + size += 4; + if (format & OBJ_VERTEX_COLOR0) + size += 8; + if ((format & OBJ_VERTEX_BONE_DATA) == OBJ_VERTEX_BONE_DATA) + size += 16; + return size; +} + +inline static uint32_t obj_vertex_format_get_vertex_size_comp2(obj_vertex_format format) { + uint32_t size = 0; + if (format & OBJ_VERTEX_POSITION) + size += 12; + if (format & OBJ_VERTEX_NORMAL) + size += 4; + if (format & OBJ_VERTEX_TANGENT) + size += 4; + if (format & OBJ_VERTEX_TEXCOORD0) + size += 4; + if (format & OBJ_VERTEX_TEXCOORD1) + size += 4; + if (format & OBJ_VERTEX_TEXCOORD2) + size += 4; + if (format & OBJ_VERTEX_TEXCOORD3) + size += 4; + if (format & OBJ_VERTEX_COLOR0) + size += 8; + if ((format & OBJ_VERTEX_BONE_DATA) == OBJ_VERTEX_BONE_DATA) + size += 8; + return size; +} diff --git a/src/DivaGL/object.hpp b/src/DivaGL/object.hpp index a1c6ef6..e600ffd 100644 --- a/src/DivaGL/object.hpp +++ b/src/DivaGL/object.hpp @@ -325,7 +325,8 @@ struct obj_mesh_attrib_member { uint32_t billboard_y_axis : 1; uint32_t translucent_sort_by_radius : 1; uint32_t billboard : 1; - uint32_t dummy : 28; + uint32_t dummy : 26; // 28 + uint32_t compression : 2; // Own stuff }; union obj_mesh_attrib { diff --git a/src/DivaGL/rob/rob.cpp b/src/DivaGL/rob/rob.cpp index 4a8cf59..39d0509 100644 --- a/src/DivaGL/rob/rob.cpp +++ b/src/DivaGL/rob/rob.cpp @@ -787,36 +787,134 @@ HOOK(void, FASTCALL, RobCloth__UpdateVertexBuffer, 0x000000014021CF00, obj_mesh* if (double_sided) indices_count /= 2; - if (mesh->vertex_format & OBJ_VERTEX_TANGENT) - for (int32_t i = 2; i; i--, facing = -1.0f) { - for (int32_t j = indices_count; j; j--, indices++) { - CLOTHNode* node = &nodes[*indices]; + bool tangent = !!(mesh->vertex_format & OBJ_VERTEX_TANGENT); - *(vec3*)data = node->trans; - *(vec3*)(data + 0x0C) = node->normal * facing; - *(vec3*)(data + 0x18) = node->tangent; - *(float_t*)(data + 0x24) = node->tangent_sign; + switch (mesh->attrib.m.compression) { + case 0: + default: + if (tangent) + for (int32_t i = 2; i; i--, facing = -1.0f) { + for (int32_t j = indices_count; j; j--, indices++) { + CLOTHNode* node = &nodes[*indices]; - data += mesh->size_vertex; + *(vec3*)data = node->trans; + *(vec3*)(data + 0x0C) = node->normal * facing; + *(vec3*)(data + 0x18) = node->tangent; + *(float_t*)(data + 0x24) = node->tangent_sign; + + data += mesh->size_vertex; + } + + if (!double_sided) + break; } + else + for (int32_t i = 2; i; i--, facing = -1.0f) { + for (int32_t j = indices_count; j; j--, indices++) { + CLOTHNode* node = &nodes[*indices]; - if (!double_sided) - break; - } - else - for (int32_t i = 2; i; i--, facing = -1.0f) { - for (int32_t j = indices_count; j; j--, indices++) { - CLOTHNode* node = &nodes[*indices]; + *(vec3*)data = node->trans; + *(vec3*)(data + 0x0C) = node->normal * facing; - *(vec3*)data = node->trans; - *(vec3*)(data + 0x0C) = node->normal * facing; + data += mesh->size_vertex; + } - data += mesh->size_vertex; + if (!double_sided) + break; } + break; + case 1: + if (tangent) + for (int32_t i = 2; i; i--, facing = -1.0f) { + for (int32_t j = indices_count; j; j--, indices++) { + CLOTHNode* node = &nodes[*indices]; - if (!double_sided) - break; - } + *(vec3*)data = node->trans; + + vec3_to_vec3i16(node->normal * (32767.0f * facing), *(vec3i16*)(data + 0x0C)); + *(int16_t*)(data + 0x12) = 0; + + vec4 tangent; + *(vec3*)&tangent = node->tangent; + tangent.w = node->tangent_sign; + vec4_to_vec4i16(tangent * 32767.0f, *(vec4i16*)(data + 0x14)); + + data += mesh->size_vertex; + } + + if (!double_sided) + break; + } + else + for (int32_t i = 2; i; i--, facing = -1.0f) { + for (int32_t j = indices_count; j; j--, indices++) { + CLOTHNode* node = &nodes[*indices]; + + *(vec3*)data = node->trans; + + vec3_to_vec3i16(node->normal * (32767.0f * facing), *(vec3i16*)(data + 0x0C)); + *(int16_t*)(data + 0x12) = 0; + + data += mesh->size_vertex; + } + + if (!double_sided) + break; + } + case 2: + if (tangent) + for (int32_t i = 2; i; i--, facing = -1.0f) { + for (int32_t j = indices_count; j; j--, indices++) { + CLOTHNode* node = &nodes[*indices]; + + *(vec3*)data = node->trans; + + vec3i16 normal_int; + vec3_to_vec3i16(node->normal * 511.0f, normal_int); + *(uint32_t*)(data + 0x0C) = (((uint32_t)0 & 0x03) << 30) + | (((uint32_t)normal_int.z & 0x3FF) << 20) + | (((uint32_t)normal_int.y & 0x3FF) << 10) + | ((uint32_t)normal_int.x & 0x3FF); + + vec4 tangent; + *(vec3*)&tangent = node->tangent; + tangent.w = node->tangent_sign; + + vec4i16 tangent_int; + vec4_to_vec4i16(tangent * 511.0f, tangent_int); + *(uint32_t*)(data + 0x10) = (((uint32_t)clamp_def(tangent_int.w, -1, 1) & 0x03) << 30) + | (((uint32_t)tangent_int.z & 0x3FF) << 20) + | (((uint32_t)tangent_int.y & 0x3FF) << 10) + | ((uint32_t)tangent_int.x & 0x3FF); + + data += mesh->size_vertex; + } + + if (!double_sided) + break; + } + else + for (int32_t i = 2; i; i--, facing = -1.0f) { + for (int32_t j = indices_count; j; j--, indices++) { + CLOTHNode* node = &nodes[*indices]; + + *(vec3*)data = node->trans; + + vec3i16 normal_int; + vec3_to_vec3i16(node->normal * 511.0f, normal_int); + *(uint32_t*)(data + 0x0C) = (((uint32_t)0 & 0x03) << 30) + | (((uint32_t)normal_int.z & 0x3FF) << 20) + | (((uint32_t)normal_int.y & 0x3FF) << 10) + | ((uint32_t)normal_int.x & 0x3FF); + + data += mesh->size_vertex; + } + + if (!double_sided) + break; + } + break; + } buffer.UnmapMemory(); }