Previously we generally expected CustomData layers to have implicit sharing info, but we didn't require it. This PR clarifies that we do require layers with non-null data to have implicit sharing info. This generally makes code simpler because we don't have to have a separate code path for non-shared layers. For example, it makes the "totelem" arguments for layer freeing functions unnecessary, since shared data knows how to free itself. Those arguments are removed in this PR. Pull Request: https://projects.blender.org/blender/blender/pulls/134578
1185 lines
36 KiB
C++
1185 lines
36 KiB
C++
/* SPDX-FileCopyrightText: 2010-2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup collada
|
|
*/
|
|
|
|
#include <iostream>
|
|
|
|
#include "COLLADAFWMeshPrimitive.h"
|
|
#include "COLLADAFWMeshVertexData.h"
|
|
#include "COLLADAFWPolygons.h"
|
|
|
|
#include "BKE_attribute.hh"
|
|
#include "BKE_customdata.hh"
|
|
#include "BKE_global.hh"
|
|
#include "BKE_lib_id.hh"
|
|
#include "BKE_material.hh"
|
|
#include "BKE_mesh.hh"
|
|
#include "BKE_mesh_runtime.hh"
|
|
#include "BKE_object.hh"
|
|
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
#include "ArmatureImporter.h"
|
|
#include "MeshImporter.h"
|
|
#include "collada_utils.h"
|
|
|
|
using blender::float3;
|
|
using blender::MutableSpan;
|
|
|
|
/* get node name, or fall back to original id if not present (name is optional) */
|
|
template<class T> static std::string bc_get_dae_name(T *node)
|
|
{
|
|
return node->getName().empty() ? node->getOriginalId() : node->getName();
|
|
}
|
|
|
|
static const char *bc_primTypeToStr(COLLADAFW::MeshPrimitive::PrimitiveType type)
|
|
{
|
|
switch (type) {
|
|
case COLLADAFW::MeshPrimitive::LINES:
|
|
return "LINES";
|
|
case COLLADAFW::MeshPrimitive::LINE_STRIPS:
|
|
return "LINESTRIPS";
|
|
case COLLADAFW::MeshPrimitive::POLYGONS:
|
|
return "POLYGONS";
|
|
case COLLADAFW::MeshPrimitive::POLYLIST:
|
|
return "POLYLIST";
|
|
case COLLADAFW::MeshPrimitive::TRIANGLES:
|
|
return "TRIANGLES";
|
|
case COLLADAFW::MeshPrimitive::TRIANGLE_FANS:
|
|
return "TRIANGLE_FANS";
|
|
case COLLADAFW::MeshPrimitive::TRIANGLE_STRIPS:
|
|
return "TRIANGLE_STRIPS";
|
|
case COLLADAFW::MeshPrimitive::POINTS:
|
|
return "POINTS";
|
|
case COLLADAFW::MeshPrimitive::UNDEFINED_PRIMITIVE_TYPE:
|
|
return "UNDEFINED_PRIMITIVE_TYPE";
|
|
}
|
|
return "UNKNOWN";
|
|
}
|
|
|
|
static const char *bc_geomTypeToStr(COLLADAFW::Geometry::GeometryType type)
|
|
{
|
|
switch (type) {
|
|
case COLLADAFW::Geometry::GEO_TYPE_MESH:
|
|
return "MESH";
|
|
case COLLADAFW::Geometry::GEO_TYPE_SPLINE:
|
|
return "SPLINE";
|
|
case COLLADAFW::Geometry::GEO_TYPE_CONVEX_MESH:
|
|
return "CONVEX_MESH";
|
|
case COLLADAFW::Geometry::GEO_TYPE_UNKNOWN:
|
|
default:
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
UVDataWrapper::UVDataWrapper(COLLADAFW::MeshVertexData &vdata) : mVData(&vdata) {}
|
|
|
|
#ifdef COLLADA_DEBUG
|
|
void WVDataWrapper::print()
|
|
{
|
|
fprintf(stderr, "UVs:\n");
|
|
switch (mVData->getType()) {
|
|
case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: {
|
|
COLLADAFW::ArrayPrimitiveType<float> *values = mVData->getFloatValues();
|
|
if (values->getCount()) {
|
|
for (int i = 0; i < values->getCount(); i += 2) {
|
|
fprintf(stderr, "%.1f, %.1f\n", (*values)[i], (*values)[i + 1]);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: {
|
|
COLLADAFW::ArrayPrimitiveType<double> *values = mVData->getDoubleValues();
|
|
if (values->getCount()) {
|
|
for (int i = 0; i < values->getCount(); i += 2) {
|
|
fprintf(stderr, "%.1f, %.1f\n", float((*values)[i]), float((*values)[i + 1]));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
#endif
|
|
|
|
void UVDataWrapper::getUV(int uv_index, float *uv)
|
|
{
|
|
int stride = mVData->getStride(0);
|
|
if (stride == 0) {
|
|
stride = 2;
|
|
}
|
|
|
|
switch (mVData->getType()) {
|
|
case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: {
|
|
COLLADAFW::ArrayPrimitiveType<float> *values = mVData->getFloatValues();
|
|
if (values->empty()) {
|
|
return;
|
|
}
|
|
uv[0] = (*values)[uv_index * stride];
|
|
uv[1] = (*values)[uv_index * stride + 1];
|
|
break;
|
|
}
|
|
case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: {
|
|
COLLADAFW::ArrayPrimitiveType<double> *values = mVData->getDoubleValues();
|
|
if (values->empty()) {
|
|
return;
|
|
}
|
|
uv[0] = float((*values)[uv_index * stride]);
|
|
uv[1] = float((*values)[uv_index * stride + 1]);
|
|
break;
|
|
}
|
|
case COLLADAFW::MeshVertexData::DATA_TYPE_UNKNOWN:
|
|
default:
|
|
fprintf(stderr, "MeshImporter.getUV(): unknown data type\n");
|
|
}
|
|
}
|
|
|
|
VCOLDataWrapper::VCOLDataWrapper(COLLADAFW::MeshVertexData &vdata) : mVData(&vdata) {}
|
|
|
|
template<typename T>
|
|
static void colladaAddColor(T values, MLoopCol *mloopcol, int v_index, int stride)
|
|
{
|
|
if (values->empty() || values->getCount() < (v_index + 1) * stride) {
|
|
fprintf(stderr,
|
|
"VCOLDataWrapper.getvcol(): Out of Bounds error: index %d points outside value "
|
|
"list of length %zd (with stride=%d) \n",
|
|
v_index,
|
|
values->getCount(),
|
|
stride);
|
|
return;
|
|
}
|
|
|
|
mloopcol->r = unit_float_to_uchar_clamp((*values)[v_index * stride]);
|
|
mloopcol->g = unit_float_to_uchar_clamp((*values)[v_index * stride + 1]);
|
|
mloopcol->b = unit_float_to_uchar_clamp((*values)[v_index * stride + 2]);
|
|
if (stride == 4) {
|
|
mloopcol->a = unit_float_to_uchar_clamp((*values)[v_index * stride + 3]);
|
|
}
|
|
}
|
|
|
|
void VCOLDataWrapper::get_vcol(int v_index, MLoopCol *mloopcol)
|
|
{
|
|
int stride = mVData->getStride(0);
|
|
if (stride == 0) {
|
|
stride = 3;
|
|
}
|
|
|
|
switch (mVData->getType()) {
|
|
case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: {
|
|
COLLADAFW::ArrayPrimitiveType<float> *values = mVData->getFloatValues();
|
|
colladaAddColor<COLLADAFW::ArrayPrimitiveType<float> *>(values, mloopcol, v_index, stride);
|
|
break;
|
|
}
|
|
case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: {
|
|
COLLADAFW::ArrayPrimitiveType<double> *values = mVData->getDoubleValues();
|
|
colladaAddColor<COLLADAFW::ArrayPrimitiveType<double> *>(values, mloopcol, v_index, stride);
|
|
break;
|
|
}
|
|
default:
|
|
fprintf(stderr, "VCOLDataWrapper.getvcol(): unknown data type\n");
|
|
}
|
|
}
|
|
|
|
MeshImporter::MeshImporter(UnitConverter *unitconv,
|
|
bool use_custom_normals,
|
|
ArmatureImporter *arm,
|
|
Main *bmain,
|
|
Scene *sce,
|
|
ViewLayer *view_layer)
|
|
: unitconverter(unitconv),
|
|
use_custom_normals(use_custom_normals),
|
|
m_bmain(bmain),
|
|
scene(sce),
|
|
view_layer(view_layer),
|
|
armature_importer(arm)
|
|
{
|
|
/* pass */
|
|
}
|
|
|
|
bool MeshImporter::set_poly_indices(int *face_verts,
|
|
int loop_index,
|
|
const uint *indices,
|
|
int loop_count)
|
|
{
|
|
bool broken_loop = false;
|
|
for (int index = 0; index < loop_count; index++) {
|
|
|
|
/* Test if loop defines a hole */
|
|
if (!broken_loop) {
|
|
for (int i = 0; i < index; i++) {
|
|
if (indices[i] == indices[index]) {
|
|
/* duplicate index -> not good */
|
|
broken_loop = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
*face_verts = indices[index];
|
|
face_verts++;
|
|
}
|
|
return broken_loop;
|
|
}
|
|
|
|
void MeshImporter::set_vcol(MLoopCol *mloopcol,
|
|
VCOLDataWrapper &vob,
|
|
int loop_index,
|
|
COLLADAFW::IndexList &index_list,
|
|
int count)
|
|
{
|
|
int index;
|
|
for (index = 0; index < count; index++, mloopcol++) {
|
|
int v_index = index_list.getIndex(index + loop_index);
|
|
vob.get_vcol(v_index, mloopcol);
|
|
}
|
|
}
|
|
|
|
void MeshImporter::set_face_uv(blender::float2 *mloopuv,
|
|
UVDataWrapper &uvs,
|
|
int start_index,
|
|
COLLADAFW::IndexList &index_list,
|
|
int count)
|
|
{
|
|
/* per face vertex indices, this means for quad we have 4 indices, not 8 */
|
|
COLLADAFW::UIntValuesArray &indices = index_list.getIndices();
|
|
|
|
for (int index = 0; index < count; index++) {
|
|
int uv_index = indices[index + start_index];
|
|
uvs.getUV(uv_index, mloopuv[index]);
|
|
}
|
|
}
|
|
|
|
#ifdef COLLADA_DEBUG
|
|
void MeshImporter::print_index_list(COLLADAFW::IndexList &index_list)
|
|
{
|
|
fprintf(stderr, "Index list for \"%s\":\n", index_list.getName().c_str());
|
|
for (int i = 0; i < index_list.getIndicesCount(); i += 2) {
|
|
fprintf(stderr, "%u, %u\n", index_list.getIndex(i), index_list.getIndex(i + 1));
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
#endif
|
|
|
|
bool MeshImporter::is_nice_mesh(COLLADAFW::Mesh *mesh)
|
|
{
|
|
COLLADAFW::MeshPrimitiveArray &prim_arr = mesh->getMeshPrimitives();
|
|
|
|
const std::string &name = bc_get_dae_name(mesh);
|
|
|
|
for (uint i = 0; i < prim_arr.getCount(); i++) {
|
|
|
|
COLLADAFW::MeshPrimitive *mp = prim_arr[i];
|
|
COLLADAFW::MeshPrimitive::PrimitiveType type = mp->getPrimitiveType();
|
|
|
|
const char *type_str = bc_primTypeToStr(type);
|
|
|
|
/* OpenCollada passes POLYGONS type for `<polylist>`. */
|
|
if (ELEM(type, COLLADAFW::MeshPrimitive::POLYLIST, COLLADAFW::MeshPrimitive::POLYGONS)) {
|
|
|
|
COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp;
|
|
COLLADAFW::Polygons::VertexCountArray &vca = mpvc->getGroupedVerticesVertexCountArray();
|
|
|
|
int hole_count = 0;
|
|
int nonface_count = 0;
|
|
|
|
for (uint j = 0; j < vca.getCount(); j++) {
|
|
int count = vca[j];
|
|
if (abs(count) < 3) {
|
|
nonface_count++;
|
|
}
|
|
|
|
if (count < 0) {
|
|
hole_count++;
|
|
}
|
|
}
|
|
|
|
if (hole_count > 0) {
|
|
fprintf(stderr,
|
|
"WARNING: Primitive %s in %s: %d holes not imported (unsupported)\n",
|
|
type_str,
|
|
name.c_str(),
|
|
hole_count);
|
|
}
|
|
|
|
if (nonface_count > 0) {
|
|
fprintf(stderr,
|
|
"WARNING: Primitive %s in %s: %d faces with vertex count < 3 (rejected)\n",
|
|
type_str,
|
|
name.c_str(),
|
|
nonface_count);
|
|
}
|
|
}
|
|
|
|
else if (type == COLLADAFW::MeshPrimitive::LINES) {
|
|
/* TODO: Add Checker for line syntax here */
|
|
}
|
|
|
|
else if (!ELEM(type,
|
|
COLLADAFW::MeshPrimitive::TRIANGLES,
|
|
COLLADAFW::MeshPrimitive::TRIANGLE_FANS))
|
|
{
|
|
fprintf(stderr, "ERROR: Primitive type %s is not supported.\n", type_str);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void MeshImporter::read_vertices(COLLADAFW::Mesh *mesh, Mesh *blender_mesh)
|
|
{
|
|
/* vertices */
|
|
COLLADAFW::MeshVertexData &pos = mesh->getPositions();
|
|
if (pos.empty()) {
|
|
return;
|
|
}
|
|
|
|
int stride = pos.getStride(0);
|
|
if (stride == 0) {
|
|
stride = 3;
|
|
}
|
|
|
|
blender_mesh->verts_num = pos.getFloatValues()->getCount() / stride;
|
|
CustomData_add_layer_named(
|
|
&blender_mesh->vert_data, CD_PROP_FLOAT3, CD_CONSTRUCT, blender_mesh->verts_num, "position");
|
|
MutableSpan<float3> positions = blender_mesh->vert_positions_for_write();
|
|
for (const int i : positions.index_range()) {
|
|
get_vector(positions[i], pos, i, stride);
|
|
}
|
|
}
|
|
|
|
bool MeshImporter::primitive_has_useable_normals(COLLADAFW::MeshPrimitive *mp)
|
|
{
|
|
|
|
bool has_useable_normals = false;
|
|
|
|
int normals_count = mp->getNormalIndices().getCount();
|
|
if (normals_count > 0) {
|
|
int index_count = mp->getPositionIndices().getCount();
|
|
if (index_count == normals_count) {
|
|
has_useable_normals = true;
|
|
}
|
|
else {
|
|
fprintf(stderr,
|
|
"Warning: Number of normals %d is different from the number of vertices %d, "
|
|
"skipping normals\n",
|
|
normals_count,
|
|
index_count);
|
|
}
|
|
}
|
|
|
|
return has_useable_normals;
|
|
}
|
|
|
|
bool MeshImporter::primitive_has_faces(COLLADAFW::MeshPrimitive *mp)
|
|
{
|
|
|
|
bool has_faces = false;
|
|
int type = mp->getPrimitiveType();
|
|
switch (type) {
|
|
case COLLADAFW::MeshPrimitive::TRIANGLES:
|
|
case COLLADAFW::MeshPrimitive::TRIANGLE_FANS:
|
|
case COLLADAFW::MeshPrimitive::POLYLIST:
|
|
case COLLADAFW::MeshPrimitive::POLYGONS: {
|
|
has_faces = true;
|
|
break;
|
|
}
|
|
default: {
|
|
has_faces = false;
|
|
break;
|
|
}
|
|
}
|
|
return has_faces;
|
|
}
|
|
|
|
static std::string extract_vcolname(const COLLADAFW::String &collada_id)
|
|
{
|
|
std::string colname = collada_id;
|
|
int spos = colname.find("-mesh-colors-");
|
|
if (spos != std::string::npos) {
|
|
colname = colname.substr(spos + 13);
|
|
}
|
|
return colname;
|
|
}
|
|
|
|
void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *mesh)
|
|
{
|
|
COLLADAFW::MeshPrimitiveArray &prim_arr = collada_mesh->getMeshPrimitives();
|
|
int total_poly_count = 0;
|
|
int total_loop_count = 0;
|
|
|
|
/* collect edge_count and face_count from all parts */
|
|
for (int i = 0; i < prim_arr.getCount(); i++) {
|
|
COLLADAFW::MeshPrimitive *mp = prim_arr[i];
|
|
int type = mp->getPrimitiveType();
|
|
switch (type) {
|
|
case COLLADAFW::MeshPrimitive::TRIANGLES:
|
|
case COLLADAFW::MeshPrimitive::TRIANGLE_FANS:
|
|
case COLLADAFW::MeshPrimitive::POLYLIST:
|
|
case COLLADAFW::MeshPrimitive::POLYGONS: {
|
|
COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp;
|
|
size_t prim_poly_count = mpvc->getFaceCount();
|
|
|
|
size_t prim_loop_count = 0;
|
|
for (int index = 0; index < prim_poly_count; index++) {
|
|
int vcount = get_vertex_count(mpvc, index);
|
|
if (vcount > 0) {
|
|
prim_loop_count += vcount;
|
|
total_poly_count++;
|
|
}
|
|
else {
|
|
/* TODO: this is a hole and not another polygon! */
|
|
}
|
|
}
|
|
|
|
total_loop_count += prim_loop_count;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Add the data containers */
|
|
if (total_poly_count > 0) {
|
|
mesh->faces_num = total_poly_count;
|
|
mesh->corners_num = total_loop_count;
|
|
BKE_mesh_face_offsets_ensure_alloc(mesh);
|
|
CustomData_add_layer_named(
|
|
&mesh->corner_data, CD_PROP_INT32, CD_SET_DEFAULT, mesh->corners_num, ".corner_vert");
|
|
|
|
uint totuvset = collada_mesh->getUVCoords().getInputInfosArray().getCount();
|
|
for (int i = 0; i < totuvset; i++) {
|
|
if (collada_mesh->getUVCoords().getLength(i) == 0) {
|
|
totuvset = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (totuvset > 0) {
|
|
for (int i = 0; i < totuvset; i++) {
|
|
COLLADAFW::MeshVertexData::InputInfos *info =
|
|
collada_mesh->getUVCoords().getInputInfosArray()[i];
|
|
COLLADAFW::String &uvname = info->mName;
|
|
/* Allocate space for UV_data */
|
|
CustomData_add_layer_named(
|
|
&mesh->corner_data, CD_PROP_FLOAT2, CD_SET_DEFAULT, mesh->corners_num, uvname);
|
|
}
|
|
/* activate the first uv map */
|
|
CustomData_set_layer_active(&mesh->corner_data, CD_PROP_FLOAT2, 0);
|
|
}
|
|
|
|
int totcolset = collada_mesh->getColors().getInputInfosArray().getCount();
|
|
if (totcolset > 0) {
|
|
for (int i = 0; i < totcolset; i++) {
|
|
COLLADAFW::MeshVertexData::InputInfos *info =
|
|
collada_mesh->getColors().getInputInfosArray()[i];
|
|
COLLADAFW::String colname = extract_vcolname(info->mName);
|
|
CustomData_add_layer_named(
|
|
&mesh->corner_data, CD_PROP_BYTE_COLOR, CD_SET_DEFAULT, mesh->corners_num, colname);
|
|
}
|
|
BKE_id_attributes_active_color_set(
|
|
&mesh->id, CustomData_get_layer_name(&mesh->corner_data, CD_PROP_BYTE_COLOR, 0));
|
|
BKE_id_attributes_default_color_set(
|
|
&mesh->id, CustomData_get_layer_name(&mesh->corner_data, CD_PROP_BYTE_COLOR, 0));
|
|
}
|
|
}
|
|
}
|
|
|
|
uint MeshImporter::get_vertex_count(COLLADAFW::Polygons *mp, int index)
|
|
{
|
|
int type = mp->getPrimitiveType();
|
|
int result;
|
|
switch (type) {
|
|
case COLLADAFW::MeshPrimitive::TRIANGLES:
|
|
case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: {
|
|
result = 3;
|
|
break;
|
|
}
|
|
case COLLADAFW::MeshPrimitive::POLYLIST:
|
|
case COLLADAFW::MeshPrimitive::POLYGONS: {
|
|
result = mp->getGroupedVerticesVertexCountArray()[index];
|
|
break;
|
|
}
|
|
default: {
|
|
result = -1;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
uint MeshImporter::get_loose_edge_count(COLLADAFW::Mesh *mesh)
|
|
{
|
|
COLLADAFW::MeshPrimitiveArray &prim_arr = mesh->getMeshPrimitives();
|
|
int loose_edge_count = 0;
|
|
|
|
/* collect edge_count and face_count from all parts */
|
|
for (int i = 0; i < prim_arr.getCount(); i++) {
|
|
COLLADAFW::MeshPrimitive *mp = prim_arr[i];
|
|
int type = mp->getPrimitiveType();
|
|
switch (type) {
|
|
case COLLADAFW::MeshPrimitive::LINES: {
|
|
size_t prim_totface = mp->getFaceCount();
|
|
loose_edge_count += prim_totface;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return loose_edge_count;
|
|
}
|
|
|
|
void MeshImporter::mesh_add_edges(Mesh *mesh, int len)
|
|
{
|
|
CustomData edge_data;
|
|
int totedge;
|
|
|
|
if (len == 0) {
|
|
return;
|
|
}
|
|
|
|
totedge = mesh->edges_num + len;
|
|
|
|
/* Update custom-data. */
|
|
CustomData_init_layout_from(
|
|
&mesh->edge_data, &edge_data, CD_MASK_MESH.emask, CD_SET_DEFAULT, totedge);
|
|
CustomData_copy_data(&mesh->edge_data, &edge_data, 0, 0, mesh->edges_num);
|
|
|
|
if (!CustomData_has_layer_named(&edge_data, CD_PROP_INT32_2D, ".edge_verts")) {
|
|
CustomData_add_layer_named(&edge_data, CD_PROP_INT32_2D, CD_CONSTRUCT, totedge, ".edge_verts");
|
|
}
|
|
|
|
CustomData_free(&mesh->edge_data);
|
|
mesh->edge_data = edge_data;
|
|
|
|
BKE_mesh_runtime_clear_cache(mesh);
|
|
|
|
mesh->edges_num = totedge;
|
|
}
|
|
|
|
void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *blender_mesh)
|
|
{
|
|
uint loose_edge_count = get_loose_edge_count(mesh);
|
|
if (loose_edge_count > 0) {
|
|
|
|
uint face_edge_count = blender_mesh->edges_num;
|
|
// uint total_edge_count = loose_edge_count + face_edge_count; /* UNUSED. */
|
|
|
|
mesh_add_edges(blender_mesh, loose_edge_count);
|
|
MutableSpan<blender::int2> edges = blender_mesh->edges_for_write();
|
|
blender::int2 *edge = edges.data() + face_edge_count;
|
|
|
|
COLLADAFW::MeshPrimitiveArray &prim_arr = mesh->getMeshPrimitives();
|
|
|
|
for (int index = 0; index < prim_arr.getCount(); index++) {
|
|
COLLADAFW::MeshPrimitive *mp = prim_arr[index];
|
|
|
|
int type = mp->getPrimitiveType();
|
|
if (type == COLLADAFW::MeshPrimitive::LINES) {
|
|
uint edge_count = mp->getFaceCount();
|
|
uint *indices = mp->getPositionIndices().getData();
|
|
|
|
for (int j = 0; j < edge_count; j++, edge++) {
|
|
(*edge)[0] = indices[2 * j];
|
|
(*edge)[1] = indices[2 * j + 1];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh,
|
|
Mesh *mesh,
|
|
blender::Vector<blender::float3> &loop_normals)
|
|
{
|
|
using namespace blender;
|
|
uint i;
|
|
|
|
allocate_poly_data(collada_mesh, mesh);
|
|
|
|
UVDataWrapper uvs(collada_mesh->getUVCoords());
|
|
VCOLDataWrapper vcol(collada_mesh->getColors());
|
|
|
|
MutableSpan<int> face_offsets = mesh->face_offsets_for_write();
|
|
MutableSpan<int> corner_verts = mesh->corner_verts_for_write();
|
|
int face_index = 0;
|
|
int loop_index = 0;
|
|
|
|
MaterialIdPrimitiveArrayMap mat_prim_map;
|
|
|
|
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
|
|
bke::SpanAttributeWriter material_indices = attributes.lookup_or_add_for_write_span<int>(
|
|
"material_index", bke::AttrDomain::Face);
|
|
bke::SpanAttributeWriter sharp_faces = attributes.lookup_or_add_for_write_span<bool>(
|
|
"sharp_face", bke::AttrDomain::Face);
|
|
|
|
COLLADAFW::MeshPrimitiveArray &prim_arr = collada_mesh->getMeshPrimitives();
|
|
COLLADAFW::MeshVertexData &nor = collada_mesh->getNormals();
|
|
|
|
for (i = 0; i < prim_arr.getCount(); i++) {
|
|
|
|
COLLADAFW::MeshPrimitive *mp = prim_arr[i];
|
|
|
|
/* faces */
|
|
size_t prim_faces_num = mp->getFaceCount();
|
|
uint *position_indices = mp->getPositionIndices().getData();
|
|
uint *normal_indices = mp->getNormalIndices().getData();
|
|
|
|
bool mp_has_normals = primitive_has_useable_normals(mp);
|
|
bool mp_has_faces = primitive_has_faces(mp);
|
|
|
|
int collada_meshtype = mp->getPrimitiveType();
|
|
|
|
if (collada_meshtype == COLLADAFW::MeshPrimitive::LINES) {
|
|
continue; /* read the lines later after all the rest is done */
|
|
}
|
|
|
|
/* Since we cannot set `poly->mat_nr` here, we store a portion of `mesh->mpoly` in Primitive.
|
|
*/
|
|
Primitive prim = {face_index, &material_indices.span[face_index], 0};
|
|
|
|
/* If MeshPrimitive is TRIANGLE_FANS we split it into triangles
|
|
* The first triangle-fan vertex will be the first vertex in every triangle
|
|
* XXX The proper function of TRIANGLE_FANS is not tested!!!
|
|
* XXX In particular the handling of the normal_indices is very wrong */
|
|
/* TODO: UV, vertex color and custom normal support */
|
|
if (collada_meshtype == COLLADAFW::MeshPrimitive::TRIANGLE_FANS) {
|
|
uint grouped_vertex_count = mp->getGroupedVertexElementsCount();
|
|
for (uint group_index = 0; group_index < grouped_vertex_count; group_index++) {
|
|
uint first_vertex = position_indices[0]; /* Store first triangle-fan vertex. */
|
|
uint first_normal = normal_indices[0]; /* Store first triangle-fan vertex normal. */
|
|
uint vertex_count = mp->getGroupedVerticesVertexCount(group_index);
|
|
|
|
for (uint vertex_index = 0; vertex_index < vertex_count - 2; vertex_index++) {
|
|
/* For each triangle store indices of its 3 vertices */
|
|
uint triangle_vertex_indices[3] = {
|
|
first_vertex, position_indices[1], position_indices[2]};
|
|
face_offsets[face_index] = loop_index;
|
|
set_poly_indices(&corner_verts[loop_index], loop_index, triangle_vertex_indices, 3);
|
|
|
|
if (mp_has_normals) { /* vertex normals, same implementation as for the triangles */
|
|
/* The same for vertices normals. */
|
|
uint vertex_normal_indices[3] = {first_normal, normal_indices[1], normal_indices[2]};
|
|
sharp_faces.span[face_index] = is_flat_face(vertex_normal_indices, nor, 3);
|
|
normal_indices++;
|
|
}
|
|
|
|
face_index++;
|
|
loop_index += 3;
|
|
prim.faces_num++;
|
|
}
|
|
|
|
/* Moving cursor to the next triangle fan. */
|
|
if (mp_has_normals) {
|
|
normal_indices += 2;
|
|
}
|
|
|
|
position_indices += 2;
|
|
}
|
|
}
|
|
|
|
if (ELEM(collada_meshtype,
|
|
COLLADAFW::MeshPrimitive::POLYLIST,
|
|
COLLADAFW::MeshPrimitive::POLYGONS,
|
|
COLLADAFW::MeshPrimitive::TRIANGLES))
|
|
{
|
|
COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp;
|
|
uint start_index = 0;
|
|
|
|
COLLADAFW::IndexListArray &index_list_array_uvcoord = mp->getUVCoordIndicesArray();
|
|
COLLADAFW::IndexListArray &index_list_array_vcolor = mp->getColorIndicesArray();
|
|
|
|
int invalid_loop_holes = 0;
|
|
for (uint j = 0; j < prim_faces_num; j++) {
|
|
|
|
/* Vertices in polygon: */
|
|
int vcount = get_vertex_count(mpvc, j);
|
|
if (vcount < 0) {
|
|
continue; /* TODO: add support for holes */
|
|
}
|
|
|
|
face_offsets[face_index] = loop_index;
|
|
bool broken_loop = set_poly_indices(
|
|
&corner_verts[loop_index], loop_index, position_indices, vcount);
|
|
if (broken_loop) {
|
|
invalid_loop_holes += 1;
|
|
}
|
|
|
|
for (uint uvset_index = 0; uvset_index < index_list_array_uvcoord.getCount();
|
|
uvset_index++)
|
|
{
|
|
COLLADAFW::IndexList &index_list = *index_list_array_uvcoord[uvset_index];
|
|
blender::float2 *mloopuv = static_cast<blender::float2 *>(
|
|
CustomData_get_layer_named_for_write(
|
|
&mesh->corner_data, CD_PROP_FLOAT2, index_list.getName(), mesh->corners_num));
|
|
if (mloopuv == nullptr) {
|
|
fprintf(stderr,
|
|
"Collada import: Mesh [%s] : Unknown reference to TEXCOORD [#%s].\n",
|
|
mesh->id.name,
|
|
index_list.getName().c_str());
|
|
}
|
|
else {
|
|
set_face_uv(mloopuv + loop_index,
|
|
uvs,
|
|
start_index,
|
|
*index_list_array_uvcoord[uvset_index],
|
|
vcount);
|
|
}
|
|
}
|
|
|
|
if (mp_has_normals) {
|
|
/* If it turns out that we have complete custom normals for each poly
|
|
* and we want to use custom normals, this will be overridden. */
|
|
sharp_faces.span[face_index] = is_flat_face(normal_indices, nor, vcount);
|
|
|
|
if (use_custom_normals) {
|
|
/* Store the custom normals for later application. */
|
|
float vert_normal[3];
|
|
uint *cur_normal = normal_indices;
|
|
for (int k = 0; k < vcount; k++, cur_normal++) {
|
|
get_vector(vert_normal, nor, *cur_normal, 3);
|
|
normalize_v3(vert_normal);
|
|
loop_normals.append(vert_normal);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mp->hasColorIndices()) {
|
|
int vcolor_count = index_list_array_vcolor.getCount();
|
|
|
|
for (uint vcolor_index = 0; vcolor_index < vcolor_count; vcolor_index++) {
|
|
|
|
COLLADAFW::IndexList &color_index_list = *mp->getColorIndices(vcolor_index);
|
|
COLLADAFW::String colname = extract_vcolname(color_index_list.getName());
|
|
MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_named_for_write(
|
|
&mesh->corner_data, CD_PROP_BYTE_COLOR, colname, mesh->corners_num);
|
|
if (mloopcol == nullptr) {
|
|
fprintf(stderr,
|
|
"Collada import: Mesh [%s] : Unknown reference to VCOLOR [#%s].\n",
|
|
mesh->id.name,
|
|
color_index_list.getName().c_str());
|
|
}
|
|
else {
|
|
set_vcol(mloopcol + loop_index, vcol, start_index, color_index_list, vcount);
|
|
}
|
|
}
|
|
}
|
|
|
|
face_index++;
|
|
loop_index += vcount;
|
|
start_index += vcount;
|
|
prim.faces_num++;
|
|
|
|
if (mp_has_normals) {
|
|
normal_indices += vcount;
|
|
}
|
|
|
|
position_indices += vcount;
|
|
}
|
|
|
|
if (invalid_loop_holes > 0) {
|
|
fprintf(stderr,
|
|
"Collada import: Mesh [%s] : contains %d unsupported loops (holes).\n",
|
|
mesh->id.name,
|
|
invalid_loop_holes);
|
|
}
|
|
}
|
|
|
|
if (mp_has_faces) {
|
|
mat_prim_map[mp->getMaterialId()].push_back(prim);
|
|
}
|
|
}
|
|
|
|
geom_uid_mat_mapping_map[collada_mesh->getUniqueId()] = mat_prim_map;
|
|
material_indices.finish();
|
|
}
|
|
|
|
void MeshImporter::get_vector(float v[3], COLLADAFW::MeshVertexData &arr, int i, int stride)
|
|
{
|
|
i *= stride;
|
|
|
|
switch (arr.getType()) {
|
|
case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: {
|
|
COLLADAFW::ArrayPrimitiveType<float> *values = arr.getFloatValues();
|
|
if (values->empty()) {
|
|
return;
|
|
}
|
|
|
|
v[0] = (*values)[i++];
|
|
v[1] = (*values)[i++];
|
|
if (stride >= 3) {
|
|
v[2] = (*values)[i];
|
|
}
|
|
else {
|
|
v[2] = 0.0f;
|
|
}
|
|
break;
|
|
}
|
|
case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: {
|
|
COLLADAFW::ArrayPrimitiveType<double> *values = arr.getDoubleValues();
|
|
if (values->empty()) {
|
|
return;
|
|
}
|
|
|
|
v[0] = float((*values)[i++]);
|
|
v[1] = float((*values)[i++]);
|
|
if (stride >= 3) {
|
|
v[2] = float((*values)[i]);
|
|
}
|
|
else {
|
|
v[2] = 0.0f;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool MeshImporter::is_flat_face(uint *nind, COLLADAFW::MeshVertexData &nor, int count)
|
|
{
|
|
float a[3], b[3];
|
|
|
|
get_vector(a, nor, *nind, 3);
|
|
normalize_v3(a);
|
|
|
|
nind++;
|
|
|
|
for (int i = 1; i < count; i++, nind++) {
|
|
get_vector(b, nor, *nind, 3);
|
|
normalize_v3(b);
|
|
|
|
float dp = dot_v3v3(a, b);
|
|
|
|
if (dp < 0.99999f || dp > 1.00001f) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Object *MeshImporter::get_object_by_geom_uid(const COLLADAFW::UniqueId &geom_uid)
|
|
{
|
|
if (uid_object_map.find(geom_uid) != uid_object_map.end()) {
|
|
return uid_object_map[geom_uid];
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Mesh *MeshImporter::get_mesh_by_geom_uid(const COLLADAFW::UniqueId &geom_uid)
|
|
{
|
|
if (uid_mesh_map.find(geom_uid) != uid_mesh_map.end()) {
|
|
return uid_mesh_map[geom_uid];
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
std::string *MeshImporter::get_geometry_name(const std::string &mesh_name)
|
|
{
|
|
if (this->mesh_geom_map.find(mesh_name) != this->mesh_geom_map.end()) {
|
|
return &this->mesh_geom_map[mesh_name];
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static bool bc_has_out_of_bound_indices(Mesh *mesh)
|
|
{
|
|
for (const int vert_i : mesh->corner_verts()) {
|
|
if (vert_i >= mesh->verts_num) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* this function checks if both objects have the same
|
|
* materials assigned to Object (in the same order)
|
|
* returns true if condition matches, otherwise false;
|
|
*/
|
|
static bool bc_has_same_material_configuration(Object *ob1, Object *ob2)
|
|
{
|
|
if (ob1->totcol != ob2->totcol) {
|
|
return false; /* not same number of materials */
|
|
}
|
|
if (ob1->totcol == 0) {
|
|
return false; /* no material at all */
|
|
}
|
|
|
|
for (int index = 0; index < ob1->totcol; index++) {
|
|
if (ob1->matbits[index] != ob2->matbits[index]) {
|
|
return false; /* shouldn't happen */
|
|
}
|
|
if (ob1->matbits[index] == 0) {
|
|
return false; /* shouldn't happen */
|
|
}
|
|
if (ob1->mat[index] != ob2->mat[index]) {
|
|
return false; /* different material assignment */
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Caution here: This code assumes that all materials are assigned to Object
|
|
* and no material is assigned to Data.
|
|
* That is true right after the objects have been imported.
|
|
*/
|
|
static void bc_copy_materials_to_data(Object *ob, Mesh *mesh)
|
|
{
|
|
for (int index = 0; index < ob->totcol; index++) {
|
|
ob->matbits[index] = 0;
|
|
mesh->mat[index] = ob->mat[index];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove all references to materials from the object.
|
|
*/
|
|
static void bc_remove_materials_from_object(Object *ob, Mesh *mesh)
|
|
{
|
|
for (int index = 0; index < ob->totcol; index++) {
|
|
ob->matbits[index] = 0;
|
|
ob->mat[index] = nullptr;
|
|
}
|
|
}
|
|
|
|
std::vector<Object *> MeshImporter::get_all_users_of(Mesh *reference_mesh)
|
|
{
|
|
std::vector<Object *> mesh_users;
|
|
for (Object *ob : imported_objects) {
|
|
if (bc_is_marked(ob)) {
|
|
bc_remove_mark(ob);
|
|
Mesh *mesh = (Mesh *)ob->data;
|
|
if (mesh == reference_mesh) {
|
|
mesh_users.push_back(ob);
|
|
}
|
|
}
|
|
}
|
|
return mesh_users;
|
|
}
|
|
|
|
void MeshImporter::optimize_material_assignements()
|
|
{
|
|
for (Object *ob : imported_objects) {
|
|
Mesh *mesh = (Mesh *)ob->data;
|
|
if (ID_REAL_USERS(&mesh->id) == 1) {
|
|
bc_copy_materials_to_data(ob, mesh);
|
|
bc_remove_materials_from_object(ob, mesh);
|
|
bc_remove_mark(ob);
|
|
}
|
|
else if (ID_REAL_USERS(&mesh->id) > 1) {
|
|
bool can_move = true;
|
|
std::vector<Object *> mesh_users = get_all_users_of(mesh);
|
|
if (mesh_users.size() > 1) {
|
|
Object *ref_ob = mesh_users[0];
|
|
for (int index = 1; index < mesh_users.size(); index++) {
|
|
if (!bc_has_same_material_configuration(ref_ob, mesh_users[index])) {
|
|
can_move = false;
|
|
break;
|
|
}
|
|
}
|
|
if (can_move) {
|
|
bc_copy_materials_to_data(ref_ob, mesh);
|
|
for (Object *object : mesh_users) {
|
|
bc_remove_materials_from_object(object, mesh);
|
|
bc_remove_mark(object);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MeshImporter::assign_material_to_geom(
|
|
COLLADAFW::MaterialBinding cmaterial,
|
|
std::map<COLLADAFW::UniqueId, Material *> &uid_material_map,
|
|
Object *ob,
|
|
const COLLADAFW::UniqueId *geom_uid,
|
|
short mat_index)
|
|
{
|
|
const COLLADAFW::UniqueId &ma_uid = cmaterial.getReferencedMaterial();
|
|
|
|
/* do we know this material? */
|
|
if (uid_material_map.find(ma_uid) == uid_material_map.end()) {
|
|
|
|
fprintf(stderr, "Cannot find material by UID.\n");
|
|
return;
|
|
}
|
|
|
|
/* first time we get geom_uid, ma_uid pair. Save for later check. */
|
|
materials_mapped_to_geom.insert(
|
|
std::pair<COLLADAFW::UniqueId, COLLADAFW::UniqueId>(*geom_uid, ma_uid));
|
|
|
|
Material *ma = uid_material_map[ma_uid];
|
|
|
|
/* Attention! This temporarily assigns material to object on purpose!
|
|
* See note above. */
|
|
ob->actcol = 0;
|
|
BKE_object_material_assign(m_bmain, ob, ma, mat_index + 1, BKE_MAT_ASSIGN_OBJECT);
|
|
|
|
MaterialIdPrimitiveArrayMap &mat_prim_map = geom_uid_mat_mapping_map[*geom_uid];
|
|
COLLADAFW::MaterialId mat_id = cmaterial.getMaterialId();
|
|
|
|
/* assign material indices to mesh faces */
|
|
if (mat_prim_map.find(mat_id) != mat_prim_map.end()) {
|
|
|
|
std::vector<Primitive> &prims = mat_prim_map[mat_id];
|
|
|
|
std::vector<Primitive>::iterator it;
|
|
|
|
for (it = prims.begin(); it != prims.end(); it++) {
|
|
Primitive &prim = *it;
|
|
|
|
for (int i = 0; i < prim.faces_num; i++) {
|
|
prim.material_indices[i] = mat_index;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Object *MeshImporter::create_mesh_object(
|
|
COLLADAFW::Node *node,
|
|
COLLADAFW::InstanceGeometry *geom,
|
|
bool isController,
|
|
std::map<COLLADAFW::UniqueId, Material *> &uid_material_map)
|
|
{
|
|
const COLLADAFW::UniqueId *geom_uid = &geom->getInstanciatedObjectId();
|
|
|
|
/* check if node instantiates controller or geometry */
|
|
if (isController) {
|
|
|
|
geom_uid = armature_importer->get_geometry_uid(*geom_uid);
|
|
|
|
if (!geom_uid) {
|
|
fprintf(stderr, "Couldn't find a mesh UID by controller's UID.\n");
|
|
return nullptr;
|
|
}
|
|
}
|
|
else {
|
|
|
|
if (uid_mesh_map.find(*geom_uid) == uid_mesh_map.end()) {
|
|
/* this could happen if a mesh was not created
|
|
* (e.g. if it contains unsupported geometry) */
|
|
fprintf(stderr, "Couldn't find a mesh by UID.\n");
|
|
return nullptr;
|
|
}
|
|
}
|
|
if (!uid_mesh_map[*geom_uid]) {
|
|
return nullptr;
|
|
}
|
|
|
|
/* name Object */
|
|
const std::string &id = node->getName().empty() ? node->getOriginalId() : node->getName();
|
|
const char *name = id.length() ? id.c_str() : nullptr;
|
|
|
|
/* add object */
|
|
Object *ob = bc_add_object(m_bmain, scene, view_layer, OB_MESH, name);
|
|
bc_set_mark(ob); /* used later for material assignment optimization */
|
|
|
|
/* store object pointer for ArmatureImporter */
|
|
uid_object_map[*geom_uid] = ob;
|
|
imported_objects.push_back(ob);
|
|
|
|
/* replace ob->data freeing the old one */
|
|
Mesh *old_mesh = (Mesh *)ob->data;
|
|
Mesh *new_mesh = uid_mesh_map[*geom_uid];
|
|
|
|
BKE_mesh_assign_object(m_bmain, ob, new_mesh);
|
|
|
|
/* Because BKE_mesh_assign_object would have already decreased it... */
|
|
id_us_plus(&old_mesh->id);
|
|
|
|
BKE_id_free_us(m_bmain, old_mesh);
|
|
|
|
COLLADAFW::MaterialBindingArray &mat_array = geom->getMaterialBindings();
|
|
|
|
/* loop through geom's materials */
|
|
for (uint i = 0; i < mat_array.getCount(); i++) {
|
|
|
|
if (mat_array[i].getReferencedMaterial().isValid()) {
|
|
assign_material_to_geom(mat_array[i], uid_material_map, ob, geom_uid, i);
|
|
}
|
|
else {
|
|
fprintf(stderr, "invalid referenced material for %s\n", mat_array[i].getName().c_str());
|
|
}
|
|
}
|
|
|
|
/* clean up the mesh */
|
|
BKE_mesh_validate((Mesh *)ob->data, false, false);
|
|
|
|
return ob;
|
|
}
|
|
|
|
bool MeshImporter::write_geometry(const COLLADAFW::Geometry *geom)
|
|
{
|
|
using namespace blender;
|
|
if (geom->getType() != COLLADAFW::Geometry::GEO_TYPE_MESH) {
|
|
/* TODO: report warning */
|
|
fprintf(stderr, "Mesh type %s is not supported\n", bc_geomTypeToStr(geom->getType()));
|
|
return true;
|
|
}
|
|
|
|
COLLADAFW::Mesh *mesh = (COLLADAFW::Mesh *)geom;
|
|
|
|
if (!is_nice_mesh(mesh)) {
|
|
fprintf(stderr, "Ignoring mesh %s\n", bc_get_dae_name(mesh).c_str());
|
|
return true;
|
|
}
|
|
|
|
const std::string &str_geom_id = mesh->getName().empty() ? mesh->getOriginalId() :
|
|
mesh->getName();
|
|
Mesh *blender_mesh = BKE_mesh_add(m_bmain, (char *)str_geom_id.c_str());
|
|
id_us_min(
|
|
&blender_mesh->id); /* is already 1 here, but will be set later in BKE_mesh_assign_object */
|
|
|
|
/* store the Mesh pointer to link it later with an Object
|
|
* mesh_geom_map needed to map mesh to its geometry name (for shape key naming) */
|
|
this->uid_mesh_map[mesh->getUniqueId()] = blender_mesh;
|
|
this->mesh_geom_map[std::string(blender_mesh->id.name)] = str_geom_id;
|
|
|
|
read_vertices(mesh, blender_mesh);
|
|
|
|
blender::Vector<blender::float3> loop_normals;
|
|
read_polys(mesh, blender_mesh, loop_normals);
|
|
|
|
blender::bke::mesh_calc_edges(*blender_mesh, false, false);
|
|
|
|
/* We must apply custom normals after edges have been calculated, because
|
|
* bke::mesh_set_custom_normals()'s internals expect mesh->medge to be populated
|
|
* and for the MLoops to have correct edge indices. */
|
|
if (use_custom_normals && !loop_normals.is_empty()) {
|
|
/* bke::mesh_set_custom_normals()'s internals also expect that each corner
|
|
* has a valid vertex index, which may not be the case due to the existing
|
|
* logic in read_faces(). This check isn't necessary in the no-custom-normals
|
|
* case because the invalid MLoops get stripped in a later step. */
|
|
if (bc_has_out_of_bound_indices(blender_mesh)) {
|
|
fprintf(stderr, "Can't apply custom normals, encountered invalid loop vert indices!\n");
|
|
}
|
|
/* There may be a mismatch in lengths if one or more of the MeshPrimitives in
|
|
* the Geometry had missing or otherwise invalid normals. */
|
|
else if (blender_mesh->corners_num != loop_normals.size()) {
|
|
fprintf(stderr,
|
|
"Can't apply custom normals, mesh->corners_num != loop_normals.size() (%d != %d)\n",
|
|
blender_mesh->corners_num,
|
|
int(loop_normals.size()));
|
|
}
|
|
else {
|
|
bke::mesh_set_custom_normals(*blender_mesh, loop_normals);
|
|
}
|
|
}
|
|
|
|
/* read_lines() must be called after the face edges have been generated.
|
|
* Otherwise the loose edges will be silently deleted again. */
|
|
read_lines(mesh, blender_mesh);
|
|
|
|
return true;
|
|
}
|