шейдеры билбордов
This commit is contained in:
@@ -236,6 +236,15 @@ struct SkinTextureUnit {
|
||||
|
||||
static constexpr uint32_t MAGIC_MD20 = 0x3032444D; // 'MD20' LE
|
||||
static constexpr uint32_t MAGIC_SKIN = 0x4E494B53; // 'SKIN' LE
|
||||
static constexpr uint32_t M2_BONE_FLAG_SPHERICAL_BILLBOARD = 0x8;
|
||||
static constexpr uint32_t M2_BONE_FLAG_CYLINDRICAL_BILLBOARD_LOCK_X = 0x10;
|
||||
static constexpr uint32_t M2_BONE_FLAG_CYLINDRICAL_BILLBOARD_LOCK_Y = 0x20;
|
||||
static constexpr uint32_t M2_BONE_FLAG_CYLINDRICAL_BILLBOARD_LOCK_Z = 0x40;
|
||||
static constexpr uint32_t M2_BONE_BILLBOARD_MASK =
|
||||
M2_BONE_FLAG_SPHERICAL_BILLBOARD
|
||||
| M2_BONE_FLAG_CYLINDRICAL_BILLBOARD_LOCK_X
|
||||
| M2_BONE_FLAG_CYLINDRICAL_BILLBOARD_LOCK_Y
|
||||
| M2_BONE_FLAG_CYLINDRICAL_BILLBOARD_LOCK_Z;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Helpers
|
||||
@@ -275,6 +284,89 @@ static Vector3 wow_vec3_to_godot(float x, float y, float z) {
|
||||
return Vector3(x, z, -y);
|
||||
}
|
||||
|
||||
static int billboard_mode_from_bone_flags(uint32_t flags) {
|
||||
if (flags & M2_BONE_FLAG_SPHERICAL_BILLBOARD) return 1;
|
||||
if (flags & M2_BONE_FLAG_CYLINDRICAL_BILLBOARD_LOCK_X) return 2;
|
||||
if (flags & M2_BONE_FLAG_CYLINDRICAL_BILLBOARD_LOCK_Y) return 3;
|
||||
if (flags & M2_BONE_FLAG_CYLINDRICAL_BILLBOARD_LOCK_Z) return 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool resolve_vertex_billboard(
|
||||
const M2Vertex *verts,
|
||||
uint32_t vertex_count,
|
||||
uint16_t vertex_lookup,
|
||||
uint16_t global_vertex_index,
|
||||
const uint8_t *skin_bones,
|
||||
uint32_t skin_property_count,
|
||||
const uint16_t *bone_combo_arr,
|
||||
uint32_t bone_combo_count,
|
||||
uint16_t bone_start,
|
||||
const M2CompBone *bones,
|
||||
uint32_t bone_count,
|
||||
Vector3 &pivot_out,
|
||||
int &mode_out,
|
||||
uint32_t &flags_out) {
|
||||
if (!verts || !bones || global_vertex_index >= vertex_count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const M2Vertex &vertex = verts[global_vertex_index];
|
||||
bool all_weights_zero = true;
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
if (vertex.boneWeights[i] > 0) {
|
||||
all_weights_zero = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int best_bone = -1;
|
||||
uint32_t best_flags = 0;
|
||||
uint8_t best_weight = 0;
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
uint8_t weight = vertex.boneWeights[i];
|
||||
if (!all_weights_zero && weight == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int local_bone = (int)vertex.boneIndices[i];
|
||||
if (skin_bones && vertex_lookup < skin_property_count) {
|
||||
local_bone = (int)skin_bones[(uint32_t)vertex_lookup * 4 + i];
|
||||
}
|
||||
|
||||
int global_bone = local_bone;
|
||||
uint32_t lookup_index = (uint32_t)bone_start + (uint32_t)std::max(local_bone, 0);
|
||||
if (bone_combo_arr && lookup_index < bone_combo_count) {
|
||||
global_bone = (int)bone_combo_arr[lookup_index];
|
||||
}
|
||||
if (global_bone < 0 || (uint32_t)global_bone >= bone_count) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t flags = bones[global_bone].flags;
|
||||
if ((flags & M2_BONE_BILLBOARD_MASK) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (best_bone < 0 || weight > best_weight) {
|
||||
best_bone = global_bone;
|
||||
best_flags = flags;
|
||||
best_weight = weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_bone < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mode_out = billboard_mode_from_bone_flags(best_flags);
|
||||
if (mode_out <= 0) {
|
||||
return false;
|
||||
}
|
||||
pivot_out = wow_vec3_to_godot(bones[best_bone].pivot);
|
||||
flags_out = best_flags;
|
||||
return true;
|
||||
}
|
||||
|
||||
static Vector4 normalize_quat(float x, float y, float z, float w) {
|
||||
float len = std::sqrt(x * x + y * y + z * z + w * w);
|
||||
if (len <= 0.000001f) {
|
||||
@@ -643,6 +735,7 @@ Dictionary M2Loader::parse_m2(const std::vector<uint8_t> &buf, const std::string
|
||||
PackedInt32Array bone_lookup_table;
|
||||
Array batches;
|
||||
Array animated_surfaces;
|
||||
const M2CompBone *bones = safe_array<M2CompBone>(buf, hdr.ofsBones, hdr.nBones);
|
||||
const uint16_t *bone_combo_arr = safe_array<uint16_t>(buf, hdr.ofsBoneCombos, hdr.nBoneCombos);
|
||||
if (bone_combo_arr) {
|
||||
bone_lookup_table.resize(hdr.nBoneCombos);
|
||||
@@ -691,6 +784,59 @@ Dictionary M2Loader::parse_m2(const std::vector<uint8_t> &buf, const std::string
|
||||
int idx_count = (int)indices.size() - idx_start;
|
||||
if (idx_count <= 0) continue;
|
||||
|
||||
PackedFloat32Array billboard_data;
|
||||
uint32_t billboard_vertex_count = 0;
|
||||
uint32_t billboard_flags_seen = 0;
|
||||
if (verts && bones) {
|
||||
for (uint32_t t = 0; t + 2 < tri_count; t += 3) {
|
||||
uint16_t tri_local[3] = {
|
||||
skin_tri[tri_start + t],
|
||||
skin_tri[tri_start + t + 2],
|
||||
skin_tri[tri_start + t + 1],
|
||||
};
|
||||
for (uint32_t corner = 0; corner < 3; ++corner) {
|
||||
uint16_t vertex_lookup = tri_local[corner];
|
||||
if (vertex_lookup >= skin.nIndices) {
|
||||
continue;
|
||||
}
|
||||
uint16_t global_vertex_index = skin_idx[vertex_lookup];
|
||||
Vector3 pivot;
|
||||
int mode = 0;
|
||||
uint32_t bone_flags = 0;
|
||||
if (!resolve_vertex_billboard(
|
||||
verts,
|
||||
hdr.nVertices,
|
||||
vertex_lookup,
|
||||
global_vertex_index,
|
||||
skin_bones,
|
||||
skin.nProperties,
|
||||
bone_combo_arr,
|
||||
hdr.nBoneCombos,
|
||||
sm.boneStart,
|
||||
bones,
|
||||
hdr.nBones,
|
||||
pivot,
|
||||
mode,
|
||||
bone_flags)) {
|
||||
continue;
|
||||
}
|
||||
if (billboard_data.is_empty()) {
|
||||
billboard_data.resize(hdr.nVertices * 4);
|
||||
}
|
||||
uint32_t dst = (uint32_t)global_vertex_index * 4;
|
||||
if (dst + 3 >= (uint32_t)billboard_data.size()) {
|
||||
continue;
|
||||
}
|
||||
billboard_data[dst] = pivot.x;
|
||||
billboard_data[dst + 1] = pivot.y;
|
||||
billboard_data[dst + 2] = pivot.z;
|
||||
billboard_data[dst + 3] = (float)mode;
|
||||
billboard_vertex_count++;
|
||||
billboard_flags_seen |= (bone_flags & M2_BONE_BILLBOARD_MASK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary batch;
|
||||
batch["index_start"] = idx_start;
|
||||
batch["index_count"] = idx_count;
|
||||
@@ -708,12 +854,19 @@ Dictionary M2Loader::parse_m2(const std::vector<uint8_t> &buf, const std::string
|
||||
batch["skin_flags2"] = (int)tu[u].flags2;
|
||||
batch["bone_count"] = (int)sm.boneCount;
|
||||
batch["bone_combo_index"] = (int)sm.boneStart;
|
||||
if (billboard_vertex_count > 0) {
|
||||
batch["has_billboard"] = true;
|
||||
batch["billboard_vertex_count"] = (int)billboard_vertex_count;
|
||||
batch["billboard_bone_flags"] = (int)billboard_flags_seen;
|
||||
batch["billboard_data"] = billboard_data;
|
||||
}
|
||||
batches.push_back(batch);
|
||||
|
||||
if (include_animation && verts) {
|
||||
PackedVector3Array surface_vertices;
|
||||
PackedVector3Array surface_normals;
|
||||
PackedVector2Array surface_uvs, surface_uvs2;
|
||||
PackedFloat32Array surface_billboard_data;
|
||||
PackedInt32Array surface_bones;
|
||||
PackedFloat32Array surface_weights;
|
||||
PackedInt32Array surface_indices;
|
||||
@@ -725,6 +878,8 @@ Dictionary M2Loader::parse_m2(const std::vector<uint8_t> &buf, const std::string
|
||||
surface_bones.resize(tri_count * 4);
|
||||
surface_weights.resize(tri_count * 4);
|
||||
surface_indices.resize(tri_count);
|
||||
uint32_t surface_billboard_vertex_count = 0;
|
||||
uint32_t surface_billboard_flags_seen = 0;
|
||||
|
||||
for (uint32_t t = 0; t + 2 < tri_count; t += 3) {
|
||||
uint16_t tri_local[3] = {
|
||||
@@ -748,6 +903,36 @@ Dictionary M2Loader::parse_m2(const std::vector<uint8_t> &buf, const std::string
|
||||
surface_uvs2[expanded_count] = Vector2(v.texCoords2[0], v.texCoords2[1]);
|
||||
surface_indices[expanded_count] = (int)expanded_count;
|
||||
|
||||
Vector3 billboard_pivot;
|
||||
int billboard_mode = 0;
|
||||
uint32_t billboard_bone_flags = 0;
|
||||
if (resolve_vertex_billboard(
|
||||
verts,
|
||||
hdr.nVertices,
|
||||
vertex_lookup,
|
||||
global_vertex_index,
|
||||
skin_bones,
|
||||
skin.nProperties,
|
||||
bone_combo_arr,
|
||||
hdr.nBoneCombos,
|
||||
sm.boneStart,
|
||||
bones,
|
||||
hdr.nBones,
|
||||
billboard_pivot,
|
||||
billboard_mode,
|
||||
billboard_bone_flags)) {
|
||||
if (surface_billboard_data.is_empty()) {
|
||||
surface_billboard_data.resize(tri_count * 4);
|
||||
}
|
||||
uint32_t dst = expanded_count * 4;
|
||||
surface_billboard_data[dst] = billboard_pivot.x;
|
||||
surface_billboard_data[dst + 1] = billboard_pivot.y;
|
||||
surface_billboard_data[dst + 2] = billboard_pivot.z;
|
||||
surface_billboard_data[dst + 3] = (float)billboard_mode;
|
||||
surface_billboard_vertex_count++;
|
||||
surface_billboard_flags_seen |= (billboard_bone_flags & M2_BONE_BILLBOARD_MASK);
|
||||
}
|
||||
|
||||
uint32_t weight_sum = 0;
|
||||
for (uint32_t j = 0; j < 4; ++j) {
|
||||
weight_sum += (uint32_t)v.boneWeights[j];
|
||||
@@ -786,6 +971,13 @@ Dictionary M2Loader::parse_m2(const std::vector<uint8_t> &buf, const std::string
|
||||
surface["normals"] = surface_normals;
|
||||
surface["uvs"] = surface_uvs;
|
||||
surface["uvs2"] = surface_uvs2;
|
||||
if (surface_billboard_vertex_count > 0) {
|
||||
surface_billboard_data.resize(expanded_count * 4);
|
||||
surface["has_billboard"] = true;
|
||||
surface["billboard_vertex_count"] = (int)surface_billboard_vertex_count;
|
||||
surface["billboard_bone_flags"] = (int)surface_billboard_flags_seen;
|
||||
surface["billboard_data"] = surface_billboard_data;
|
||||
}
|
||||
surface["bones"] = surface_bones;
|
||||
surface["weights"] = surface_weights;
|
||||
surface["indices"] = surface_indices;
|
||||
@@ -822,7 +1014,6 @@ Dictionary M2Loader::parse_m2(const std::vector<uint8_t> &buf, const std::string
|
||||
uint32_t animation_activity_score = 0;
|
||||
if (include_animation) {
|
||||
const M2Sequence *seqs = safe_array<M2Sequence>(buf, hdr.ofsAnimations, hdr.nAnimations);
|
||||
const M2CompBone *bones = safe_array<M2CompBone>(buf, hdr.ofsBones, hdr.nBones);
|
||||
if (seqs && hdr.nAnimations > 0) {
|
||||
std::string lower_path = path;
|
||||
std::transform(lower_path.begin(), lower_path.end(), lower_path.begin(), [](unsigned char c) { return (char)std::tolower(c); });
|
||||
|
||||
Reference in New Issue
Block a user