Fix reordering 'mesh.point' related attribute when the vertex indices is

being built.
This commit is contained in:
Syoyo Fujita
2024-04-16 04:39:36 +09:00
parent 1923f11522
commit 6c54abec65
4 changed files with 295 additions and 36 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,104 @@
#usda 1.0
# uvmap has different UV value in shared vertex, so vertex index rebuild is requierd in Tydra RenderMesh.
(
defaultPrim = "root"
doc = "Blender v4.1.0"
metersPerUnit = 1
upAxis = "Z"
)
def Xform "root" (
customData = {
dictionary Blender = {
bool generated = 1
}
}
)
{
def Xform "Camera"
{
matrix4d xformOp:transform = ( (0.6859206557273865, 0.7276763319969177, 0, 0), (-0.32401347160339355, 0.305420845746994, 0.8953956365585327, 0), (0.6515582203865051, -0.6141703724861145, 0.44527140259742737, 0), (7.358891487121582, -6.925790786743164, 4.958309173583984, 1) )
uniform token[] xformOpOrder = ["xformOp:transform"]
def Camera "Camera"
{
float2 clippingRange = (0.1, 100)
float focalLength = 0.5
float horizontalAperture = 0.36
float horizontalApertureOffset = 0
token projection = "perspective"
float verticalAperture = 0.2025
float verticalApertureOffset = 0
}
}
def Xform "Grid"
{
matrix4d xformOp:transform = ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1) )
uniform token[] xformOpOrder = ["xformOp:transform"]
def Mesh "Grid" (
prepend apiSchemas = ["MaterialBindingAPI"]
)
{
uniform bool doubleSided = 1
float3[] extent = [(-1, -1, 0), (1, 1, 0)]
int[] faceVertexCounts = [4, 4]
int[] faceVertexIndices = [0, 1, 3, 2, 2, 3, 5, 4]
rel material:binding = </root/_materials/Material_001>
normal3f[] normals = [(0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1)] (
interpolation = "faceVarying"
)
point3f[] points = [(-1, -1, 0), (1, -1, 0), (-1, 0, 0), (1, 0, 0), (-1, 1, 0), (1, 1, 0)]
bool[] primvars:sharp_face = [1, 1] (
interpolation = "uniform"
)
# modified 5th item.
texCoord2f[] primvars:UVMap = [(0, 0), (1, 0), (1, 0.5), (0, 0.5), (0, 0.6), (1, 0.5), (1, 1), (0, 1)] (
interpolation = "faceVarying"
)
uniform token subdivisionScheme = "none"
}
}
def Scope "_materials"
{
def Material "Material_001"
{
token outputs:surface.connect = </root/_materials/Material_001/Principled_BSDF.outputs:surface>
def Shader "Principled_BSDF"
{
uniform token info:id = "UsdPreviewSurface"
float inputs:clearcoat = 0
float inputs:clearcoatRoughness = 0.03
color3f inputs:diffuseColor.connect = </root/_materials/Material_001/Image_Texture.outputs:rgb>
float inputs:ior = 1.5
float inputs:metallic = 0
float inputs:opacity = 1
float inputs:roughness = 0.5
float inputs:specular = 0.5
token outputs:surface
}
def Shader "Image_Texture"
{
uniform token info:id = "UsdUVTexture"
asset inputs:file = @./textures/texture-cat.jpg@
token inputs:sourceColorSpace = "sRGB"
float2 inputs:st.connect = </root/_materials/Material_001/uvmap.outputs:result>
token inputs:wrapS = "repeat"
token inputs:wrapT = "repeat"
float3 outputs:rgb
}
def Shader "uvmap"
{
uniform token info:id = "UsdPrimvarReader_float2"
token inputs:varname = "UVMap"
float2 outputs:result
}
}
}
}

View File

@@ -18,6 +18,7 @@
// - [ ] Adjust normal vector computation with handness?
// - [ ] Node xform animation
// - [ ] Better build of index buffer
// - [ ] Preserve the order of 'points' variable(mesh.points, Skin indices/weights, BlendShape points, ...) as much as possible.
// - Implement spatial hash
//
#include <numeric>
@@ -1686,7 +1687,8 @@ static void GenerateBasis(const vec3 &n, vec3 *tangent,
#endif
struct ComputeTangentPackedVertexData {
value::float3 position;
//value::float3 position;
uint32_t point_index;
value::float3 normal;
value::float2 uv;
@@ -1729,17 +1731,18 @@ struct ComputeTangentPackedVertexDataEqual {
template <class PackedVert>
struct ComputeTangentVertexInput {
std::vector<value::float3> positions;
//std::vector<value::float3> positions;
std::vector<uint32_t> point_indices;
std::vector<value::float3> normals;
std::vector<value::float2> uvs;
size_t size() const { return positions.size(); }
size_t size() const { return point_indices.size(); }
void get(size_t idx, PackedVert &output) const {
if (idx < positions.size()) {
output.position = positions[idx];
if (idx < point_indices.size()) {
output.point_index = point_indices[idx];
} else {
output.position = {0.0f, 0.0f, 0.0f};
output.point_index = ~0u; // never should reach here though.
}
if (idx < normals.size()) {
output.normal = normals[idx];
@@ -1756,14 +1759,16 @@ struct ComputeTangentVertexInput {
template <class PackedVert>
struct ComputeTangentVertexOutput {
std::vector<value::float3> positions;
//std::vector<value::float3> positions;
std::vector<uint32_t> point_indices;
std::vector<value::float3> normals;
std::vector<value::float2> uvs;
size_t size() const { return positions.size(); }
size_t size() const { return point_indices.size(); }
void push_back(const PackedVert &v) {
positions.push_back(v.position);
//positions.push_back(v.position);
point_indices.push_back(v.point_index);
normals.push_back(v.normal);
uvs.push_back(v.uv);
}
@@ -2048,29 +2053,31 @@ static bool ComputeTangentsAndBinormals(
if (is_facevarying_input) {
// input position is still in 'vertex' variability.
for (size_t i = 0; i < faceVertexIndices.size(); i++) {
vertex_input.positions.push_back(vertices[faceVertexIndices[i]]);
vertex_input.point_indices.push_back(faceVertexIndices[i]);
}
vertex_input.normals = normals;
vertex_input.uvs = texcoords;
} else {
// expand to facevarying.
for (size_t i = 0; i < faceVertexIndices.size(); i++) {
vertex_input.positions.push_back(vertices[faceVertexIndices[i]]);
vertex_input.point_indices.push_back(faceVertexIndices[i]);
vertex_input.normals.push_back(normals[faceVertexIndices[i]]);
vertex_input.uvs.push_back(texcoords[faceVertexIndices[i]]);
}
}
std::vector<uint32_t> vertex_point_indices;
BuildIndices<ComputeTangentVertexInput<ComputeTangentPackedVertexData>,
ComputeTangentVertexOutput<ComputeTangentPackedVertexData>,
ComputeTangentPackedVertexData,
ComputeTangentPackedVertexDataHasher,
ComputeTangentPackedVertexDataEqual>(
vertex_input, vertex_output, vertex_indices);
vertex_input, vertex_output, vertex_indices, vertex_point_indices);
DCOUT("faceVertexIndices.size : " << faceVertexIndices.size());
DCOUT("# of indices after the build: " << vertex_indices.size() << ", reduced " << (faceVertexIndices.size() - vertex_indices.size()) << " indices.");
// We only need indices. Discard vertex_output
// We only need indices. Discard vertex_output and vertrex_point_indices
}
const uint32_t num_verts =
@@ -2428,7 +2435,7 @@ bool RenderSceneConverter::BuildVertexIndicesImpl(RenderMesh &mesh) {
DefaultVertexInput<DefaultPackedVertexData> vertex_input;
size_t num_fvs = fvIndices.size();
vertex_input.positions.assign(num_fvs, {0.0f, 0.0f, 0.0f});
vertex_input.point_indices = fvIndices;
vertex_input.uv0s.assign(num_fvs, {0.0f, 0.0f});
vertex_input.uv1s.assign(num_fvs, {0.0f, 0.0f});
vertex_input.normals.assign(num_fvs, {0.0f, 0.0f, 0.0f});
@@ -2557,11 +2564,6 @@ bool RenderSceneConverter::BuildVertexIndicesImpl(RenderMesh &mesh) {
PUSH_ERROR_AND_RETURN(fmt::format("Invalid faceVertexIndex {}. Must be less than {}", fvi, num_fvs));
}
//
// position is 'vertex' varying, others are 'facevarying'
//
vertex_input.positions[i] = mesh.points[fvi];
if (normals_ptr) {
vertex_input.normals[i] = normals_ptr[i];
}
@@ -2586,13 +2588,21 @@ bool RenderSceneConverter::BuildVertexIndicesImpl(RenderMesh &mesh) {
}
std::vector<uint32_t> out_indices;
std::vector<uint32_t> out_point_indices; // to reorder position data
DefaultVertexOutput<DefaultPackedVertexData> vertex_output;
BuildIndices<DefaultVertexInput<DefaultPackedVertexData>,
DefaultVertexOutput<DefaultPackedVertexData>,
DefaultPackedVertexData, DefaultPackedVertexDataHasher,
DefaultPackedVertexDataEqual>(vertex_input, vertex_output,
out_indices);
out_indices, out_point_indices);
if (out_indices.size() != out_point_indices.size()) {
PUSH_ERROR_AND_RETURN("Internal error. out_indices.size != out_point_indices.");
}
DCOUT("faceVertexIndices.size : " << fvIndices.size());
DCOUT("# of indices after the build: " << out_indices.size() << ", reduced " << (fvIndices.size() - out_indices.size()) << " indices.");
if (mesh.is_triangulated()) {
mesh.triangulatedFaceVertexIndices = out_indices;
@@ -2600,9 +2610,54 @@ bool RenderSceneConverter::BuildVertexIndicesImpl(RenderMesh &mesh) {
mesh.usdFaceVertexIndices = out_indices;
}
mesh.points = vertex_output.positions;
//
// Reorder 'vertex' varying attributes(points, jointIndices/jointWeights, BlendShape points, ...)
// TODO: Preserve input order as much as possible.
//
{
uint32_t numPoints = *std::max_element(out_indices.begin(), out_indices.end()) + 1;
{
std::vector<value::float3> tmp_points(numPoints);
for (size_t i = 0; i < out_point_indices.size(); i++) {
if (out_point_indices[i] >= mesh.points.size()) {
PUSH_ERROR_AND_RETURN("Internal error. point index out-of-range.");
}
tmp_points[out_indices[i]] = mesh.points[out_point_indices[i]];
}
mesh.points.swap(tmp_points);
}
// attributes are now 'vertex' variability
if (mesh.joint_and_weights.jointIndices.size()) {
if (mesh.joint_and_weights.elementSize < 1) {
PUSH_ERROR_AND_RETURN("Internal error. Invalid elementSize in mesh.joint_and_weights.");
}
uint32_t elementSize = uint32_t(mesh.joint_and_weights.elementSize);
std::vector<int> tmp_indices(numPoints * elementSize);
std::vector<float> tmp_weights(numPoints * elementSize);
for (size_t i = 0; i < out_point_indices.size(); i++) {
if ((elementSize * out_point_indices[i]) >= mesh.joint_and_weights.jointIndices.size()) {
PUSH_ERROR_AND_RETURN("Internal error. point index exceeds jointIndices.size.");
}
for (size_t k = 0; k < elementSize; k++) {
tmp_indices[elementSize * out_indices[i] + k] = mesh.joint_and_weights.jointIndices[elementSize * out_point_indices[i] + k];
}
if ((elementSize * out_point_indices[i]) >= mesh.joint_and_weights.jointWeights.size()) {
PUSH_ERROR_AND_RETURN("Internal error. point index exceeds jointWeights.size.");
}
for (size_t k = 0; k < elementSize; k++) {
tmp_weights[elementSize * out_indices[i] + k] = mesh.joint_and_weights.jointWeights[elementSize * out_point_indices[i] + k];
}
}
mesh.joint_and_weights.jointIndices.swap(tmp_indices);
mesh.joint_and_weights.jointWeights.swap(tmp_weights);
}
// TODO: Reorder BlendShape points
}
// Other 'facevarying' attributes are now 'vertex' variability
if (normals_ptr) {
mesh.normals.set_buffer(
reinterpret_cast<const uint8_t *>(vertex_output.normals.data()),
@@ -5759,6 +5814,14 @@ std::string DumpMesh(const RenderMesh &mesh, uint32_t indent) {
ss << pprint::Indent(indent + 1) << "}\n";
}
if (mesh.joint_and_weights.jointIndices.size()) {
ss << pprint::Indent(indent + 1) << "skin {\n";
ss << pprint::Indent(indent + 2) << "geomBindTransform " << quote(tinyusdz::to_string(mesh.joint_and_weights.geomBindTransform)) << "\n";
ss << pprint::Indent(indent + 2) << "elementSize " << mesh.joint_and_weights.elementSize << "\n";
ss << pprint::Indent(indent + 2) << "jointIndices " << quote(value::print_array_snipped(mesh.joint_and_weights.jointIndices)) << "\n";
ss << pprint::Indent(indent + 2) << "jointWeights " << quote(value::print_array_snipped(mesh.joint_and_weights.jointWeights)) << "\n";
ss << pprint::Indent(indent + 1) << "}\n";
}
if (mesh.material_subsetMap.size()) {
ss << pprint::Indent(indent + 1) << "material_subsets {\n";
for (const auto &msubset : mesh.material_subsetMap) {

View File

@@ -731,15 +731,15 @@ struct JointAndWeight {
value::matrix4d::identity()}; // matrix4d primvars:skel:geomBindTransform
//
// NOTE: both jointIndices and jointWeights' USD interpolation must be
// 'vertex'
// NOTE: variability of jointIndices and jointWeights are 'vertex'
// NOTE: Values in jointIndices and jointWeights will be reordered when `MeshConverterConfig::build_vertex_indices` is set true.
//
std::vector<int> jointIndices; // int[] primvars:skel:jointIndices
// NOTE: weight is converted from USD as-is. not normalized.
std::vector<float> jointWeights; // float[] primvars:skel:jointWeight;
int elementSize{1};
int elementSize{1}; // # of weights per vertex
};
struct MaterialPath {
@@ -1331,7 +1331,8 @@ struct RenderSceneConverterConfig {
// TODO: Polish interface to support arbitrary vertex configuration.
//
struct DefaultPackedVertexData {
value::float3 position;
//value::float3 position;
uint32_t point_index;
value::float3 normal;
value::float2 uv0;
value::float2 uv1;
@@ -1379,7 +1380,8 @@ struct DefaultPackedVertexDataEqual {
template <class PackedVert>
struct DefaultVertexInput {
std::vector<value::float3> positions;
//std::vector<value::float3> positions;
std::vector<uint32_t> point_indices;
std::vector<value::float3> normals;
std::vector<value::float2> uv0s;
std::vector<value::float2> uv1s;
@@ -1388,13 +1390,13 @@ struct DefaultVertexInput {
std::vector<value::float3> colors;
std::vector<float> opacities;
size_t size() const { return positions.size(); }
size_t size() const { return point_indices.size(); }
void get(size_t idx, PackedVert &output) const {
if (idx < positions.size()) {
output.position = positions[idx];
if (idx < point_indices.size()) {
output.point_index = point_indices[idx];
} else {
output.position = {0.0f, 0.0f, 0.0f};
output.point_index = ~0u; // this case should not happen though
}
if (idx < normals.size()) {
output.normal = normals[idx];
@@ -1436,7 +1438,8 @@ struct DefaultVertexInput {
template <class PackedVert>
struct DefaultVertexOutput {
std::vector<value::float3> positions;
//std::vector<value::float3> positions;
std::vector<uint32_t> point_indices;
std::vector<value::float3> normals;
std::vector<value::float2> uv0s;
std::vector<value::float2> uv1s;
@@ -1445,10 +1448,10 @@ struct DefaultVertexOutput {
std::vector<value::float3> colors;
std::vector<float> opacities;
size_t size() const { return positions.size(); }
size_t size() const { return point_indices.size(); }
void push_back(const PackedVert &v) {
positions.push_back(v.position);
point_indices.push_back(v.point_index);
normals.push_back(v.normal);
uv0s.push_back(v.uv0);
uv1s.push_back(v.uv1);
@@ -1465,7 +1468,8 @@ struct DefaultVertexOutput {
template <class VertexInput, class VertexOutput, class PackedVert,
class PackedVertHasher, class PackedVertEqual>
void BuildIndices(const VertexInput &input, VertexOutput &output,
std::vector<uint32_t> &out_indices) {
std::vector<uint32_t> &out_indices, std::vector<uint32_t> &out_point_indices)
{
// TODO: Use LSH(locally sensitive hashing) or BVH for kNN point query.
std::unordered_map<PackedVert, uint32_t, PackedVertHasher, PackedVertEqual>
vertexToIndexMap;
@@ -1489,9 +1493,12 @@ void BuildIndices(const VertexInput &input, VertexOutput &output,
if (found) {
out_indices.push_back(index);
} else {
out_indices.push_back(uint32_t(output.size()));
uint32_t new_index = uint32_t(output.size());
out_indices.push_back(new_index);
output.push_back(v);
vertexToIndexMap[v] = new_index;
}
out_point_indices.push_back(v.point_index);
}
}