Skip to content

Commit

Permalink
Add vertex colors to subdivision
Browse files Browse the repository at this point in the history
Need to refactor. Currently face varying fully relies on UV data.
That or I'll look into a custom glft extension again. Restoring topology
by value comparison is a bad choice to begin with.
  • Loading branch information
tefusion committed Sep 6, 2024
1 parent cf63608 commit 7eb2189
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 49 deletions.
64 changes: 52 additions & 12 deletions src/import/topology_data_importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ TopologyDataImporter::SurfaceVertexArrays::SurfaceVertexArrays(const Array &p_me
if (p_mesh_arrays[Mesh::ARRAY_TEX_UV].get_type() == Variant::PACKED_VECTOR2_ARRAY)
uv_array = p_mesh_arrays[Mesh::ARRAY_TEX_UV];

if (p_mesh_arrays[Mesh::ARRAY_COLOR].get_type() == Variant::PACKED_COLOR_ARRAY)
color_array = p_mesh_arrays[Mesh::ARRAY_COLOR];

if (p_mesh_arrays[Mesh::ARRAY_BONES].get_type() == Variant::PACKED_INT32_ARRAY)
bones_array = p_mesh_arrays[Mesh::ARRAY_BONES];

Expand Down Expand Up @@ -160,8 +163,9 @@ void TopologyDataImporter::convert_importer_meshinstance_to_subdiv(Object *impor
TopologyDataMesh::TopologyType TopologyDataImporter::_generate_topology_surface_arrays(const SurfaceVertexArrays &surface, int32_t format, Array &surface_arrays) {
ERR_FAIL_COND_V(!(format & Mesh::ARRAY_FORMAT_INDEX), TopologyDataMesh::TopologyType::QUAD);
TopologySurfaceData topology_surface = _remove_duplicate_vertices(surface, format);
bool is_quad = _merge_to_quads(topology_surface.index_array, topology_surface.uv_array, format);
bool is_quad = _merge_to_quads(topology_surface.index_array, topology_surface.uv_array, topology_surface.color_array, format);
bool has_uv = format & Mesh::ARRAY_FORMAT_TEX_UV;
bool has_color = format & Mesh::ARRAY_FORMAT_COLOR;

surface_arrays.resize(TopologyDataMesh::ARRAY_MAX);
surface_arrays[TopologyDataMesh::ARRAY_VERTEX] = topology_surface.vertex_array;
Expand All @@ -171,12 +175,22 @@ TopologyDataMesh::TopologyType TopologyDataImporter::_generate_topology_surface_
surface_arrays[TopologyDataMesh::ARRAY_WEIGHTS] = topology_surface.weights_array;
surface_arrays[TopologyDataMesh::ARRAY_INDEX] = topology_surface.index_array;
if (has_uv) {
PackedInt32Array uv_index_array = _generate_uv_index_array(topology_surface.uv_array);
PackedInt32Array fvar_index_array = _generate_varying_index_array(topology_surface.uv_array, topology_surface.color_array);
surface_arrays[TopologyDataMesh::ARRAY_TEX_UV] = topology_surface.uv_array;
surface_arrays[TopologyDataMesh::ARRAY_UV_INDEX] = uv_index_array;
surface_arrays[TopologyDataMesh::ARRAY_FV_INDEX] = fvar_index_array;
} else { //this is to avoid issues with casting null to Array when using these as reference
surface_arrays[TopologyDataMesh::ARRAY_TEX_UV] = PackedVector2Array();
surface_arrays[TopologyDataMesh::ARRAY_UV_INDEX] = PackedInt32Array();
surface_arrays[TopologyDataMesh::ARRAY_FV_INDEX] = PackedInt32Array();
}

//TODO: remove after abstraction
if (has_uv && has_color) {
surface_arrays[TopologyDataMesh::ARRAY_COLOR] = topology_surface.color_array;
} else {
if (has_color) {
WARN_PRINT("Ignoring color array. Currently color subdivision only works if a UV array exists since the needed varying index array is created with the help of uv data.");
}
surface_arrays[TopologyDataMesh::ARRAY_COLOR] = PackedColorArray();
}

if (is_quad) {
Expand All @@ -192,6 +206,7 @@ TopologyDataImporter::TopologySurfaceData TopologyDataImporter::_remove_duplicat
bool has_skinning = (format & Mesh::ARRAY_FORMAT_BONES) && (format & Mesh::ARRAY_FORMAT_WEIGHTS);
bool double_bone_weights = format & Mesh::ARRAY_FLAG_USE_8_BONE_WEIGHTS;
bool has_normals = format & Mesh::ARRAY_FORMAT_NORMAL;
bool has_color = format & Mesh::ARRAY_FORMAT_COLOR;
//TODO: maybe add tangents, uv2 here too, considering that data is lost after subdivisions not that urgent

TopologySurfaceData topology_surface;
Expand Down Expand Up @@ -227,22 +242,28 @@ TopologyDataImporter::TopologySurfaceData TopologyDataImporter::_remove_duplicat
if (has_uv) {
topology_surface.uv_array.append(surface.uv_array[index]);
}

if (has_color) {
topology_surface.color_array.append(surface.color_array[index]);
}
}

return topology_surface;
}

// Goes through index_array and always merges the 6 indices of 2 triangles to 1 quad (uv_array also updated)
// returns boolean if conversion to quad was successful
bool TopologyDataImporter::_merge_to_quads(PackedInt32Array &index_array, PackedVector2Array &uv_array, int32_t format) {
bool TopologyDataImporter::_merge_to_quads(PackedInt32Array &index_array, PackedVector2Array &uv_array, PackedColorArray &color_array, int32_t format) {
if (index_array.size() % 6 != 0) {
return false;
}

bool has_uv = format & Mesh::ARRAY_FORMAT_TEX_UV;
bool has_color = format & Mesh::ARRAY_FORMAT_COLOR;

PackedInt32Array quad_index_array;
PackedVector2Array quad_uv_array;
PackedColorArray quad_color_array;
for (int i = 0; i < index_array.size(); i += 6) {
// initalize unshared with verts from triangle 1
PackedInt32Array unshared_verts;
Expand Down Expand Up @@ -288,34 +309,53 @@ bool TopologyDataImporter::_merge_to_quads(PackedInt32Array &index_array, Packed
quad_uv_array.append(uv_array[pos_unshared_verts[1]]);
quad_uv_array.append(uv_array[pos_shared_verts[1]]);
}

if (has_color) {
quad_color_array.append(color_array[pos_unshared_verts[0]]);
quad_color_array.append(color_array[pos_shared_verts[0]]);
quad_color_array.append(color_array[pos_unshared_verts[1]]);
quad_color_array.append(color_array[pos_shared_verts[1]]);
}
}
index_array = quad_index_array;
uv_array = quad_uv_array;
color_array = quad_color_array;
return true;
}

// tries to store uv's as compact as possible with an index array, an additional index array
// is needed for uv data, because the index array for the vertices connects faces while uv's
// can still be different for faces even when it's the same vertex, works similar to _remove_duplicate_vertices
PackedInt32Array TopologyDataImporter::_generate_uv_index_array(PackedVector2Array &uv_array) {
PackedInt32Array TopologyDataImporter::_generate_varying_index_array(PackedVector2Array &uv_array, PackedColorArray &color_array) {
ERR_FAIL_COND_V(uv_array.is_empty(), PackedInt32Array());
int max_index = 0;
const bool use_color = color_array.size() > 0;
if (use_color) {
ERR_FAIL_COND_V(color_array.size() != uv_array.size(), PackedInt32Array());
}

HashMap<Vector2, int> found_uvs;
PackedInt32Array uv_index_array;
PackedInt32Array fvar_index_array;
PackedVector2Array packed_uv_array; // will overwrite the given uv_array before returning
for (int uv_index = 0; uv_index < uv_array.size(); uv_index++) {
Vector2 single_uv = uv_array[uv_index];
PackedColorArray packed_color_array;
for (int fv_index = 0; fv_index < uv_array.size(); fv_index++) {
Vector2 single_uv = uv_array[fv_index];
if (found_uvs.has(single_uv)) {
uv_index_array.append(found_uvs.get(single_uv));
fvar_index_array.append(found_uvs.get(single_uv));
} else {
packed_uv_array.append(single_uv);
uv_index_array.append(max_index);
if (use_color) {
packed_color_array.append(color_array[fv_index]);
}

fvar_index_array.append(max_index);
found_uvs.insert(single_uv, max_index);
max_index++;
}
}
uv_array = packed_uv_array;
return uv_index_array;
color_array = packed_color_array;
return fvar_index_array;
}

//same as remove duplicate vertices, just runs for every blend shape
Expand Down
11 changes: 7 additions & 4 deletions src/import/topology_data_importer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class TopologyDataImporter : public Object {
struct TopologySurfaceData {
godot::PackedVector3Array vertex_array;
godot::PackedVector3Array normal_array;
godot::PackedColorArray color_array;
godot::PackedVector2Array uv_array;
godot::PackedInt32Array index_array;
godot::PackedInt32Array bones_array;
Expand All @@ -33,12 +34,13 @@ class TopologyDataImporter : public Object {
struct SurfaceVertexArrays { //of imported triangle mesh
PackedVector3Array vertex_array;
PackedVector3Array normal_array;
PackedColorArray color_array;
PackedInt32Array index_array;
PackedVector2Array uv_array;
PackedInt32Array bones_array; //could be float or int array after docs
PackedFloat32Array weights_array;
SurfaceVertexArrays(const Array &p_mesh_arrays);
SurfaceVertexArrays(){};
SurfaceVertexArrays() {};
};

/**
Expand Down Expand Up @@ -71,18 +73,19 @@ class TopologyDataImporter : public Object {
* @param index_array Topology Index Array after removing duplicate vertices
* @param uv_array Array of original UV's. UV's are in most cases different and part of the reason why
* the Triangles even got split up at export even when they are at the same position
* @param color_array Array of original colors. Also vertex variant like uv
* @param format
* @return true The mesh is a QuadMesh
* @return false The mesh is not a QuadMesh and didn't merge faces -> fallback to TriangleMesh
*/
bool _merge_to_quads(PackedInt32Array &index_array, PackedVector2Array &uv_array, int32_t format);
bool _merge_to_quads(PackedInt32Array &index_array, PackedVector2Array &uv_array, PackedColorArray &color_array, int32_t format);
/**
* @brief Generates minimal needed UV index array (as vertex index array would cause data to be lost)
* @brief Generates minimal needed FV index array (as vertex index array would cause data to be lost)
*
* @param uv_array
* @return PackedInt32Array
*/
PackedInt32Array _generate_uv_index_array(PackedVector2Array &uv_array);
PackedInt32Array _generate_varying_index_array(PackedVector2Array &uv_array, PackedColorArray &color_array);
/**
* @brief Goes through all of the above methods (remove_duplicate, merge_to_quads) and saves the result in surface_arrays
*
Expand Down
2 changes: 1 addition & 1 deletion src/resources/topology_data_mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,6 @@ void TopologyDataMesh::_bind_methods() {
BIND_ENUM_CONSTANT(ARRAY_BONES);
BIND_ENUM_CONSTANT(ARRAY_WEIGHTS);
BIND_ENUM_CONSTANT(ARRAY_INDEX);
BIND_ENUM_CONSTANT(ARRAY_UV_INDEX);
BIND_ENUM_CONSTANT(ARRAY_FV_INDEX);
BIND_ENUM_CONSTANT(ARRAY_MAX);
}
6 changes: 3 additions & 3 deletions src/resources/topology_data_mesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class TopologyDataMesh : public Resource {
ARRAY_BONES = Mesh::ARRAY_BONES,
ARRAY_WEIGHTS = Mesh::ARRAY_WEIGHTS,
ARRAY_INDEX = Mesh::ARRAY_INDEX,
ARRAY_UV_INDEX = Mesh::ARRAY_MAX, //just an index array for uv's (ARRAY_INDEX does not work with uv's anymore in favour of face connection)
ARRAY_FV_INDEX = Mesh::ARRAY_MAX, //just an index array for uv's (ARRAY_INDEX does not work with uv's anymore in favour of face connection)
ARRAY_MAX = Mesh::ARRAY_MAX + 1
};

Expand Down Expand Up @@ -71,7 +71,7 @@ class TopologyDataMesh : public Resource {
godot::PackedVector3Array vertex_array;
godot::PackedVector3Array normal_array;
godot::PackedVector2Array uv_array;
godot::PackedInt32Array uv_index_array;
godot::PackedInt32Array fvar_index_array;
godot::PackedInt32Array index_array;
godot::PackedFloat32Array bones_array;
godot::PackedFloat32Array weights_array;
Expand All @@ -80,7 +80,7 @@ class TopologyDataMesh : public Resource {
normal_array = p_mesh_arrays[TopologyDataMesh::ARRAY_NORMAL];
index_array = p_mesh_arrays[TopologyDataMesh::ARRAY_INDEX];
uv_array = p_mesh_arrays[TopologyDataMesh::ARRAY_TEX_UV];
uv_index_array = p_mesh_arrays[TopologyDataMesh::ARRAY_UV_INDEX];
fvar_index_array = p_mesh_arrays[TopologyDataMesh::ARRAY_FV_INDEX];
if (p_mesh_arrays[TopologyDataMesh::ARRAY_BONES])
bones_array = p_mesh_arrays[TopologyDataMesh::ARRAY_BONES];

Expand Down
6 changes: 5 additions & 1 deletion src/subdivision/quad_subdivider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Array QuadSubdivider::_get_triangle_arrays() const {
st.instantiate();

bool use_uv = topology_data.uv_array.size();
bool has_colors = topology_data.color_array.size();
bool use_bones = topology_data.bones_array.size();
bool has_normals = topology_data.normal_array.size();

Expand All @@ -23,7 +24,10 @@ Array QuadSubdivider::_get_triangle_arrays() const {
//after for loop unshared0 vertex will be at the positon quad_index in the new vertex_array in the SurfaceTool
for (int single_quad_index = quad_index; single_quad_index < quad_index + 4; single_quad_index++) {
if (use_uv) {
st->set_uv(topology_data.uv_array[topology_data.uv_index_array[single_quad_index]]);
st->set_uv(topology_data.uv_array[topology_data.fvar_index_array[single_quad_index]]);
}
if (has_colors) {
st->set_color(topology_data.color_array[topology_data.fvar_index_array[single_quad_index]]);
}

if (has_normals) {
Expand Down
Loading

0 comments on commit 7eb2189

Please sign in to comment.