UI: Operator Props Dialog Changes #117528
|
@ -177,9 +177,9 @@ Batch *VKBackend::batch_alloc()
|
|||
return new VKBatch();
|
||||
}
|
||||
|
||||
DrawList *VKBackend::drawlist_alloc(int /*list_length*/)
|
||||
DrawList *VKBackend::drawlist_alloc(int list_length)
|
||||
{
|
||||
return new VKDrawList();
|
||||
return new VKDrawList(list_length);
|
||||
}
|
||||
|
||||
Fence *VKBackend::fence_alloc()
|
||||
|
|
|
@ -31,4 +31,9 @@ class VKBatch : public Batch {
|
|||
void draw_setup();
|
||||
};
|
||||
|
||||
BLI_INLINE VKBatch *unwrap(GPUBatch *batch)
|
||||
{
|
||||
return static_cast<VKBatch *>(batch);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -8,15 +8,81 @@
|
|||
|
||||
#include "GPU_batch.h"
|
||||
|
||||
#include "vk_batch.hh"
|
||||
#include "vk_common.hh"
|
||||
#include "vk_drawlist.hh"
|
||||
#include "vk_index_buffer.hh"
|
||||
#include "vk_vertex_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
void VKDrawList::append(GPUBatch *batch, int instance_first, int instance_count)
|
||||
VKDrawList::VKDrawList(int list_length)
|
||||
: command_buffer_(
|
||||
list_length * sizeof(VkDrawIndexedIndirectCommand), GPU_USAGE_STREAM, __func__),
|
||||
length_(list_length)
|
||||
{
|
||||
GPU_batch_draw_advanced(batch, 0, 0, instance_first, instance_count);
|
||||
command_buffer_.ensure_allocated();
|
||||
}
|
||||
|
||||
void VKDrawList::submit() {}
|
||||
void VKDrawList::append(GPUBatch *gpu_batch, int instance_first, int instance_count)
|
||||
{
|
||||
/* Check for different batch. When batch is different the previous commands should be flushed to
|
||||
* the gpu. */
|
||||
VKBatch *batch = unwrap(gpu_batch);
|
||||
if (batch_ != batch) {
|
||||
submit();
|
||||
batch_ = batch;
|
||||
}
|
||||
|
||||
/* Record the new command */
|
||||
const VKIndexBuffer *index_buffer = batch_->index_buffer_get();
|
||||
const bool is_indexed = index_buffer != nullptr;
|
||||
if (is_indexed) {
|
||||
VkDrawIndexedIndirectCommand &command = get_command<VkDrawIndexedIndirectCommand>();
|
||||
command.firstIndex = index_buffer->index_base_get();
|
||||
command.vertexOffset = index_buffer->index_start_get();
|
||||
command.indexCount = index_buffer->index_len_get();
|
||||
command.firstInstance = instance_first;
|
||||
command.instanceCount = instance_count;
|
||||
}
|
||||
else {
|
||||
const VKVertexBuffer *vertex_buffer = batch_->vertex_buffer_get(0);
|
||||
if (vertex_buffer == nullptr) {
|
||||
batch_ = nullptr;
|
||||
return;
|
||||
}
|
||||
VkDrawIndirectCommand &command = get_command<VkDrawIndirectCommand>();
|
||||
command.vertexCount = vertex_buffer->vertex_len;
|
||||
command.instanceCount = instance_count;
|
||||
command.firstVertex = 0;
|
||||
command.firstInstance = instance_first;
|
||||
}
|
||||
command_index_++;
|
||||
|
||||
/* Submit commands when command buffer is full. */
|
||||
if (command_index_ == length_) {
|
||||
submit();
|
||||
}
|
||||
}
|
||||
|
||||
void VKDrawList::submit()
|
||||
{
|
||||
if (batch_ == nullptr || command_index_ == 0) {
|
||||
command_index_ = 0;
|
||||
batch_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
const VKIndexBuffer *index_buffer = batch_->index_buffer_get();
|
||||
const bool is_indexed = index_buffer != nullptr;
|
||||
command_buffer_.buffer_get().flush();
|
||||
batch_->multi_draw_indirect(wrap(wrap(&command_buffer_)),
|
||||
command_index_,
|
||||
0,
|
||||
is_indexed ? sizeof(VkDrawIndexedIndirectCommand) :
|
||||
sizeof(VkDrawIndirectCommand));
|
||||
command_index_ = 0;
|
||||
batch_ = nullptr;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -10,12 +10,68 @@
|
|||
|
||||
#include "gpu_drawlist_private.hh"
|
||||
|
||||
#include "vk_storage_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKBatch;
|
||||
|
||||
class VKDrawList : public DrawList {
|
||||
private:
|
||||
/**
|
||||
* Batch from who the commands are being recorded.
|
||||
*/
|
||||
VKBatch *batch_ = nullptr;
|
||||
|
||||
/**
|
||||
* Storage buffer containing the commands.
|
||||
*
|
||||
* The storage buffer is host visible and new commands are directly added to the buffer. Reducing
|
||||
* the need to copy the commands from an intermediate buffer to the GPU. The commands are only
|
||||
* written once and used once.
|
||||
*
|
||||
* The data can be used to record VkDrawIndirectCommands or VkDrawIndirectIndexedCommands.
|
||||
*/
|
||||
VKStorageBuffer command_buffer_;
|
||||
|
||||
/**
|
||||
* Maximum number of commands that can be recorded per batch. Commands will be flushed when this
|
||||
* number of commands are added.
|
||||
*/
|
||||
const int length_;
|
||||
|
||||
/**
|
||||
* Current number of recorded commands.
|
||||
*/
|
||||
int command_index_ = 0;
|
||||
|
||||
public:
|
||||
VKDrawList(int list_length);
|
||||
|
||||
/**
|
||||
* Append a new command for the given batch to the draw list.
|
||||
*
|
||||
* Will flush when batch is different than the previous one or when the command_buffer_ is full.
|
||||
*/
|
||||
void append(GPUBatch *batch, int instance_first, int instance_count) override;
|
||||
|
||||
/**
|
||||
* Submit buffered commands to the GPU.
|
||||
*
|
||||
* NOTE: after calling this method the command_index_ and the batch_ are reset.
|
||||
*/
|
||||
void submit() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Retrieve command to write to. The returned memory is part of the mapped memory of the
|
||||
* commands_buffer_.
|
||||
*/
|
||||
template<typename CommandType> CommandType &get_command() const
|
||||
{
|
||||
return MutableSpan<CommandType>(
|
||||
static_cast<CommandType *>(command_buffer_.buffer_get().mapped_memory_get()),
|
||||
length_)[command_index_];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -38,12 +38,14 @@ void VKStorageBuffer::ensure_allocated()
|
|||
|
||||
void VKStorageBuffer::allocate()
|
||||
{
|
||||
const bool is_host_visible = false;
|
||||
buffer_.create(size_in_bytes_,
|
||||
usage_,
|
||||
VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
is_host_visible);
|
||||
const bool is_host_visible = ELEM(usage_, GPU_USAGE_STREAM);
|
||||
VkBufferUsageFlags buffer_usage_flags = ELEM(usage_, GPU_USAGE_STREAM) ?
|
||||
VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT :
|
||||
VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
||||
buffer_.create(size_in_bytes_, usage_, buffer_usage_flags, is_host_visible);
|
||||
debug::object_label(buffer_.vk_handle(), name_);
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,11 @@ class VKStorageBuffer : public StorageBuf, public VKBindableResource {
|
|||
|
||||
void ensure_allocated();
|
||||
|
||||
const VKBuffer &buffer_get() const
|
||||
{
|
||||
return buffer_;
|
||||
}
|
||||
|
||||
private:
|
||||
void allocate();
|
||||
};
|
||||
|
@ -57,5 +62,9 @@ BLI_INLINE VKStorageBuffer *unwrap(StorageBuf *storage_buffer)
|
|||
{
|
||||
return static_cast<VKStorageBuffer *>(storage_buffer);
|
||||
}
|
||||
BLI_INLINE StorageBuf *wrap(VKStorageBuffer *storage_buffer)
|
||||
{
|
||||
return static_cast<StorageBuf *>(storage_buffer);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -62,10 +62,10 @@ void OBJWriter::write_vert_uv_normal_indices(FormatHandler &fh,
|
|||
const int uv_offset = offsets.uv_vertex_offset + 1;
|
||||
const int normal_offset = offsets.normal_offset + 1;
|
||||
const int n = vert_indices.size();
|
||||
fh.write_obj_poly_begin();
|
||||
fh.write_obj_face_begin();
|
||||
if (!flip) {
|
||||
for (int j = 0; j < n; ++j) {
|
||||
fh.write_obj_poly_v_uv_normal(vert_indices[j] + vertex_offset,
|
||||
fh.write_obj_face_v_uv_normal(vert_indices[j] + vertex_offset,
|
||||
uv_indices[j] + uv_offset,
|
||||
normal_indices[j] + normal_offset);
|
||||
}
|
||||
|
@ -76,12 +76,12 @@ void OBJWriter::write_vert_uv_normal_indices(FormatHandler &fh,
|
|||
* then go backwards. Same logic in other write_*_indices functions below. */
|
||||
for (int k = 0; k < n; ++k) {
|
||||
int j = k == 0 ? 0 : n - k;
|
||||
fh.write_obj_poly_v_uv_normal(vert_indices[j] + vertex_offset,
|
||||
fh.write_obj_face_v_uv_normal(vert_indices[j] + vertex_offset,
|
||||
uv_indices[j] + uv_offset,
|
||||
normal_indices[j] + normal_offset);
|
||||
}
|
||||
}
|
||||
fh.write_obj_poly_end();
|
||||
fh.write_obj_face_end();
|
||||
}
|
||||
|
||||
void OBJWriter::write_vert_normal_indices(FormatHandler &fh,
|
||||
|
@ -95,21 +95,21 @@ void OBJWriter::write_vert_normal_indices(FormatHandler &fh,
|
|||
const int vertex_offset = offsets.vertex_offset + 1;
|
||||
const int normal_offset = offsets.normal_offset + 1;
|
||||
const int n = vert_indices.size();
|
||||
fh.write_obj_poly_begin();
|
||||
fh.write_obj_face_begin();
|
||||
if (!flip) {
|
||||
for (int j = 0; j < n; ++j) {
|
||||
fh.write_obj_poly_v_normal(vert_indices[j] + vertex_offset,
|
||||
fh.write_obj_face_v_normal(vert_indices[j] + vertex_offset,
|
||||
normal_indices[j] + normal_offset);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int k = 0; k < n; ++k) {
|
||||
int j = k == 0 ? 0 : n - k;
|
||||
fh.write_obj_poly_v_normal(vert_indices[j] + vertex_offset,
|
||||
fh.write_obj_face_v_normal(vert_indices[j] + vertex_offset,
|
||||
normal_indices[j] + normal_offset);
|
||||
}
|
||||
}
|
||||
fh.write_obj_poly_end();
|
||||
fh.write_obj_face_end();
|
||||
}
|
||||
|
||||
void OBJWriter::write_vert_uv_indices(FormatHandler &fh,
|
||||
|
@ -123,19 +123,19 @@ void OBJWriter::write_vert_uv_indices(FormatHandler &fh,
|
|||
const int vertex_offset = offsets.vertex_offset + 1;
|
||||
const int uv_offset = offsets.uv_vertex_offset + 1;
|
||||
const int n = vert_indices.size();
|
||||
fh.write_obj_poly_begin();
|
||||
fh.write_obj_face_begin();
|
||||
if (!flip) {
|
||||
for (int j = 0; j < n; ++j) {
|
||||
fh.write_obj_poly_v_uv(vert_indices[j] + vertex_offset, uv_indices[j] + uv_offset);
|
||||
fh.write_obj_face_v_uv(vert_indices[j] + vertex_offset, uv_indices[j] + uv_offset);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int k = 0; k < n; ++k) {
|
||||
int j = k == 0 ? 0 : n - k;
|
||||
fh.write_obj_poly_v_uv(vert_indices[j] + vertex_offset, uv_indices[j] + uv_offset);
|
||||
fh.write_obj_face_v_uv(vert_indices[j] + vertex_offset, uv_indices[j] + uv_offset);
|
||||
}
|
||||
}
|
||||
fh.write_obj_poly_end();
|
||||
fh.write_obj_face_end();
|
||||
}
|
||||
|
||||
void OBJWriter::write_vert_indices(FormatHandler &fh,
|
||||
|
@ -147,19 +147,19 @@ void OBJWriter::write_vert_indices(FormatHandler &fh,
|
|||
{
|
||||
const int vertex_offset = offsets.vertex_offset + 1;
|
||||
const int n = vert_indices.size();
|
||||
fh.write_obj_poly_begin();
|
||||
fh.write_obj_face_begin();
|
||||
if (!flip) {
|
||||
for (int j = 0; j < n; ++j) {
|
||||
fh.write_obj_poly_v(vert_indices[j] + vertex_offset);
|
||||
fh.write_obj_face_v(vert_indices[j] + vertex_offset);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int k = 0; k < n; ++k) {
|
||||
int j = k == 0 ? 0 : n - k;
|
||||
fh.write_obj_poly_v(vert_indices[j] + vertex_offset);
|
||||
fh.write_obj_face_v(vert_indices[j] + vertex_offset);
|
||||
}
|
||||
}
|
||||
fh.write_obj_poly_end();
|
||||
fh.write_obj_face_end();
|
||||
}
|
||||
|
||||
void OBJWriter::write_header() const
|
||||
|
@ -295,7 +295,7 @@ void OBJWriter::write_uv_coords(FormatHandler &fh, OBJMesh &r_obj_mesh_data) con
|
|||
});
|
||||
}
|
||||
|
||||
void OBJWriter::write_poly_normals(FormatHandler &fh, OBJMesh &obj_mesh_data)
|
||||
void OBJWriter::write_normals(FormatHandler &fh, OBJMesh &obj_mesh_data)
|
||||
{
|
||||
/* Poly normals should be calculated earlier via store_normal_coords_and_indices. */
|
||||
const Span<float3> normal_coords = obj_mesh_data.get_normal_coords();
|
||||
|
@ -305,7 +305,7 @@ void OBJWriter::write_poly_normals(FormatHandler &fh, OBJMesh &obj_mesh_data)
|
|||
});
|
||||
}
|
||||
|
||||
OBJWriter::func_vert_uv_normal_indices OBJWriter::get_poly_element_writer(
|
||||
OBJWriter::func_vert_uv_normal_indices OBJWriter::get_face_element_writer(
|
||||
const int total_uv_vertices) const
|
||||
{
|
||||
if (export_params_.export_normals) {
|
||||
|
@ -330,18 +330,18 @@ static int get_smooth_group(const OBJMesh &mesh, const OBJExportParams ¶ms,
|
|||
return NEGATIVE_INIT;
|
||||
}
|
||||
int group = SMOOTH_GROUP_DISABLED;
|
||||
if (mesh.is_ith_poly_smooth(face_idx)) {
|
||||
if (mesh.is_ith_face_smooth(face_idx)) {
|
||||
group = !params.export_smooth_groups ? SMOOTH_GROUP_DEFAULT : mesh.ith_smooth_group(face_idx);
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
void OBJWriter::write_poly_elements(FormatHandler &fh,
|
||||
void OBJWriter::write_face_elements(FormatHandler &fh,
|
||||
const IndexOffsets &offsets,
|
||||
const OBJMesh &obj_mesh_data,
|
||||
FunctionRef<const char *(int)> matname_fn)
|
||||
{
|
||||
const func_vert_uv_normal_indices poly_element_writer = get_poly_element_writer(
|
||||
const func_vert_uv_normal_indices face_element_writer = get_face_element_writer(
|
||||
obj_mesh_data.tot_uv_vertices());
|
||||
|
||||
const int tot_faces = obj_mesh_data.tot_faces();
|
||||
|
@ -358,9 +358,9 @@ void OBJWriter::write_poly_elements(FormatHandler &fh,
|
|||
int prev_i = obj_mesh_data.remap_face_index(idx - 1);
|
||||
int i = obj_mesh_data.remap_face_index(idx);
|
||||
|
||||
Span<int> poly_vertex_indices = obj_mesh_data.calc_poly_vertex_indices(i);
|
||||
Span<int> poly_uv_indices = obj_mesh_data.calc_poly_uv_indices(i);
|
||||
Vector<int> poly_normal_indices = obj_mesh_data.calc_poly_normal_indices(i);
|
||||
const Span<int> face_vertex_indices = obj_mesh_data.calc_face_vert_indices(i);
|
||||
const Span<int> face_uv_indices = obj_mesh_data.get_face_uv_indices(i);
|
||||
const Span<int> face_normal_indices = obj_mesh_data.get_face_normal_indices(i);
|
||||
|
||||
/* Write smoothing group if different from previous. */
|
||||
{
|
||||
|
@ -376,12 +376,12 @@ void OBJWriter::write_poly_elements(FormatHandler &fh,
|
|||
Vector<float> &local_weights = group_weights.local();
|
||||
local_weights.resize(tot_deform_groups);
|
||||
const int16_t prev_group = idx == 0 ? NEGATIVE_INIT :
|
||||
obj_mesh_data.get_poly_deform_group_index(
|
||||
obj_mesh_data.get_face_deform_group_index(
|
||||
prev_i, local_weights);
|
||||
const int16_t group = obj_mesh_data.get_poly_deform_group_index(i, local_weights);
|
||||
const int16_t group = obj_mesh_data.get_face_deform_group_index(i, local_weights);
|
||||
if (group != prev_group) {
|
||||
buf.write_obj_group(group == NOT_FOUND ? DEFORM_GROUP_DISABLED :
|
||||
obj_mesh_data.get_poly_deform_group_name(group));
|
||||
obj_mesh_data.get_face_deform_group_name(group));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -409,11 +409,11 @@ void OBJWriter::write_poly_elements(FormatHandler &fh,
|
|||
}
|
||||
|
||||
/* Write face elements. */
|
||||
(this->*poly_element_writer)(buf,
|
||||
(this->*face_element_writer)(buf,
|
||||
offsets,
|
||||
poly_vertex_indices,
|
||||
poly_uv_indices,
|
||||
poly_normal_indices,
|
||||
face_vertex_indices,
|
||||
face_uv_indices,
|
||||
face_normal_indices,
|
||||
obj_mesh_data.is_mirrored_transform());
|
||||
});
|
||||
}
|
||||
|
@ -465,7 +465,7 @@ void OBJWriter::write_nurbs_curve(FormatHandler &fh, const OBJCurve &obj_nurbs_d
|
|||
for (int i = 0; i < total_control_points; i++) {
|
||||
/* "+1" to keep indices one-based, even if they're negative: i.e., -1 refers to the
|
||||
* last vertex coordinate, -2 second last. */
|
||||
fh.write_obj_poly_v(-((i % total_vertices) + 1));
|
||||
fh.write_obj_face_v(-((i % total_vertices) + 1));
|
||||
}
|
||||
fh.write_obj_curve_end();
|
||||
|
||||
|
|
|
@ -81,22 +81,22 @@ class OBJWriter : NonMovable, NonCopyable {
|
|||
bool write_colors) const;
|
||||
/**
|
||||
* Write UV vertex coordinates for all vertices as `vt u v`.
|
||||
* \note UV indices are stored here, but written with polygons later.
|
||||
* \note UV indices are stored here, but written with faces later.
|
||||
*/
|
||||
void write_uv_coords(FormatHandler &fh, OBJMesh &obj_mesh_data) const;
|
||||
/**
|
||||
* Write loop normals for smooth-shaded polygons, and polygon normals otherwise, as "vn x y z".
|
||||
* \note Normal indices ares stored here, but written with polygons later.
|
||||
* Write corner normals for smooth-shaded faces, and face normals otherwise, as "vn x y z".
|
||||
* \note Normal indices ares stored here, but written with faces later.
|
||||
*/
|
||||
void write_poly_normals(FormatHandler &fh, OBJMesh &obj_mesh_data);
|
||||
void write_normals(FormatHandler &fh, OBJMesh &obj_mesh_data);
|
||||
/**
|
||||
* Write polygon elements with at least vertex indices, and conditionally with UV vertex
|
||||
* indices and polygon normal indices. Also write groups: smooth, vertex, material.
|
||||
* Write face elements with at least vertex indices, and conditionally with UV vertex
|
||||
* indices and face normal indices. Also write groups: smooth, vertex, material.
|
||||
* The matname_fn turns a 0-indexed material slot number in an Object into the
|
||||
* name used in the .obj file.
|
||||
* \note UV indices were stored while writing UV vertices.
|
||||
*/
|
||||
void write_poly_elements(FormatHandler &fh,
|
||||
void write_face_elements(FormatHandler &fh,
|
||||
const IndexOffsets &offsets,
|
||||
const OBJMesh &obj_mesh_data,
|
||||
FunctionRef<const char *(int)> matname_fn);
|
||||
|
@ -119,12 +119,12 @@ class OBJWriter : NonMovable, NonCopyable {
|
|||
Span<int> normal_indices,
|
||||
bool flip) const;
|
||||
/**
|
||||
* \return Writer function with appropriate polygon-element syntax.
|
||||
* \return Writer function with appropriate face-element syntax.
|
||||
*/
|
||||
func_vert_uv_normal_indices get_poly_element_writer(int total_uv_vertices) const;
|
||||
func_vert_uv_normal_indices get_face_element_writer(int total_uv_vertices) const;
|
||||
|
||||
/**
|
||||
* Write one line of polygon indices as "f v1/vt1/vn1 v2/vt2/vn2 ...".
|
||||
* Write one line of face indices as "f v1/vt1/vn1 v2/vt2/vn2 ...".
|
||||
*/
|
||||
void write_vert_uv_normal_indices(FormatHandler &fh,
|
||||
const IndexOffsets &offsets,
|
||||
|
@ -133,7 +133,7 @@ class OBJWriter : NonMovable, NonCopyable {
|
|||
Span<int> normal_indices,
|
||||
bool flip) const;
|
||||
/**
|
||||
* Write one line of polygon indices as "f v1//vn1 v2//vn2 ...".
|
||||
* Write one line of face indices as "f v1//vn1 v2//vn2 ...".
|
||||
*/
|
||||
void write_vert_normal_indices(FormatHandler &fh,
|
||||
const IndexOffsets &offsets,
|
||||
|
@ -142,7 +142,7 @@ class OBJWriter : NonMovable, NonCopyable {
|
|||
Span<int> normal_indices,
|
||||
bool flip) const;
|
||||
/**
|
||||
* Write one line of polygon indices as "f v1/vt1 v2/vt2 ...".
|
||||
* Write one line of face indices as "f v1/vt1 v2/vt2 ...".
|
||||
*/
|
||||
void write_vert_uv_indices(FormatHandler &fh,
|
||||
const IndexOffsets &offsets,
|
||||
|
@ -151,7 +151,7 @@ class OBJWriter : NonMovable, NonCopyable {
|
|||
Span<int> /*normal_indices*/,
|
||||
bool flip) const;
|
||||
/**
|
||||
* Write one line of polygon indices as "f v1 v2 ...".
|
||||
* Write one line of face indices as "f v1 v2 ...".
|
||||
*/
|
||||
void write_vert_indices(FormatHandler &fh,
|
||||
const IndexOffsets &offsets,
|
||||
|
|
|
@ -85,27 +85,27 @@ class FormatHandler : NonCopyable, NonMovable {
|
|||
{
|
||||
write_impl("vn {:.4f} {:.4f} {:.4f}\n", x, y, z);
|
||||
}
|
||||
void write_obj_poly_begin()
|
||||
void write_obj_face_begin()
|
||||
{
|
||||
write_impl("f");
|
||||
}
|
||||
void write_obj_poly_end()
|
||||
void write_obj_face_end()
|
||||
{
|
||||
write_obj_newline();
|
||||
}
|
||||
void write_obj_poly_v_uv_normal(int v, int uv, int n)
|
||||
void write_obj_face_v_uv_normal(int v, int uv, int n)
|
||||
{
|
||||
write_impl(" {}/{}/{}", v, uv, n);
|
||||
}
|
||||
void write_obj_poly_v_normal(int v, int n)
|
||||
void write_obj_face_v_normal(int v, int n)
|
||||
{
|
||||
write_impl(" {}//{}", v, n);
|
||||
}
|
||||
void write_obj_poly_v_uv(int v, int uv)
|
||||
void write_obj_face_v_uv(int v, int uv)
|
||||
{
|
||||
write_impl(" {}/{}", v, uv);
|
||||
}
|
||||
void write_obj_poly_v(int v)
|
||||
void write_obj_face_v(int v)
|
||||
{
|
||||
write_impl(" {}", v);
|
||||
}
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
#include "BKE_mesh_mapping.hh"
|
||||
#include "BKE_object.hh"
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_sort.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
|
@ -48,7 +50,6 @@ OBJMesh::OBJMesh(Depsgraph *depsgraph, const OBJExportParams &export_params, Obj
|
|||
}
|
||||
|
||||
if (export_mesh_) {
|
||||
mesh_positions_ = export_mesh_->vert_positions();
|
||||
mesh_edges_ = export_mesh_->edges();
|
||||
mesh_faces_ = export_mesh_->faces();
|
||||
mesh_corner_verts_ = export_mesh_->corner_verts();
|
||||
|
@ -89,7 +90,6 @@ void OBJMesh::set_mesh(Mesh *mesh)
|
|||
}
|
||||
owned_export_mesh_ = mesh;
|
||||
export_mesh_ = owned_export_mesh_;
|
||||
mesh_positions_ = mesh->vert_positions();
|
||||
mesh_edges_ = mesh->edges();
|
||||
mesh_faces_ = mesh->faces();
|
||||
mesh_corner_verts_ = mesh->corner_verts();
|
||||
|
@ -104,14 +104,14 @@ void OBJMesh::clear()
|
|||
owned_export_mesh_ = nullptr;
|
||||
}
|
||||
export_mesh_ = nullptr;
|
||||
loop_to_uv_index_ = {};
|
||||
corner_to_uv_index_ = {};
|
||||
uv_coords_.clear_and_shrink();
|
||||
loop_to_normal_index_ = {};
|
||||
normal_coords_.clear_and_shrink();
|
||||
poly_order_ = {};
|
||||
if (poly_smooth_groups_) {
|
||||
MEM_freeN(poly_smooth_groups_);
|
||||
poly_smooth_groups_ = nullptr;
|
||||
corner_to_normal_index_ = {};
|
||||
normal_coords_ = {};
|
||||
face_order_ = {};
|
||||
if (face_smooth_groups_) {
|
||||
MEM_freeN(face_smooth_groups_);
|
||||
face_smooth_groups_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,17 +195,12 @@ int16_t OBJMesh::tot_materials() const
|
|||
return this->materials.size();
|
||||
}
|
||||
|
||||
int OBJMesh::tot_normal_indices() const
|
||||
{
|
||||
return tot_normal_indices_;
|
||||
}
|
||||
|
||||
int OBJMesh::ith_smooth_group(const int face_index) const
|
||||
{
|
||||
/* Calculate smooth groups first: #OBJMesh::calc_smooth_groups. */
|
||||
BLI_assert(tot_smooth_groups_ != -NEGATIVE_INIT);
|
||||
BLI_assert(poly_smooth_groups_);
|
||||
return poly_smooth_groups_[face_index];
|
||||
BLI_assert(face_smooth_groups_);
|
||||
return face_smooth_groups_[face_index];
|
||||
}
|
||||
|
||||
void OBJMesh::calc_smooth_groups(const bool use_bitflags)
|
||||
|
@ -213,7 +208,7 @@ void OBJMesh::calc_smooth_groups(const bool use_bitflags)
|
|||
const bke::AttributeAccessor attributes = export_mesh_->attributes();
|
||||
const VArraySpan sharp_edges = *attributes.lookup<bool>("sharp_edge", bke::AttrDomain::Edge);
|
||||
const VArraySpan sharp_faces = *attributes.lookup<bool>("sharp_face", bke::AttrDomain::Face);
|
||||
poly_smooth_groups_ = BKE_mesh_calc_smoothgroups(mesh_edges_.size(),
|
||||
face_smooth_groups_ = BKE_mesh_calc_smoothgroups(mesh_edges_.size(),
|
||||
mesh_faces_,
|
||||
export_mesh_->corner_edges(),
|
||||
sharp_edges,
|
||||
|
@ -222,7 +217,7 @@ void OBJMesh::calc_smooth_groups(const bool use_bitflags)
|
|||
use_bitflags);
|
||||
}
|
||||
|
||||
void OBJMesh::calc_poly_order()
|
||||
void OBJMesh::calc_face_order()
|
||||
{
|
||||
const bke::AttributeAccessor attributes = export_mesh_->attributes();
|
||||
const VArray<int> material_indices = *attributes.lookup_or_default<int>(
|
||||
|
@ -232,13 +227,10 @@ void OBJMesh::calc_poly_order()
|
|||
}
|
||||
const VArraySpan<int> material_indices_span(material_indices);
|
||||
|
||||
poly_order_.reinitialize(material_indices_span.size());
|
||||
for (const int i : material_indices_span.index_range()) {
|
||||
poly_order_[i] = i;
|
||||
}
|
||||
|
||||
/* Sort polygons by their material index. */
|
||||
blender::parallel_sort(poly_order_.begin(), poly_order_.end(), [&](int a, int b) {
|
||||
/* Sort faces by their material index. */
|
||||
face_order_.reinitialize(material_indices_span.size());
|
||||
array_utils::fill_index_range(face_order_.as_mutable_span());
|
||||
blender::parallel_sort(face_order_.begin(), face_order_.end(), [&](int a, int b) {
|
||||
int mat_a = material_indices_span[a];
|
||||
int mat_b = material_indices_span[b];
|
||||
if (mat_a != mat_b) {
|
||||
|
@ -248,26 +240,21 @@ void OBJMesh::calc_poly_order()
|
|||
});
|
||||
}
|
||||
|
||||
bool OBJMesh::is_ith_poly_smooth(const int face_index) const
|
||||
bool OBJMesh::is_ith_face_smooth(const int face_index) const
|
||||
{
|
||||
return !sharp_faces_[face_index];
|
||||
}
|
||||
|
||||
const char *OBJMesh::get_object_name() const
|
||||
StringRef OBJMesh::get_object_name() const
|
||||
{
|
||||
return object_name_.c_str();
|
||||
return object_name_;
|
||||
}
|
||||
|
||||
const char *OBJMesh::get_object_mesh_name() const
|
||||
StringRef OBJMesh::get_object_mesh_name() const
|
||||
{
|
||||
return export_mesh_->id.name + 2;
|
||||
}
|
||||
|
||||
Span<int> OBJMesh::calc_poly_vertex_indices(const int face_index) const
|
||||
{
|
||||
return mesh_corner_verts_.slice(mesh_faces_[face_index]);
|
||||
}
|
||||
|
||||
void OBJMesh::store_uv_coords_and_indices()
|
||||
{
|
||||
const StringRef active_uv_name = CustomData_get_active_layer_name(&export_mesh_->corner_data,
|
||||
|
@ -289,7 +276,7 @@ void OBJMesh::store_uv_coords_and_indices()
|
|||
uv_to_index.reserve(export_mesh_->verts_num);
|
||||
uv_coords_.reserve(export_mesh_->verts_num);
|
||||
|
||||
loop_to_uv_index_.reinitialize(uv_map.size());
|
||||
corner_to_uv_index_.reinitialize(uv_map.size());
|
||||
|
||||
for (int index = 0; index < int(uv_map.size()); index++) {
|
||||
float2 uv = uv_map[index];
|
||||
|
@ -299,26 +286,10 @@ void OBJMesh::store_uv_coords_and_indices()
|
|||
uv_to_index.add(uv, uv_index);
|
||||
uv_coords_.append(uv);
|
||||
}
|
||||
loop_to_uv_index_[index] = uv_index;
|
||||
corner_to_uv_index_[index] = uv_index;
|
||||
}
|
||||
}
|
||||
|
||||
Span<int> OBJMesh::calc_poly_uv_indices(const int face_index) const
|
||||
{
|
||||
if (uv_coords_.is_empty()) {
|
||||
return {};
|
||||
}
|
||||
BLI_assert(face_index < export_mesh_->faces_num);
|
||||
return loop_to_uv_index_.as_span().slice(mesh_faces_[face_index]);
|
||||
}
|
||||
|
||||
float3 OBJMesh::calc_poly_normal(const int face_index) const
|
||||
{
|
||||
const Span<int> face_verts = mesh_corner_verts_.slice(mesh_faces_[face_index]);
|
||||
const float3 normal = bke::mesh::face_normal_calc(mesh_positions_, face_verts);
|
||||
return math::normalize(world_and_axes_normal_transform_ * normal);
|
||||
}
|
||||
|
||||
/** Round \a f to \a round_digits decimal digits. */
|
||||
static float round_float_to_n_digits(const float f, int round_digits)
|
||||
{
|
||||
|
@ -343,68 +314,59 @@ void OBJMesh::store_normal_coords_and_indices()
|
|||
* Since normals are normalized, there will be no perceptible loss
|
||||
* of precision when rounding to 4 digits. */
|
||||
constexpr int round_digits = 4;
|
||||
int cur_normal_index = 0;
|
||||
Map<float3, int> normal_to_index;
|
||||
VectorSet<float3> unique_normals;
|
||||
/* We don't know how many unique normals there will be, but this is a guess. */
|
||||
normal_to_index.reserve(export_mesh_->faces_num);
|
||||
loop_to_normal_index_.reinitialize(export_mesh_->corners_num);
|
||||
loop_to_normal_index_.fill(-1);
|
||||
unique_normals.reserve(export_mesh_->faces_num);
|
||||
corner_to_normal_index_.reinitialize(export_mesh_->corners_num);
|
||||
|
||||
Span<float3> corner_normals;
|
||||
if (ELEM(export_mesh_->normals_domain(),
|
||||
bke::MeshNormalDomain::Point,
|
||||
bke::MeshNormalDomain::Corner))
|
||||
{
|
||||
corner_normals = export_mesh_->corner_normals();
|
||||
}
|
||||
/* Normals need inverse transpose of the regular matrix to handle non-uniform scale. */
|
||||
const float3x3 transform = world_and_axes_normal_transform_;
|
||||
auto add_normal = [&](const float3 &normal) {
|
||||
const float3 transformed = math::normalize(transform * normal);
|
||||
const float3 rounded = round_float3_to_n_digits(transformed, round_digits);
|
||||
return unique_normals.index_of_or_add(rounded);
|
||||
};
|
||||
|
||||
for (int face_index = 0; face_index < export_mesh_->faces_num; ++face_index) {
|
||||
const IndexRange face = mesh_faces_[face_index];
|
||||
bool need_per_loop_normals = !corner_normals.is_empty() || !(sharp_faces_[face_index]);
|
||||
if (need_per_loop_normals) {
|
||||
for (const int corner : face) {
|
||||
BLI_assert(corner < export_mesh_->corners_num);
|
||||
const float3 normal = math::normalize(world_and_axes_normal_transform_ *
|
||||
corner_normals[corner]);
|
||||
const float3 rounded = round_float3_to_n_digits(normal, round_digits);
|
||||
int loop_norm_index = normal_to_index.lookup_default(rounded, -1);
|
||||
if (loop_norm_index == -1) {
|
||||
loop_norm_index = cur_normal_index++;
|
||||
normal_to_index.add(rounded, loop_norm_index);
|
||||
normal_coords_.append(rounded);
|
||||
switch (export_mesh_->normals_domain()) {
|
||||
case bke::MeshNormalDomain::Face: {
|
||||
const Span<float3> face_normals = export_mesh_->face_normals();
|
||||
for (const int face : mesh_faces_.index_range()) {
|
||||
const int index = add_normal(face_normals[face]);
|
||||
corner_to_normal_index_.as_mutable_span().slice(mesh_faces_[face]).fill(index);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case bke::MeshNormalDomain::Point: {
|
||||
const Span<float3> vert_normals = export_mesh_->vert_normals();
|
||||
Array<int> vert_normal_indices(vert_normals.size());
|
||||
const bke::LooseVertCache &verts_no_face = export_mesh_->verts_no_face();
|
||||
if (verts_no_face.count == 0) {
|
||||
for (const int vert : vert_normals.index_range()) {
|
||||
vert_normal_indices[vert] = add_normal(vert_normals[vert]);
|
||||
}
|
||||
loop_to_normal_index_[corner] = loop_norm_index;
|
||||
}
|
||||
else {
|
||||
for (const int vert : vert_normals.index_range()) {
|
||||
if (!verts_no_face.is_loose_bits[vert]) {
|
||||
vert_normal_indices[vert] = add_normal(vert_normals[vert]);
|
||||
}
|
||||
}
|
||||
}
|
||||
array_utils::gather(vert_normal_indices.as_span(),
|
||||
mesh_corner_verts_,
|
||||
corner_to_normal_index_.as_mutable_span());
|
||||
break;
|
||||
}
|
||||
else {
|
||||
float3 poly_normal = calc_poly_normal(face_index);
|
||||
float3 rounded_poly_normal = round_float3_to_n_digits(poly_normal, round_digits);
|
||||
int poly_norm_index = normal_to_index.lookup_default(rounded_poly_normal, -1);
|
||||
if (poly_norm_index == -1) {
|
||||
poly_norm_index = cur_normal_index++;
|
||||
normal_to_index.add(rounded_poly_normal, poly_norm_index);
|
||||
normal_coords_.append(rounded_poly_normal);
|
||||
}
|
||||
for (const int corner : face) {
|
||||
BLI_assert(corner < export_mesh_->corners_num);
|
||||
loop_to_normal_index_[corner] = poly_norm_index;
|
||||
case bke::MeshNormalDomain::Corner: {
|
||||
const Span<float3> corner_normals = export_mesh_->corner_normals();
|
||||
for (const int corner : corner_normals.index_range()) {
|
||||
corner_to_normal_index_[corner] = add_normal(corner_normals[corner]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
tot_normal_indices_ = cur_normal_index;
|
||||
}
|
||||
|
||||
Vector<int> OBJMesh::calc_poly_normal_indices(const int face_index) const
|
||||
{
|
||||
if (loop_to_normal_index_.is_empty()) {
|
||||
return {};
|
||||
}
|
||||
const IndexRange face = mesh_faces_[face_index];
|
||||
Vector<int> r_poly_normal_indices(face.size());
|
||||
for (const int i : IndexRange(face.size())) {
|
||||
r_poly_normal_indices[i] = loop_to_normal_index_[face[i]];
|
||||
}
|
||||
return r_poly_normal_indices;
|
||||
normal_coords_ = unique_normals.as_span();
|
||||
}
|
||||
|
||||
int OBJMesh::tot_deform_groups() const
|
||||
|
@ -412,7 +374,7 @@ int OBJMesh::tot_deform_groups() const
|
|||
return BLI_listbase_count(&export_mesh_->vertex_group_names);
|
||||
}
|
||||
|
||||
int16_t OBJMesh::get_poly_deform_group_index(const int face_index,
|
||||
int16_t OBJMesh::get_face_deform_group_index(const int face_index,
|
||||
MutableSpan<float> group_weights) const
|
||||
{
|
||||
BLI_assert(face_index < export_mesh_->faces_num);
|
||||
|
@ -444,7 +406,7 @@ int16_t OBJMesh::get_poly_deform_group_index(const int face_index,
|
|||
return max_idx;
|
||||
}
|
||||
|
||||
const char *OBJMesh::get_poly_deform_group_name(const int16_t def_group_index) const
|
||||
const char *OBJMesh::get_face_deform_group_name(const int16_t def_group_index) const
|
||||
{
|
||||
const bDeformGroup &vertex_group = *(static_cast<bDeformGroup *>(
|
||||
BLI_findlink(&export_mesh_->vertex_group_names, def_group_index)));
|
||||
|
|
|
@ -35,7 +35,6 @@ class OBJMesh : NonCopyable {
|
|||
const Mesh *export_mesh_;
|
||||
/** A mesh owned here, if created or modified for the export. May be null. */
|
||||
Mesh *owned_export_mesh_ = nullptr;
|
||||
Span<float3> mesh_positions_;
|
||||
Span<int2> mesh_edges_;
|
||||
OffsetIndices<int> mesh_faces_;
|
||||
Span<int> mesh_corner_verts_;
|
||||
|
@ -49,28 +48,15 @@ class OBJMesh : NonCopyable {
|
|||
float3x3 world_and_axes_normal_transform_;
|
||||
bool mirrored_transform_;
|
||||
|
||||
/**
|
||||
* Per-loop UV index.
|
||||
*/
|
||||
Array<int> loop_to_uv_index_;
|
||||
/*
|
||||
* UV vertices.
|
||||
*/
|
||||
/** Per-corner UV index. */
|
||||
Array<int> corner_to_uv_index_;
|
||||
/** UV vertices. */
|
||||
Vector<float2> uv_coords_;
|
||||
|
||||
/**
|
||||
* Per-loop normal index.
|
||||
*/
|
||||
Array<int> loop_to_normal_index_;
|
||||
/*
|
||||
* Normal coords.
|
||||
*/
|
||||
Vector<float3> normal_coords_;
|
||||
/*
|
||||
* Total number of normal indices (maximum entry, plus 1, in
|
||||
* the loop_to_norm_index_ vector).
|
||||
*/
|
||||
int tot_normal_indices_ = 0;
|
||||
/** Index into #normal_coords_ for every face corner. */
|
||||
Array<int> corner_to_normal_index_;
|
||||
/** De-duplicated normals, indexed by #corner_to_normal_index_. */
|
||||
Array<float3> normal_coords_;
|
||||
/**
|
||||
* Total smooth groups in an object.
|
||||
*/
|
||||
|
@ -78,11 +64,11 @@ class OBJMesh : NonCopyable {
|
|||
/**
|
||||
* Polygon aligned array of their smooth groups.
|
||||
*/
|
||||
int *poly_smooth_groups_ = nullptr;
|
||||
int *face_smooth_groups_ = nullptr;
|
||||
/**
|
||||
* Order in which the polygons should be written into the file (sorted by material index).
|
||||
* Order in which the faces should be written into the file (sorted by material index).
|
||||
*/
|
||||
Array<int> poly_order_;
|
||||
Array<int> face_order_;
|
||||
|
||||
public:
|
||||
Array<const Material *> materials;
|
||||
|
@ -100,7 +86,6 @@ class OBJMesh : NonCopyable {
|
|||
int tot_vertices() const;
|
||||
int tot_faces() const;
|
||||
int tot_uv_vertices() const;
|
||||
int tot_normal_indices() const;
|
||||
int tot_edges() const;
|
||||
int tot_deform_groups() const;
|
||||
bool is_mirrored_transform() const
|
||||
|
@ -115,23 +100,23 @@ class OBJMesh : NonCopyable {
|
|||
|
||||
/**
|
||||
* Calculate smooth groups of a smooth-shaded object.
|
||||
* \return A polygon aligned array of smooth group numbers.
|
||||
* \return A face aligned array of smooth group numbers.
|
||||
*/
|
||||
void calc_smooth_groups(bool use_bitflags);
|
||||
/**
|
||||
* \return Smooth group of the polygon at the given index.
|
||||
* \return Smooth group of the face at the given index.
|
||||
*/
|
||||
int ith_smooth_group(int face_index) const;
|
||||
bool is_ith_poly_smooth(int face_index) const;
|
||||
bool is_ith_face_smooth(int face_index) const;
|
||||
|
||||
/**
|
||||
* Get object name as it appears in the outliner.
|
||||
*/
|
||||
const char *get_object_name() const;
|
||||
StringRef get_object_name() const;
|
||||
/**
|
||||
* Get Object's Mesh's name.
|
||||
*/
|
||||
const char *get_object_mesh_name() const;
|
||||
StringRef get_object_mesh_name() const;
|
||||
|
||||
const float4x4 &get_world_axes_transform() const
|
||||
{
|
||||
|
@ -139,72 +124,86 @@ class OBJMesh : NonCopyable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Calculate vertex indices of all vertices of the polygon at the given index.
|
||||
* Calculate vertex indices of all vertices of the face at the given index.
|
||||
*/
|
||||
Span<int> calc_poly_vertex_indices(int face_index) const;
|
||||
Span<int> calc_face_vert_indices(const int face_index) const
|
||||
{
|
||||
return mesh_corner_verts_.slice(mesh_faces_[face_index]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate UV vertex coordinates of an Object.
|
||||
* Stores the coordinates and UV vertex indices in the member variables.
|
||||
*/
|
||||
void store_uv_coords_and_indices();
|
||||
/* Get UV coordinates computed by store_uv_coords_and_indices. */
|
||||
const Vector<float2> &get_uv_coords() const
|
||||
const Span<float2> get_uv_coords() const
|
||||
{
|
||||
return uv_coords_;
|
||||
}
|
||||
Span<int> calc_poly_uv_indices(int face_index) const;
|
||||
/**
|
||||
* Calculate polygon normal of a polygon at given index.
|
||||
*
|
||||
* Should be used for flat-shaded polygons.
|
||||
*/
|
||||
float3 calc_poly_normal(int face_index) const;
|
||||
Span<int> get_face_uv_indices(const int face_index) const
|
||||
{
|
||||
if (uv_coords_.is_empty()) {
|
||||
return {};
|
||||
}
|
||||
BLI_assert(face_index < mesh_faces_.size());
|
||||
return corner_to_uv_index_.as_span().slice(mesh_faces_[face_index]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the unique normals of the mesh and stores them in a member variable.
|
||||
* Also stores the indices into that vector with for each loop.
|
||||
* Also stores the indices into that vector with for each corner.
|
||||
*/
|
||||
void store_normal_coords_and_indices();
|
||||
/* Get normals calculate by store_normal_coords_and_indices. */
|
||||
const Vector<float3> &get_normal_coords() const
|
||||
Span<float3> get_normal_coords() const
|
||||
{
|
||||
return normal_coords_;
|
||||
}
|
||||
/**
|
||||
* Calculate a polygon's polygon/loop normal indices.
|
||||
* \param face_index: Index of the polygon to calculate indices for.
|
||||
* \return Vector of normal indices, aligned with vertices of polygon.
|
||||
* Calculate a face's face/corner normal indices.
|
||||
* \param face_index: Index of the face to calculate indices for.
|
||||
* \return Span of normal indices, aligned with vertices of face.
|
||||
*/
|
||||
Vector<int> calc_poly_normal_indices(int face_index) const;
|
||||
Span<int> get_face_normal_indices(const int face_index) const
|
||||
{
|
||||
if (corner_to_normal_index_.is_empty()) {
|
||||
return {};
|
||||
}
|
||||
const IndexRange face = mesh_faces_[face_index];
|
||||
return corner_to_normal_index_.as_span().slice(face);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the most representative vertex group of a polygon.
|
||||
* Find the most representative vertex group of a face.
|
||||
*
|
||||
* This adds up vertex group weights, and the group with the largest
|
||||
* weight sum across the polygon is the one returned.
|
||||
* weight sum across the face is the one returned.
|
||||
*
|
||||
* group_weights is temporary storage to avoid reallocations, it must
|
||||
* be the size of amount of vertex groups in the object.
|
||||
*/
|
||||
int16_t get_poly_deform_group_index(int face_index, MutableSpan<float> group_weights) const;
|
||||
int16_t get_face_deform_group_index(int face_index, MutableSpan<float> group_weights) const;
|
||||
/**
|
||||
* Find the name of the vertex deform group at the given index.
|
||||
* The index indices into the #Object.defbase.
|
||||
*/
|
||||
const char *get_poly_deform_group_name(int16_t def_group_index) const;
|
||||
const char *get_face_deform_group_name(int16_t def_group_index) const;
|
||||
|
||||
/**
|
||||
* Calculate the order in which the polygons should be written into the file (sorted by material
|
||||
* Calculate the order in which the faces should be written into the file (sorted by material
|
||||
* index).
|
||||
*/
|
||||
void calc_poly_order();
|
||||
void calc_face_order();
|
||||
|
||||
/**
|
||||
* Remap polygon index according to polygon writing order.
|
||||
* When materials are not being written, the polygon order array
|
||||
* Remap face index according to face writing order.
|
||||
* When materials are not being written, the face order array
|
||||
* might be empty, in which case remap is a no-op.
|
||||
*/
|
||||
int remap_face_index(int i) const
|
||||
{
|
||||
return i < 0 || i >= poly_order_.size() ? i : poly_order_[i];
|
||||
return i < 0 || i >= face_order_.size() ? i : face_order_[i];
|
||||
}
|
||||
|
||||
const Mesh *get_mesh() const
|
||||
|
|
|
@ -185,7 +185,7 @@ static void write_mesh_objects(const Span<std::unique_ptr<OBJMesh>> exportable_a
|
|||
index_offsets.append(offsets);
|
||||
offsets.vertex_offset += obj.tot_vertices();
|
||||
offsets.uv_vertex_offset += obj.tot_uv_vertices();
|
||||
offsets.normal_offset += obj.tot_normal_indices();
|
||||
offsets.normal_offset += obj.get_normal_coords().size();
|
||||
}
|
||||
|
||||
/* Parallel over meshes: main result writing. */
|
||||
|
@ -202,10 +202,10 @@ static void write_mesh_objects(const Span<std::unique_ptr<OBJMesh>> exportable_a
|
|||
obj.calc_smooth_groups(export_params.smooth_groups_bitflags);
|
||||
}
|
||||
if (export_params.export_materials) {
|
||||
obj.calc_poly_order();
|
||||
obj.calc_face_order();
|
||||
}
|
||||
if (export_params.export_normals) {
|
||||
obj_writer.write_poly_normals(fh, obj);
|
||||
obj_writer.write_normals(fh, obj);
|
||||
}
|
||||
if (export_params.export_uv) {
|
||||
obj_writer.write_uv_coords(fh, obj);
|
||||
|
@ -219,7 +219,7 @@ static void write_mesh_objects(const Span<std::unique_ptr<OBJMesh>> exportable_a
|
|||
}
|
||||
return mtl_writer->mtlmaterial_name((*obj_mtlindices)[s]);
|
||||
};
|
||||
obj_writer.write_poly_elements(fh, index_offsets[i], obj, matname_fn);
|
||||
obj_writer.write_face_elements(fh, index_offsets[i], obj, matname_fn);
|
||||
}
|
||||
obj_writer.write_edges_indices(fh, index_offsets[i], obj);
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
namespace blender::io::obj {
|
||||
|
||||
Vector<Vector<int>> fixup_invalid_polygon(Span<float3> vert_positions, Span<int> face_verts)
|
||||
Vector<Vector<int>> fixup_invalid_face(Span<float3> vert_positions, Span<int> face_verts)
|
||||
{
|
||||
using namespace blender::meshintersect;
|
||||
if (face_verts.size() < 3) {
|
||||
|
@ -68,11 +68,9 @@ Vector<Vector<int>> fixup_invalid_polygon(Span<float3> vert_positions, Span<int>
|
|||
int idx = f[i];
|
||||
BLI_assert(idx >= 0 && idx < res.vert_orig.size());
|
||||
if (res.vert_orig[idx].is_empty()) {
|
||||
/* If we have a whole new vertex in the tessellated result,
|
||||
* we won't quite know what to do with it (how to create normal/UV
|
||||
* for it, for example). Such vertices are often due to
|
||||
* self-intersecting polygons. Just skip them from the output
|
||||
* face. */
|
||||
/* If we have a whole new vertex in the tessellated result, we won't quite know what to do
|
||||
* with it (how to create normal/UV for it, for example). Such vertices are often due to
|
||||
* self-intersecting faces. Just skip them from the output face. */
|
||||
}
|
||||
else {
|
||||
/* Vertex corresponds to one or more of the input vertices, use it. */
|
||||
|
|
|
@ -19,18 +19,17 @@ struct OBJImportParams;
|
|||
namespace blender::io::obj {
|
||||
|
||||
/**
|
||||
* Given an invalid polygon (with holes or duplicated vertex indices),
|
||||
* turn it into possibly multiple polygons that are valid.
|
||||
* Given an invalid face (with holes or duplicated vertex indices),
|
||||
* turn it into possibly multiple faces that are valid.
|
||||
*
|
||||
* \param vertex_coords: Polygon's vertex coordinate list.
|
||||
* \param face_vertex_indices: A polygon's indices that index into the given vertex coordinate
|
||||
* \param vert_coords: Polygon's vertex coordinate list.
|
||||
* \param face_vert_indices: A face's indices that index into the given vertex coordinate
|
||||
* list.
|
||||
*
|
||||
* \return List of polygons with each element containing indices of one polygon. The indices
|
||||
* are into face_vertex_indices array.
|
||||
* \return List of faces with each element containing indices of one face. The indices
|
||||
* are into face_vert_indices array.
|
||||
*/
|
||||
Vector<Vector<int>> fixup_invalid_polygon(Span<float3> vertex_coords,
|
||||
Span<int> face_vertex_indices);
|
||||
Vector<Vector<int>> fixup_invalid_face(Span<float3> vert_coords, Span<int> face_vert_indices);
|
||||
|
||||
/**
|
||||
* Apply axes transform to the Object, and clamp object dimensions to the specified value.
|
||||
|
|
|
@ -229,7 +229,7 @@ static void geom_add_polygon(Geometry *geom,
|
|||
const int group_index,
|
||||
const bool shaded_smooth)
|
||||
{
|
||||
PolyElem curr_face;
|
||||
FaceElem curr_face;
|
||||
curr_face.shaded_smooth = shaded_smooth;
|
||||
curr_face.material_index = material_index;
|
||||
if (group_index >= 0) {
|
||||
|
@ -243,7 +243,7 @@ static void geom_add_polygon(Geometry *geom,
|
|||
bool face_valid = true;
|
||||
p = drop_whitespace(p, end);
|
||||
while (p < end && face_valid) {
|
||||
PolyCorner corner;
|
||||
FaceCorner corner;
|
||||
bool got_uv = false, got_normal = false;
|
||||
/* Parse vertex index. */
|
||||
p = parse_int(p, end, INT32_MAX, corner.vert_index, false);
|
||||
|
@ -313,7 +313,7 @@ static void geom_add_polygon(Geometry *geom,
|
|||
|
||||
if (face_valid) {
|
||||
geom->face_elements_.append(curr_face);
|
||||
geom->total_loops_ += curr_face.corner_count_;
|
||||
geom->total_corner_ += curr_face.corner_count_;
|
||||
}
|
||||
else {
|
||||
/* Remove just-added corners for the invalid face. */
|
||||
|
|
|
@ -48,17 +48,16 @@ Object *MeshFromGeometry::create_mesh(Main *bmain,
|
|||
}
|
||||
fixup_invalid_faces();
|
||||
|
||||
/* Total explicitly imported edges, not the ones belonging the polygons to be created. */
|
||||
const int64_t tot_edges{mesh_geometry_.edges_.size()};
|
||||
const int64_t tot_face_elems{mesh_geometry_.face_elements_.size()};
|
||||
const int64_t tot_loops{mesh_geometry_.total_loops_};
|
||||
|
||||
Mesh *mesh = BKE_mesh_new_nomain(tot_verts_object, tot_edges, tot_face_elems, tot_loops);
|
||||
/* Includes explicitly imported edges, not the ones belonging the faces to be created. */
|
||||
Mesh *mesh = BKE_mesh_new_nomain(tot_verts_object,
|
||||
mesh_geometry_.edges_.size(),
|
||||
mesh_geometry_.face_elements_.size(),
|
||||
mesh_geometry_.total_corner_);
|
||||
Object *obj = BKE_object_add_only_object(bmain, OB_MESH, ob_name.c_str());
|
||||
obj->data = BKE_object_obdata_add_from_type(bmain, OB_MESH, ob_name.c_str());
|
||||
|
||||
create_vertices(mesh);
|
||||
create_faces_loops(mesh, import_params.import_vertex_groups && !import_params.use_split_groups);
|
||||
create_faces(mesh, import_params.import_vertex_groups && !import_params.use_split_groups);
|
||||
create_edges(mesh);
|
||||
create_uv_verts(mesh);
|
||||
create_normals(mesh);
|
||||
|
@ -85,11 +84,11 @@ Object *MeshFromGeometry::create_mesh(Main *bmain,
|
|||
void MeshFromGeometry::fixup_invalid_faces()
|
||||
{
|
||||
for (int64_t face_idx = 0; face_idx < mesh_geometry_.face_elements_.size(); ++face_idx) {
|
||||
const PolyElem &curr_face = mesh_geometry_.face_elements_[face_idx];
|
||||
const FaceElem &curr_face = mesh_geometry_.face_elements_[face_idx];
|
||||
|
||||
if (curr_face.corner_count_ < 3) {
|
||||
/* Skip and remove faces that have fewer than 3 corners. */
|
||||
mesh_geometry_.total_loops_ -= curr_face.corner_count_;
|
||||
mesh_geometry_.total_corner_ -= curr_face.corner_count_;
|
||||
mesh_geometry_.face_elements_.remove_and_reorder(face_idx);
|
||||
--face_idx;
|
||||
continue;
|
||||
|
@ -122,7 +121,7 @@ void MeshFromGeometry::fixup_invalid_faces()
|
|||
face_normals.reserve(curr_face.corner_count_);
|
||||
for (int i = 0; i < curr_face.corner_count_; ++i) {
|
||||
int corner_idx = curr_face.start_index_ + i;
|
||||
const PolyCorner &corner = mesh_geometry_.face_corners_[corner_idx];
|
||||
const FaceCorner &corner = mesh_geometry_.face_corners_[corner_idx];
|
||||
face_verts.append(corner.vert_index);
|
||||
face_normals.append(corner.vertex_normal_index);
|
||||
face_uvs.append(corner.uv_vert_index);
|
||||
|
@ -132,18 +131,18 @@ void MeshFromGeometry::fixup_invalid_faces()
|
|||
bool face_shaded_smooth = curr_face.shaded_smooth;
|
||||
|
||||
/* Remove the invalid face. */
|
||||
mesh_geometry_.total_loops_ -= curr_face.corner_count_;
|
||||
mesh_geometry_.total_corner_ -= curr_face.corner_count_;
|
||||
mesh_geometry_.face_elements_.remove_and_reorder(face_idx);
|
||||
--face_idx;
|
||||
|
||||
Vector<Vector<int>> new_faces = fixup_invalid_polygon(global_vertices_.vertices, face_verts);
|
||||
Vector<Vector<int>> new_faces = fixup_invalid_face(global_vertices_.vertices, face_verts);
|
||||
|
||||
/* Create the newly formed faces. */
|
||||
for (Span<int> face : new_faces) {
|
||||
if (face.size() < 3) {
|
||||
continue;
|
||||
}
|
||||
PolyElem new_face{};
|
||||
FaceElem new_face{};
|
||||
new_face.vertex_group_index = face_vertex_group;
|
||||
new_face.material_index = face_material;
|
||||
new_face.shaded_smooth = face_shaded_smooth;
|
||||
|
@ -154,7 +153,7 @@ void MeshFromGeometry::fixup_invalid_faces()
|
|||
mesh_geometry_.face_corners_.append({face_verts[idx], face_uvs[idx], face_normals[idx]});
|
||||
}
|
||||
mesh_geometry_.face_elements_.append(new_face);
|
||||
mesh_geometry_.total_loops_ += face.size();
|
||||
mesh_geometry_.total_corner_ += face.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -180,7 +179,7 @@ void MeshFromGeometry::create_vertices(Mesh *mesh)
|
|||
}
|
||||
}
|
||||
|
||||
void MeshFromGeometry::create_faces_loops(Mesh *mesh, bool use_vertex_groups)
|
||||
void MeshFromGeometry::create_faces(Mesh *mesh, bool use_vertex_groups)
|
||||
{
|
||||
MutableSpan<MDeformVert> dverts;
|
||||
const int64_t total_verts = mesh_geometry_.get_vertex_count();
|
||||
|
@ -196,18 +195,17 @@ void MeshFromGeometry::create_faces_loops(Mesh *mesh, bool use_vertex_groups)
|
|||
bke::SpanAttributeWriter<bool> sharp_faces = attributes.lookup_or_add_for_write_span<bool>(
|
||||
"sharp_face", bke::AttrDomain::Face);
|
||||
|
||||
const int64_t tot_face_elems{mesh->faces_num};
|
||||
int tot_loop_idx = 0;
|
||||
int corner_index = 0;
|
||||
|
||||
for (int face_idx = 0; face_idx < tot_face_elems; ++face_idx) {
|
||||
const PolyElem &curr_face = mesh_geometry_.face_elements_[face_idx];
|
||||
for (int face_idx = 0; face_idx < mesh->faces_num; ++face_idx) {
|
||||
const FaceElem &curr_face = mesh_geometry_.face_elements_[face_idx];
|
||||
if (curr_face.corner_count_ < 3) {
|
||||
/* Don't add single vertex face, or edges. */
|
||||
std::cerr << "Face with less than 3 vertices found, skipping." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
face_offsets[face_idx] = tot_loop_idx;
|
||||
face_offsets[face_idx] = corner_index;
|
||||
sharp_faces.span[face_idx] = !curr_face.shaded_smooth;
|
||||
material_indices.span[face_idx] = curr_face.material_index;
|
||||
/* Importing obj files without any materials would result in negative indices, which is not
|
||||
|
@ -217,8 +215,8 @@ void MeshFromGeometry::create_faces_loops(Mesh *mesh, bool use_vertex_groups)
|
|||
}
|
||||
|
||||
for (int idx = 0; idx < curr_face.corner_count_; ++idx) {
|
||||
const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
|
||||
corner_verts[tot_loop_idx] = mesh_geometry_.global_to_local_vertices_.lookup_default(
|
||||
const FaceCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
|
||||
corner_verts[corner_index] = mesh_geometry_.global_to_local_vertices_.lookup_default(
|
||||
curr_corner.vert_index, 0);
|
||||
|
||||
/* Setup vertex group data, if needed. */
|
||||
|
@ -226,13 +224,13 @@ void MeshFromGeometry::create_faces_loops(Mesh *mesh, bool use_vertex_groups)
|
|||
const int group_index = curr_face.vertex_group_index;
|
||||
/* NOTE: face might not belong to any group. */
|
||||
if (group_index >= 0 || true) {
|
||||
MDeformWeight *dw = BKE_defvert_ensure_index(&dverts[corner_verts[tot_loop_idx]],
|
||||
MDeformWeight *dw = BKE_defvert_ensure_index(&dverts[corner_verts[corner_index]],
|
||||
group_index);
|
||||
dw->weight = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
tot_loop_idx++;
|
||||
corner_index++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,7 +265,7 @@ void MeshFromGeometry::create_edges(Mesh *mesh)
|
|||
}
|
||||
|
||||
/* Set argument `update` to true so that existing, explicitly imported edges can be merged
|
||||
* with the new ones created from polygons. */
|
||||
* with the new ones created from faces. */
|
||||
bke::mesh_calc_edges(*mesh, true, false);
|
||||
}
|
||||
|
||||
|
@ -281,22 +279,22 @@ void MeshFromGeometry::create_uv_verts(Mesh *mesh)
|
|||
bke::SpanAttributeWriter<float2> uv_map = attributes.lookup_or_add_for_write_only_span<float2>(
|
||||
"UVMap", bke::AttrDomain::Corner);
|
||||
|
||||
int tot_loop_idx = 0;
|
||||
int corner_index = 0;
|
||||
bool added_uv = false;
|
||||
|
||||
for (const PolyElem &curr_face : mesh_geometry_.face_elements_) {
|
||||
for (const FaceElem &curr_face : mesh_geometry_.face_elements_) {
|
||||
for (int idx = 0; idx < curr_face.corner_count_; ++idx) {
|
||||
const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
|
||||
const FaceCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
|
||||
if (curr_corner.uv_vert_index >= 0 &&
|
||||
curr_corner.uv_vert_index < global_vertices_.uv_vertices.size())
|
||||
{
|
||||
uv_map.span[tot_loop_idx] = global_vertices_.uv_vertices[curr_corner.uv_vert_index];
|
||||
uv_map.span[corner_index] = global_vertices_.uv_vertices[curr_corner.uv_vert_index];
|
||||
added_uv = true;
|
||||
}
|
||||
else {
|
||||
uv_map.span[tot_loop_idx] = {0.0f, 0.0f};
|
||||
uv_map.span[corner_index] = {0.0f, 0.0f};
|
||||
}
|
||||
tot_loop_idx++;
|
||||
corner_index++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -368,27 +366,25 @@ void MeshFromGeometry::create_normals(Mesh *mesh)
|
|||
return;
|
||||
}
|
||||
/* Custom normals can only be stored on face corners. */
|
||||
if (mesh_geometry_.total_loops_ == 0) {
|
||||
if (mesh_geometry_.total_corner_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
float(*corner_normals)[3] = static_cast<float(*)[3]>(
|
||||
MEM_malloc_arrayN(mesh_geometry_.total_loops_, sizeof(float[3]), __func__));
|
||||
int tot_loop_idx = 0;
|
||||
for (const PolyElem &curr_face : mesh_geometry_.face_elements_) {
|
||||
Array<float3> corner_normals(mesh_geometry_.total_corner_);
|
||||
int corner_index = 0;
|
||||
for (const FaceElem &curr_face : mesh_geometry_.face_elements_) {
|
||||
for (int idx = 0; idx < curr_face.corner_count_; ++idx) {
|
||||
const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
|
||||
const FaceCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
|
||||
int n_index = curr_corner.vertex_normal_index;
|
||||
float3 normal(0, 0, 0);
|
||||
if (n_index >= 0 && n_index < global_vertices_.vert_normals.size()) {
|
||||
normal = global_vertices_.vert_normals[n_index];
|
||||
}
|
||||
copy_v3_v3(corner_normals[tot_loop_idx], normal);
|
||||
tot_loop_idx++;
|
||||
corner_normals[corner_index] = normal;
|
||||
corner_index++;
|
||||
}
|
||||
}
|
||||
BKE_mesh_set_custom_normals(mesh, corner_normals);
|
||||
MEM_freeN(corner_normals);
|
||||
BKE_mesh_set_custom_normals(mesh, reinterpret_cast<float(*)[3]>(corner_normals.data()));
|
||||
}
|
||||
|
||||
void MeshFromGeometry::create_colors(Mesh *mesh)
|
||||
|
|
|
@ -42,14 +42,14 @@ class MeshFromGeometry : NonMovable, NonCopyable {
|
|||
/**
|
||||
* OBJ files coming from the wild might have faces that are invalid in Blender
|
||||
* (mostly with duplicate vertex indices, used by some software to indicate
|
||||
* polygons with holes). This method tries to fix them up.
|
||||
* faces with holes). This method tries to fix them up.
|
||||
*/
|
||||
void fixup_invalid_faces();
|
||||
void create_vertices(Mesh *mesh);
|
||||
/**
|
||||
* Create polygons for the Mesh, set smooth shading flags, Materials.
|
||||
* Create faces for the Mesh, set smooth shading flags, Materials.
|
||||
*/
|
||||
void create_faces_loops(Mesh *mesh, bool use_vertex_groups);
|
||||
void create_faces(Mesh *mesh, bool use_vertex_groups);
|
||||
/**
|
||||
* Add explicitly imported OBJ edges to the mesh.
|
||||
*/
|
||||
|
|
|
@ -44,7 +44,7 @@ struct GlobalVertices {
|
|||
/**
|
||||
* A face's corner in an OBJ file. In Blender, it translates to a corner vertex.
|
||||
*/
|
||||
struct PolyCorner {
|
||||
struct FaceCorner {
|
||||
/* These indices range from zero to total vertices in the OBJ file. */
|
||||
int vert_index;
|
||||
/* -1 is to indicate absence of UV vertices. Only < 0 condition should be checked since
|
||||
|
@ -53,7 +53,7 @@ struct PolyCorner {
|
|||
int vertex_normal_index = -1;
|
||||
};
|
||||
|
||||
struct PolyElem {
|
||||
struct FaceElem {
|
||||
int vertex_group_index = -1;
|
||||
int material_index = -1;
|
||||
bool shaded_smooth = false;
|
||||
|
@ -101,13 +101,13 @@ struct Geometry {
|
|||
/* Loose edges in the file. */
|
||||
Vector<int2> edges_;
|
||||
|
||||
Vector<PolyCorner> face_corners_;
|
||||
Vector<PolyElem> face_elements_;
|
||||
Vector<FaceCorner> face_corners_;
|
||||
Vector<FaceElem> face_elements_;
|
||||
|
||||
bool has_invalid_faces_ = false;
|
||||
bool has_vertex_groups_ = false;
|
||||
NurbsElement nurbs_element_;
|
||||
int total_loops_ = 0;
|
||||
int total_corner_ = 0;
|
||||
|
||||
int get_vertex_count() const
|
||||
{
|
||||
|
|
|
@ -40,8 +40,8 @@ namespace blender::io::obj {
|
|||
struct Expectation {
|
||||
std::string name;
|
||||
short type; /* OB_MESH, ... */
|
||||
int totvert, mesh_totedge_or_curve_endp, mesh_faces_num_or_curve_order,
|
||||
mesh_totloop_or_curve_cyclic;
|
||||
int totvert, mesh_edges_num_or_curve_endp, mesh_faces_num_or_curve_order,
|
||||
mesh_corner_num_or_curve_cyclic;
|
||||
float3 vert_first, vert_last;
|
||||
float3 normal_first;
|
||||
float2 uv_first;
|
||||
|
@ -131,9 +131,9 @@ class OBJImportTest : public BlendfileLoadingBaseTest {
|
|||
if (object->type == OB_MESH) {
|
||||
Mesh *mesh = BKE_object_get_evaluated_mesh(object);
|
||||
EXPECT_EQ(mesh->verts_num, exp.totvert);
|
||||
EXPECT_EQ(mesh->edges_num, exp.mesh_totedge_or_curve_endp);
|
||||
EXPECT_EQ(mesh->edges_num, exp.mesh_edges_num_or_curve_endp);
|
||||
EXPECT_EQ(mesh->faces_num, exp.mesh_faces_num_or_curve_order);
|
||||
EXPECT_EQ(mesh->corners_num, exp.mesh_totloop_or_curve_cyclic);
|
||||
EXPECT_EQ(mesh->corners_num, exp.mesh_corner_num_or_curve_cyclic);
|
||||
const Span<float3> positions = mesh->vert_positions();
|
||||
EXPECT_V3_NEAR(positions.first(), exp.vert_first, 0.0001f);
|
||||
EXPECT_V3_NEAR(positions.last(), exp.vert_last, 0.0001f);
|
||||
|
@ -167,10 +167,10 @@ class OBJImportTest : public BlendfileLoadingBaseTest {
|
|||
const Nurb *nurb = static_cast<const Nurb *>(BLI_findlink(&curve->nurb, 0));
|
||||
int endpoint = (nurb->flagu & CU_NURB_ENDPOINT) ? 1 : 0;
|
||||
EXPECT_EQ(nurb->orderu, exp.mesh_faces_num_or_curve_order);
|
||||
EXPECT_EQ(endpoint, exp.mesh_totedge_or_curve_endp);
|
||||
EXPECT_EQ(endpoint, exp.mesh_edges_num_or_curve_endp);
|
||||
/* Cyclic flag is not set by the importer yet. */
|
||||
// int cyclic = (nurb->flagu & CU_NURB_CYCLIC) ? 1 : 0;
|
||||
// EXPECT_EQ(cyclic, exp.mesh_totloop_or_curve_cyclic);
|
||||
// EXPECT_EQ(cyclic, exp.mesh_corner_num_or_curve_cyclic);
|
||||
}
|
||||
if (!exp.first_mat.empty()) {
|
||||
Material *mat = BKE_object_material_get(object, 1);
|
||||
|
|
Loading…
Reference in New Issue