Files
filament/libs/gltfio/src/TangentsJob.cpp

163 lines
6.9 KiB
C++

/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "TangentsJob.h"
#include <cstdlib>
#include <memory>
#include <geometry/SurfaceOrientation.h>
using namespace filament::gltfio;
using namespace filament;
using namespace filament::math;
// This procedure is designed to run in an isolated job.
void TangentsJob::run(Params* params) {
const cgltf_primitive& prim = *params->in.prim;
const int morphTargetIndex = params->in.morphTargetIndex;
const bool isMorphTarget = morphTargetIndex != kMorphTargetUnused;
// Extract the vertex count from the first attribute. All attributes must have the same count.
assert(prim.attributes_count > 0);
const cgltf_size vertexCount = prim.attributes[0].data->count;
params->out.vertexCount = vertexCount;
if (vertexCount == 0) {
return;
}
// Declare storage for data that has been unpacked and converted from the source buffers.
// This data needs to be held until after the SurfaceOrientation helper consumes it.
// Not all of these will be required, so they get allocated lazily.
std::unique_ptr<float3[]> unpackedNormals;
std::unique_ptr<float4[]> unpackedTangents;
std::unique_ptr<float3[]> unpackedPositions;
std::unique_ptr<float2[]> unpackedTexCoords;
std::unique_ptr<uint3[]> unpackedTriangles;
std::unique_ptr<float3[]> morphDeltas;
// Build a mapping from cgltf_attribute_type to cgltf_accessor.
const int NUM_ATTRIBUTES = cgltf_attribute_type_max_enum;
const cgltf_accessor* baseAccessors[NUM_ATTRIBUTES] = {};
const cgltf_accessor* morphTargetAccessors[NUM_ATTRIBUTES] = {};
// Collect accessors for normals, tangents, etc. Note that we skip over attributes with
// non-zero set indices likes TEXCOORD_1, TEXCOORD_2 to avoid overflowing the tiny arrays.
// The SurfaceOrientation helper does not need them anyway.
for (cgltf_size aindex = 0; aindex < prim.attributes_count; aindex++) {
const cgltf_attribute& attr = prim.attributes[aindex];
if (attr.index == 0) {
baseAccessors[attr.type] = attr.data;
}
}
if (isMorphTarget) {
const cgltf_morph_target& morphTarget = prim.targets[morphTargetIndex];
for (cgltf_size aindex = 0; aindex < morphTarget.attributes_count; aindex++) {
const cgltf_attribute& attr = morphTarget.attributes[aindex];
if (attr.index == 0) {
assert(baseAccessors[attr.type] &&
"Morph target data has no corresponding base vertex data.");
morphTargetAccessors[attr.type] = attr.data;
}
}
}
geometry::SurfaceOrientation::Builder sob;
sob.vertexCount(vertexCount);
// Allocate scratch space to store morph deltas.
if (isMorphTarget) {
morphDeltas.reset(new float3[vertexCount]);
}
// Convert normals into packed floats.
if (auto baseNormalsInfo = baseAccessors[cgltf_attribute_type_normal]; baseNormalsInfo) {
assert(baseNormalsInfo->count == vertexCount);
assert(baseNormalsInfo->type == cgltf_type_vec3);
unpackedNormals.reset(new float3[vertexCount]);
cgltf_accessor_unpack_floats(baseNormalsInfo, &unpackedNormals[0].x, vertexCount * 3);
if (auto mtNormalsInfo = morphTargetAccessors[cgltf_attribute_type_normal]) {
cgltf_accessor_unpack_floats(mtNormalsInfo, &morphDeltas[0].x, vertexCount * 3);
for (cgltf_size i = 0; i < vertexCount; i++) {
unpackedNormals[i] += morphDeltas[i];
}
}
sob.normals(unpackedNormals.get());
}
// Convert tangents into packed floats.
if (auto baseTangentsInfo = baseAccessors[cgltf_attribute_type_tangent]; baseTangentsInfo) {
assert(baseTangentsInfo->count == vertexCount);
unpackedTangents.reset(new float4[vertexCount]);
cgltf_accessor_unpack_floats(baseTangentsInfo, &unpackedTangents[0].x, vertexCount * 4);
if (auto mtTangentsInfo = morphTargetAccessors[cgltf_attribute_type_tangent]) {
cgltf_accessor_unpack_floats(mtTangentsInfo, &morphDeltas[0].x, vertexCount * 3);
for (cgltf_size i = 0; i < vertexCount; i++) {
unpackedTangents[i].xyz += morphDeltas[i];
}
}
sob.tangents(unpackedTangents.get());
}
if (auto basePosInfo = baseAccessors[cgltf_attribute_type_position]; basePosInfo) {
assert(basePosInfo->count == vertexCount && basePosInfo->type == cgltf_type_vec3);
unpackedPositions.reset(new float3[vertexCount]);
cgltf_accessor_unpack_floats(basePosInfo, &unpackedPositions[0].x, vertexCount * 3);
sob.positions(unpackedPositions.get());
if (auto mtPositionsInfo = morphTargetAccessors[cgltf_attribute_type_position]) {
cgltf_accessor_unpack_floats(mtPositionsInfo, &morphDeltas[0].x, vertexCount * 3);
for (cgltf_size i = 0; i < vertexCount; i++) {
unpackedPositions[i] += morphDeltas[i];
}
}
}
const size_t triangleCount = prim.indices ? (prim.indices->count / 3) : (vertexCount / 3);
unpackedTriangles.reset(new uint3[triangleCount]);
if (prim.indices) {
for (size_t tri = 0, j = 0; tri < triangleCount; ++tri) {
auto& triangle = unpackedTriangles[tri];
triangle.x = cgltf_accessor_read_index(prim.indices, j++);
triangle.y = cgltf_accessor_read_index(prim.indices, j++);
triangle.z = cgltf_accessor_read_index(prim.indices, j++);
}
} else {
for (size_t tri = 0, j = 0; tri < triangleCount; ++tri) {
auto& triangle = unpackedTriangles[tri];
triangle.x = j++;
triangle.y = j++;
triangle.z = j++;
}
}
sob.triangleCount(triangleCount);
sob.triangles(unpackedTriangles.get());
auto uvInfo = baseAccessors[cgltf_attribute_type_texcoord];
if (uvInfo && uvInfo->count == vertexCount && uvInfo->type == cgltf_type_vec2) {
unpackedTexCoords.reset(new float2[vertexCount]);
cgltf_accessor_unpack_floats(uvInfo, &unpackedTexCoords[0].x, vertexCount * 2);
sob.uvs(unpackedTexCoords.get());
}
// Compute surface orientation quaternions.
params->out.results = (short4*) malloc(sizeof(short4) * vertexCount);
geometry::SurfaceOrientation* helper = sob.build();
helper->getQuats(params->out.results, vertexCount);
delete helper;
}