#include #include #include #include #include #include #include #include "api.h" constexpr size_t kInvalidIndex = std::numeric_limits::max(); static bool is_power_of_two(int n) { return n > 0 && (n & (n - 1)) == 0; } template static inline U lerp(const T& a, const T& b, const T& t, const U& val_a, const U& val_b) { if (a == b) return val_a; // Avoid divide by zero T alpha = (t - a) / (b - a); return (1 - alpha) * val_a + alpha * val_b; } template static auto get_or_default(const Map& map, const Key& key, const Default& default_val) -> typename Map::mapped_type { auto it = map.find(key); return (it != map.end()) ? it->second : default_val; } // 3D voxel coordinate struct VoxelCoord { int x, y, z; int& operator[](int i) { return (&x)[i]; } bool operator==(const VoxelCoord& other) const { return x == other.x && y == other.y && z == other.z; } }; // Hash function for VoxelCoord to use in unordered_map namespace std { template <> struct hash { size_t operator()(const VoxelCoord& v) const { const std::size_t p1 = 73856093; const std::size_t p2 = 19349663; const std::size_t p3 = 83492791; return (std::size_t)(v.x) * p1 ^ (std::size_t)(v.y) * p2 ^ (std::size_t)(v.z) * p3; } }; } /** * Compute the Normal Tangent and Bitangent vectors for a triangle. * * @param v0 The first vertex of the triangle. * @param v1 The second vertex of the triangle. * @param v2 The third vertex of the triangle. * @param uv0 The texture coordinates of the first vertex. * @param uv1 The texture coordinates of the second vertex. * @param uv2 The texture coordinates of the third vertex. * * @return A tuple containing: * - t The tangent vector. * - b The bitangent vector. * - n The normal vector. * - mip_length The norms of the partial derivatives of the 3D coordinates with respect to the 2D texture coordinates. */ static std::tuple compute_TBN( const Eigen::Vector3f& v0, const Eigen::Vector3f& v1, const Eigen::Vector3f& v2, const Eigen::Vector2f& uv0, const Eigen::Vector2f& uv1, const Eigen::Vector2f& uv2 ) { Eigen::Vector3f e1 = v1 - v0; Eigen::Vector3f e2 = v2 - v0; Eigen::Vector2f duv1 = uv1 - uv0; Eigen::Vector2f duv2 = uv2 - uv0; Eigen::Vector3f n = e1.cross(e2).normalized(); float det = duv1.x() * duv2.y() - duv1.y() * duv2.x(); if (fabs(det) < 1e-6) { // Use default Eigen::Vector3f t(1.0f, 0.0f, 0.0f); Eigen::Vector3f b(0.0f, 1.0f, 0.0f); Eigen::Vector2f mip_length(1e6, 1e6); return std::make_tuple(t, b, n, mip_length); } float invDet = 1.0f / det; Eigen::Vector3f t = (duv2.y() * e1 - duv1.y() * e2); Eigen::Vector3f b = (duv1.x() * e2 - duv2.x() * e1); float t_norm = t.norm(); float b_norm = b.norm(); t = t / t_norm; b = b / b_norm; Eigen::Vector2f mip_length(invDet * t_norm, invDet * b_norm); return std::make_tuple(t, b, n, mip_length); } /** * Project a point onto a triangle defined by three vertices. * * @param p The point to project. * @param a The first vertex of the triangle. * @param b The second vertex of the triangle. * @param c The third vertex of the triangle. * @param n The normal of the triangle. * * @return The projected point represented as barycentric coordinates (u, v, w) and distance from the plane. */ static Eigen::Vector4f project_onto_triangle( const Eigen::Vector3f& p, const Eigen::Vector3f& a, const Eigen::Vector3f& b, const Eigen::Vector3f& c, const Eigen::Vector3f& n ) { float d = (p - a).dot(n); Eigen::Vector3f p_proj = p - d * n; Eigen::Vector3f ab = b - a; Eigen::Vector3f ac = c - a; Eigen::Vector3f ap = p_proj - a; float d00 = ab.dot(ab); float d01 = ab.dot(ac); float d11 = ac.dot(ac); float d20 = ap.dot(ab); float d21 = ap.dot(ac); float denom = d00 * d11 - d01 * d01; float v = (d11 * d20 - d01 * d21) / denom; float w = (d00 * d21 - d01 * d20) / denom; float u = 1.0f - v - w; return Eigen::Vector4f(u, v, w, d); } static inline int wrap_texcoord(const int& x, const int& W, const int& filter) { if (filter == 0) { // REPEAT return (x % W + W) % W; } else if (filter == 1) { // CLAMP_TO_EDGE return std::max(0, std::min(x, W - 1)); } else if (filter == 2) { // MIRROR_REPEAT int period = 2 * W; int x_mod = (x % period + period) % period; return (x_mod < W) ? x_mod : (period - x_mod - 1); } else { // Default to repeat return (x % W + W) % W; } } static std::vector> build_mipmaps( const uint8_t* texture, const int& H, const int& W, const int& C ) { if (H != W || !is_power_of_two(H)) { throw std::invalid_argument("Texture width and height must be equal and a power of two."); } std::vector> mipmaps; const uint8_t* cur_map = texture; int cur_H = H; int cur_W = W; int next_H = cur_H >> 1; int next_W = cur_W >> 1; while (next_H > 0 && next_W > 0) { std::vector next_map(next_H * next_W * C); for (int y = 0; y < next_H; y++) { for (int x = 0; x < next_W; x++) { for (int c = 0; c < C; c++) { size_t sum = 0; size_t xx = static_cast(x) << 1; size_t yy = static_cast(y) << 1; sum += cur_map[yy * static_cast(cur_W) * C + xx * C + c]; sum += cur_map[(yy + 1) * static_cast(cur_W) * C + xx * C + c]; sum += cur_map[yy * static_cast(cur_W) * C + (xx + 1) * C + c]; sum += cur_map[(yy + 1) * static_cast(cur_W) * C + (xx + 1) * C + c]; next_map[y * next_W * C + x * C + c] = static_cast(sum / 4); } } } mipmaps.push_back(std::move(next_map)); cur_map = mipmaps.back().data(); cur_H = next_H; cur_W = next_W; next_H = cur_H >> 1; next_W = cur_W >> 1; } return mipmaps; } static void sample_texture( const uint8_t* texture, const int& H, const int& W, const int& C, const float& u, const float& v, const int& filter, const int& wrap, float* color ) { float x = u * W; float y = (1 - v) * H; if (filter == 0) { // NEAREST int x_int = floorf(x); int y_int = floorf(y); x_int = wrap_texcoord(x_int, W, wrap); y_int = wrap_texcoord(y_int, H, wrap); for (int c = 0; c < C; c++) { color[c] = texture[y_int * W * C + x_int * C + c] / 255.0f; } } else { // LINEAR int x_low = floorf(x - 0.5); int x_high = x_low + 1; int y_low = floorf(y - 0.5); int y_high = y_low + 1; float w_x = x - x_low - 0.5; float w_y = y - y_low - 0.5; x_low = wrap_texcoord(x_low, W, wrap); x_high = wrap_texcoord(x_high, W, wrap); y_low = wrap_texcoord(y_low, H, wrap); y_high = wrap_texcoord(y_high, H, wrap); for (int c = 0; c < C; c++) { color[c] = (1 - w_x) * (1 - w_y) * texture[y_low * W * C + x_low * C + c] + w_x * (1 - w_y) * texture[y_low * W * C + x_high * C + c] + (1 - w_x) * w_y * texture[y_high * W * C + x_low * C + c] + w_x * w_y * texture[y_high * W * C + x_high * C + c]; color[c] /= 255.0f; } } } static void sample_texture_mipmap( const uint8_t* texture, const int& H, const int& W, const int& C, const std::vector>& mipmaps, const float& u, const float& v, const float& mip_length, const float& mipLevelOffset, const int& filter, const int& wrap, float* color ) { if (filter == 0) { // NEAREST sample_texture(texture, H, W, C, u, v, filter, wrap, color); } else { // LINEAR float mip_level = std::log2(mip_length * H) + mipLevelOffset; if (!std::isfinite(mip_level) || mip_level <= 0 || mipmaps.empty()) { sample_texture(texture, H, W, C, u, v, filter, wrap, color); } else if (mip_level >= mipmaps.size()) { sample_texture(mipmaps[mipmaps.size() - 1].data(), H >> mipmaps.size(), W >> mipmaps.size(), C, u, v, filter, wrap, color); } else { int lower_mip_level = std::floor(mip_level); int upper_mip_level = lower_mip_level + 1; float mip_frac = mip_level - lower_mip_level; const uint8_t* lower_mip_ptr = lower_mip_level == 0 ? texture : mipmaps[lower_mip_level - 1].data(); const uint8_t* upper_mip_ptr = mipmaps[upper_mip_level - 1].data(); int lower_mip_H = H >> lower_mip_level; int lower_mip_W = W >> lower_mip_level; int upper_mip_H = H >> upper_mip_level; int upper_mip_W = W >> upper_mip_level; std::vector lower_mip_sample(C); std::vector upper_mip_sample(C); sample_texture(lower_mip_ptr, lower_mip_H, lower_mip_W, C, u, v, filter, wrap, lower_mip_sample.data()); sample_texture(upper_mip_ptr, upper_mip_H, upper_mip_W, C, u, v, filter, wrap, upper_mip_sample.data()); for (int c = 0; c < C; c++) { color[c] = (1 - mip_frac) * lower_mip_sample[c] + mip_frac * upper_mip_sample[c]; } } } } static std::tuple, std::vector, std::vector, std::vector, std::vector, std::vector, std::vector> voxelize_trimesh_pbr_impl( const float* voxel_size, const int* grid_range, const int N_tri, const float* vertices, const float* normals, const float* uvs, const int* materialIds, const std::vector baseColorFactor, const std::vector baseColorTexture, const std::vector H_bcTex, const std::vector W_bcTex, const std::vector baseColorTextureFilter, const std::vector baseColorTextureWrap, const std::vector metallicFactor, const std::vector metallicTexture, const std::vector H_mtlTex, const std::vector W_mtlTex, const std::vector metallicTextureFilter, const std::vector metallicTextureWrap, const std::vector roughnessFactor, const std::vector roughnessTexture, const std::vector H_rghTex, const std::vector W_rghTex, const std::vector roughnessTextureFilter, const std::vector roughnessTextureWrap, const std::vector emissiveFactor, const std::vector emissiveTexture, const std::vector H_emTex, const std::vector W_emTex, const std::vector emissiveTextureFilter, const std::vector emissiveTextureWrap, const std::vector alphaMode, const std::vector alphaCutoff, const std::vector alphaFactor, const std::vector alphaTexture, const std::vector H_aTex, const std::vector W_aTex, const std::vector alphaTextureFilter, const std::vector alphaTextureWrap, const std::vector normalTexture, const std::vector H_nTex, const std::vector W_nTex, const std::vector normalTextureFilter, const std::vector normalTextureWrap, const float mipLevelOffset, const bool timing ) { clock_t start, end; // Common variables used in the voxelization process Eigen::Vector3f delta_p(voxel_size[0], voxel_size[1], voxel_size[2]); Eigen::Vector3i grid_min(grid_range[0], grid_range[1], grid_range[2]); Eigen::Vector3i grid_max(grid_range[3], grid_range[4], grid_range[5]); // Construct Mipmaps start = clock(); std::vector>> baseColorMipmaps(baseColorTexture.size()); std::vector>> metallicMipmaps(metallicTexture.size()); std::vector>> roughnessMipmaps(roughnessTexture.size()); std::vector>> emissiveMipmaps(emissiveTexture.size()); std::vector>> alphaMipmaps(alphaTexture.size()); std::vector>> normalMipmaps(normalTexture.size()); for (size_t i = 0; i < baseColorTexture.size(); i++) { if (baseColorTexture[i] != nullptr && baseColorTextureFilter[i] != 0) { baseColorMipmaps[i] = build_mipmaps(baseColorTexture[i], H_bcTex[i], W_bcTex[i], 3); } } for (size_t i = 0; i < metallicTexture.size(); i++) { if (metallicTexture[i] != nullptr && metallicTextureFilter[i] != 0) { metallicMipmaps[i] = build_mipmaps(metallicTexture[i], H_mtlTex[i], W_mtlTex[i], 1); } } for (size_t i = 0; i < roughnessTexture.size(); i++) { if (roughnessTexture[i] != nullptr && roughnessTextureFilter[i] != 0) { roughnessMipmaps[i] = build_mipmaps(roughnessTexture[i], H_rghTex[i], W_rghTex[i], 1); } } for (size_t i = 0; i < emissiveTexture.size(); i++) { if (emissiveTexture[i] != nullptr && emissiveTextureFilter[i] != 0) { emissiveMipmaps[i] = build_mipmaps(emissiveTexture[i], H_emTex[i], W_emTex[i], 3); } } for (size_t i = 0; i < alphaTexture.size(); i++) { if (alphaTexture[i] != nullptr && alphaTextureFilter[i] != 0) { alphaMipmaps[i] = build_mipmaps(alphaTexture[i], H_aTex[i], W_aTex[i], 1); } } for (size_t i = 0; i < normalTexture.size(); i++) { if (normalTexture[i] != nullptr && normalTextureFilter[i] != 0) { normalMipmaps[i] = build_mipmaps(normalTexture[i], H_nTex[i], W_nTex[i], 3); } } end = clock(); if (timing) std::cout << "Mipmaps construction took " << double(end - start) / CLOCKS_PER_SEC << " seconds." << std::endl; // Buffers std::unordered_map hash_table; std::vector coords; std::vector buf_weights; std::vector buf_baseColors; std::vector buf_metallics; std::vector buf_roughnesses; std::vector buf_emissives; std::vector buf_alphas; std::vector buf_normals; // Enumerate all triangles start = clock(); for (size_t tid = 0; tid < N_tri; tid++) { // COMPUTE COMMON TRIANGLE PROPERTIES // Move vertices to origin using bbox size_t ptr = tid * 9; Eigen::Vector3f v0(vertices[ptr], vertices[ptr + 1], vertices[ptr + 2]); Eigen::Vector3f v1(vertices[ptr + 3], vertices[ptr + 4], vertices[ptr + 5]); Eigen::Vector3f v2(vertices[ptr + 6], vertices[ptr + 7], vertices[ptr + 8]); // Normals Eigen::Vector3f n0(normals[ptr], normals[ptr + 1], normals[ptr + 2]); Eigen::Vector3f n1(normals[ptr + 3], normals[ptr + 4], normals[ptr + 5]); Eigen::Vector3f n2(normals[ptr + 6], normals[ptr + 7], normals[ptr + 8]); // UV vectors ptr = tid * 6; Eigen::Vector2f uv0(uvs[ptr], uvs[ptr + 1]); Eigen::Vector2f uv1(uvs[ptr + 2], uvs[ptr + 3]); Eigen::Vector2f uv2(uvs[ptr + 4], uvs[ptr + 5]); // TBN auto tbn = compute_TBN(v0, v1, v2, uv0, uv1, uv2); Eigen::Vector3f t = std::get<0>(tbn); Eigen::Vector3f b = std::get<1>(tbn); Eigen::Vector3f n = std::get<2>(tbn); Eigen::Vector2f v_mip_length = std::get<3>(tbn); float mip_length = delta_p.maxCoeff() / std::sqrt(v_mip_length.x() * v_mip_length.y()); // Material ID int mid = materialIds[tid]; // Find intersected voxel for each triangle std::unordered_set intersected_voxels; // Scan-line algorithm to find intersections with the voxel grid from three directions /* t0 | \ | t1 | / t2 */ auto scan_line_fill = [&] (const int ax2) { int ax0 = (ax2 + 1) % 3; int ax1 = (ax2 + 2) % 3; // Canonical question std::array t = { Eigen::Vector3d(v0[ax0], v0[ax1], v0[ax2]), Eigen::Vector3d(v1[ax0], v1[ax1], v1[ax2]), Eigen::Vector3d(v2[ax0], v2[ax1], v2[ax2]) }; std::sort(t.begin(), t.end(), [](const Eigen::Vector3d& a, const Eigen::Vector3d& b) { return a.y() < b.y(); }); // Scan-line algorithm int start = std::clamp(int(t[0].y() / voxel_size[ax1]), grid_min[ax1], grid_max[ax1] - 1); int mid = std::clamp(int(t[1].y() / voxel_size[ax1]), grid_min[ax1], grid_max[ax1] - 1); int end = std::clamp(int(t[2].y() / voxel_size[ax1]), grid_min[ax1], grid_max[ax1] - 1); auto scan_line_half = [&] (const int row_start, const int row_end, const Eigen::Vector3d t0, const Eigen::Vector3d t1, const Eigen::Vector3d t2) { /* t0 | \ t3-t4 | \ t1---t2 */ for (int y_idx = row_start; y_idx < row_end; ++y_idx) { double y = (y_idx + 1) * voxel_size[ax1]; Eigen::Vector2d t3 = lerp(t0.y(), t1.y(), y, Eigen::Vector2d(t0.x(), t0.z()), Eigen::Vector2d(t1.x(), t1.z())); Eigen::Vector2d t4 = lerp(t0.y(), t2.y(), y, Eigen::Vector2d(t0.x(), t0.z()), Eigen::Vector2d(t2.x(), t2.z())); if (t3.x() > t4.x()) std::swap(t3, t4); int line_start = std::clamp(int(t3.x() / voxel_size[ax0]), grid_min[ax0], grid_max[ax0] - 1); int line_end = std::clamp(int(t4.x() / voxel_size[ax0]), grid_min[ax0], grid_max[ax0] - 1); for (int x_idx = line_start; x_idx < line_end; ++x_idx) { double x = (x_idx + 1) * voxel_size[ax0]; double z = lerp(t3.x(), t4.x(), x, t3.y(), t4.y()); int z_idx = int(z / voxel_size[ax2]); if (z_idx >= grid_min[ax2] && z_idx < grid_max[ax2]) { // For 4-connected voxels for (int dx = 0; dx < 2; ++dx) { for (int dy = 0; dy < 2; ++dy) { VoxelCoord coord; coord[ax0] = x_idx + dx; coord[ax1] = y_idx + dy; coord[ax2] = z_idx; intersected_voxels.insert(coord); } } } } } }; scan_line_half(start, mid, t[0], t[1], t[2]); scan_line_half(mid, end, t[2], t[1], t[0]); }; scan_line_fill(0); scan_line_fill(1); scan_line_fill(2); // For all intersected voxels, ample texture and write to voxel grid for (auto voxel : intersected_voxels) { int x = voxel.x; int y = voxel.y; int z = voxel.z; // Compute barycentric coordinates and weight Eigen::Vector4f barycentric = project_onto_triangle( Eigen::Vector3f((x + 0.5f) * delta_p.x(), (y + 0.5f) * delta_p.y(), (z + 0.5f) * delta_p.z()), v0, v1, v2, n ); Eigen::Vector2f uv = { barycentric.x() * uv0.x() + barycentric.y() * uv1.x() + barycentric.z() * uv2.x(), barycentric.x() * uv0.y() + barycentric.y() * uv1.y() + barycentric.z() * uv2.y() }; Eigen::Vector3f int_n = { barycentric.x() * n0.x() + barycentric.y() * n1.x() + barycentric.z() * n2.x(), barycentric.x() * n0.y() + barycentric.y() * n1.y() + barycentric.z() * n2.y(), barycentric.x() * n0.z() + barycentric.y() * n1.z() + barycentric.z() * n2.z() }; float weight = 1 - barycentric.w(); /// base color float baseColor[3] = {1, 1, 1}; if (baseColorTexture[mid]) { sample_texture_mipmap( baseColorTexture[mid], H_bcTex[mid], W_bcTex[mid], 3, baseColorMipmaps[mid], uv.x(), uv.y(), mip_length, mipLevelOffset, baseColorTextureFilter[mid], baseColorTextureWrap[mid], baseColor ); } baseColor[0] *= baseColorFactor[mid][0]; baseColor[1] *= baseColorFactor[mid][1]; baseColor[2] *= baseColorFactor[mid][2]; /// metallic float metallic = 1.0f; if (metallicTexture[mid]) { sample_texture_mipmap( metallicTexture[mid], H_mtlTex[mid], W_mtlTex[mid], 1, metallicMipmaps[mid], uv.x(), uv.y(), mip_length, mipLevelOffset, metallicTextureFilter[mid], metallicTextureWrap[mid], &metallic ); } metallic *= metallicFactor[mid]; /// roughness float roughness = 1.0f; if (roughnessTexture[mid]) { sample_texture_mipmap( roughnessTexture[mid], H_rghTex[mid], W_rghTex[mid], 1, roughnessMipmaps[mid], uv.x(), uv.y(), mip_length, mipLevelOffset, roughnessTextureFilter[mid], roughnessTextureWrap[mid], &roughness ); } roughness *= roughnessFactor[mid]; /// emissive float emissive[3] = {1, 1, 1}; if (emissiveTexture[mid]) { sample_texture_mipmap( emissiveTexture[mid], H_emTex[mid], W_emTex[mid], 3, roughnessMipmaps[mid], uv.x(), uv.y(), mip_length, mipLevelOffset, emissiveTextureFilter[mid], emissiveTextureWrap[mid], emissive ); } emissive[0] *= emissiveFactor[mid][0]; emissive[1] *= emissiveFactor[mid][1]; emissive[2] *= emissiveFactor[mid][2]; /// alpha float alpha = 1.0f; if (alphaMode[mid] != 0) { if (alphaTexture[mid]) { sample_texture_mipmap( alphaTexture[mid], H_aTex[mid], W_aTex[mid], 1, alphaMipmaps[mid], uv.x(), uv.y(), mip_length, mipLevelOffset, alphaTextureFilter[mid], alphaTextureWrap[mid], &alpha ); } alpha *= alphaFactor[mid]; if (alphaMode[mid] == 1) { // MASK alpha = alpha < alphaCutoff[mid] ? 0.0f : 1.0f; } } /// normal float normal[3] = {int_n.x(), int_n.y(), int_n.z()}; if (normalTexture[mid]) { sample_texture_mipmap( normalTexture[mid], H_nTex[mid], W_nTex[mid], 3, normalMipmaps[mid], uv.x(), uv.y(), mip_length, mipLevelOffset, normalTextureFilter[mid], normalTextureWrap[mid], normal ); normal[0] = normal[0] * 2 - 1; normal[1] = normal[1] * 2 - 1; normal[2] = normal[2] * 2 - 1; Eigen::Vector3f _n = (normal[0] * t + normal[1] * b + normal[2] * int_n).normalized(); normal[0] = _n.x(); normal[1] = _n.y(); normal[2] = _n.z(); } // Write to voxel grid auto coord = VoxelCoord{x-grid_min.x(), y-grid_min.y(), z-grid_min.z()}; auto kv = hash_table.find(coord); if (kv == hash_table.end()) { hash_table[coord] = coords.size(); coords.push_back({coord.x, coord.y, coord.z}); buf_weights.push_back(weight); buf_baseColors.push_back(Eigen::Vector3f(baseColor[0], baseColor[1], baseColor[2]) * weight); buf_metallics.push_back(metallic * weight); buf_roughnesses.push_back(roughness * weight); buf_emissives.push_back(Eigen::Vector3f(emissive[0], emissive[1], emissive[2]) * weight); buf_alphas.push_back(alpha * weight); buf_normals.push_back(Eigen::Vector3f(normal[0], normal[1], normal[2]) * weight); } else { auto i = kv->second; buf_weights[i] += weight; buf_baseColors[i] += Eigen::Vector3f(baseColor[0], baseColor[1], baseColor[2]) * weight; buf_metallics[i] += metallic * weight; buf_roughnesses[i] += roughness * weight; buf_emissives[i] += Eigen::Vector3f(emissive[0], emissive[1], emissive[2]) * weight; buf_alphas[i] += alpha * weight; buf_normals[i] += Eigen::Vector3f(normal[0], normal[1], normal[2]) * weight; } } } end = clock(); if (timing) std::cout << "Voxelization took " << double(end - start) / CLOCKS_PER_SEC << " seconds." << std::endl; // Normalize buffers start = clock(); std::vector out_coord(coords.size() * 3); std::vector out_baseColor(coords.size() * 3); std::vector out_metallic(coords.size()); std::vector out_roughness(coords.size()); std::vector out_emissive(coords.size() * 3); std::vector out_alpha(coords.size()); std::vector out_normal(coords.size() * 3); for (int i = 0; i < coords.size(); i++) { out_coord[i * 3 + 0] = coords[i].x; out_coord[i * 3 + 1] = coords[i].y; out_coord[i * 3 + 2] = coords[i].z; out_baseColor[i * 3 + 0] = buf_baseColors[i].x() / buf_weights[i]; out_baseColor[i * 3 + 1] = buf_baseColors[i].y() / buf_weights[i]; out_baseColor[i * 3 + 2] = buf_baseColors[i].z() / buf_weights[i]; out_metallic[i] = buf_metallics[i] / buf_weights[i]; out_roughness[i] = buf_roughnesses[i] / buf_weights[i]; out_emissive[i * 3 + 0] = buf_emissives[i].x() / buf_weights[i]; out_emissive[i * 3 + 1] = buf_emissives[i].y() / buf_weights[i]; out_emissive[i * 3 + 2] = buf_emissives[i].z() / buf_weights[i]; out_alpha[i] = buf_alphas[i] / buf_weights[i]; out_normal[i * 3 + 0] = buf_normals[i].x() / buf_weights[i]; out_normal[i * 3 + 1] = buf_normals[i].y() / buf_weights[i]; out_normal[i * 3 + 2] = buf_normals[i].z() / buf_weights[i]; } end = clock(); if (timing) std::cout << "Normalization took " << double(end - start) / CLOCKS_PER_SEC << " seconds." << std::endl; return std::make_tuple( std::move(out_coord), std::move(out_baseColor), std::move(out_metallic), std::move(out_roughness), std::move(out_emissive), std::move(out_alpha), std::move(out_normal) ); } std::tuple textured_mesh_to_volumetric_attr_cpu( const torch::Tensor& voxel_size, const torch::Tensor& grid_range, const torch::Tensor& vertices, const torch::Tensor& normals, const torch::Tensor& uvs, const torch::Tensor& materialIds, const std::vector& baseColorFactor, const std::vector& baseColorTexture, const std::vector& baseColorTextureFilter, const std::vector& baseColorTextureWrap, const std::vector& metallicFactor, const std::vector& metallicTexture, const std::vector& metallicTextureFilter, const std::vector& metallicTextureWrap, const std::vector& roughnessFactor, const std::vector& roughnessTexture, const std::vector& roughnessTextureFilter, const std::vector& roughnessTextureWrap, const std::vector& emissiveFactor, const std::vector& emissiveTexture, const std::vector& emissiveTextureFilter, const std::vector& emissiveTextureWrap, const std::vector& alphaMode, const std::vector& alphaCutoff, const std::vector& alphaFactor, const std::vector& alphaTexture, const std::vector& alphaTextureFilter, const std::vector& alphaTextureWrap, const std::vector& normalTexture, const std::vector& normalTextureFilter, const std::vector& normalTextureWrap, const float mipLevelOffset, const bool timing ) { auto N_mat = baseColorFactor.size(); int N_tri = vertices.size(0); // Get the size of the input tensors std::vector baseColorFactor_ptrs(N_mat); std::vector baseColorTexture_ptrs(N_mat); std::vector H_bcTex(N_mat), W_bcTex(N_mat); std::vector metallicFactor_vec(N_mat); std::vector metallicTexture_ptrs(N_mat); std::vector H_mtlTex(N_mat), W_mtlTex(N_mat); std::vector roughnessFactor_vec(N_mat); std::vector roughnessTexture_ptrs(N_mat); std::vector H_rghTex(N_mat), W_rghTex(N_mat); std::vector emissiveFactor_ptrs(N_mat); std::vector emissiveTexture_ptrs(N_mat); std::vector H_emTex(N_mat), W_emTex(N_mat); std::vector alphaMode_vec(N_mat); std::vector alphaCutoff_vec(N_mat); std::vector alphaFactor_vec(N_mat); std::vector alphaTexture_ptrs(N_mat); std::vector H_aTex(N_mat), W_aTex(N_mat); std::vector normalTexture_ptrs(N_mat); std::vector H_nTex(N_mat), W_nTex(N_mat); for (int i = 0; i < N_mat; ++i) { baseColorFactor_ptrs[i] = baseColorFactor[i].contiguous().data_ptr(); if (baseColorTexture[i].numel() > 0) { baseColorTexture_ptrs[i] = baseColorTexture[i].contiguous().data_ptr(); H_bcTex[i] = baseColorTexture[i].size(0); W_bcTex[i] = baseColorTexture[i].size(1); } else { baseColorTexture_ptrs[i] = nullptr; H_bcTex[i] = 0; W_bcTex[i] = 0; } metallicFactor_vec[i] = metallicFactor[i]; if (metallicTexture[i].numel() > 0) { metallicTexture_ptrs[i] = metallicTexture[i].contiguous().data_ptr(); H_mtlTex[i] = metallicTexture[i].size(0); W_mtlTex[i] = metallicTexture[i].size(1); } else { metallicTexture_ptrs[i] = nullptr; H_mtlTex[i] = 0; W_mtlTex[i] = 0; } roughnessFactor_vec[i] = roughnessFactor[i]; if (roughnessTexture[i].numel() > 0) { roughnessTexture_ptrs[i] = roughnessTexture[i].contiguous().data_ptr(); H_rghTex[i] = roughnessTexture[i].size(0); W_rghTex[i] = roughnessTexture[i].size(1); } else { roughnessTexture_ptrs[i] = nullptr; H_rghTex[i] = 0; W_rghTex[i] = 0; } emissiveFactor_ptrs[i] = emissiveFactor[i].contiguous().data_ptr(); if (emissiveTexture[i].numel() > 0) { emissiveTexture_ptrs[i] = emissiveTexture[i].contiguous().data_ptr(); H_emTex[i] = emissiveTexture[i].size(0); W_emTex[i] = emissiveTexture[i].size(1); } else { emissiveTexture_ptrs[i] = nullptr; H_emTex[i] = 0; W_emTex[i] = 0; } alphaMode_vec[i] = alphaMode[i]; alphaCutoff_vec[i] = alphaCutoff[i]; alphaFactor_vec[i] = alphaFactor[i]; if (alphaTexture[i].numel() > 0) { alphaTexture_ptrs[i] = alphaTexture[i].contiguous().data_ptr(); H_aTex[i] = alphaTexture[i].size(0); W_aTex[i] = alphaTexture[i].size(1); } else { alphaTexture_ptrs[i] = nullptr; H_aTex[i] = 0; W_aTex[i] = 0; } if (normalTexture[i].numel() > 0) { normalTexture_ptrs[i] = normalTexture[i].contiguous().data_ptr(); H_nTex[i] = normalTexture[i].size(0); W_nTex[i] = normalTexture[i].size(1); } else { normalTexture_ptrs[i] = nullptr; H_nTex[i] = 0; W_nTex[i] = 0; } } auto outputs = voxelize_trimesh_pbr_impl( voxel_size.contiguous().data_ptr(), grid_range.contiguous().data_ptr(), N_tri, vertices.contiguous().data_ptr(), normals.contiguous().data_ptr(), uvs.contiguous().data_ptr(), materialIds.contiguous().data_ptr(), baseColorFactor_ptrs, baseColorTexture_ptrs, H_bcTex, W_bcTex, baseColorTextureFilter, baseColorTextureWrap, metallicFactor_vec, metallicTexture_ptrs, H_mtlTex, W_mtlTex, metallicTextureFilter, metallicTextureWrap, roughnessFactor_vec, roughnessTexture_ptrs, H_rghTex, W_rghTex, roughnessTextureFilter, roughnessTextureWrap, emissiveFactor_ptrs, emissiveTexture_ptrs, H_emTex, W_emTex, emissiveTextureFilter, emissiveTextureWrap, alphaMode_vec, alphaCutoff_vec, alphaFactor_vec, alphaTexture_ptrs, H_aTex, W_aTex, alphaTextureFilter, alphaTextureWrap, normalTexture_ptrs, H_nTex, W_nTex, normalTextureFilter, normalTextureWrap, mipLevelOffset, timing ); std::vector coords_vec = std::get<0>(outputs); std::vector baseColors_vec = std::get<1>(outputs); std::vector metallics_vec = std::get<2>(outputs); std::vector roughnesses_vec = std::get<3>(outputs); std::vector emissives_vec = std::get<4>(outputs); std::vector alphas_vec = std::get<5>(outputs); std::vector normals_vec = std::get<6>(outputs); // Create output tensors auto out_coords = torch::from_blob(coords_vec.data(), {static_cast(coords_vec.size() / 3), 3}, torch::kInt32).clone(); auto out_baseColors = torch::from_blob(baseColors_vec.data(), {static_cast(baseColors_vec.size() / 3), 3}, torch::kFloat32).clone(); auto out_metallics = torch::from_blob(metallics_vec.data(), {static_cast(metallics_vec.size())}, torch::kFloat32).clone(); auto out_roughnesses = torch::from_blob(roughnesses_vec.data(), {static_cast(roughnesses_vec.size())}, torch::kFloat32).clone(); auto out_emissives = torch::from_blob(emissives_vec.data(), {static_cast(emissives_vec.size() / 3), 3}, torch::kFloat32).clone(); auto out_alphas = torch::from_blob(alphas_vec.data(), {static_cast(alphas_vec.size())}, torch::kFloat32).clone(); auto out_normals = torch::from_blob(normals_vec.data(), {static_cast(normals_vec.size() / 3), 3}, torch::kFloat32).clone(); return std::make_tuple( out_coords, out_baseColors, out_metallics, out_roughnesses, out_emissives, out_alphas, out_normals ); }