Compare commits

..

106 Commits

Author SHA1 Message Date
e1c1c65578 cleanup 2021-10-31 14:28:01 +01:00
2154631e85 cleanups 2021-10-31 14:20:04 +01:00
71b7e59e28 remove print 2021-10-31 14:09:16 +01:00
7106fe299e cleanup 2021-10-31 14:06:48 +01:00
4b8e52bdbc add timeit include 2021-10-31 13:50:05 +01:00
462d3bde6e fix 2021-10-31 13:36:24 +01:00
e354ff5b86 cleanup 2021-10-31 13:27:43 +01:00
27da814444 add test 2021-10-31 13:08:01 +01:00
493571c3c3 add some noexcepts 2021-10-31 13:02:49 +01:00
a6eb04cce4 cleanups 2021-10-31 12:39:46 +01:00
139f69b3b9 Merge branch 'master' into virtual-array-value-type 2021-10-31 12:33:45 +01:00
Christoph Lendenfeld
4e502bb6d2 Merge branch 'blender-v3.0-release' 2021-10-31 11:10:11 +00:00
Christoph Lendenfeld
a06abbac92 fix: pose slider consistent color
Change the way the pose slider gets its color so it is consistent
 between editors
Previously the highlight  color would be different between
the 3D viewport and the graph editor.

Reviewed by: Sybren A. Stüvel, Pablo Vazquez
Differential Revision: https://developer.blender.org/D11878
Ref: D11878
2021-10-31 11:08:57 +00:00
ae9052a33e Cleanup: Simplify logic for adding grid in points to volume node
Instead of creating a separate grid first and then merging the points
to volume grid, use the recently added `BKE_volume_grid_add_vdb`
helper function for this purpose.
2021-10-30 17:26:18 -05:00
8a1cc2f291 Merge branch 'master' into virtual-array-value-type 2021-10-30 23:04:24 +02:00
572fa82cc9 cleanup 2021-10-30 23:04:04 +02:00
9cfffe8468 UI: Open File Browser with Thumbnails for Fonts
When browsing to open a font file, open File Browser in Thumbnail View
and sorting by name, instead of using the last-used states.

See D13040 for more details.

Differential Revision: https://developer.blender.org/D13040

Reviewed by Julian Eisel
2021-10-30 14:02:34 -07:00
c8ab4dcce1 cleanup 2021-10-30 23:02:05 +02:00
55a6af1c1e cleanup 2021-10-30 22:52:28 +02:00
0827245a91 cleanup 2021-10-30 22:49:26 +02:00
2f284f42b6 progress 2021-10-30 22:38:35 +02:00
1131bf6ec6 progress 2021-10-30 22:32:48 +02:00
2b0e890a55 comments 2021-10-30 22:20:04 +02:00
c4d3380d2e progress 2021-10-30 21:36:11 +02:00
05cef9da88 cleanup 2021-10-30 21:20:15 +02:00
43a53f97c4 cleanup tests 2021-10-30 21:17:24 +02:00
1d7a893d12 cleanup 2021-10-30 21:07:33 +02:00
5e60ba93f7 progress 2021-10-30 21:05:56 +02:00
65258dc98f progress 2021-10-30 21:02:49 +02:00
d449e66c1d cleanup 2021-10-30 20:59:26 +02:00
01fe332b9d progress 2021-10-30 20:54:00 +02:00
d6b0f3a3a4 progress 2021-10-30 20:42:37 +02:00
928b7b5340 cleanup 2021-10-30 20:35:26 +02:00
aa22839aa1 progress 2021-10-30 20:31:53 +02:00
78daf1b697 progress 2021-10-30 20:24:20 +02:00
dcd9b21d07 progress 2021-10-30 20:21:48 +02:00
42b350ed7f progress 2021-10-30 20:14:43 +02:00
e79c7e088e progress 2021-10-30 19:59:35 +02:00
99c5b788eb progress 2021-10-30 19:55:53 +02:00
a2b5a74d49 progress 2021-10-30 19:46:17 +02:00
84626dfa16 progress 2021-10-30 19:40:58 +02:00
3d0abb3be9 progress 2021-10-30 19:31:19 +02:00
9b6c13e66c progress 2021-10-30 19:10:44 +02:00
7f4273d373 progress 2021-10-30 18:50:28 +02:00
d1b8d6acd3 progress 2021-10-30 18:39:34 +02:00
2267f19486 progress 2021-10-30 18:18:27 +02:00
acd8874205 progress 2021-10-30 18:13:52 +02:00
a69bd34fad fix 2021-10-30 16:56:05 +02:00
2516fc953a Merge branch 'blender-v3.0-release' 2021-10-30 16:54:06 +02:00
50f93cbf52 cleanup 2021-10-25 00:35:58 +02:00
8af541dcfd Geometry Nodes: Refactor virtual array system.
Differential Revision: https://developer.blender.org/D12986
2021-10-25 00:16:40 +02:00
f8c0682a67 progress 2021-10-24 23:58:16 +02:00
b40e753c81 fix 2021-10-24 23:20:32 +02:00
2cff0676af cleanup 2021-10-24 23:13:25 +02:00
b0444a347b cleanup 2021-10-24 23:11:47 +02:00
becfc547b9 cleanup 2021-10-24 23:08:22 +02:00
6a558a18f9 progress 2021-10-24 23:02:51 +02:00
c066102f30 progress 2021-10-24 22:55:05 +02:00
ba49545060 progress 2021-10-24 22:50:52 +02:00
ca70c0521a progress 2021-10-24 22:44:54 +02:00
4323b3f592 progress 2021-10-24 22:28:43 +02:00
d1e6606d4d progress 2021-10-24 22:10:47 +02:00
b64ad0b30a cleanup 2021-10-24 22:00:32 +02:00
ed4b15ecd3 cleanup 2021-10-24 21:57:56 +02:00
f79a673bf1 cleanup 2021-10-24 21:44:08 +02:00
1438f16663 fix 2021-10-24 21:09:58 +02:00
d613451c3a Merge branch 'master' into virtual-array-value-type 2021-10-24 20:56:50 +02:00
3f8ac0e5ef Merge branch 'master' into virtual-array-value-type 2021-10-24 17:27:01 +02:00
56bd1f46bd fixes 2021-10-24 17:20:54 +02:00
1e4cbec720 fix 2021-10-24 17:09:24 +02:00
f9b669c588 fixes 2021-10-24 16:45:02 +02:00
1ac0a2db11 cleanup 2021-10-24 16:38:23 +02:00
771cce22f1 cleanup 2021-10-24 16:34:50 +02:00
c1a1046ed7 cleanup 2021-10-24 16:29:33 +02:00
f430e7850a cleanup 2021-10-24 16:24:58 +02:00
116eb09681 fix 2021-10-24 16:23:35 +02:00
841d393c15 cleanup 2021-10-24 16:23:17 +02:00
bc1acbcc5a cleanup 2021-10-24 16:15:11 +02:00
cd49f68db6 cleanup 2021-10-24 16:08:02 +02:00
5b51065347 cleanup 2021-10-24 16:05:15 +02:00
e736fe67c9 cleanup 2021-10-24 15:59:12 +02:00
1d20f60616 fixes 2021-10-24 15:27:21 +02:00
91b58c95ac fixes 2021-10-24 14:57:51 +02:00
955b01e359 fix assignment operators 2021-10-24 13:59:19 +02:00
1b3758f205 cleanup 2021-10-24 13:59:11 +02:00
33870b21f3 fix output attribute 2021-10-24 13:05:45 +02:00
e49f70745b fix dst fields 2021-10-24 12:56:11 +02:00
c48a99542d compiling works again 2021-10-24 02:26:08 +02:00
68b5670268 more renaming 2021-10-23 22:50:37 +02:00
dea268de72 progress 2021-10-23 22:42:30 +02:00
399463c548 progress 2021-10-23 22:35:12 +02:00
1d94de4ac3 progress 2021-10-23 20:22:49 +02:00
0453361d20 progress 2021-10-23 20:11:19 +02:00
b21a3ae6ad rename to *Impl 2021-10-23 19:15:29 +02:00
edb8ccf31f support converting VMutableArray to VArray 2021-10-23 19:14:14 +02:00
7af712c343 add mutable virtual array 2021-10-23 19:06:53 +02:00
a9e0cb6ec8 rename to VMutableArrayImpl 2021-10-23 18:53:34 +02:00
5a7b30f8f9 cleanup 2021-10-23 18:52:44 +02:00
024c07af84 more virtual array tests 2021-10-23 18:10:11 +02:00
c256298924 rename VArray to VArrayImpl 2021-10-23 16:48:22 +02:00
a11950531f add comments 2021-10-23 16:36:28 +02:00
ae01d1db98 add extra type info 2021-10-23 16:08:20 +02:00
f3a5b31196 fix assign any 2021-10-23 15:54:56 +02:00
8d1a5fcfea add tests 2021-10-23 15:45:49 +02:00
39c4103f9e move methods 2021-10-23 15:22:28 +02:00
4e03ac6b25 initial Any type 2021-10-23 14:50:11 +02:00
141 changed files with 3602 additions and 9755 deletions

View File

@@ -463,7 +463,6 @@ class TOPBAR_MT_file_import(Menu):
bl_owner_use_filter = False
def draw(self, _context):
self.layout.operator("wm.obj_import", text="Wavefront OBJ (.obj) - New")
if bpy.app.build_options.collada:
self.layout.operator("wm.collada_import",
text="Collada (Default) (.dae)")
@@ -482,7 +481,6 @@ class TOPBAR_MT_file_export(Menu):
bl_owner_use_filter = False
def draw(self, _context):
self.layout.operator("wm.obj_export", text="Wavefront OBJ (.obj) - New")
if bpy.app.build_options.collada:
self.layout.operator("wm.collada_export",
text="Collada (Default) (.dae)")

View File

@@ -115,10 +115,10 @@ struct AttributeInitDefault : public AttributeInit {
* Note that this can be used to fill the new attribute with the default
*/
struct AttributeInitVArray : public AttributeInit {
const blender::fn::GVArray *varray;
blender::fn::GVArray varray;
AttributeInitVArray(const blender::fn::GVArray *varray)
: AttributeInit(Type::VArray), varray(varray)
AttributeInitVArray(blender::fn::GVArray varray)
: AttributeInit(Type::VArray), varray(std::move(varray))
{
}
};
@@ -150,9 +150,7 @@ namespace blender::bke {
using fn::CPPType;
using fn::GVArray;
using fn::GVArrayPtr;
using fn::GVMutableArray;
using fn::GVMutableArrayPtr;
const CPPType *custom_data_type_to_cpp_type(const CustomDataType type);
CustomDataType cpp_type_to_custom_data_type(const CPPType &type);
@@ -164,14 +162,14 @@ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains)
*/
struct ReadAttributeLookup {
/* The virtual array that is used to read from this attribute. */
GVArrayPtr varray;
GVArray varray;
/* Domain the attribute lives on in the geometry. */
AttributeDomain domain;
/* Convenience function to check if the attribute has been found. */
operator bool() const
{
return this->varray.get() != nullptr;
return this->varray;
}
};
@@ -180,7 +178,7 @@ struct ReadAttributeLookup {
*/
struct WriteAttributeLookup {
/* The virtual array that is used to read from and write to the attribute. */
GVMutableArrayPtr varray;
GVMutableArray varray;
/* Domain the attributes lives on in the geometry. */
AttributeDomain domain;
/* Call this after changing the attribute to invalidate caches that depend on this attribute. */
@@ -189,7 +187,7 @@ struct WriteAttributeLookup {
/* Convenience function to check if the attribute has been found. */
operator bool() const
{
return this->varray.get() != nullptr;
return this->varray;
}
};
@@ -209,7 +207,7 @@ class OutputAttribute {
using SaveFn = std::function<void(OutputAttribute &)>;
private:
GVMutableArrayPtr varray_;
GVMutableArray varray_;
AttributeDomain domain_ = ATTR_DOMAIN_AUTO;
SaveFn save_;
std::unique_ptr<fn::GVMutableArray_GSpan> optional_span_varray_;
@@ -219,7 +217,7 @@ class OutputAttribute {
public:
OutputAttribute();
OutputAttribute(OutputAttribute &&other);
OutputAttribute(GVMutableArrayPtr varray,
OutputAttribute(GVMutableArray varray,
AttributeDomain domain,
SaveFn save,
const bool ignore_old_values);
@@ -229,7 +227,7 @@ class OutputAttribute {
operator bool() const;
GVMutableArray &operator*();
GVMutableArray *operator->();
fn::GVMutableArray *operator->();
GVMutableArray &varray();
AttributeDomain domain() const;
const CPPType &cpp_type() const;
@@ -247,16 +245,14 @@ class OutputAttribute {
template<typename T> class OutputAttribute_Typed {
private:
OutputAttribute attribute_;
std::unique_ptr<fn::GVMutableArray_Typed<T>> optional_varray_;
VMutableArray<T> *varray_ = nullptr;
VMutableArray<T> varray_;
public:
OutputAttribute_Typed();
OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute))
{
if (attribute_) {
optional_varray_ = std::make_unique<fn::GVMutableArray_Typed<T>>(attribute_.varray());
varray_ = &**optional_varray_;
varray_ = attribute_.varray().template typed<T>();
}
}
@@ -275,22 +271,22 @@ template<typename T> class OutputAttribute_Typed {
operator bool() const
{
return varray_ != nullptr;
return varray_;
}
VMutableArray<T> &operator*()
{
return *varray_;
return varray_;
}
VMutableArray<T> *operator->()
{
return varray_;
return &varray_;
}
VMutableArray<T> &varray()
{
return *varray_;
return varray_;
}
AttributeDomain domain() const
@@ -351,18 +347,17 @@ class CustomDataAttributes {
std::optional<blender::fn::GSpan> get_for_read(const AttributeIDRef &attribute_id) const;
blender::fn::GVArrayPtr get_for_read(const AttributeIDRef &attribute_id,
const CustomDataType data_type,
const void *default_value) const;
blender::fn::GVArray get_for_read(const AttributeIDRef &attribute_id,
const CustomDataType data_type,
const void *default_value) const;
template<typename T>
blender::fn::GVArray_Typed<T> get_for_read(const AttributeIDRef &attribute_id,
const T &default_value) const
blender::VArray<T> get_for_read(const AttributeIDRef &attribute_id, const T &default_value) const
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
GVArrayPtr varray = this->get_for_read(attribute_id, type, &default_value);
return blender::fn::GVArray_Typed<T>(std::move(varray));
GVArray varray = this->get_for_read(attribute_id, type, &default_value);
return varray.typed<T>();
}
std::optional<blender::fn::GMutableSpan> get_for_write(const AttributeIDRef &attribute_id);
@@ -460,7 +455,7 @@ inline bool AttributeIDRef::should_be_kept() const
inline OutputAttribute::OutputAttribute() = default;
inline OutputAttribute::OutputAttribute(OutputAttribute &&other) = default;
inline OutputAttribute::OutputAttribute(GVMutableArrayPtr varray,
inline OutputAttribute::OutputAttribute(GVMutableArray varray,
AttributeDomain domain,
SaveFn save,
const bool ignore_old_values)
@@ -473,22 +468,22 @@ inline OutputAttribute::OutputAttribute(GVMutableArrayPtr varray,
inline OutputAttribute::operator bool() const
{
return varray_.get() != nullptr;
return varray_;
}
inline GVMutableArray &OutputAttribute::operator*()
{
return *varray_;
return varray_;
}
inline GVMutableArray *OutputAttribute::operator->()
inline fn::GVMutableArray *OutputAttribute::operator->()
{
return varray_.get();
return &varray_;
}
inline GVMutableArray &OutputAttribute::varray()
{
return *varray_;
return varray_;
}
inline AttributeDomain OutputAttribute::domain() const
@@ -498,7 +493,7 @@ inline AttributeDomain OutputAttribute::domain() const
inline const CPPType &OutputAttribute::cpp_type() const
{
return varray_->type();
return varray_.type();
}
inline CustomDataType OutputAttribute::custom_data_type() const

View File

@@ -119,10 +119,21 @@ class GeometryComponent {
/* Get a read-only attribute for the domain based on the given attribute. This can be used to
* interpolate from one domain to another.
* Returns null if the interpolation is not implemented. */
virtual std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
std::unique_ptr<blender::fn::GVArray> varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const;
blender::fn::GVArray attribute_try_adapt_domain(const blender::fn::GVArray &varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const
{
return this->attribute_try_adapt_domain_impl(varray, from_domain, to_domain);
}
template<typename T>
blender::VArray<T> attribute_try_adapt_domain(const blender::VArray<T> &varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const
{
return this->attribute_try_adapt_domain_impl(varray, from_domain, to_domain)
.template typed<T>();
}
/* Returns true when the attribute has been deleted. */
bool attribute_try_delete(const blender::bke::AttributeIDRef &attribute_id);
@@ -146,16 +157,15 @@ class GeometryComponent {
/* Get a virtual array to read the data of an attribute on the given domain and data type.
* Returns null when the attribute does not exist or cannot be converted to the requested domain
* and data type. */
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type) const;
blender::fn::GVArray attribute_try_get_for_read(const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type) const;
/* Get a virtual array to read the data of an attribute on the given domain. The data type is
* left unchanged. Returns null when the attribute does not exist or cannot be adapted to the
* requested domain. */
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain) const;
blender::fn::GVArray attribute_try_get_for_read(const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain) const;
/* Get a virtual array to read data of an attribute with the given data type. The domain is
* left unchanged. Returns null when the attribute does not exist or cannot be converted to the
@@ -165,25 +175,22 @@ class GeometryComponent {
/* Get a virtual array to read the data of an attribute. If that is not possible, the returned
* virtual array will contain a default value. This never returns null. */
std::unique_ptr<blender::fn::GVArray> attribute_get_for_read(
const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value = nullptr) const;
blender::fn::GVArray attribute_get_for_read(const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value = nullptr) const;
/* Should be used instead of the method above when the requested data type is known at compile
* time for better type safety. */
template<typename T>
blender::fn::GVArray_Typed<T> attribute_get_for_read(
const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const T &default_value) const
blender::VArray<T> attribute_get_for_read(const blender::bke::AttributeIDRef &attribute_id,
const AttributeDomain domain,
const T &default_value) const
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
std::unique_ptr varray = this->attribute_get_for_read(
attribute_id, domain, type, &default_value);
return blender::fn::GVArray_Typed<T>(std::move(varray));
return this->attribute_get_for_read(attribute_id, domain, type, &default_value)
.template typed<T>();
}
/**
@@ -234,6 +241,11 @@ class GeometryComponent {
private:
virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const;
virtual blender::fn::GVArray attribute_try_adapt_domain_impl(
const blender::fn::GVArray &varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const;
};
template<typename T>
@@ -391,10 +403,6 @@ class MeshComponent : public GeometryComponent {
Mesh *get_for_write();
int attribute_domain_size(const AttributeDomain domain) const final;
std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
std::unique_ptr<blender::fn::GVArray> varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const final;
bool is_empty() const final;
@@ -405,6 +413,11 @@ class MeshComponent : public GeometryComponent {
private:
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
blender::fn::GVArray attribute_try_adapt_domain_impl(
const blender::fn::GVArray &varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const final;
};
/** A geometry component that stores a point cloud. */
@@ -469,10 +482,6 @@ class CurveComponent : public GeometryComponent {
CurveEval *get_for_write();
int attribute_domain_size(const AttributeDomain domain) const final;
std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
std::unique_ptr<blender::fn::GVArray> varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const final;
bool is_empty() const final;
@@ -485,6 +494,11 @@ class CurveComponent : public GeometryComponent {
private:
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
blender::fn::GVArray attribute_try_adapt_domain_impl(
const blender::fn::GVArray &varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const final;
};
class InstanceReference {
@@ -759,9 +773,9 @@ class AttributeFieldInput : public fn::FieldInput {
return name_;
}
const GVArray *get_varray_for_context(const fn::FieldContext &context,
IndexMask mask,
ResourceScope &scope) const override;
GVArray get_varray_for_context(const fn::FieldContext &context,
IndexMask mask,
ResourceScope &scope) const override;
std::string socket_inspection_name() const override;
@@ -776,9 +790,9 @@ class IDAttributeFieldInput : public fn::FieldInput {
category_ = Category::Generated;
}
const GVArray *get_varray_for_context(const fn::FieldContext &context,
IndexMask mask,
ResourceScope &scope) const override;
GVArray get_varray_for_context(const fn::FieldContext &context,
IndexMask mask,
ResourceScope &scope) const override;
std::string socket_inspection_name() const override;
@@ -815,9 +829,9 @@ class AnonymousAttributeFieldInput : public fn::FieldInput {
return fn::Field<T>{field_input};
}
const GVArray *get_varray_for_context(const fn::FieldContext &context,
IndexMask mask,
ResourceScope &scope) const override;
GVArray get_varray_for_context(const fn::FieldContext &context,
IndexMask mask,
ResourceScope &scope) const override;
std::string socket_inspection_name() const override;

View File

@@ -187,14 +187,14 @@ class Spline {
blender::MutableSpan<T> dst) const
{
this->sample_with_index_factors(
blender::fn::GVArray_For_VArray(src), index_factors, blender::fn::GMutableSpan(dst));
blender::fn::GVArray(src), index_factors, blender::fn::GMutableSpan(dst));
}
template<typename T>
void sample_with_index_factors(blender::Span<T> src,
blender::Span<float> index_factors,
blender::MutableSpan<T> dst) const
{
this->sample_with_index_factors(blender::VArray_For_Span(src), index_factors, dst);
this->sample_with_index_factors(blender::VArray<T>::ForSpan(src), index_factors, dst);
}
/**
@@ -202,13 +202,11 @@ class Spline {
* evaluated points. For poly splines, the lifetime of the returned virtual array must not
* exceed the lifetime of the input data.
*/
virtual blender::fn::GVArrayPtr interpolate_to_evaluated(
const blender::fn::GVArray &src) const = 0;
blender::fn::GVArrayPtr interpolate_to_evaluated(blender::fn::GSpan data) const;
template<typename T>
blender::fn::GVArray_Typed<T> interpolate_to_evaluated(blender::Span<T> data) const
virtual blender::fn::GVArray interpolate_to_evaluated(const blender::fn::GVArray &src) const = 0;
blender::fn::GVArray interpolate_to_evaluated(blender::fn::GSpan data) const;
template<typename T> blender::VArray<T> interpolate_to_evaluated(blender::Span<T> data) const
{
return blender::fn::GVArray_Typed<T>(this->interpolate_to_evaluated(blender::fn::GSpan(data)));
return this->interpolate_to_evaluated(blender::fn::GSpan(data)).typed<T>();
}
protected:
@@ -338,7 +336,7 @@ class BezierSpline final : public Spline {
};
InterpolationData interpolation_data_from_index_factor(const float index_factor) const;
virtual blender::fn::GVArrayPtr interpolate_to_evaluated(
virtual blender::fn::GVArray interpolate_to_evaluated(
const blender::fn::GVArray &src) const override;
void evaluate_segment(const int index,
@@ -475,7 +473,7 @@ class NURBSpline final : public Spline {
blender::Span<blender::float3> evaluated_positions() const final;
blender::fn::GVArrayPtr interpolate_to_evaluated(const blender::fn::GVArray &src) const final;
blender::fn::GVArray interpolate_to_evaluated(const blender::fn::GVArray &src) const final;
protected:
void correct_end_tangents() const final;
@@ -526,7 +524,7 @@ class PolySpline final : public Spline {
blender::Span<blender::float3> evaluated_positions() const final;
blender::fn::GVArrayPtr interpolate_to_evaluated(const blender::fn::GVArray &src) const final;
blender::fn::GVArray interpolate_to_evaluated(const blender::fn::GVArray &src) const final;
protected:
void correct_end_tangents() const final;

View File

@@ -50,9 +50,7 @@ using blender::bke::AttributeIDRef;
using blender::bke::OutputAttribute;
using blender::fn::GMutableSpan;
using blender::fn::GSpan;
using blender::fn::GVArray_For_GSpan;
using blender::fn::GVArray_For_SingleValue;
using blender::fn::GVMutableArray_For_GMutableSpan;
using blender::fn::GVMutableArrayImpl_For_GMutableSpan;
namespace blender::bke {
@@ -207,7 +205,7 @@ fn::GMutableSpan OutputAttribute::as_span()
{
if (!optional_span_varray_) {
const bool materialize_old_values = !ignore_old_values_;
optional_span_varray_ = std::make_unique<fn::GVMutableArray_GSpan>(*varray_,
optional_span_varray_ = std::make_unique<fn::GVMutableArray_GSpan>(varray_,
materialize_old_values);
}
fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_;
@@ -249,8 +247,8 @@ static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data
if (data == nullptr) {
return false;
}
const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray;
varray->materialize_to_uninitialized(IndexRange(varray->size()), data);
const GVArray &varray = static_cast<const AttributeInitVArray &>(initializer).varray;
varray.materialize_to_uninitialized(varray.index_range(), data);
return true;
}
case AttributeInit::Type::MoveArray: {
@@ -305,8 +303,8 @@ static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attr
if (data == nullptr) {
return false;
}
const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray;
varray->materialize_to_uninitialized(IndexRange(varray->size()), data);
const GVArray &varray = static_cast<const AttributeInitVArray &>(initializer).varray;
varray.materialize_to_uninitialized(varray.index_range(), data);
return true;
}
case AttributeInit::Type::MoveArray: {
@@ -337,8 +335,7 @@ static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer,
return layer.name == attribute_id.name();
}
GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read(
const GeometryComponent &component) const
GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const GeometryComponent &component) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
if (custom_data == nullptr) {
@@ -503,7 +500,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
continue;
}
GSpan data{*type, layer.data, domain_size};
return {std::make_unique<GVArray_For_GSpan>(data), domain_};
return {GVArray::ForSpan(data), domain_};
}
return {};
}
@@ -533,7 +530,7 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
continue;
}
GMutableSpan data{*type, layer.data, domain_size};
return {std::make_unique<GVMutableArray_For_GMutableSpan>(data), domain_};
return {GVMutableArray::ForSpan(data), domain_};
}
return {};
}
@@ -749,25 +746,25 @@ std::optional<GSpan> CustomDataAttributes::get_for_read(const AttributeIDRef &at
* value if the attribute doesn't exist. If no default value is provided, the default value for the
* type will be used.
*/
GVArrayPtr CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id,
const CustomDataType data_type,
const void *default_value) const
GVArray CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id,
const CustomDataType data_type,
const void *default_value) const
{
const CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
std::optional<GSpan> attribute = this->get_for_read(attribute_id);
if (!attribute) {
const int domain_size = this->size_;
return std::make_unique<GVArray_For_SingleValue>(
return GVArray::ForSingle(
*type, domain_size, (default_value == nullptr) ? type->default_value() : default_value);
}
if (attribute->type() == *type) {
return std::make_unique<GVArray_For_GSpan>(*attribute);
return GVArray::ForSpan(*attribute);
}
const blender::nodes::DataTypeConversions &conversions =
blender::nodes::get_implicit_type_conversions();
return conversions.try_convert(std::make_unique<GVArray_For_GSpan>(*attribute), *type);
return conversions.try_convert(GVArray::ForSpan(*attribute), *type);
}
std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const AttributeIDRef &attribute_id)
@@ -906,8 +903,8 @@ blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
return {};
}
std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_adapt_domain(
std::unique_ptr<blender::fn::GVArray> varray,
blender::fn::GVArray GeometryComponent::attribute_try_adapt_domain_impl(
const blender::fn::GVArray &varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const
{
@@ -1094,15 +1091,15 @@ std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data(
return result;
}
static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type)
static blender::fn::GVArray try_adapt_data_type(blender::fn::GVArray varray,
const blender::fn::CPPType &to_type)
{
const blender::nodes::DataTypeConversions &conversions =
blender::nodes::get_implicit_type_conversions();
return conversions.try_convert(std::move(varray), to_type);
}
std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read(
blender::fn::GVArray GeometryComponent::attribute_try_get_for_read(
const AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type) const
@@ -1112,7 +1109,7 @@ std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_r
return {};
}
std::unique_ptr<blender::fn::GVArray> varray = std::move(attribute.varray);
blender::fn::GVArray varray = std::move(attribute.varray);
if (!ELEM(domain, ATTR_DOMAIN_AUTO, attribute.domain)) {
varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain);
if (!varray) {
@@ -1122,7 +1119,7 @@ std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_r
const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
if (varray->type() != *cpp_type) {
if (varray.type() != *cpp_type) {
varray = try_adapt_data_type(std::move(varray), *cpp_type);
if (!varray) {
return {};
@@ -1132,7 +1129,7 @@ std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_r
return varray;
}
std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_read(
blender::fn::GVArray GeometryComponent::attribute_try_get_for_read(
const AttributeIDRef &attribute_id, const AttributeDomain domain) const
{
if (!this->attribute_domain_supported(domain)) {
@@ -1160,7 +1157,7 @@ blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
}
const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(type != nullptr);
if (attribute.varray->type() == *type) {
if (attribute.varray.type() == *type) {
return attribute;
}
const blender::nodes::DataTypeConversions &conversions =
@@ -1168,14 +1165,12 @@ blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
return {conversions.try_convert(std::move(attribute.varray), *type), attribute.domain};
}
std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read(
const AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value) const
blender::fn::GVArray GeometryComponent::attribute_get_for_read(const AttributeIDRef &attribute_id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value) const
{
std::unique_ptr<blender::bke::GVArray> varray = this->attribute_try_get_for_read(
attribute_id, domain, data_type);
blender::fn::GVArray varray = this->attribute_try_get_for_read(attribute_id, domain, data_type);
if (varray) {
return varray;
}
@@ -1184,11 +1179,11 @@ std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read
default_value = type->default_value();
}
const int domain_size = this->attribute_domain_size(domain);
return std::make_unique<blender::fn::GVArray_For_SingleValue>(*type, domain_size, default_value);
return blender::fn::GVArray::ForSingle(*type, domain_size, default_value);
}
class GVMutableAttribute_For_OutputAttribute
: public blender::fn::GVMutableArray_For_GMutableSpan {
: public blender::fn::GVMutableArrayImpl_For_GMutableSpan {
public:
GeometryComponent *component;
std::string attribute_name;
@@ -1197,7 +1192,7 @@ class GVMutableAttribute_For_OutputAttribute
GVMutableAttribute_For_OutputAttribute(GMutableSpan data,
GeometryComponent &component,
const AttributeIDRef &attribute_id)
: blender::fn::GVMutableArray_For_GMutableSpan(data), component(&component)
: blender::fn::GVMutableArrayImpl_For_GMutableSpan(data), component(&component)
{
if (attribute_id.is_named()) {
this->attribute_name = attribute_id.name();
@@ -1223,7 +1218,8 @@ static void save_output_attribute(OutputAttribute &output_attribute)
using namespace blender::bke;
GVMutableAttribute_For_OutputAttribute &varray =
dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(output_attribute.varray());
dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(
*output_attribute.varray().get_implementation());
GeometryComponent &component = *varray.component;
AttributeIDRef attribute_id;
@@ -1251,7 +1247,7 @@ static void save_output_attribute(OutputAttribute &output_attribute)
BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer);
for (const int i : IndexRange(varray.size())) {
varray.get(i, buffer);
write_attribute.varray->set_by_relocate(i, buffer);
write_attribute.varray.set_by_relocate(i, buffer);
}
if (write_attribute.tag_modified_fn) {
write_attribute.tag_modified_fn();
@@ -1294,9 +1290,9 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
if (!attribute) {
if (default_value) {
const int64_t domain_size = component.attribute_domain_size(domain);
const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
component.attribute_try_create_builtin(attribute_name,
AttributeInitVArray(&default_varray));
component.attribute_try_create_builtin(
attribute_name,
AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_size, default_value)));
}
else {
component.attribute_try_create_builtin(attribute_name, AttributeInitDefault());
@@ -1311,9 +1307,8 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
/* Builtin attribute is on different domain. */
return {};
}
GVMutableArrayPtr varray = std::move(attribute.varray);
if (varray->type() == *cpp_type) {
GVMutableArray varray = std::move(attribute.varray);
if (varray.type() == *cpp_type) {
/* Builtin attribute matches exactly. */
return OutputAttribute(std::move(varray),
domain,
@@ -1333,9 +1328,11 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_id);
if (!attribute) {
if (default_value) {
const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
component.attribute_try_create(
attribute_id, domain, data_type, AttributeInitVArray(&default_varray));
attribute_id,
domain,
data_type,
AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_size, default_value)));
}
else {
component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault());
@@ -1347,7 +1344,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
return {};
}
}
if (attribute.domain == domain && attribute.varray->type() == *cpp_type) {
if (attribute.domain == domain && attribute.varray.type() == *cpp_type) {
/* Existing generic attribute matches exactly. */
return OutputAttribute(std::move(attribute.varray),
@@ -1366,11 +1363,11 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
}
else {
/* Fill the temporary array with values from the existing attribute. */
GVArrayPtr old_varray = component.attribute_get_for_read(
GVArray old_varray = component.attribute_get_for_read(
attribute_id, domain, data_type, default_value);
old_varray->materialize_to_uninitialized(IndexRange(domain_size), data);
old_varray.materialize_to_uninitialized(IndexRange(domain_size), data);
}
GVMutableArrayPtr varray = std::make_unique<GVMutableAttribute_For_OutputAttribute>(
GVMutableArray varray = GVMutableArray::For<GVMutableAttribute_For_OutputAttribute>(
GMutableSpan{*cpp_type, data, domain_size}, component, attribute_id);
return OutputAttribute(std::move(varray), domain, save_output_attribute, true);
@@ -1394,21 +1391,21 @@ OutputAttribute GeometryComponent::attribute_try_get_for_output_only(
namespace blender::bke {
const GVArray *AttributeFieldInput::get_varray_for_context(const fn::FieldContext &context,
IndexMask UNUSED(mask),
ResourceScope &scope) const
GVArray AttributeFieldInput::get_varray_for_context(const fn::FieldContext &context,
IndexMask UNUSED(mask),
ResourceScope &UNUSED(scope)) const
{
if (const GeometryComponentFieldContext *geometry_context =
dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
const GeometryComponent &component = geometry_context->geometry_component();
const AttributeDomain domain = geometry_context->domain();
const CustomDataType data_type = cpp_type_to_custom_data_type(*type_);
GVArrayPtr attribute = component.attribute_try_get_for_read(name_, domain, data_type);
GVArray attribute = component.attribute_try_get_for_read(name_, domain, data_type);
if (attribute) {
return scope.add(std::move(attribute));
return attribute;
}
}
return nullptr;
return {};
}
std::string AttributeFieldInput::socket_inspection_name() const
@@ -1441,25 +1438,25 @@ static StringRef get_random_id_attribute_name(const AttributeDomain domain)
}
}
const GVArray *IDAttributeFieldInput::get_varray_for_context(const fn::FieldContext &context,
IndexMask mask,
ResourceScope &scope) const
GVArray IDAttributeFieldInput::get_varray_for_context(const fn::FieldContext &context,
IndexMask mask,
ResourceScope &scope) const
{
if (const GeometryComponentFieldContext *geometry_context =
dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
const GeometryComponent &component = geometry_context->geometry_component();
const AttributeDomain domain = geometry_context->domain();
const StringRef name = get_random_id_attribute_name(domain);
GVArrayPtr attribute = component.attribute_try_get_for_read(name, domain, CD_PROP_INT32);
GVArray attribute = component.attribute_try_get_for_read(name, domain, CD_PROP_INT32);
if (attribute) {
BLI_assert(attribute->size() == component.attribute_domain_size(domain));
return scope.add(std::move(attribute));
BLI_assert(attribute.size() == component.attribute_domain_size(domain));
return attribute;
}
/* Use the index as the fallback if no random ID attribute exists. */
return fn::IndexFieldInput::get_index_varray(mask, scope);
}
return nullptr;
return {};
}
std::string IDAttributeFieldInput::socket_inspection_name() const
@@ -1479,19 +1476,20 @@ bool IDAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const
return dynamic_cast<const IDAttributeFieldInput *>(&other) != nullptr;
}
const GVArray *AnonymousAttributeFieldInput::get_varray_for_context(
const fn::FieldContext &context, IndexMask UNUSED(mask), ResourceScope &scope) const
GVArray AnonymousAttributeFieldInput::get_varray_for_context(const fn::FieldContext &context,
IndexMask UNUSED(mask),
ResourceScope &UNUSED(scope)) const
{
if (const GeometryComponentFieldContext *geometry_context =
dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
const GeometryComponent &component = geometry_context->geometry_component();
const AttributeDomain domain = geometry_context->domain();
const CustomDataType data_type = cpp_type_to_custom_data_type(*type_);
GVArrayPtr attribute = component.attribute_try_get_for_read(
GVArray attribute = component.attribute_try_get_for_read(
anonymous_id_.get(), domain, data_type);
return scope.add(std::move(attribute));
return attribute;
}
return nullptr;
return {};
}
std::string AnonymousAttributeFieldInput::socket_inspection_name() const

View File

@@ -24,9 +24,6 @@
namespace blender::bke {
using fn::GVArrayPtr;
using fn::GVMutableArrayPtr;
/**
* Utility to group together multiple functions that are used to access custom data on geometry
* components in a generic way.
@@ -86,7 +83,7 @@ class BuiltinAttributeProvider {
{
}
virtual GVArrayPtr try_get_for_read(const GeometryComponent &component) const = 0;
virtual GVArray try_get_for_read(const GeometryComponent &component) const = 0;
virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component) const = 0;
virtual bool try_delete(GeometryComponent &component) const = 0;
virtual bool try_create(GeometryComponent &UNUSED(component),
@@ -188,8 +185,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
*/
class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
private:
using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size);
using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size);
using AsReadAttribute = GVArray (*)(const void *data, const int domain_size);
using AsWriteAttribute = GVMutableArray (*)(void *data, const int domain_size);
const AttributeDomain domain_;
const CustomDataType attribute_type_;
const CustomDataType stored_type_;
@@ -232,8 +229,8 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
* if the stored type is the same as the attribute type.
*/
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size);
using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size);
using AsReadAttribute = GVArray (*)(const void *data, const int domain_size);
using AsWriteAttribute = GVMutableArray (*)(void *data, const int domain_size);
using UpdateOnRead = void (*)(const GeometryComponent &component);
using UpdateOnWrite = void (*)(GeometryComponent &component);
const CustomDataType stored_type_;
@@ -266,7 +263,7 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
{
}
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final;
GVArray try_get_for_read(const GeometryComponent &component) const final;
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final;
bool try_delete(GeometryComponent &component) const final;
bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final;

View File

@@ -32,8 +32,6 @@
using blender::fn::GMutableSpan;
using blender::fn::GSpan;
using blender::fn::GVArray_Typed;
using blender::fn::GVArrayPtr;
namespace blender::bke {
@@ -218,7 +216,7 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info,
Span<float3> normals = spline.evaluated_normals();
Span<float3> profile_positions = profile.evaluated_positions();
GVArray_Typed<float> radii = spline.interpolate_to_evaluated(spline.radii());
VArray<float> radii = spline.interpolate_to_evaluated(spline.radii());
for (const int i_ring : IndexRange(info.spline_vert_len)) {
float4x4 point_matrix = float4x4::from_normalized_axis_data(
positions[i_ring], normals[i_ring], tangents[i_ring]);
@@ -486,8 +484,8 @@ static void copy_curve_point_attribute_to_mesh(const GSpan src,
const ResultInfo &info,
ResultAttributeData &dst)
{
GVArrayPtr interpolated_gvarray = info.spline.interpolate_to_evaluated(src);
GSpan interpolated = interpolated_gvarray->get_internal_span();
GVArray interpolated_gvarray = info.spline.interpolate_to_evaluated(src);
GSpan interpolated = interpolated_gvarray.get_internal_span();
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
@@ -552,8 +550,8 @@ static void copy_profile_point_attribute_to_mesh(const GSpan src,
const ResultInfo &info,
ResultAttributeData &dst)
{
GVArrayPtr interpolated_gvarray = info.profile.interpolate_to_evaluated(src);
GSpan interpolated = interpolated_gvarray->get_internal_span();
GVArray interpolated_gvarray = info.profile.interpolate_to_evaluated(src);
GSpan interpolated = interpolated_gvarray.get_internal_span();
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);

View File

@@ -28,10 +28,8 @@
using blender::fn::GMutableSpan;
using blender::fn::GSpan;
using blender::fn::GVArray_For_GSpan;
using blender::fn::GVArray;
using blender::fn::GVArray_GSpan;
using blender::fn::GVArrayPtr;
using blender::fn::GVMutableArray_For_GMutableSpan;
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
@@ -253,15 +251,15 @@ void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve,
}
}
static GVArrayPtr adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArrayPtr varray)
static GVArray adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArray varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(curve.splines().size());
adapt_curve_domain_point_to_spline_impl<T>(curve, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
adapt_curve_domain_point_to_spline_impl<T>(curve, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
}
});
return new_varray;
@@ -272,29 +270,29 @@ static GVArrayPtr adapt_curve_domain_point_to_spline(const CurveEval &curve, GVA
* attributes. The goal is to avoid copying the spline value for every one of its control points
* unless it is necessary (in that case the materialize functions will be called).
*/
template<typename T> class VArray_For_SplineToPoint final : public VArray<T> {
GVArrayPtr original_varray_;
template<typename T> class VArray_For_SplineToPoint final : public VArrayImpl<T> {
GVArray original_varray_;
/* Store existing data materialized if it was not already a span. This is expected
* to be worth it because a single spline's value will likely be accessed many times. */
fn::GVArray_Span<T> original_data_;
VArray_Span<T> original_data_;
Array<int> offsets_;
public:
VArray_For_SplineToPoint(GVArrayPtr original_varray, Array<int> offsets)
: VArray<T>(offsets.last()),
VArray_For_SplineToPoint(GVArray original_varray, Array<int> offsets)
: VArrayImpl<T>(offsets.last()),
original_varray_(std::move(original_varray)),
original_data_(*original_varray_),
original_data_(original_varray_.typed<T>()),
offsets_(std::move(offsets))
{
}
T get_impl(const int64_t index) const final
T get(const int64_t index) const final
{
const PointIndices indices = lookup_point_indices(offsets_, index);
return original_data_[indices.spline_index];
}
void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
void materialize(const IndexMask mask, MutableSpan<T> r_span) const final
{
const int total_size = offsets_.last();
if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
@@ -315,7 +313,7 @@ template<typename T> class VArray_For_SplineToPoint final : public VArray<T> {
}
}
void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
void materialize_to_uninitialized(const IndexMask mask, MutableSpan<T> r_span) const final
{
T *dst = r_span.data();
const int total_size = offsets_.last();
@@ -338,29 +336,29 @@ template<typename T> class VArray_For_SplineToPoint final : public VArray<T> {
}
};
static GVArrayPtr adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArrayPtr varray)
static GVArray adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArray varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
Array<int> offsets = curve.control_point_offsets();
new_varray = std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplineToPoint<T>>>(
offsets.last(), std::move(varray), std::move(offsets));
new_varray = VArray<T>::template For<VArray_For_SplineToPoint<T>>(std::move(varray),
std::move(offsets));
});
return new_varray;
}
} // namespace blender::bke
GVArrayPtr CurveComponent::attribute_try_adapt_domain(GVArrayPtr varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const
GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const
{
if (!varray) {
return {};
}
if (varray->size() == 0) {
if (varray.is_empty()) {
return {};
}
if (from_domain == to_domain) {
@@ -402,8 +400,8 @@ static const CurveEval *get_curve_from_component_for_read(const GeometryComponen
namespace blender::bke {
class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
using AsReadAttribute = GVArrayPtr (*)(const CurveEval &data);
using AsWriteAttribute = GVMutableArrayPtr (*)(CurveEval &data);
using AsReadAttribute = GVArray (*)(const CurveEval &data);
using AsWriteAttribute = GVMutableArray (*)(CurveEval &data);
const AsReadAttribute as_read_attribute_;
const AsWriteAttribute as_write_attribute_;
@@ -424,7 +422,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
{
}
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
GVArray try_get_for_read(const GeometryComponent &component) const final
{
const CurveEval *curve = get_curve_from_component_for_read(component);
if (curve == nullptr) {
@@ -483,19 +481,15 @@ static void set_spline_resolution(SplinePtr &spline, const int resolution)
}
}
static GVArrayPtr make_resolution_read_attribute(const CurveEval &curve)
static GVArray make_resolution_read_attribute(const CurveEval &curve)
{
return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, int, get_spline_resolution>>(
curve.splines());
return VArray<int>::ForDerivedSpan<SplinePtr, get_spline_resolution>(curve.splines());
}
static GVMutableArrayPtr make_resolution_write_attribute(CurveEval &curve)
static GVMutableArray make_resolution_write_attribute(CurveEval &curve)
{
return std::make_unique<fn::GVMutableArray_For_DerivedSpan<SplinePtr,
int,
get_spline_resolution,
set_spline_resolution>>(
curve.splines());
return VMutableArray<int>::
ForDerivedSpan<SplinePtr, get_spline_resolution, set_spline_resolution>(curve.splines());
}
static bool get_cyclic_value(const SplinePtr &spline)
@@ -511,16 +505,14 @@ static void set_cyclic_value(SplinePtr &spline, const bool value)
}
}
static GVArrayPtr make_cyclic_read_attribute(const CurveEval &curve)
static GVArray make_cyclic_read_attribute(const CurveEval &curve)
{
return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value>>(
curve.splines());
return VArray<bool>::ForDerivedSpan<SplinePtr, get_cyclic_value>(curve.splines());
}
static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve)
static GVMutableArray make_cyclic_write_attribute(CurveEval &curve)
{
return std::make_unique<
fn::GVMutableArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value, set_cyclic_value>>(
return VMutableArray<bool>::ForDerivedSpan<SplinePtr, get_cyclic_value, set_cyclic_value>(
curve.splines());
}
@@ -623,9 +615,9 @@ static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
}
}
static GVArrayPtr varray_from_initializer(const AttributeInit &initializer,
const CustomDataType data_type,
const Span<SplinePtr> splines)
static GVArray varray_from_initializer(const AttributeInit &initializer,
const CustomDataType data_type,
const Span<SplinePtr> splines)
{
switch (initializer.type) {
case AttributeInit::Type::Default:
@@ -634,16 +626,15 @@ static GVArrayPtr varray_from_initializer(const AttributeInit &initializer,
BLI_assert_unreachable();
return {};
case AttributeInit::Type::VArray:
return static_cast<const AttributeInitVArray &>(initializer).varray->shallow_copy();
return static_cast<const AttributeInitVArray &>(initializer).varray;
case AttributeInit::Type::MoveArray:
int total_size = 0;
for (const SplinePtr &spline : splines) {
total_size += spline->size();
}
return std::make_unique<fn::GVArray_For_GSpan>(
GSpan(*bke::custom_data_type_to_cpp_type(data_type),
static_cast<const AttributeInitMove &>(initializer).data,
total_size));
return GVArray::ForSpan(GSpan(*bke::custom_data_type_to_cpp_type(data_type),
static_cast<const AttributeInitMove &>(initializer).data,
total_size));
}
BLI_assert_unreachable();
return {};
@@ -691,11 +682,11 @@ static bool create_point_attribute(GeometryComponent &component,
/* We just created the attribute, it should exist. */
BLI_assert(write_attribute);
GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, splines);
GVArray source_varray = varray_from_initializer(initializer, data_type, splines);
/* TODO: When we can call a variant of #set_all with a virtual array argument,
* this theoretically unnecessary materialize step could be removed. */
GVArray_GSpan source_varray_span{*source_varray};
write_attribute.varray->set_all(source_varray_span.data());
GVArray_GSpan source_varray_span{source_varray};
write_attribute.varray.set_all(source_varray_span.data());
if (initializer.type == AttributeInit::Type::MoveArray) {
MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data);
@@ -723,29 +714,29 @@ static bool remove_point_attribute(GeometryComponent &component,
/**
* Virtual array for any control point data accessed with spans and an offset array.
*/
template<typename T> class VArray_For_SplinePoints : public VArray<T> {
template<typename T> class VArray_For_SplinePoints : public VArrayImpl<T> {
private:
const Array<Span<T>> data_;
Array<int> offsets_;
public:
VArray_For_SplinePoints(Array<Span<T>> data, Array<int> offsets)
: VArray<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
: VArrayImpl<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
{
}
T get_impl(const int64_t index) const final
T get(const int64_t index) const final
{
const PointIndices indices = lookup_point_indices(offsets_, index);
return data_[indices.spline_index][indices.point_index];
}
void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
void materialize(const IndexMask mask, MutableSpan<T> r_span) const final
{
point_attribute_materialize(data_.as_span(), offsets_, mask, r_span);
}
void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
void materialize_to_uninitialized(const IndexMask mask, MutableSpan<T> r_span) const final
{
point_attribute_materialize_to_uninitialized(data_.as_span(), offsets_, mask, r_span);
}
@@ -754,30 +745,30 @@ template<typename T> class VArray_For_SplinePoints : public VArray<T> {
/**
* Mutable virtual array for any control point data accessed with spans and an offset array.
*/
template<typename T> class VMutableArray_For_SplinePoints final : public VMutableArray<T> {
template<typename T> class VMutableArray_For_SplinePoints final : public VMutableArrayImpl<T> {
private:
Array<MutableSpan<T>> data_;
Array<int> offsets_;
public:
VMutableArray_For_SplinePoints(Array<MutableSpan<T>> data, Array<int> offsets)
: VMutableArray<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
: VMutableArrayImpl<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
{
}
T get_impl(const int64_t index) const final
T get(const int64_t index) const final
{
const PointIndices indices = lookup_point_indices(offsets_, index);
return data_[indices.spline_index][indices.point_index];
}
void set_impl(const int64_t index, T value) final
void set(const int64_t index, T value) final
{
const PointIndices indices = lookup_point_indices(offsets_, index);
data_[indices.spline_index][indices.point_index] = value;
}
void set_all_impl(Span<T> src) final
void set_all(Span<T> src) final
{
for (const int spline_index : data_.index_range()) {
const int offset = offsets_[spline_index];
@@ -786,30 +777,28 @@ template<typename T> class VMutableArray_For_SplinePoints final : public VMutabl
}
}
void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
void materialize(const IndexMask mask, MutableSpan<T> r_span) const final
{
point_attribute_materialize({(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span);
}
void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
void materialize_to_uninitialized(const IndexMask mask, MutableSpan<T> r_span) const final
{
point_attribute_materialize_to_uninitialized(
{(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span);
}
};
template<typename T> GVArrayPtr point_data_gvarray(Array<Span<T>> spans, Array<int> offsets)
template<typename T> VArray<T> point_data_varray(Array<Span<T>> spans, Array<int> offsets)
{
return std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplinePoints<T>>>(
offsets.last(), std::move(spans), std::move(offsets));
return VArray<T>::template For<VArray_For_SplinePoints<T>>(std::move(spans), std::move(offsets));
}
template<typename T>
GVMutableArrayPtr point_data_gvarray(Array<MutableSpan<T>> spans, Array<int> offsets)
VMutableArray<T> point_data_varray(Array<MutableSpan<T>> spans, Array<int> offsets)
{
return std::make_unique<
fn::GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_SplinePoints<T>>>(
offsets.last(), std::move(spans), std::move(offsets));
return VMutableArray<T>::template For<VMutableArray_For_SplinePoints<T>>(std::move(spans),
std::move(offsets));
}
/**
@@ -820,24 +809,24 @@ GVMutableArrayPtr point_data_gvarray(Array<MutableSpan<T>> spans, Array<int> off
* \note There is no need to check the handle type to avoid changing auto handles, since
* retrieving write access to the position data will mark them for recomputation anyway.
*/
class VMutableArray_For_SplinePosition final : public VMutableArray<float3> {
class VMutableArray_For_SplinePosition final : public VMutableArrayImpl<float3> {
private:
MutableSpan<SplinePtr> splines_;
Array<int> offsets_;
public:
VMutableArray_For_SplinePosition(MutableSpan<SplinePtr> splines, Array<int> offsets)
: VMutableArray<float3>(offsets.last()), splines_(splines), offsets_(std::move(offsets))
: VMutableArrayImpl<float3>(offsets.last()), splines_(splines), offsets_(std::move(offsets))
{
}
float3 get_impl(const int64_t index) const final
float3 get(const int64_t index) const final
{
const PointIndices indices = lookup_point_indices(offsets_, index);
return splines_[indices.spline_index]->positions()[indices.point_index];
}
void set_impl(const int64_t index, float3 value) final
void set(const int64_t index, float3 value) final
{
const PointIndices indices = lookup_point_indices(offsets_, index);
Spline &spline = *splines_[indices.spline_index];
@@ -852,7 +841,7 @@ class VMutableArray_For_SplinePosition final : public VMutableArray<float3> {
}
}
void set_all_impl(Span<float3> src) final
void set_all(Span<float3> src) final
{
for (const int spline_index : splines_.index_range()) {
Spline &spline = *splines_[spline_index];
@@ -885,21 +874,20 @@ class VMutableArray_For_SplinePosition final : public VMutableArray<float3> {
return spans;
}
void materialize_impl(const IndexMask mask, MutableSpan<float3> r_span) const final
void materialize(const IndexMask mask, MutableSpan<float3> r_span) const final
{
Array<Span<float3>> spans = this->get_position_spans();
point_attribute_materialize(spans.as_span(), offsets_, mask, r_span);
}
void materialize_to_uninitialized_impl(const IndexMask mask,
MutableSpan<float3> r_span) const final
void materialize_to_uninitialized(const IndexMask mask, MutableSpan<float3> r_span) const final
{
Array<Span<float3>> spans = this->get_position_spans();
point_attribute_materialize_to_uninitialized(spans.as_span(), offsets_, mask, r_span);
}
};
class VArray_For_BezierHandle final : public VArray<float3> {
class VArray_For_BezierHandle final : public VArrayImpl<float3> {
private:
Span<SplinePtr> splines_;
Array<int> offsets_;
@@ -907,7 +895,7 @@ class VArray_For_BezierHandle final : public VArray<float3> {
public:
VArray_For_BezierHandle(Span<SplinePtr> splines, Array<int> offsets, const bool is_right)
: VArray<float3>(offsets.last()),
: VArrayImpl<float3>(offsets.last()),
splines_(std::move(splines)),
offsets_(std::move(offsets)),
is_right_(is_right)
@@ -929,7 +917,7 @@ class VArray_For_BezierHandle final : public VArray<float3> {
return float3(0);
}
float3 get_impl(const int64_t index) const final
float3 get(const int64_t index) const final
{
return get_internal(index, splines_, offsets_, is_right_);
}
@@ -976,19 +964,18 @@ class VArray_For_BezierHandle final : public VArray<float3> {
point_attribute_materialize_to_uninitialized(spans.as_span(), offsets, mask, r_span);
}
void materialize_impl(const IndexMask mask, MutableSpan<float3> r_span) const final
void materialize(const IndexMask mask, MutableSpan<float3> r_span) const final
{
materialize_internal(mask, splines_, offsets_, is_right_, r_span);
}
void materialize_to_uninitialized_impl(const IndexMask mask,
MutableSpan<float3> r_span) const final
void materialize_to_uninitialized(const IndexMask mask, MutableSpan<float3> r_span) const final
{
materialize_to_uninitialized_internal(mask, splines_, offsets_, is_right_, r_span);
}
};
class VMutableArray_For_BezierHandles final : public VMutableArray<float3> {
class VMutableArray_For_BezierHandles final : public VMutableArrayImpl<float3> {
private:
MutableSpan<SplinePtr> splines_;
Array<int> offsets_;
@@ -998,19 +985,19 @@ class VMutableArray_For_BezierHandles final : public VMutableArray<float3> {
VMutableArray_For_BezierHandles(MutableSpan<SplinePtr> splines,
Array<int> offsets,
const bool is_right)
: VMutableArray<float3>(offsets.last()),
: VMutableArrayImpl<float3>(offsets.last()),
splines_(splines),
offsets_(std::move(offsets)),
is_right_(is_right)
{
}
float3 get_impl(const int64_t index) const final
float3 get(const int64_t index) const final
{
return VArray_For_BezierHandle::get_internal(index, splines_, offsets_, is_right_);
}
void set_impl(const int64_t index, float3 value) final
void set(const int64_t index, float3 value) final
{
const PointIndices indices = lookup_point_indices(offsets_, index);
Spline &spline = *splines_[indices.spline_index];
@@ -1026,7 +1013,7 @@ class VMutableArray_For_BezierHandles final : public VMutableArray<float3> {
}
}
void set_all_impl(Span<float3> src) final
void set_all(Span<float3> src) final
{
for (const int spline_index : splines_.index_range()) {
Spline &spline = *splines_[spline_index];
@@ -1049,13 +1036,12 @@ class VMutableArray_For_BezierHandles final : public VMutableArray<float3> {
}
}
void materialize_impl(const IndexMask mask, MutableSpan<float3> r_span) const final
void materialize(const IndexMask mask, MutableSpan<float3> r_span) const final
{
VArray_For_BezierHandle::materialize_internal(mask, splines_, offsets_, is_right_, r_span);
}
void materialize_to_uninitialized_impl(const IndexMask mask,
MutableSpan<float3> r_span) const final
void materialize_to_uninitialized(const IndexMask mask, MutableSpan<float3> r_span) const final
{
VArray_For_BezierHandle::materialize_to_uninitialized_internal(
mask, splines_, offsets_, is_right_, r_span);
@@ -1097,7 +1083,7 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
{
}
GVArrayPtr try_get_for_read(const GeometryComponent &component) const override
GVArray try_get_for_read(const GeometryComponent &component) const override
{
const CurveEval *curve = get_curve_from_component_for_read(component);
if (curve == nullptr) {
@@ -1110,7 +1096,7 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
Span<SplinePtr> splines = curve->splines();
if (splines.size() == 1) {
return std::make_unique<fn::GVArray_For_GSpan>(get_span_(*splines.first()));
return GVArray::ForSpan(get_span_(*splines.first()));
}
Array<int> offsets = curve->control_point_offsets();
@@ -1119,7 +1105,7 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
spans[i] = get_span_(*splines[i]);
}
return point_data_gvarray(spans, offsets);
return point_data_varray(spans, offsets);
}
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override
@@ -1144,8 +1130,7 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
MutableSpan<SplinePtr> splines = curve->splines();
if (splines.size() == 1) {
return {std::make_unique<fn::GVMutableArray_For_GMutableSpan>(
get_mutable_span_(*splines.first())),
return {GVMutableArray::ForSpan(get_mutable_span_(*splines.first())),
domain_,
std::move(tag_modified_fn)};
}
@@ -1156,7 +1141,7 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
spans[i] = get_mutable_span_(*splines[i]);
}
return {point_data_gvarray(spans, offsets), domain_, tag_modified_fn};
return {point_data_varray(spans, offsets), domain_, tag_modified_fn};
}
bool try_delete(GeometryComponent &component) const final
@@ -1248,10 +1233,8 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
};
Array<int> offsets = curve->control_point_offsets();
return {std::make_unique<
fn::GVMutableArray_For_EmbeddedVMutableArray<float3,
VMutableArray_For_SplinePosition>>(
offsets.last(), curve->splines(), std::move(offsets)),
return {VMutableArray<float3>::For<VMutableArray_For_SplinePosition>(curve->splines(),
std::move(offsets)),
domain_,
tag_modified_fn};
}
@@ -1273,7 +1256,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
{
}
GVArrayPtr try_get_for_read(const GeometryComponent &component) const override
GVArray try_get_for_read(const GeometryComponent &component) const override
{
const CurveEval *curve = get_curve_from_component_for_read(component);
if (curve == nullptr) {
@@ -1285,8 +1268,8 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
}
Array<int> offsets = curve->control_point_offsets();
return std::make_unique<fn::GVArray_For_EmbeddedVArray<float3, VArray_For_BezierHandle>>(
offsets.last(), curve->splines(), std::move(offsets), is_right_);
return VArray<float3>::For<VArray_For_BezierHandle>(
curve->splines(), std::move(offsets), is_right_);
}
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override
@@ -1303,12 +1286,10 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
auto tag_modified_fn = [curve]() { curve->mark_cache_invalid(); };
Array<int> offsets = curve->control_point_offsets();
return {
std::make_unique<
fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_BezierHandles>>(
offsets.last(), curve->splines(), std::move(offsets), is_right_),
domain_,
tag_modified_fn};
return {VMutableArray<float3>::For<VMutableArray_For_BezierHandles>(
curve->splines(), std::move(offsets), is_right_),
domain_,
tag_modified_fn};
}
bool try_delete(GeometryComponent &UNUSED(component)) const final
@@ -1387,7 +1368,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
/* First check for the simpler situation when we can return a simpler span virtual array. */
if (spans.size() == 1) {
return {std::make_unique<GVArray_For_GSpan>(spans.first()), ATTR_DOMAIN_POINT};
return {GVArray::ForSpan(spans.first()), ATTR_DOMAIN_POINT};
}
ReadAttributeLookup attribute = {};
@@ -1399,7 +1380,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
data[i] = spans[i].typed<T>();
BLI_assert(data[i].data() != nullptr);
}
attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
attribute = {point_data_varray(data, offsets), ATTR_DOMAIN_POINT};
});
return attribute;
}
@@ -1440,7 +1421,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
/* First check for the simpler situation when we can return a simpler span virtual array. */
if (spans.size() == 1) {
return {std::make_unique<GVMutableArray_For_GMutableSpan>(spans.first()), ATTR_DOMAIN_POINT};
return {GVMutableArray::ForSpan(spans.first()), ATTR_DOMAIN_POINT};
}
WriteAttributeLookup attribute = {};
@@ -1452,7 +1433,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
data[i] = spans[i].typed<T>();
BLI_assert(data[i].data() != nullptr);
}
attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
attribute = {point_data_varray(data, offsets), ATTR_DOMAIN_POINT};
});
return attribute;
}

View File

@@ -389,25 +389,22 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider
{
}
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
GVArray try_get_for_read(const GeometryComponent &component) const final
{
const InstancesComponent &instances_component = static_cast<const InstancesComponent &>(
component);
Span<float4x4> transforms = instances_component.instance_transforms();
return std::make_unique<fn::GVArray_For_DerivedSpan<float4x4, float3, get_transform_position>>(
transforms);
return VArray<float3>::ForDerivedSpan<float4x4, get_transform_position>(transforms);
}
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final
{
InstancesComponent &instances_component = static_cast<InstancesComponent &>(component);
MutableSpan<float4x4> transforms = instances_component.instance_transforms();
return {
std::make_unique<fn::GVMutableArray_For_DerivedSpan<float4x4,
float3,
get_transform_position,
set_transform_position>>(transforms),
domain_};
return {VMutableArray<float3>::ForDerivedSpan<float4x4,
get_transform_position,
set_transform_position>(transforms),
domain_};
}
bool try_delete(GeometryComponent &UNUSED(component)) const final
@@ -435,13 +432,13 @@ class InstanceIDAttributeProvider final : public BuiltinAttributeProvider {
{
}
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
GVArray try_get_for_read(const GeometryComponent &component) const final
{
const InstancesComponent &instances = static_cast<const InstancesComponent &>(component);
if (instances.instance_ids().is_empty()) {
return {};
}
return std::make_unique<fn::GVArray_For_Span<int>>(instances.instance_ids());
return VArray<int>::ForSpan(instances.instance_ids());
}
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final
@@ -450,8 +447,7 @@ class InstanceIDAttributeProvider final : public BuiltinAttributeProvider {
if (instances.instance_ids().is_empty()) {
return {};
}
return {std::make_unique<fn::GVMutableArray_For_MutableSpan<int>>(instances.instance_ids()),
domain_};
return {VMutableArray<int>::ForSpan(instances.instance_ids()), domain_};
}
bool try_delete(GeometryComponent &component) const final
@@ -477,8 +473,8 @@ class InstanceIDAttributeProvider final : public BuiltinAttributeProvider {
break;
}
case AttributeInit::Type::VArray: {
const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray;
varray->materialize_to_uninitialized(IndexRange(varray->size()), ids.data());
const GVArray &varray = static_cast<const AttributeInitVArray &>(initializer).varray;
varray.materialize_to_uninitialized(varray.index_range(), ids.data());
break;
}
case AttributeInit::Type::MoveArray: {

View File

@@ -32,8 +32,6 @@
/* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */
extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id);
using blender::fn::GVArray;
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
* \{ */
@@ -203,17 +201,17 @@ void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
}
}
static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray)
static GVArray adapt_mesh_domain_corner_to_point(const Mesh &mesh, const GVArray &varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
/* We compute all interpolated values at once, because for this interpolation, one has to
* iterate over all loops anyway. */
Array<T> values(mesh.totvert);
adapt_mesh_domain_corner_to_point_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
adapt_mesh_domain_corner_to_point_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
}
});
return new_varray;
@@ -239,14 +237,14 @@ static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
}
}
static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr varray)
static GVArray adapt_mesh_domain_point_to_corner(const Mesh &mesh, const GVArray &varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
Array<T> values(mesh.totloop);
adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
});
return new_varray;
}
@@ -295,15 +293,15 @@ void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
}
}
static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray)
static GVArray adapt_mesh_domain_corner_to_face(const Mesh &mesh, const GVArray &varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
adapt_mesh_domain_corner_to_face_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
adapt_mesh_domain_corner_to_face_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
}
});
return new_varray;
@@ -368,15 +366,15 @@ void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
}
}
static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray)
static GVArray adapt_mesh_domain_corner_to_edge(const Mesh &mesh, const GVArray &varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
adapt_mesh_domain_corner_to_edge_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
adapt_mesh_domain_corner_to_edge_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
}
});
return new_varray;
@@ -424,15 +422,15 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
}
}
static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray)
static GVArray adapt_mesh_domain_face_to_point(const Mesh &mesh, const GVArray &varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totvert);
adapt_mesh_domain_face_to_point_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
adapt_mesh_domain_face_to_point_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
}
});
return new_varray;
@@ -453,15 +451,15 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
}
}
static GVArrayPtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, GVArrayPtr varray)
static GVArray adapt_mesh_domain_face_to_corner(const Mesh &mesh, const GVArray &varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totloop);
adapt_mesh_domain_face_to_corner_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
adapt_mesh_domain_face_to_corner_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
}
});
return new_varray;
@@ -507,15 +505,15 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
}
}
static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray)
static GVArray adapt_mesh_domain_face_to_edge(const Mesh &mesh, const GVArray &varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
adapt_mesh_domain_face_to_edge_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
adapt_mesh_domain_face_to_edge_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
}
});
return new_varray;
@@ -567,15 +565,15 @@ void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
}
}
static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray)
static GVArray adapt_mesh_domain_point_to_face(const Mesh &mesh, const GVArray &varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
adapt_mesh_domain_point_to_face_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
adapt_mesh_domain_point_to_face_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
}
});
return new_varray;
@@ -617,15 +615,15 @@ void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
}
}
static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray)
static GVArray adapt_mesh_domain_point_to_edge(const Mesh &mesh, const GVArray &varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
adapt_mesh_domain_point_to_edge_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
adapt_mesh_domain_point_to_edge_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
}
});
return new_varray;
@@ -678,15 +676,15 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
}
}
static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray)
static GVArray adapt_mesh_domain_edge_to_corner(const Mesh &mesh, const GVArray &varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totloop);
adapt_mesh_domain_edge_to_corner_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
adapt_mesh_domain_edge_to_corner_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
}
});
return new_varray;
@@ -728,15 +726,15 @@ void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
}
}
static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray)
static GVArray adapt_mesh_domain_edge_to_point(const Mesh &mesh, const GVArray &varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totvert);
adapt_mesh_domain_edge_to_point_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
adapt_mesh_domain_edge_to_point_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
}
});
return new_varray;
@@ -788,15 +786,15 @@ void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
}
}
static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray)
static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
adapt_mesh_domain_edge_to_face_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
adapt_mesh_domain_edge_to_face_impl<T>(mesh, varray.typed<T>(), values);
new_varray = VArray<T>::ForContainer(std::move(values));
}
});
return new_varray;
@@ -804,15 +802,15 @@ static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr va
} // namespace blender::bke
blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
blender::fn::GVArrayPtr varray,
blender::fn::GVArray MeshComponent::attribute_try_adapt_domain_impl(
const blender::fn::GVArray &varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const
{
if (!varray) {
return {};
}
if (varray->size() == 0) {
if (varray.size() == 0) {
return {};
}
if (from_domain == to_domain) {
@@ -823,11 +821,11 @@ blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
case ATTR_DOMAIN_CORNER: {
switch (to_domain) {
case ATTR_DOMAIN_POINT:
return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(varray));
return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, varray);
case ATTR_DOMAIN_FACE:
return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(varray));
return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, varray);
case ATTR_DOMAIN_EDGE:
return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(varray));
return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, varray);
default:
break;
}
@@ -836,11 +834,11 @@ blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
case ATTR_DOMAIN_POINT: {
switch (to_domain) {
case ATTR_DOMAIN_CORNER:
return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(varray));
return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, varray);
case ATTR_DOMAIN_FACE:
return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(varray));
return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, varray);
case ATTR_DOMAIN_EDGE:
return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(varray));
return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, varray);
default:
break;
}
@@ -849,11 +847,11 @@ blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
case ATTR_DOMAIN_FACE: {
switch (to_domain) {
case ATTR_DOMAIN_POINT:
return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(varray));
return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, varray);
case ATTR_DOMAIN_CORNER:
return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(varray));
return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, varray);
case ATTR_DOMAIN_EDGE:
return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(varray));
return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, varray);
default:
break;
}
@@ -862,11 +860,11 @@ blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
case ATTR_DOMAIN_EDGE: {
switch (to_domain) {
case ATTR_DOMAIN_CORNER:
return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(varray));
return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, varray);
case ATTR_DOMAIN_POINT:
return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(varray));
return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, varray);
case ATTR_DOMAIN_FACE:
return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(varray));
return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, varray);
default:
break;
}
@@ -896,9 +894,9 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com
namespace blender::bke {
template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
static GVArrayPtr make_derived_read_attribute(const void *data, const int domain_size)
static GVArray make_derived_read_attribute(const void *data, const int domain_size)
{
return std::make_unique<fn::GVArray_For_DerivedSpan<StructT, ElemT, GetFunc>>(
return VArray<ElemT>::template ForDerivedSpan<StructT, GetFunc>(
Span<StructT>((const StructT *)data, domain_size));
}
@@ -906,23 +904,22 @@ template<typename StructT,
typename ElemT,
ElemT (*GetFunc)(const StructT &),
void (*SetFunc)(StructT &, ElemT)>
static GVMutableArrayPtr make_derived_write_attribute(void *data, const int domain_size)
static GVMutableArray make_derived_write_attribute(void *data, const int domain_size)
{
return std::make_unique<fn::GVMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(
return VMutableArray<ElemT>::template ForDerivedSpan<StructT, GetFunc, SetFunc>(
MutableSpan<StructT>((StructT *)data, domain_size));
}
template<typename T>
static GVArrayPtr make_array_read_attribute(const void *data, const int domain_size)
static GVArray make_array_read_attribute(const void *data, const int domain_size)
{
return std::make_unique<fn::GVArray_For_Span<T>>(Span<T>((const T *)data, domain_size));
return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size));
}
template<typename T>
static GVMutableArrayPtr make_array_write_attribute(void *data, const int domain_size)
static GVMutableArray make_array_write_attribute(void *data, const int domain_size)
{
return std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
MutableSpan<T>((T *)data, domain_size));
return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size));
}
static float3 get_vertex_position(const MVert &vert)
@@ -999,23 +996,23 @@ static void set_crease(MEdge &edge, float value)
edge.crease = round_fl_to_uchar_clamp(value * 255.0f);
}
class VMutableArray_For_VertexWeights final : public VMutableArray<float> {
class VMutableArray_For_VertexWeights final : public VMutableArrayImpl<float> {
private:
MDeformVert *dverts_;
const int dvert_index_;
public:
VMutableArray_For_VertexWeights(MDeformVert *dverts, const int totvert, const int dvert_index)
: VMutableArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
: VMutableArrayImpl<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
{
}
float get_impl(const int64_t index) const override
float get(const int64_t index) const override
{
return get_internal(dverts_, dvert_index_, index);
}
void set_impl(const int64_t index, const float value) override
void set(const int64_t index, const float value) override
{
MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_);
weight->weight = value;
@@ -1036,18 +1033,18 @@ class VMutableArray_For_VertexWeights final : public VMutableArray<float> {
}
};
class VArray_For_VertexWeights final : public VArray<float> {
class VArray_For_VertexWeights final : public VArrayImpl<float> {
private:
const MDeformVert *dverts_;
const int dvert_index_;
public:
VArray_For_VertexWeights(const MDeformVert *dverts, const int totvert, const int dvert_index)
: VArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
: VArrayImpl<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
{
}
float get_impl(const int64_t index) const override
float get(const int64_t index) const override
{
return VMutableArray_For_VertexWeights::get_internal(dverts_, dvert_index_, index);
}
@@ -1078,12 +1075,10 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
}
if (mesh->dvert == nullptr) {
static const float default_value = 0.0f;
return {std::make_unique<fn::GVArray_For_SingleValueRef>(
CPPType::get<float>(), mesh->totvert, &default_value),
ATTR_DOMAIN_POINT};
return {VArray<float>::ForSingle(default_value, mesh->totvert), ATTR_DOMAIN_POINT};
}
return {std::make_unique<fn::GVArray_For_EmbeddedVArray<float, VArray_For_VertexWeights>>(
mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
return {VArray<float>::For<VArray_For_VertexWeights>(
mesh->dvert, mesh->totvert, vertex_group_index),
ATTR_DOMAIN_POINT};
}
@@ -1114,11 +1109,9 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer(
&mesh->vdata, CD_MDEFORMVERT, mesh->totvert);
}
return {
std::make_unique<
fn::GVMutableArray_For_EmbeddedVMutableArray<float, VMutableArray_For_VertexWeights>>(
mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
ATTR_DOMAIN_POINT};
return {VMutableArray<float>::For<VMutableArray_For_VertexWeights>(
mesh->dvert, mesh->totvert, vertex_group_index),
ATTR_DOMAIN_POINT};
}
bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final
@@ -1184,7 +1177,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
{
}
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
GVArray try_get_for_read(const GeometryComponent &component) const final
{
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
const Mesh *mesh = mesh_component.get_for_read();
@@ -1197,8 +1190,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
CustomData_has_layer(&mesh->pdata, CD_NORMAL)) {
const void *data = CustomData_get_layer(&mesh->pdata, CD_NORMAL);
return std::make_unique<fn::GVArray_For_Span<float3>>(
Span<float3>((const float3 *)data, mesh->totpoly));
return VArray<float3>::ForSpan(Span<float3>((const float3 *)data, mesh->totpoly));
}
Array<float3> normals(mesh->totpoly);
@@ -1207,7 +1199,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
BKE_mesh_calc_poly_normal(poly, &mesh->mloop[poly->loopstart], mesh->mvert, normals[i]);
}
return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals));
return VArray<float3>::ForContainer(std::move(normals));
}
WriteAttributeLookup try_get_for_write(GeometryComponent &UNUSED(component)) const final

View File

@@ -141,16 +141,15 @@ int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) con
namespace blender::bke {
template<typename T>
static GVArrayPtr make_array_read_attribute(const void *data, const int domain_size)
static GVArray make_array_read_attribute(const void *data, const int domain_size)
{
return std::make_unique<fn::GVArray_For_Span<T>>(Span<T>((const T *)data, domain_size));
return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size));
}
template<typename T>
static GVMutableArrayPtr make_array_write_attribute(void *data, const int domain_size)
static GVMutableArray make_array_write_attribute(void *data, const int domain_size)
{
return std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
MutableSpan<T>((T *)data, domain_size));
return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size));
}
/**

View File

@@ -364,12 +364,12 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
result.attribute_try_create(
entry.key, domain_output, data_type_output, AttributeInitDefault());
WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(attribute_id);
if (!write_attribute || &write_attribute.varray->type() != cpp_type ||
if (!write_attribute || &write_attribute.varray.type() != cpp_type ||
write_attribute.domain != domain_output) {
continue;
}
fn::GVMutableArray_GSpan dst_span{*write_attribute.varray};
fn::GVMutableArray_GSpan dst_span{write_attribute.varray};
int offset = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
@@ -381,11 +381,11 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
if (domain_size == 0) {
continue; /* Domain size is 0, so no need to increment the offset. */
}
GVArrayPtr source_attribute = component.attribute_try_get_for_read(
GVArray source_attribute = component.attribute_try_get_for_read(
attribute_id, domain_output, data_type_output);
if (source_attribute) {
fn::GVArray_GSpan src_span{*source_attribute};
fn::GVArray_GSpan src_span{source_attribute};
const void *src_buffer = src_span.data();
for (const int UNUSED(i) : set_group.transforms.index_range()) {
void *dst_buffer = dst_span[offset];

View File

@@ -269,7 +269,7 @@ void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_
eAttributeMapMode mode)
{
if (src_attribute && dst_attribute) {
this->sample_data(*src_attribute.varray, src_attribute.domain, mode, dst_attribute.as_span());
this->sample_data(src_attribute.varray, src_attribute.domain, mode, dst_attribute.as_span());
}
}

View File

@@ -30,14 +30,12 @@ using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
using blender::VArray;
using blender::attribute_math::convert_to_static_type;
using blender::bke::AttributeIDRef;
using blender::fn::GMutableSpan;
using blender::fn::GSpan;
using blender::fn::GVArray;
using blender::fn::GVArray_For_GSpan;
using blender::fn::GVArray_Typed;
using blender::fn::GVArrayPtr;
Spline::Type Spline::type() const
{
@@ -416,7 +414,7 @@ Span<float3> Spline::evaluated_normals() const
}
/* Rotate the generated normals with the interpolated tilt data. */
GVArray_Typed<float> tilts = this->interpolate_to_evaluated(this->tilts());
VArray<float> tilts = this->interpolate_to_evaluated(this->tilts());
for (const int i : normals.index_range()) {
normals[i] = rotate_direction_around_axis(normals[i], tangents[i], tilts[i]);
}
@@ -529,9 +527,9 @@ void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated)
}
}
GVArrayPtr Spline::interpolate_to_evaluated(GSpan data) const
GVArray Spline::interpolate_to_evaluated(GSpan data) const
{
return this->interpolate_to_evaluated(GVArray_For_GSpan(data));
return this->interpolate_to_evaluated(GVArray::ForSpan(data));
}
/**
@@ -547,7 +545,7 @@ void Spline::sample_with_index_factors(const GVArray &src,
blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
const GVArray_Typed<T> src_typed = src.typed<T>();
const VArray<T> src_typed = src.typed<T>();
MutableSpan<T> dst_typed = dst.typed<T>();
if (src.size() == 1) {
dst_typed.fill(src_typed[0]);

View File

@@ -25,9 +25,8 @@ using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
using blender::VArray;
using blender::fn::GVArray;
using blender::fn::GVArray_For_ArrayContainer;
using blender::fn::GVArrayPtr;
void BezierSpline::copy_settings(Spline &dst) const
{
@@ -697,26 +696,26 @@ static void interpolate_to_evaluated_impl(const BezierSpline &spline,
}
}
GVArrayPtr BezierSpline::interpolate_to_evaluated(const GVArray &src) const
GVArray BezierSpline::interpolate_to_evaluated(const GVArray &src) const
{
BLI_assert(src.size() == this->size());
if (src.is_single()) {
return src.shallow_copy();
return src;
}
const int eval_size = this->evaluated_points_size();
if (eval_size == 1) {
return src.shallow_copy();
return src;
}
GVArrayPtr new_varray;
GVArray new_varray;
blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
Array<T> values(eval_size);
interpolate_to_evaluated_impl<T>(*this, src.typed<T>(), values);
new_varray = std::make_unique<GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
new_varray = VArray<T>::ForContainer(std::move(values));
}
});

View File

@@ -26,10 +26,8 @@ using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
using blender::VArray;
using blender::fn::GVArray;
using blender::fn::GVArray_For_ArrayContainer;
using blender::fn::GVArray_Typed;
using blender::fn::GVArrayPtr;
void NURBSpline::copy_settings(Spline &dst) const
{
@@ -410,23 +408,23 @@ void interpolate_to_evaluated_impl(Span<NURBSpline::BasisCache> weights,
mixer.finalize();
}
GVArrayPtr NURBSpline::interpolate_to_evaluated(const GVArray &src) const
GVArray NURBSpline::interpolate_to_evaluated(const GVArray &src) const
{
BLI_assert(src.size() == this->size());
if (src.is_single()) {
return src.shallow_copy();
return src;
}
Span<BasisCache> basis_cache = this->calculate_basis_cache();
GVArrayPtr new_varray;
GVArray new_varray;
blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
Array<T> values(this->evaluated_points_size());
interpolate_to_evaluated_impl<T>(basis_cache, src.typed<T>(), values);
new_varray = std::make_unique<GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
new_varray = VArray<T>::ForContainer(std::move(values));
}
});
@@ -448,8 +446,8 @@ Span<float3> NURBSpline::evaluated_positions() const
evaluated_position_cache_.resize(eval_size);
/* TODO: Avoid copying the evaluated data from the temporary array. */
GVArray_Typed<float3> evaluated = Spline::interpolate_to_evaluated(positions_.as_span());
evaluated->materialize(evaluated_position_cache_);
VArray<float3> evaluated = Spline::interpolate_to_evaluated(positions_.as_span());
evaluated.materialize(evaluated_position_cache_);
position_cache_dirty_ = false;
return evaluated_position_cache_;

View File

@@ -23,7 +23,6 @@ using blender::float3;
using blender::MutableSpan;
using blender::Span;
using blender::fn::GVArray;
using blender::fn::GVArrayPtr;
void PolySpline::copy_settings(Spline &UNUSED(dst)) const
{
@@ -122,9 +121,8 @@ Span<float3> PolySpline::evaluated_positions() const
* the original data. Therefore the lifetime of the returned virtual array must not be longer than
* the source data.
*/
GVArrayPtr PolySpline::interpolate_to_evaluated(const GVArray &src) const
GVArray PolySpline::interpolate_to_evaluated(const GVArray &src) const
{
BLI_assert(src.size() == this->size());
return src.shallow_copy();
return src;
}

View File

@@ -0,0 +1,320 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
/** \file
* \ingroup bli
*
* A #blender::Any is a type-safe container for single values of any copy constructible type.
* It is similar to #std::any but provides the following two additional features:
* - Adjustable inline buffer capacity and alignment. #std::any has a small inline buffer in most
* implementations as well, but its size is not guaranteed.
* - Can store additional user-defined type information without increasing the stack size of #Any.
*/
#include <algorithm>
#include <utility>
#include "BLI_memory_utils.hh"
namespace blender {
namespace detail {
/**
* Contains function pointers that manage the memory in an #Any.
* Additional type specific #ExtraInfo can be embedded here as well.
*/
template<typename ExtraInfo> struct AnyTypeInfo {
void (*copy_construct)(void *dst, const void *src);
void (*move_construct)(void *dst, void *src);
void (*destruct)(void *src);
const void *(*get)(const void *src);
ExtraInfo extra_info;
/**
* Used when #T is stored directly in the inline buffer of the #Any.
*/
template<typename T> static const AnyTypeInfo &get_for_inline()
{
static AnyTypeInfo funcs = {[](void *dst, const void *src) { new (dst) T(*(const T *)src); },
[](void *dst, void *src) { new (dst) T(std::move(*(T *)src)); },
[](void *src) { ((T *)src)->~T(); },
[](const void *src) { return src; },
ExtraInfo::template get<T>()};
return funcs;
}
/**
* Used when #T can't be stored directly in the inline buffer and is stored in a #std::unique_ptr
* instead. In this scenario, the #std::unique_ptr is stored in the inline buffer.
*/
template<typename T> static const AnyTypeInfo &get_for_unique_ptr()
{
using Ptr = std::unique_ptr<T>;
static AnyTypeInfo funcs = {
[](void *dst, const void *src) { new (dst) Ptr(new T(**(const Ptr *)src)); },
[](void *dst, void *src) { new (dst) Ptr(new T(std::move(**(Ptr *)src))); },
[](void *src) { ((Ptr *)src)->~Ptr(); },
[](const void *src) -> const void * { return &**(const Ptr *)src; },
ExtraInfo::template get<T>()};
return funcs;
}
/**
* Used when the #Any does not contain any type currently.
*/
static const AnyTypeInfo &get_for_empty()
{
static AnyTypeInfo funcs = {[](void *UNUSED(dst), const void *UNUSED(src)) {},
[](void *UNUSED(dst), void *UNUSED(src)) {},
[](void *UNUSED(src)) {},
[](const void *UNUSED(src)) -> const void * { return nullptr; },
ExtraInfo{}};
return funcs;
}
};
/**
* Dummy extra info that is used when no additional type information should be stored in the #Any.
*/
struct NoExtraInfo {
template<typename T> static NoExtraInfo get()
{
return {};
}
};
} // namespace detail
template<
/**
* Either void or a struct that contains data members for additional type information.
* The struct has to have a static `ExtraInfo get<T>()` method that initializes the struct
* based on a type.
*/
typename ExtraInfo = void,
/**
* Size of the inline buffer. This allows types that are small enough to be stored directly
* inside the #Any without an additional allocation.
*/
size_t InlineBufferCapacity = 8,
/**
* Required minimum alignment of the inline buffer. If this is smaller than the alignment
* requirement of a used type, a separate allocation is necessary.
*/
size_t Alignment = 8>
class Any {
private:
/* Makes it possible to use void in the template parameters. */
using RealExtraInfo =
std::conditional_t<std::is_void_v<ExtraInfo>, detail::NoExtraInfo, ExtraInfo>;
using Info = detail::AnyTypeInfo<RealExtraInfo>;
/**
* Inline buffer that either contains nothing, the stored value directly, or a #std::unique_ptr
* to the value.
*/
AlignedBuffer<std::max(InlineBufferCapacity, sizeof(std::unique_ptr<int>)), Alignment> buffer_{};
/**
* Information about the type that is currently stored.
*/
const Info *info_ = &Info::get_for_empty();
public:
/** Only copy constructible types can be stored in #Any. */
template<typename T> static constexpr inline bool is_allowed_v = std::is_copy_constructible_v<T>;
/**
* Checks if the type will be stored in the inline buffer or if it requires a separate
* allocation.
*/
template<typename T>
static constexpr inline bool is_inline_v = std::is_nothrow_move_constructible_v<T> &&
sizeof(T) <= InlineBufferCapacity &&
alignof(T) <= Alignment;
/**
* Checks if #T is the same type as this #Any, because in this case the behavior of e.g. the
* assignment operator is different.
*/
template<typename T>
static constexpr inline bool is_same_any_v = std::is_same_v<std::decay_t<T>, Any>;
private:
template<typename T> const Info &get_info() const
{
using DecayT = std::decay_t<T>;
static_assert(is_allowed_v<DecayT>);
if constexpr (is_inline_v<DecayT>) {
return Info::template get_for_inline<DecayT>();
}
else {
return Info::template get_for_unique_ptr<DecayT>();
}
}
public:
Any() = default;
Any(const Any &other) : info_(other.info_)
{
info_->copy_construct(&buffer_, &other.buffer_);
}
/**
* \note: The #other #Any will not be empty afterwards if it was not before. Just its value is in
* a moved-from state.
*/
Any(Any &&other) noexcept : info_(other.info_)
{
info_->move_construct(&buffer_, &other.buffer_);
}
/**
* Constructs a new #Any that contains the given type #T from #args. The #std::in_place_type_t is
* used to disambiguate this and the copy/move constructors.
*/
template<typename T, typename... Args> explicit Any(std::in_place_type_t<T>, Args &&...args)
{
using DecayT = std::decay_t<T>;
static_assert(is_allowed_v<DecayT>);
info_ = &this->template get_info<DecayT>();
if constexpr (is_inline_v<DecayT>) {
/* Construct the value directly in the inline buffer. */
new (&buffer_) DecayT(std::forward<Args>(args)...);
}
else {
/* Construct the value in a new allocation and store a #std::unique_ptr to it in the inline
* buffer. */
new (&buffer_) std::unique_ptr<DecayT>(new DecayT(std::forward<Args>(args)...));
}
}
/**
* Constructs a new #Any that contains the given value.
*/
template<typename T, typename X = std::enable_if_t<!is_same_any_v<T>, void>>
Any(T &&value) : Any(std::in_place_type<T>, std::forward<T>(value))
{
}
~Any()
{
info_->destruct(&buffer_);
}
/**
* \note: Only needed because the template below does not count as copy assignment operator.
*/
Any &operator=(const Any &other)
{
if (this == &other) {
return *this;
}
this->~Any();
new (this) Any(other);
return *this;
}
/** Assign any value to the #Any. */
template<typename T> Any &operator=(T &&other)
{
if constexpr (is_same_any_v<T>) {
if (this == &other) {
return *this;
}
}
this->~Any();
new (this) Any(std::forward<T>(other));
return *this;
}
/** Destruct any existing value to make it empty. */
void reset()
{
info_->destruct(&buffer_);
info_ = &Info::get_for_empty();
}
operator bool() const
{
return this->has_value();
}
bool has_value() const
{
return info_ != &Info::get_for_empty();
}
template<typename T, typename... Args> std::decay_t<T> &emplace(Args &&...args)
{
this->~Any();
new (this) Any(std::in_place_type<T>, std::forward<Args>(args)...);
return this->get<T>();
}
/** Return true when the value that is currently stored is a #T. */
template<typename T> bool is() const
{
return info_ == &this->template get_info<T>();
}
/** Get a pointer to the stored value. */
void *get()
{
return const_cast<void *>(info_->get(&buffer_));
}
/** Get a pointer to the stored value. */
const void *get() const
{
return info_->get(&buffer_);
}
/**
* Get a reference to the stored value. This invokes undefined behavior when #T does not have the
* correct type.
*/
template<typename T> std::decay_t<T> &get()
{
BLI_assert(this->is<T>());
return *static_cast<std::decay_t<T> *>(this->get());
}
/**
* Get a reference to the stored value. This invokes undefined behavior when #T does not have the
* correct type.
*/
template<typename T> const std::decay_t<T> &get() const
{
BLI_assert(this->is<T>());
return *static_cast<const std::decay_t<T> *>(this->get());
}
/**
* Get extra information that has been stored for the contained type.
*/
const RealExtraInfo &extra_info() const
{
return info_->extra_info;
}
};
} // namespace blender

File diff suppressed because it is too large Load Diff

View File

@@ -165,6 +165,7 @@ set(SRC
BLI_alloca.h
BLI_allocator.hh
BLI_any.hh
BLI_args.h
BLI_array.h
BLI_array.hh
@@ -411,6 +412,7 @@ blender_add_lib(bf_blenlib "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
if(WITH_GTESTS)
set(TEST_SRC
tests/BLI_any_test.cc
tests/BLI_array_store_test.cc
tests/BLI_array_test.cc
tests/BLI_array_utils_test.cc

View File

@@ -0,0 +1,108 @@
/* Apache License, Version 2.0 */
#include "BLI_any.hh"
#include "BLI_map.hh"
#include "testing/testing.h"
namespace blender::tests {
TEST(any, DefaultConstructor)
{
Any a;
EXPECT_FALSE(a.has_value());
}
TEST(any, AssignInt)
{
Any<> a = 5;
EXPECT_TRUE(a.has_value());
EXPECT_TRUE(a.is<int>());
EXPECT_FALSE(a.is<float>());
const int &value = a.get<int>();
EXPECT_EQ(value, 5);
a = 10;
EXPECT_EQ(value, 10);
Any b = a;
EXPECT_TRUE(b.has_value());
EXPECT_EQ(b.get<int>(), 10);
Any c = std::move(a);
EXPECT_TRUE(c);
EXPECT_EQ(c.get<int>(), 10);
EXPECT_EQ(a.get<int>(), 10); /* NOLINT: bugprone-use-after-move */
a.reset();
EXPECT_FALSE(a);
}
TEST(any, AssignMap)
{
Any<> a = Map<int, int>();
EXPECT_TRUE(a.has_value());
EXPECT_TRUE((a.is<Map<int, int>>()));
EXPECT_FALSE((a.is<Map<int, float>>()));
Map<int, int> &map = a.get<Map<int, int>>();
map.add(4, 2);
EXPECT_EQ((a.get<Map<int, int>>().lookup(4)), 2);
Any b = a;
EXPECT_TRUE(b);
EXPECT_EQ((b.get<Map<int, int>>().lookup(4)), 2);
Any c = std::move(a);
c = c;
EXPECT_TRUE(b);
EXPECT_EQ((c.get<Map<int, int>>().lookup(4)), 2);
EXPECT_TRUE((a.get<Map<int, int>>().is_empty())); /* NOLINT: bugprone-use-after-move */
}
TEST(any, AssignAny)
{
Any<> a = 5;
Any<> b = std::string("hello");
Any c;
Any z;
EXPECT_FALSE(z.has_value());
z = a;
EXPECT_TRUE(z.has_value());
EXPECT_EQ(z.get<int>(), 5);
z = b;
EXPECT_EQ(z.get<std::string>(), "hello");
z = c;
EXPECT_FALSE(z.has_value());
z = Any(std::in_place_type<Any<>>, a);
EXPECT_FALSE(z.is<int>());
EXPECT_TRUE(z.is<Any<>>());
EXPECT_EQ(z.get<Any<>>().get<int>(), 5);
}
struct ExtraSizeInfo {
size_t size;
template<typename T> static ExtraSizeInfo get()
{
return {sizeof(T)};
}
};
TEST(any, ExtraInfo)
{
using MyAny = Any<ExtraSizeInfo>;
MyAny a = 5;
EXPECT_EQ(a.extra_info().size, sizeof(int));
a = std::string("hello");
EXPECT_EQ(a.extra_info().size, sizeof(std::string));
}
} // namespace blender::tests

View File

@@ -12,7 +12,7 @@ namespace blender::tests {
TEST(virtual_array, Span)
{
std::array<int, 5> data = {3, 4, 5, 6, 7};
VArray_For_Span<int> varray{data};
VArray<int> varray = VArray<int>::ForSpan(data);
EXPECT_EQ(varray.size(), 5);
EXPECT_EQ(varray.get(0), 3);
EXPECT_EQ(varray.get(4), 7);
@@ -23,7 +23,7 @@ TEST(virtual_array, Span)
TEST(virtual_array, Single)
{
VArray_For_Single<int> varray{10, 4};
VArray<int> varray = VArray<int>::ForSingle(10, 4);
EXPECT_EQ(varray.size(), 4);
EXPECT_EQ(varray.get(0), 10);
EXPECT_EQ(varray.get(3), 10);
@@ -35,7 +35,7 @@ TEST(virtual_array, Array)
{
Array<int> array = {1, 2, 3, 5, 8};
{
VArray_For_ArrayContainer varray{array};
VArray<int> varray = VArray<int>::ForContainer(array);
EXPECT_EQ(varray.size(), 5);
EXPECT_EQ(varray[0], 1);
EXPECT_EQ(varray[2], 3);
@@ -43,7 +43,7 @@ TEST(virtual_array, Array)
EXPECT_TRUE(varray.is_span());
}
{
VArray_For_ArrayContainer varray{std::move(array)};
VArray<int> varray = VArray<int>::ForContainer(std::move(array));
EXPECT_EQ(varray.size(), 5);
EXPECT_EQ(varray[0], 1);
EXPECT_EQ(varray[2], 3);
@@ -51,7 +51,7 @@ TEST(virtual_array, Array)
EXPECT_TRUE(varray.is_span());
}
{
VArray_For_ArrayContainer varray{array}; /* NOLINT: bugprone-use-after-move */
VArray<int> varray = VArray<int>::ForContainer(array); /* NOLINT: bugprone-use-after-move */
EXPECT_TRUE(varray.is_empty());
}
}
@@ -59,7 +59,7 @@ TEST(virtual_array, Array)
TEST(virtual_array, Vector)
{
Vector<int> vector = {9, 8, 7, 6};
VArray_For_ArrayContainer varray{std::move(vector)};
VArray<int> varray = VArray<int>::ForContainer(std::move(vector));
EXPECT_EQ(varray.size(), 4);
EXPECT_EQ(varray[0], 9);
EXPECT_EQ(varray[3], 6);
@@ -68,7 +68,7 @@ TEST(virtual_array, Vector)
TEST(virtual_array, StdVector)
{
std::vector<int> vector = {5, 6, 7, 8};
VArray_For_ArrayContainer varray{std::move(vector)};
VArray<int> varray = VArray<int>::ForContainer(std::move(vector));
EXPECT_EQ(varray.size(), 4);
EXPECT_EQ(varray[0], 5);
EXPECT_EQ(varray[1], 6);
@@ -77,7 +77,7 @@ TEST(virtual_array, StdVector)
TEST(virtual_array, StdArray)
{
std::array<int, 4> array = {2, 3, 4, 5};
VArray_For_ArrayContainer varray{array};
VArray<int> varray = VArray<int>::ForContainer(std::move(array));
EXPECT_EQ(varray.size(), 4);
EXPECT_EQ(varray[0], 2);
EXPECT_EQ(varray[1], 3);
@@ -86,7 +86,7 @@ TEST(virtual_array, StdArray)
TEST(virtual_array, VectorSet)
{
VectorSet<int> vector_set = {5, 3, 7, 3, 3, 5, 1};
VArray_For_ArrayContainer varray{std::move(vector_set)};
VArray<int> varray = VArray<int>::ForContainer(std::move(vector_set));
EXPECT_TRUE(vector_set.is_empty()); /* NOLINT: bugprone-use-after-move. */
EXPECT_EQ(varray.size(), 4);
EXPECT_EQ(varray[0], 5);
@@ -98,7 +98,7 @@ TEST(virtual_array, VectorSet)
TEST(virtual_array, Func)
{
auto func = [](int64_t index) { return (int)(index * index); };
VArray_For_Func<int, decltype(func)> varray{10, func};
VArray<int> varray = VArray<int>::ForFunc(10, func);
EXPECT_EQ(varray.size(), 10);
EXPECT_EQ(varray[0], 0);
EXPECT_EQ(varray[3], 9);
@@ -108,7 +108,7 @@ TEST(virtual_array, Func)
TEST(virtual_array, AsSpan)
{
auto func = [](int64_t index) { return (int)(10 * index); };
VArray_For_Func<int, decltype(func)> func_varray{10, func};
VArray<int> func_varray = VArray<int>::ForFunc(10, func);
VArray_Span span_varray{func_varray};
EXPECT_EQ(span_varray.size(), 10);
Span<int> span = span_varray;
@@ -134,13 +134,14 @@ TEST(virtual_array, DerivedSpan)
vector.append({3, 4, 5});
vector.append({1, 1, 1});
{
VArray_For_DerivedSpan<std::array<int, 3>, int, get_x> varray{vector};
VArray<int> varray = VArray<int>::ForDerivedSpan<std::array<int, 3>, get_x>(vector);
EXPECT_EQ(varray.size(), 2);
EXPECT_EQ(varray[0], 3);
EXPECT_EQ(varray[1], 1);
}
{
VMutableArray_For_DerivedSpan<std::array<int, 3>, int, get_x, set_x> varray{vector};
VMutableArray<int> varray =
VMutableArray<int>::ForDerivedSpan<std::array<int, 3>, get_x, set_x>(vector);
EXPECT_EQ(varray.size(), 2);
EXPECT_EQ(varray[0], 3);
EXPECT_EQ(varray[1], 1);
@@ -151,4 +152,32 @@ TEST(virtual_array, DerivedSpan)
}
}
TEST(virtual_array, MutableToImmutable)
{
std::array<int, 4> array = {4, 2, 6, 4};
{
VMutableArray<int> mutable_varray = VMutableArray<int>::ForSpan(array);
VArray<int> varray = mutable_varray;
EXPECT_TRUE(varray.is_span());
EXPECT_EQ(varray.size(), 4);
EXPECT_EQ(varray[1], 2);
EXPECT_EQ(mutable_varray.size(), 4);
}
{
VMutableArray<int> mutable_varray = VMutableArray<int>::ForSpan(array);
EXPECT_EQ(mutable_varray.size(), 4);
VArray<int> varray = std::move(mutable_varray);
EXPECT_TRUE(varray.is_span());
EXPECT_EQ(varray.size(), 4);
EXPECT_EQ(varray[1], 2);
EXPECT_EQ(mutable_varray.size(), 0);
}
{
VArray<int> varray = VMutableArray<int>::ForSpan(array);
EXPECT_TRUE(varray.is_span());
EXPECT_EQ(varray.size(), 4);
EXPECT_EQ(varray[1], 2);
}
}
} // namespace blender::tests

View File

@@ -26,7 +26,6 @@ set(INC
../blentranslation
../depsgraph
../draw
../editors/include
../imbuf
../makesdna
../makesrna

View File

@@ -26,11 +26,9 @@
#include "BKE_idtype.h"
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_mball_tessellate.h"
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_scene.h"
#include "BKE_vfont.h"
#include "BLI_path_util.h"
#include "BLI_threads.h"
@@ -45,8 +43,6 @@
#include "IMB_imbuf.h"
#include "ED_datafiles.h"
#include "RNA_define.h"
#include "WM_api.h"
@@ -74,7 +70,6 @@ void BlendfileLoadingBaseTest::SetUpTestCase()
DEG_register_node_types();
RNA_init();
BKE_node_system_init();
BKE_vfont_builtin_register(datatoc_bfont_pfb, datatoc_bfont_pfb_size);
G.background = true;
G.factory_startup = true;
@@ -112,7 +107,6 @@ void BlendfileLoadingBaseTest::TearDownTestCase()
void BlendfileLoadingBaseTest::TearDown()
{
BKE_mball_cubeTable_free();
depsgraph_free();
blendfile_free();

View File

@@ -2155,8 +2155,8 @@ void FONT_OT_open(wmOperatorType *ot)
FILE_SPECIAL,
FILE_OPENFILE,
WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
FILE_IMGDISPLAY,
FILE_SORT_ALPHA);
}
/** \} */

View File

@@ -25,20 +25,6 @@ set(INC
../../io/alembic
../../io/collada
../../io/gpencil
../../io/wavefront_obj
../../io/usd
../../makesdna
../../makesrna
../../windowmanager
../../../../intern/guardedalloc
)
set(INC_SYS
)
set(SRC
io_alembic.c
../../io/usd
../../makesdna
../../makesrna
@@ -57,7 +43,6 @@ set(SRC
io_gpencil_export.c
io_gpencil_import.c
io_gpencil_utils.c
io_obj.c
io_ops.c
io_usd.c
@@ -72,7 +57,6 @@ set(SRC
set(LIB
bf_blenkernel
bf_blenlib
bf_wavefront_obj
)
if(WITH_OPENCOLLADA)

View File

@@ -1,456 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup editor/io
*/
#include "DNA_space_types.h"
#include "BKE_context.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
#include "MEM_guardedalloc.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "WM_api.h"
#include "WM_types.h"
#include "DEG_depsgraph.h"
#include "IO_wavefront_obj.h"
#include "io_obj.h"
const EnumPropertyItem io_obj_transform_axis_forward[] = {
{OBJ_AXIS_X_FORWARD, "X_FORWARD", 0, "X", "Positive X axis"},
{OBJ_AXIS_Y_FORWARD, "Y_FORWARD", 0, "Y", "Positive Y axis"},
{OBJ_AXIS_Z_FORWARD, "Z_FORWARD", 0, "Z", "Positive Z axis"},
{OBJ_AXIS_NEGATIVE_X_FORWARD, "NEGATIVE_X_FORWARD", 0, "-X", "Negative X axis"},
{OBJ_AXIS_NEGATIVE_Y_FORWARD, "NEGATIVE_Y_FORWARD", 0, "-Y", "Negative Y axis"},
{OBJ_AXIS_NEGATIVE_Z_FORWARD, "NEGATIVE_Z_FORWARD", 0, "-Z (Default)", "Negative Z axis"},
{0, NULL, 0, NULL, NULL}};
const EnumPropertyItem io_obj_transform_axis_up[] = {
{OBJ_AXIS_X_UP, "X_UP", 0, "X", "Positive X axis"},
{OBJ_AXIS_Y_UP, "Y_UP", 0, "Y (Default)", "Positive Y axis"},
{OBJ_AXIS_Z_UP, "Z_UP", 0, "Z", "Positive Z axis"},
{OBJ_AXIS_NEGATIVE_X_UP, "NEGATIVE_X_UP", 0, "-X", "Negative X axis"},
{OBJ_AXIS_NEGATIVE_Y_UP, "NEGATIVE_Y_UP", 0, "-Y", "Negative Y axis"},
{OBJ_AXIS_NEGATIVE_Z_UP, "NEGATIVE_Z_UP", 0, "-Z", "Negative Z axis"},
{0, NULL, 0, NULL, NULL}};
const EnumPropertyItem io_obj_export_evaluation_mode[] = {
{DAG_EVAL_RENDER, "DAG_EVAL_RENDER", 0, "Render", "Export objects as they appear in render"},
{DAG_EVAL_VIEWPORT,
"DAG_EVAL_VIEWPORT",
0,
"Viewport (Default)",
"Export objects as they appear in the viewport"},
{0, NULL, 0, NULL, NULL}};
static int wm_obj_export_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
Main *bmain = CTX_data_main(C);
char filepath[FILE_MAX];
if (BKE_main_blendfile_path(bmain)[0] == '\0') {
BLI_strncpy(filepath, "untitled", sizeof(filepath));
}
else {
BLI_strncpy(filepath, BKE_main_blendfile_path(bmain), sizeof(filepath));
}
BLI_path_extension_replace(filepath, sizeof(filepath), ".obj");
RNA_string_set(op->ptr, "filepath", filepath);
}
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
}
static int wm_obj_export_exec(bContext *C, wmOperator *op)
{
if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
struct OBJExportParams export_params;
RNA_string_get(op->ptr, "filepath", export_params.filepath);
export_params.blen_filepath = CTX_data_main(C)->name;
export_params.export_animation = RNA_boolean_get(op->ptr, "export_animation");
export_params.start_frame = RNA_int_get(op->ptr, "start_frame");
export_params.end_frame = RNA_int_get(op->ptr, "end_frame");
export_params.forward_axis = RNA_enum_get(op->ptr, "forward_axis");
export_params.up_axis = RNA_enum_get(op->ptr, "up_axis");
export_params.scaling_factor = RNA_float_get(op->ptr, "scaling_factor");
export_params.export_eval_mode = RNA_enum_get(op->ptr, "export_eval_mode");
export_params.export_selected_objects = RNA_boolean_get(op->ptr, "export_selected_objects");
export_params.export_uv = RNA_boolean_get(op->ptr, "export_uv");
export_params.export_normals = RNA_boolean_get(op->ptr, "export_normals");
export_params.export_materials = RNA_boolean_get(op->ptr, "export_materials");
export_params.export_triangulated_mesh = RNA_boolean_get(op->ptr, "export_triangulated_mesh");
export_params.export_curves_as_nurbs = RNA_boolean_get(op->ptr, "export_curves_as_nurbs");
export_params.export_object_groups = RNA_boolean_get(op->ptr, "export_object_groups");
export_params.export_material_groups = RNA_boolean_get(op->ptr, "export_material_groups");
export_params.export_vertex_groups = RNA_boolean_get(op->ptr, "export_vertex_groups");
export_params.export_smooth_groups = RNA_boolean_get(op->ptr, "export_smooth_groups");
export_params.smooth_groups_bitflags = RNA_boolean_get(op->ptr, "smooth_group_bitflags");
OBJ_export(C, &export_params);
return OPERATOR_FINISHED;
}
static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr)
{
const bool export_animation = RNA_boolean_get(imfptr, "export_animation");
const bool export_smooth_groups = RNA_boolean_get(imfptr, "export_smooth_groups");
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
/* Animation options. */
uiLayout *box = uiLayoutBox(layout);
uiItemL(box, IFACE_("Animation"), ICON_ANIM);
uiLayout *col = uiLayoutColumn(box, false);
uiLayout *sub = uiLayoutColumn(col, false);
uiItemR(sub, imfptr, "export_animation", 0, NULL, ICON_NONE);
sub = uiLayoutColumn(sub, true);
uiItemR(sub, imfptr, "start_frame", 0, IFACE_("Frame Start"), ICON_NONE);
uiItemR(sub, imfptr, "end_frame", 0, IFACE_("End"), ICON_NONE);
uiLayoutSetEnabled(sub, export_animation);
/* Object Transform options. */
box = uiLayoutBox(layout);
uiItemL(box, IFACE_("Object Properties"), ICON_OBJECT_DATA);
col = uiLayoutColumn(box, false);
sub = uiLayoutColumn(col, false);
uiItemR(sub, imfptr, "forward_axis", 0, IFACE_("Axis Forward"), ICON_NONE);
uiItemR(sub, imfptr, "up_axis", 0, IFACE_("Up"), ICON_NONE);
sub = uiLayoutColumn(col, false);
uiItemR(sub, imfptr, "scaling_factor", 0, NULL, ICON_NONE);
sub = uiLayoutColumnWithHeading(col, false, IFACE_("Objects"));
uiItemR(sub, imfptr, "export_selected_objects", 0, IFACE_("Selected Only"), ICON_NONE);
uiItemR(sub, imfptr, "export_eval_mode", 0, IFACE_("Properties"), ICON_NONE);
/* Options for what to write. */
box = uiLayoutBox(layout);
uiItemL(box, IFACE_("Geometry Export"), ICON_EXPORT);
col = uiLayoutColumn(box, false);
sub = uiLayoutColumnWithHeading(col, false, IFACE_("Export"));
uiItemR(sub, imfptr, "export_uv", 0, IFACE_("UV Coordinates"), ICON_NONE);
uiItemR(sub, imfptr, "export_normals", 0, IFACE_("Normals"), ICON_NONE);
uiItemR(sub, imfptr, "export_materials", 0, IFACE_("Materials"), ICON_NONE);
uiItemR(sub, imfptr, "export_triangulated_mesh", 0, IFACE_("Triangulated Mesh"), ICON_NONE);
uiItemR(sub, imfptr, "export_curves_as_nurbs", 0, IFACE_("Curves as NURBS"), ICON_NONE);
box = uiLayoutBox(layout);
uiItemL(box, IFACE_("Grouping"), ICON_GROUP);
col = uiLayoutColumn(box, false);
sub = uiLayoutColumnWithHeading(col, false, IFACE_("Export"));
uiItemR(sub, imfptr, "export_object_groups", 0, IFACE_("Object Groups"), ICON_NONE);
uiItemR(sub, imfptr, "export_material_groups", 0, IFACE_("Material Groups"), ICON_NONE);
uiItemR(sub, imfptr, "export_vertex_groups", 0, IFACE_("Vertex Groups"), ICON_NONE);
uiItemR(sub, imfptr, "export_smooth_groups", 0, IFACE_("Smooth Groups"), ICON_NONE);
sub = uiLayoutColumn(sub, false);
uiLayoutSetEnabled(sub, export_smooth_groups);
uiItemR(sub, imfptr, "smooth_group_bitflags", 0, IFACE_("Smooth Group Bitflags"), ICON_NONE);
}
static void wm_obj_export_draw(bContext *UNUSED(C), wmOperator *op)
{
PointerRNA ptr;
RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
ui_obj_export_settings(op->layout, &ptr);
}
/**
* Return true if any property in the UI is changed.
*/
static bool wm_obj_export_check(bContext *C, wmOperator *op)
{
char filepath[FILE_MAX];
Scene *scene = CTX_data_scene(C);
bool changed = false;
RNA_string_get(op->ptr, "filepath", filepath);
if (!BLI_path_extension_check(filepath, ".obj")) {
BLI_path_extension_ensure(filepath, FILE_MAX, ".obj");
RNA_string_set(op->ptr, "filepath", filepath);
changed = true;
}
{
int start = RNA_int_get(op->ptr, "start_frame");
int end = RNA_int_get(op->ptr, "end_frame");
/* Set the defaults. */
if (start == INT_MIN) {
start = SFRA;
changed = true;
}
if (end == INT_MAX) {
end = EFRA;
changed = true;
}
/* Fix user errors. */
if (end < start) {
end = start;
changed = true;
}
RNA_int_set(op->ptr, "start_frame", start);
RNA_int_set(op->ptr, "end_frame", end);
}
/* Both forward and up axes cannot be the same (or same except opposite sign). */
if (RNA_enum_get(op->ptr, "forward_axis") % TOTAL_AXES ==
(RNA_enum_get(op->ptr, "up_axis") % TOTAL_AXES)) {
/* TODO (ankitm) Show a warning here. */
RNA_enum_set(op->ptr, "up_axis", RNA_enum_get(op->ptr, "up_axis") % TOTAL_AXES + 1);
changed = true;
}
return changed;
}
void WM_OT_obj_export(struct wmOperatorType *ot)
{
ot->name = "Export Wavefront OBJ";
ot->description = "Save the scene to a Wavefront OBJ file";
ot->idname = "WM_OT_obj_export";
ot->invoke = wm_obj_export_invoke;
ot->exec = wm_obj_export_exec;
ot->poll = WM_operator_winactive;
ot->ui = wm_obj_export_draw;
ot->check = wm_obj_export_check;
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_OBJECT_IO,
FILE_BLENDER,
FILE_SAVE,
WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS,
FILE_DEFAULTDISPLAY,
FILE_SORT_ALPHA);
/* Animation options. */
RNA_def_boolean(ot->srna,
"export_animation",
false,
"Export Animation",
"Export multiple frames instead of the current frame only");
RNA_def_int(ot->srna,
"start_frame",
INT_MIN, /* wm_obj_export_check uses this to set SFRA. */
INT_MIN,
INT_MAX,
"Start Frame",
"The first frame to be exported",
INT_MIN,
INT_MAX);
RNA_def_int(ot->srna,
"end_frame",
INT_MAX, /* wm_obj_export_check uses this to set EFRA. */
INT_MIN,
INT_MAX,
"End Frame",
"The last frame to be exported",
INT_MIN,
INT_MAX);
/* Object transform options. */
RNA_def_enum(ot->srna,
"forward_axis",
io_obj_transform_axis_forward,
OBJ_AXIS_NEGATIVE_Z_FORWARD,
"Forward Axis",
"");
RNA_def_enum(ot->srna, "up_axis", io_obj_transform_axis_up, OBJ_AXIS_Y_UP, "Up Axis", "");
RNA_def_float(ot->srna,
"scaling_factor",
1.0f,
0.001f,
10000.0f,
"Scale",
"Upscale the object by this factor",
0.01,
1000.0f);
/* File Writer options. */
RNA_def_enum(ot->srna,
"export_eval_mode",
io_obj_export_evaluation_mode,
DAG_EVAL_VIEWPORT,
"Object Properties",
"Determines properties like object visibility, modifiers etc., where they differ "
"for Render and Viewport");
RNA_def_boolean(ot->srna,
"export_selected_objects",
false,
"Export Selected Objects",
"Export only selected objects instead of all supported objects");
RNA_def_boolean(ot->srna, "export_uv", true, "Export UVs", "");
RNA_def_boolean(ot->srna,
"export_normals",
true,
"Export Normals",
"Export per-face normals if the face is flat-shaded, per-face-per-loop "
"normals if smooth-shaded");
RNA_def_boolean(ot->srna,
"export_materials",
true,
"Export Materials",
"Export MTL library. There must be a Principled-BSDF node for image textures to "
"be exported to the MTL file");
RNA_def_boolean(ot->srna,
"export_triangulated_mesh",
false,
"Export Triangulated Mesh",
"All ngons with four or more vertices will be triangulated. Meshes in "
"the scene will not be affected. Behaves like Triangulate Modifier with "
"ngon-method: \"Beauty\", quad-method: \"Shortest Diagonal\", min vertices: 4");
RNA_def_boolean(ot->srna,
"export_curves_as_nurbs",
false,
"Export Curves as NURBS",
"Export curves in parametric form instead of exporting as mesh");
RNA_def_boolean(ot->srna,
"export_object_groups",
false,
"Export Object Groups",
"Append mesh name to object name, separated by a '_'");
RNA_def_boolean(ot->srna,
"export_material_groups",
false,
"Export Material Groups",
"Append mesh name and material name to object name, separated by a '_'");
RNA_def_boolean(
ot->srna,
"export_vertex_groups",
false,
"Export Vertex Groups",
"Export the name of the vertex group of a face. It is approximated "
"by choosing the vertex group with the most members among the vertices of a face");
RNA_def_boolean(
ot->srna,
"export_smooth_groups",
false,
"Export Smooth Groups",
"Every smooth-shaded face is assigned group \"1\" and every flat-shaded face \"off\"");
RNA_def_boolean(
ot->srna, "smooth_group_bitflags", false, "Generate Bitflags for Smooth Groups", "");
}
static int wm_obj_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
}
static int wm_obj_import_exec(bContext *C, wmOperator *op)
{
if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
struct OBJImportParams import_params;
RNA_string_get(op->ptr, "filepath", import_params.filepath);
import_params.clamp_size = RNA_float_get(op->ptr, "clamp_size");
import_params.forward_axis = RNA_enum_get(op->ptr, "forward_axis");
import_params.up_axis = RNA_enum_get(op->ptr, "up_axis");
OBJ_import(C, &import_params);
return OPERATOR_FINISHED;
}
static void ui_obj_import_settings(uiLayout *layout, PointerRNA *imfptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiLayout *box = uiLayoutBox(layout);
uiItemL(box, IFACE_("Transform"), ICON_OBJECT_DATA);
uiLayout *col = uiLayoutColumn(box, false);
uiLayout *sub = uiLayoutColumn(col, false);
uiItemR(sub, imfptr, "clamp_size", 0, NULL, ICON_NONE);
sub = uiLayoutColumn(col, false);
uiItemR(sub, imfptr, "forward_axis", 0, IFACE_("Axis Forward"), ICON_NONE);
uiItemR(sub, imfptr, "up_axis", 0, IFACE_("Up"), ICON_NONE);
}
static void wm_obj_import_draw(bContext *C, wmOperator *op)
{
PointerRNA ptr;
wmWindowManager *wm = CTX_wm_manager(C);
RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
ui_obj_import_settings(op->layout, &ptr);
}
void WM_OT_obj_import(struct wmOperatorType *ot)
{
ot->name = "Import Wavefront OBJ";
ot->description = "Load a Wavefront OBJ scene";
ot->idname = "WM_OT_obj_import";
ot->invoke = wm_obj_import_invoke;
ot->exec = wm_obj_import_exec;
ot->poll = WM_operator_winactive;
ot->ui = wm_obj_import_draw;
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_OBJECT_IO,
FILE_BLENDER,
FILE_OPENFILE,
WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS,
FILE_DEFAULTDISPLAY,
FILE_SORT_ALPHA);
RNA_def_float(
ot->srna,
"clamp_size",
0.0f,
0.0f,
1000.0f,
"Clamp Bounding Box",
"Resize the objects to keep bounding box under this value. Value 0 diables clamping",
0.0f,
1000.0f);
RNA_def_enum(ot->srna,
"forward_axis",
io_obj_transform_axis_forward,
OBJ_AXIS_NEGATIVE_Z_FORWARD,
"Forward Axis",
"");
RNA_def_enum(ot->srna, "up_axis", io_obj_transform_axis_up, OBJ_AXIS_Y_UP, "Up Axis", "");
}

View File

@@ -1,29 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup editor/io
*/
#pragma once
struct wmOperatorType;
void WM_OT_obj_export(struct wmOperatorType *ot);
void WM_OT_obj_import(struct wmOperatorType *ot);

View File

@@ -39,7 +39,6 @@
#include "io_cache.h"
#include "io_gpencil.h"
#include "io_obj.h"
void ED_operatortypes_io(void)
{
@@ -69,6 +68,4 @@ void ED_operatortypes_io(void)
WM_operatortype_append(CACHEFILE_OT_open);
WM_operatortype_append(CACHEFILE_OT_reload);
WM_operatortype_append(WM_OT_obj_import);
WM_operatortype_append(WM_OT_obj_export);
}

View File

@@ -275,9 +275,6 @@ static FileSelectParams *fileselect_ensure_updated_file_params(SpaceFile *sfile)
if ((prop = RNA_struct_find_property(op->ptr, "filter_usd"))) {
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_USD : 0;
}
if ((prop = RNA_struct_find_property(op->ptr, "filter_obj"))) {
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_OBJECT_IO : 0;
}
if ((prop = RNA_struct_find_property(op->ptr, "filter_volume"))) {
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_VOLUME : 0;
}

View File

@@ -72,7 +72,7 @@ static std::optional<eSpreadsheetColumnValueType> cpp_type_to_column_value_type(
void ExtraColumns::foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
for (const auto &item : columns_.items()) {
for (const auto item : columns_.items()) {
SpreadsheetColumnID column_id;
column_id.name = (char *)item.key.c_str();
fn(column_id, true);
@@ -159,12 +159,12 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
if (!attribute) {
return {};
}
const fn::GVArray *varray = scope_.add(std::move(attribute.varray));
fn::GVArray varray = std::move(attribute.varray);
if (attribute.domain != domain_) {
return {};
}
int domain_size = varray->size();
const CustomDataType type = bke::cpp_type_to_custom_data_type(varray->type());
int domain_size = varray.size();
const CustomDataType type = bke::cpp_type_to_custom_data_type(varray.type());
switch (type) {
case CD_PROP_FLOAT:
return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT,
@@ -172,7 +172,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
domain_size,
[varray](int index, CellValue &r_cell_value) {
float value;
varray->get(index, &value);
varray.get(index, &value);
r_cell_value.value_float = value;
});
case CD_PROP_INT32:
@@ -182,7 +182,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
domain_size,
[varray](int index, CellValue &r_cell_value) {
int value;
varray->get(index, &value);
varray.get(index, &value);
r_cell_value.value_int = value;
},
STREQ(column_id.name, "id") ? 5.5f : 0.0f);
@@ -192,7 +192,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
domain_size,
[varray](int index, CellValue &r_cell_value) {
bool value;
varray->get(index, &value);
varray.get(index, &value);
r_cell_value.value_bool = value;
});
case CD_PROP_FLOAT2: {
@@ -201,7 +201,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
domain_size,
[varray](int index, CellValue &r_cell_value) {
float2 value;
varray->get(index, &value);
varray.get(index, &value);
r_cell_value.value_float2 = value;
});
}
@@ -211,7 +211,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
domain_size,
[varray](int index, CellValue &r_cell_value) {
float3 value;
varray->get(index, &value);
varray.get(index, &value);
r_cell_value.value_float3 = value;
});
}
@@ -221,7 +221,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
domain_size,
[varray](int index, CellValue &r_cell_value) {
ColorGeometry4f value;
varray->get(index, &value);
varray.get(index, &value);
r_cell_value.value_color = value;
});
}
@@ -644,7 +644,7 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain;
const int domain_size = component.attribute_domain_size(domain);
for (const auto &item : fields_to_show.items()) {
for (const auto item : fields_to_show.items()) {
StringRef name = item.key;
const GField &field = item.value;

View File

@@ -223,7 +223,7 @@ static void draw_main_line(const rctf *main_line_rect,
static void draw_backdrop(const int fontid,
const rctf *main_line_rect,
const float color_bg[4],
const uint8_t color_bg[4],
const short region_y_size,
const float base_tick_height)
{
@@ -241,7 +241,7 @@ static void draw_backdrop(const int fontid,
.ymin = pad[1],
.ymax = region_y_size - pad[1],
};
UI_draw_roundbox_aa(&backdrop_rect, true, 4.0f, color_bg);
UI_draw_roundbox_3ub_alpha(&backdrop_rect, true, 4.0f, color_bg, color_bg[3]);
}
/**
@@ -260,19 +260,19 @@ static void slider_draw(const struct bContext *UNUSED(C), ARegion *region, void
uint8_t color_line[4];
uint8_t color_handle[4];
uint8_t color_overshoot[4];
float color_bg[4];
uint8_t color_bg[4];
/* Get theme colors. */
UI_GetThemeColor4ubv(TH_TEXT, color_text);
UI_GetThemeColor4ubv(TH_TEXT, color_line);
UI_GetThemeColor4ubv(TH_TEXT, color_overshoot);
UI_GetThemeColor4ubv(TH_ACTIVE, color_handle);
UI_GetThemeColor3fv(TH_BACK, color_bg);
UI_GetThemeColor4ubv(TH_HEADER_TEXT_HI, color_handle);
UI_GetThemeColor4ubv(TH_HEADER_TEXT, color_text);
UI_GetThemeColor4ubv(TH_HEADER_TEXT, color_line);
UI_GetThemeColor4ubv(TH_HEADER_TEXT, color_overshoot);
UI_GetThemeColor4ubv(TH_HEADER, color_bg);
color_bg[3] = 0.5f;
color_overshoot[0] = color_overshoot[0] * 0.7;
color_overshoot[1] = color_overshoot[1] * 0.7;
color_overshoot[2] = color_overshoot[2] * 0.7;
color_overshoot[0] = color_overshoot[0] * 0.8;
color_overshoot[1] = color_overshoot[1] * 0.8;
color_overshoot[2] = color_overshoot[2] * 0.8;
color_bg[3] = 160;
/* Get the default font. */
const uiStyle *style = UI_style_get();

View File

@@ -248,9 +248,9 @@ class FieldInput : public FieldNode {
* Get the value of this specific input based on the given context. The returned virtual array,
* should live at least as long as the passed in #scope. May return null.
*/
virtual const GVArray *get_varray_for_context(const FieldContext &context,
IndexMask mask,
ResourceScope &scope) const = 0;
virtual GVArray get_varray_for_context(const FieldContext &context,
IndexMask mask,
ResourceScope &scope) const = 0;
virtual std::string socket_inspection_name() const;
blender::StringRef debug_name() const;
@@ -268,9 +268,9 @@ class FieldContext {
public:
~FieldContext() = default;
virtual const GVArray *get_varray_for_input(const FieldInput &field_input,
IndexMask mask,
ResourceScope &scope) const;
virtual GVArray get_varray_for_input(const FieldInput &field_input,
IndexMask mask,
ResourceScope &scope) const;
};
/**
@@ -289,8 +289,8 @@ class FieldEvaluator : NonMovable, NonCopyable {
const FieldContext &context_;
const IndexMask mask_;
Vector<GField> fields_to_evaluate_;
Vector<GVMutableArray *> dst_varrays_;
Vector<const GVArray *> evaluated_varrays_;
Vector<GVMutableArray> dst_varrays_;
Vector<GVArray> evaluated_varrays_;
Vector<OutputPointerInfo> output_pointer_infos_;
bool is_evaluated_ = false;
@@ -317,13 +317,12 @@ class FieldEvaluator : NonMovable, NonCopyable {
* \param field: Field to add to the evaluator.
* \param dst: Mutable virtual array that the evaluated result for this field is be written into.
*/
int add_with_destination(GField field, GVMutableArray &dst);
int add_with_destination(GField field, GVMutableArray dst);
/** Same as #add_with_destination but typed. */
template<typename T> int add_with_destination(Field<T> field, VMutableArray<T> &dst)
template<typename T> int add_with_destination(Field<T> field, VMutableArray<T> dst)
{
GVMutableArray &varray = scope_.construct<GVMutableArray_For_VMutableArray<T>>(dst);
return this->add_with_destination(GField(std::move(field)), varray);
return this->add_with_destination(GField(std::move(field)), GVMutableArray(std::move(dst)));
}
/**
@@ -342,11 +341,10 @@ class FieldEvaluator : NonMovable, NonCopyable {
*/
template<typename T> int add_with_destination(Field<T> field, MutableSpan<T> dst)
{
GVMutableArray &varray = scope_.construct<GVMutableArray_For_MutableSpan<T>>(dst);
return this->add_with_destination(std::move(field), varray);
return this->add_with_destination(std::move(field), VMutableArray<T>::ForSpan(dst));
}
int add(GField field, const GVArray **varray_ptr);
int add(GField field, GVArray *varray_ptr);
/**
* \param field: Field to add to the evaluator.
@@ -354,14 +352,14 @@ class FieldEvaluator : NonMovable, NonCopyable {
* assigned to the given position.
* \return Index of the field in the evaluator which can be used in the #get_evaluated methods.
*/
template<typename T> int add(Field<T> field, const VArray<T> **varray_ptr)
template<typename T> int add(Field<T> field, VArray<T> *varray_ptr)
{
const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field));
dst_varrays_.append(nullptr);
output_pointer_infos_.append(
OutputPointerInfo{varray_ptr, [](void *dst, const GVArray &varray, ResourceScope &scope) {
*(const VArray<T> **)dst = &*scope.construct<GVArray_Typed<T>>(varray);
}});
dst_varrays_.append({});
output_pointer_infos_.append(OutputPointerInfo{
varray_ptr, [](void *dst, const GVArray &varray, ResourceScope &UNUSED(scope)) {
*(VArray<T> *)dst = varray.typed<T>();
}});
return field_index;
}
@@ -378,14 +376,12 @@ class FieldEvaluator : NonMovable, NonCopyable {
const GVArray &get_evaluated(const int field_index) const
{
BLI_assert(is_evaluated_);
return *evaluated_varrays_[field_index];
return evaluated_varrays_[field_index];
}
template<typename T> const VArray<T> &get_evaluated(const int field_index)
template<typename T> VArray<T> get_evaluated(const int field_index)
{
const GVArray &varray = this->get_evaluated(field_index);
GVArray_Typed<T> &typed_varray = scope_.construct<GVArray_Typed<T>>(varray);
return *typed_varray;
return this->get_evaluated(field_index).typed<T>();
}
/**
@@ -396,11 +392,11 @@ class FieldEvaluator : NonMovable, NonCopyable {
IndexMask get_evaluated_as_mask(const int field_index);
};
Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
Span<GFieldRef> fields_to_evaluate,
IndexMask mask,
const FieldContext &context,
Span<GVMutableArray *> dst_varrays = {});
Vector<GVArray> evaluate_fields(ResourceScope &scope,
Span<GFieldRef> fields_to_evaluate,
IndexMask mask,
const FieldContext &context,
Span<GVMutableArray> dst_varrays = {});
/* -------------------------------------------------------------------- */
/** \name Utility functions for simple field creation and evaluation
@@ -429,11 +425,11 @@ class IndexFieldInput final : public FieldInput {
public:
IndexFieldInput();
static GVArray *get_index_varray(IndexMask mask, ResourceScope &scope);
static GVArray get_index_varray(IndexMask mask, ResourceScope &scope);
const GVArray *get_varray_for_context(const FieldContext &context,
IndexMask mask,
ResourceScope &scope) const final;
GVArray get_varray_for_context(const FieldContext &context,
IndexMask mask,
ResourceScope &scope) const final;
uint64_t hash() const override;
bool is_equal_to(const fn::FieldNode &other) const override;

View File

@@ -125,8 +125,7 @@ template<typename T> class GVectorArray_TypedMutableRef {
void extend(const int64_t index, const VArray<T> &values)
{
GVArray_For_VArray<T> array{values};
this->extend(index, array);
vector_array_->extend(index, values);
}
MutableSpan<T> operator[](const int64_t index)

File diff suppressed because it is too large Load Diff

View File

@@ -100,31 +100,31 @@ class GVVectorArray {
}
};
class GVArray_For_GVVectorArrayIndex : public GVArray {
class GVArray_For_GVVectorArrayIndex : public GVArrayImpl {
private:
const GVVectorArray &vector_array_;
const int64_t index_;
public:
GVArray_For_GVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index)
: GVArray(vector_array.type(), vector_array.get_vector_size(index)),
: GVArrayImpl(vector_array.type(), vector_array.get_vector_size(index)),
vector_array_(vector_array),
index_(index)
{
}
protected:
void get_impl(const int64_t index_in_vector, void *r_value) const override;
void get_to_uninitialized_impl(const int64_t index_in_vector, void *r_value) const override;
void get(const int64_t index_in_vector, void *r_value) const override;
void get_to_uninitialized(const int64_t index_in_vector, void *r_value) const override;
};
class GVVectorArray_For_SingleGVArray : public GVVectorArray {
private:
const GVArray &array_;
GVArray varray_;
public:
GVVectorArray_For_SingleGVArray(const GVArray &array, const int64_t size)
: GVVectorArray(array.type(), size), array_(array)
GVVectorArray_For_SingleGVArray(GVArray varray, const int64_t size)
: GVVectorArray(varray.type(), size), varray_(std::move(varray))
{
}

View File

@@ -40,7 +40,7 @@ class MFParamsBuilder {
const MFSignature *signature_;
IndexMask mask_;
int64_t min_array_size_;
Vector<const GVArray *> virtual_arrays_;
Vector<GVArray> virtual_arrays_;
Vector<GMutableSpan> mutable_spans_;
Vector<const GVVectorArray *> virtual_vector_arrays_;
Vector<GVectorArray *> vector_arrays_;
@@ -68,24 +68,22 @@ class MFParamsBuilder {
template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "")
{
this->add_readonly_single_input(
scope_.construct<GVArray_For_SingleValueRef>(CPPType::get<T>(), min_array_size_, value),
expected_name);
GVArray::ForSingleRef(CPPType::get<T>(), min_array_size_, value), expected_name);
}
void add_readonly_single_input(const GSpan span, StringRef expected_name = "")
{
this->add_readonly_single_input(scope_.construct<GVArray_For_GSpan>(span), expected_name);
this->add_readonly_single_input(GVArray::ForSpan(span), expected_name);
}
void add_readonly_single_input(GPointer value, StringRef expected_name = "")
{
this->add_readonly_single_input(
scope_.construct<GVArray_For_SingleValueRef>(*value.type(), min_array_size_, value.get()),
expected_name);
GVArray::ForSingleRef(*value.type(), min_array_size_, value.get()), expected_name);
}
void add_readonly_single_input(const GVArray &ref, StringRef expected_name = "")
void add_readonly_single_input(GVArray varray, StringRef expected_name = "")
{
this->assert_current_param_type(MFParamType::ForSingleInput(ref.type()), expected_name);
BLI_assert(ref.size() >= min_array_size_);
virtual_arrays_.append(&ref);
this->assert_current_param_type(MFParamType::ForSingleInput(varray.type()), expected_name);
BLI_assert(varray.size() >= min_array_size_);
virtual_arrays_.append(varray);
}
void add_readonly_vector_input(const GVectorArray &vector_array, StringRef expected_name = "")
@@ -221,16 +219,16 @@ class MFParams {
{
}
template<typename T> const VArray<T> &readonly_single_input(int param_index, StringRef name = "")
template<typename T> VArray<T> readonly_single_input(int param_index, StringRef name = "")
{
const GVArray &array = this->readonly_single_input(param_index, name);
return builder_->scope_.construct<GVArray_Typed<T>>(array);
const GVArray &varray = this->readonly_single_input(param_index, name);
return varray.typed<T>();
}
const GVArray &readonly_single_input(int param_index, StringRef name = "")
{
this->assert_correct_param(param_index, name, MFParamType::SingleInput);
int data_index = builder_->signature_->data_index(param_index);
return *builder_->virtual_arrays_[data_index];
return builder_->virtual_arrays_[data_index];
}
/**

View File

@@ -81,19 +81,18 @@ static FieldTreeInfo preprocess_field_tree(Span<GFieldRef> entry_fields)
/**
* Retrieves the data from the context that is passed as input into the field.
*/
static Vector<const GVArray *> get_field_context_inputs(
static Vector<GVArray> get_field_context_inputs(
ResourceScope &scope,
const IndexMask mask,
const FieldContext &context,
const Span<std::reference_wrapper<const FieldInput>> field_inputs)
{
Vector<const GVArray *> field_context_inputs;
Vector<GVArray> field_context_inputs;
for (const FieldInput &field_input : field_inputs) {
const GVArray *varray = context.get_varray_for_input(field_input, mask, scope);
if (varray == nullptr) {
GVArray varray = context.get_varray_for_input(field_input, mask, scope);
if (!varray) {
const CPPType &type = field_input.cpp_type();
varray = &scope.construct<GVArray_For_SingleValueRef>(
type, mask.min_array_size(), type.default_value());
varray = GVArray::ForSingleDefault(type, mask.min_array_size());
}
field_context_inputs.append(varray);
}
@@ -105,7 +104,7 @@ static Vector<const GVArray *> get_field_context_inputs(
* for different indices.
*/
static Set<GFieldRef> find_varying_fields(const FieldTreeInfo &field_tree_info,
Span<const GVArray *> field_context_inputs)
Span<GVArray> field_context_inputs)
{
Set<GFieldRef> found_fields;
Stack<GFieldRef> fields_to_check;
@@ -114,8 +113,8 @@ static Set<GFieldRef> find_varying_fields(const FieldTreeInfo &field_tree_info,
* start the tree search at the non-constant input fields and traverse through all fields that
* depend on them. */
for (const int i : field_context_inputs.index_range()) {
const GVArray *varray = field_context_inputs[i];
if (varray->is_single()) {
const GVArray &varray = field_context_inputs[i];
if (varray.is_single()) {
continue;
}
const FieldInput &field_input = field_tree_info.deduplicated_field_inputs[i];
@@ -278,29 +277,42 @@ static void build_multi_function_procedure_for_fields(MFProcedure &procedure,
* \return The computed virtual arrays for each provided field. If #dst_varrays is passed, the
* provided virtual arrays are returned.
*/
Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
Span<GFieldRef> fields_to_evaluate,
IndexMask mask,
const FieldContext &context,
Span<GVMutableArray *> dst_varrays)
Vector<GVArray> evaluate_fields(ResourceScope &scope,
Span<GFieldRef> fields_to_evaluate,
IndexMask mask,
const FieldContext &context,
Span<GVMutableArray> dst_varrays)
{
Vector<const GVArray *> r_varrays(fields_to_evaluate.size(), nullptr);
Vector<GVArray> r_varrays(fields_to_evaluate.size());
Array<bool> is_output_written_to_dst(fields_to_evaluate.size(), false);
const int array_size = mask.min_array_size();
/* Destination arrays are optional. Create a small utility method to access them. */
auto get_dst_varray_if_available = [&](int index) -> GVMutableArray * {
if (dst_varrays.is_empty()) {
return nullptr;
if (mask.is_empty()) {
for (const int i : fields_to_evaluate.index_range()) {
const CPPType &type = fields_to_evaluate[i].cpp_type();
r_varrays[i] = GVArray::ForEmpty(type);
}
BLI_assert(dst_varrays[index] == nullptr || dst_varrays[index]->size() >= array_size);
return dst_varrays[index];
return r_varrays;
}
/* Destination arrays are optional. Create a small utility method to access them. */
auto get_dst_varray = [&](int index) -> GVMutableArray {
if (dst_varrays.is_empty()) {
return {};
}
const GVMutableArray &varray = dst_varrays[index];
if (!varray) {
return {};
}
BLI_assert(varray.size() >= array_size);
return varray;
};
/* Traverse the field tree and prepare some data that is used in later steps. */
FieldTreeInfo field_tree_info = preprocess_field_tree(fields_to_evaluate);
/* Get inputs that will be passed into the field when evaluated. */
Vector<const GVArray *> field_context_inputs = get_field_context_inputs(
Vector<GVArray> field_context_inputs = get_field_context_inputs(
scope, mask, context, field_tree_info.deduplicated_field_inputs);
/* Finish fields that output an input varray directly. For those we don't have to do any further
@@ -312,7 +324,7 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
}
const FieldInput &field_input = static_cast<const FieldInput &>(field.node());
const int field_input_index = field_tree_info.deduplicated_field_inputs.index_of(field_input);
const GVArray *varray = field_context_inputs[field_input_index];
const GVArray &varray = field_context_inputs[field_input_index];
r_varrays[out_index] = varray;
}
@@ -325,7 +337,7 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
Vector<GFieldRef> constant_fields_to_evaluate;
Vector<int> constant_field_indices;
for (const int i : fields_to_evaluate.index_range()) {
if (r_varrays[i] != nullptr) {
if (r_varrays[i]) {
/* Already done. */
continue;
}
@@ -357,8 +369,8 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
MFContextBuilder mf_context;
/* Provide inputs to the procedure executor. */
for (const GVArray *varray : field_context_inputs) {
mf_params.add_readonly_single_input(*varray);
for (const GVArray &varray : field_context_inputs) {
mf_params.add_readonly_single_input(varray);
}
for (const int i : varying_fields_to_evaluate.index_range()) {
@@ -367,9 +379,9 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
const int out_index = varying_field_indices[i];
/* Try to get an existing virtual array that the result should be written into. */
GVMutableArray *output_varray = get_dst_varray_if_available(out_index);
GVMutableArray dst_varray = get_dst_varray(out_index);
void *buffer;
if (output_varray == nullptr || !output_varray->is_span()) {
if (!dst_varray || !dst_varray.is_span()) {
/* Allocate a new buffer for the computed result. */
buffer = scope.linear_allocator().allocate(type.size() * array_size, type.alignment());
@@ -379,14 +391,14 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
[buffer, mask, &type]() { type.destruct_indices(buffer, mask); });
}
r_varrays[out_index] = &scope.construct<GVArray_For_GSpan>(
GSpan{type, buffer, array_size});
r_varrays[out_index] = GVArray::ForSpan({type, buffer, array_size});
}
else {
/* Write the result into the existing span. */
buffer = output_varray->get_internal_span().data();
buffer = dst_varray.get_internal_span().data();
r_varrays[out_index] = output_varray;
r_varrays[out_index] = dst_varray;
is_output_written_to_dst[out_index] = true;
}
/* Pass output buffer to the procedure executor. */
@@ -404,15 +416,12 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
build_multi_function_procedure_for_fields(
procedure, scope, field_tree_info, constant_fields_to_evaluate);
MFProcedureExecutor procedure_executor{"Procedure", procedure};
/* Run the code below even when the mask is empty, so that outputs are properly prepared.
* Higher level code can detect this as well and just skip evaluating the field. */
const int mask_size = mask.is_empty() ? 0 : 1;
MFParamsBuilder mf_params{procedure_executor, mask_size};
MFParamsBuilder mf_params{procedure_executor, 1};
MFContextBuilder mf_context;
/* Provide inputs to the procedure executor. */
for (const GVArray *varray : field_context_inputs) {
mf_params.add_readonly_single_input(*varray);
for (const GVArray &varray : field_context_inputs) {
mf_params.add_readonly_single_input(varray);
}
for (const int i : constant_fields_to_evaluate.index_range()) {
@@ -421,55 +430,52 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
/* Allocate memory where the computed value will be stored in. */
void *buffer = scope.linear_allocator().allocate(type.size(), type.alignment());
if (!type.is_trivially_destructible() && mask_size > 0) {
BLI_assert(mask_size == 1);
if (!type.is_trivially_destructible()) {
/* Destruct value in the end. */
scope.add_destruct_call([buffer, &type]() { type.destruct(buffer); });
}
/* Pass output buffer to the procedure executor. */
mf_params.add_uninitialized_single_output({type, buffer, mask_size});
mf_params.add_uninitialized_single_output({type, buffer, 1});
/* Create virtual array that can be used after the procedure has been executed below. */
const int out_index = constant_field_indices[i];
r_varrays[out_index] = &scope.construct<GVArray_For_SingleValueRef>(
type, array_size, buffer);
r_varrays[out_index] = GVArray::ForSingleRef(type, array_size, buffer);
}
procedure_executor.call(IndexRange(mask_size), mf_params, mf_context);
procedure_executor.call(IndexRange(1), mf_params, mf_context);
}
/* Copy data to supplied destination arrays if necessary. In some cases the evaluation above has
* written the computed data in the right place already. */
/* Copy data to supplied destination arrays if necessary. In some cases the evaluation above
* has written the computed data in the right place already. */
if (!dst_varrays.is_empty()) {
for (const int out_index : fields_to_evaluate.index_range()) {
GVMutableArray *output_varray = get_dst_varray_if_available(out_index);
if (output_varray == nullptr) {
GVMutableArray dst_varray = get_dst_varray(out_index);
if (!dst_varray) {
/* Caller did not provide a destination for this output. */
continue;
}
const GVArray *computed_varray = r_varrays[out_index];
BLI_assert(computed_varray->type() == output_varray->type());
if (output_varray == computed_varray) {
const GVArray &computed_varray = r_varrays[out_index];
BLI_assert(computed_varray.type() == dst_varray.type());
if (is_output_written_to_dst[out_index]) {
/* The result has been written into the destination provided by the caller already. */
continue;
}
/* Still have to copy over the data in the destination provided by the caller. */
if (output_varray->is_span()) {
if (dst_varray.is_span()) {
/* Materialize into a span. */
computed_varray->materialize_to_uninitialized(mask,
output_varray->get_internal_span().data());
computed_varray.materialize_to_uninitialized(mask, dst_varray.get_internal_span().data());
}
else {
/* Slower materialize into a different structure. */
const CPPType &type = computed_varray->type();
const CPPType &type = computed_varray.type();
BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
for (const int i : mask) {
computed_varray->get_to_uninitialized(i, buffer);
output_varray->set_by_relocate(i, buffer);
computed_varray.get_to_uninitialized(i, buffer);
dst_varray.set_by_relocate(i, buffer);
}
}
r_varrays[out_index] = output_varray;
r_varrays[out_index] = dst_varray;
}
}
return r_varrays;
@@ -485,8 +491,8 @@ void evaluate_constant_field(const GField &field, void *r_value)
ResourceScope scope;
FieldContext context;
Vector<const GVArray *> varrays = evaluate_fields(scope, {field}, IndexRange(1), context);
varrays[0]->get_to_uninitialized(0, r_value);
Vector<GVArray> varrays = evaluate_fields(scope, {field}, IndexRange(1), context);
varrays[0].get_to_uninitialized(0, r_value);
}
/**
@@ -512,9 +518,9 @@ GField make_field_constant_if_possible(GField field)
return GField{operation, 0};
}
const GVArray *FieldContext::get_varray_for_input(const FieldInput &field_input,
IndexMask mask,
ResourceScope &scope) const
GVArray FieldContext::get_varray_for_input(const FieldInput &field_input,
IndexMask mask,
ResourceScope &scope) const
{
/* By default ask the field input to create the varray. Another field context might overwrite
* the context here. */
@@ -526,17 +532,15 @@ IndexFieldInput::IndexFieldInput() : FieldInput(CPPType::get<int>(), "Index")
category_ = Category::Generated;
}
GVArray *IndexFieldInput::get_index_varray(IndexMask mask, ResourceScope &scope)
GVArray IndexFieldInput::get_index_varray(IndexMask mask, ResourceScope &UNUSED(scope))
{
auto index_func = [](int i) { return i; };
return &scope.construct<
fn::GVArray_For_EmbeddedVArray<int, VArray_For_Func<int, decltype(index_func)>>>(
mask.min_array_size(), mask.min_array_size(), index_func);
return VArray<int>::ForFunc(mask.min_array_size(), index_func);
}
const GVArray *IndexFieldInput::get_varray_for_context(const fn::FieldContext &UNUSED(context),
IndexMask mask,
ResourceScope &scope) const
GVArray IndexFieldInput::get_varray_for_context(const fn::FieldContext &UNUSED(context),
IndexMask mask,
ResourceScope &scope) const
{
/* TODO: Investigate a similar method to IndexRange::as_span() */
return get_index_varray(mask, scope);
@@ -631,27 +635,26 @@ static Vector<int64_t> indices_from_selection(const VArray<bool> &selection)
return indices;
}
int FieldEvaluator::add_with_destination(GField field, GVMutableArray &dst)
int FieldEvaluator::add_with_destination(GField field, GVMutableArray dst)
{
const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field));
dst_varrays_.append(&dst);
dst_varrays_.append(dst);
output_pointer_infos_.append({});
return field_index;
}
int FieldEvaluator::add_with_destination(GField field, GMutableSpan dst)
{
GVMutableArray &varray = scope_.construct<GVMutableArray_For_GMutableSpan>(dst);
return this->add_with_destination(std::move(field), varray);
return this->add_with_destination(std::move(field), GVMutableArray::ForSpan(dst));
}
int FieldEvaluator::add(GField field, const GVArray **varray_ptr)
int FieldEvaluator::add(GField field, GVArray *varray_ptr)
{
const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field));
dst_varrays_.append(nullptr);
output_pointer_infos_.append(OutputPointerInfo{
varray_ptr, [](void *dst, const GVArray &varray, ResourceScope &UNUSED(scope)) {
*(const GVArray **)dst = &varray;
*(GVArray *)dst = varray;
}});
return field_index;
}
@@ -676,7 +679,7 @@ void FieldEvaluator::evaluate()
for (const int i : fields_to_evaluate_.index_range()) {
OutputPointerInfo &info = output_pointer_infos_[i];
if (info.dst != nullptr) {
info.set(info.dst, *evaluated_varrays_[i], scope_);
info.set(info.dst, evaluated_varrays_[i], scope_);
}
}
is_evaluated_ = true;
@@ -684,17 +687,16 @@ void FieldEvaluator::evaluate()
IndexMask FieldEvaluator::get_evaluated_as_mask(const int field_index)
{
const GVArray &varray = this->get_evaluated(field_index);
GVArray_Typed<bool> typed_varray{varray};
VArray<bool> varray = this->get_evaluated(field_index).typed<bool>();
if (typed_varray->is_single()) {
if (typed_varray->get_internal_single()) {
return IndexRange(typed_varray.size());
if (varray.is_single()) {
if (varray.get_internal_single()) {
return IndexRange(varray.size());
}
return IndexRange(0);
}
return scope_.add_value(indices_from_selection(*typed_varray)).as_span();
return scope_.add_value(indices_from_selection(varray)).as_span();
}
} // namespace blender::fn

View File

@@ -60,15 +60,14 @@ void GVectorArray::extend(const int64_t index, const GVArray &values)
void GVectorArray::extend(const int64_t index, const GSpan values)
{
GVArray_For_GSpan varray{values};
this->extend(index, varray);
this->extend(index, GVArray::ForSpan(values));
}
void GVectorArray::extend(IndexMask mask, const GVVectorArray &values)
{
for (const int i : mask) {
GVArray_For_GVVectorArrayIndex array{values, i};
this->extend(i, array);
this->extend(i, GVArray(&array));
}
}

View File

@@ -19,52 +19,10 @@
namespace blender::fn {
/* -------------------------------------------------------------------- */
/** \name #GVArray_For_ShallowCopy
/** \name #GVArrayImpl
* \{ */
class GVArray_For_ShallowCopy : public GVArray {
private:
const GVArray &varray_;
public:
GVArray_For_ShallowCopy(const GVArray &varray)
: GVArray(varray.type(), varray.size()), varray_(varray)
{
}
private:
void get_impl(const int64_t index, void *r_value) const override
{
varray_.get(index, r_value);
}
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
{
varray_.get_to_uninitialized(index, r_value);
}
void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override
{
varray_.materialize_to_uninitialized(mask, dst);
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name #GVArray
* \{ */
void GVArray::materialize(void *dst) const
{
this->materialize(IndexMask(size_), dst);
}
void GVArray::materialize(const IndexMask mask, void *dst) const
{
this->materialize_impl(mask, dst);
}
void GVArray::materialize_impl(const IndexMask mask, void *dst) const
void GVArrayImpl::materialize(const IndexMask mask, void *dst) const
{
for (const int64_t i : mask) {
void *elem_dst = POINTER_OFFSET(dst, type_->size() * i);
@@ -72,18 +30,7 @@ void GVArray::materialize_impl(const IndexMask mask, void *dst) const
}
}
void GVArray::materialize_to_uninitialized(void *dst) const
{
this->materialize_to_uninitialized(IndexMask(size_), dst);
}
void GVArray::materialize_to_uninitialized(const IndexMask mask, void *dst) const
{
BLI_assert(mask.min_array_size() <= size_);
this->materialize_to_uninitialized_impl(mask, dst);
}
void GVArray::materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const
void GVArrayImpl::materialize_to_uninitialized(const IndexMask mask, void *dst) const
{
for (const int64_t i : mask) {
void *elem_dst = POINTER_OFFSET(dst, type_->size() * i);
@@ -91,83 +38,75 @@ void GVArray::materialize_to_uninitialized_impl(const IndexMask mask, void *dst)
}
}
void GVArray::get_impl(const int64_t index, void *r_value) const
void GVArrayImpl::get(const int64_t index, void *r_value) const
{
type_->destruct(r_value);
this->get_to_uninitialized_impl(index, r_value);
this->get_to_uninitialized(index, r_value);
}
bool GVArray::is_span_impl() const
bool GVArrayImpl::is_span() const
{
return false;
}
GSpan GVArray::get_internal_span_impl() const
GSpan GVArrayImpl::get_internal_span() const
{
BLI_assert(false);
return GSpan(*type_);
}
bool GVArray::is_single_impl() const
bool GVArrayImpl::is_single() const
{
return false;
}
void GVArray::get_internal_single_impl(void *UNUSED(r_value)) const
void GVArrayImpl::get_internal_single(void *UNUSED(r_value)) const
{
BLI_assert(false);
}
const void *GVArray::try_get_internal_varray_impl() const
bool GVArrayImpl::try_assign_VArray(void *UNUSED(varray)) const
{
return nullptr;
return false;
}
/**
* Creates a new `std::unique_ptr<GVArray>` based on this `GVArray`.
* The lifetime of the returned virtual array must not be longer than the lifetime of this virtual
* array.
*/
GVArrayPtr GVArray::shallow_copy() const
bool GVArrayImpl::may_have_ownership() const
{
if (this->is_span()) {
return std::make_unique<GVArray_For_GSpan>(this->get_internal_span());
}
if (this->is_single()) {
BUFFER_FOR_CPP_TYPE_VALUE(*type_, buffer);
this->get_internal_single(buffer);
std::unique_ptr new_varray = std::make_unique<GVArray_For_SingleValue>(*type_, size_, buffer);
type_->destruct(buffer);
return new_varray;
}
return std::make_unique<GVArray_For_ShallowCopy>(*this);
/* Use true as default to avoid accidentally creating subclasses that have this set to false but
* actually own data. Subclasses should set the to false instead. */
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name #GVMutableArray
/** \name #GVMutableArrayImpl
* \{ */
void GVMutableArray::set_by_copy_impl(const int64_t index, const void *value)
GVMutableArrayImpl::GVMutableArrayImpl(const CPPType &type, const int64_t size)
: GVArrayImpl(type, size)
{
}
void GVMutableArrayImpl::set_by_copy(const int64_t index, const void *value)
{
BUFFER_FOR_CPP_TYPE_VALUE(*type_, buffer);
type_->copy_construct(value, buffer);
this->set_by_move_impl(index, buffer);
this->set_by_move(index, buffer);
type_->destruct(buffer);
}
void GVMutableArray::set_by_relocate_impl(const int64_t index, void *value)
void GVMutableArrayImpl::set_by_relocate(const int64_t index, void *value)
{
this->set_by_move_impl(index, value);
this->set_by_move(index, value);
type_->destruct(value);
}
void GVMutableArray::set_all_impl(const void *src)
void GVMutableArrayImpl::set_all(const void *src)
{
if (this->is_span()) {
const GMutableSpan span = this->get_internal_span();
type_->copy_assign_n(src, span.data(), size_);
const GSpan span = this->get_internal_span();
type_->copy_assign_n(src, const_cast<void *>(span.data()), size_);
}
else {
for (int64_t i : IndexRange(size_)) {
@@ -176,149 +115,223 @@ void GVMutableArray::set_all_impl(const void *src)
}
}
void *GVMutableArray::try_get_internal_mutable_varray_impl()
{
return nullptr;
}
void GVMutableArray::fill(const void *value)
{
if (this->is_span()) {
const GMutableSpan span = this->get_internal_span();
type_->fill_assign_n(value, span.data(), size_);
const GSpan span = this->get_internal_span();
this->type().fill_assign_n(value, const_cast<void *>(span.data()), this->size());
}
else {
for (int64_t i : IndexRange(size_)) {
for (int64_t i : IndexRange(this->size())) {
this->set_by_copy(i, value);
}
}
}
bool GVMutableArrayImpl::try_assign_VMutableArray(void *UNUSED(varray)) const
{
return false;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name #GVArray_For_GSpan
/** \name #GVArrayImpl_For_GSpan
* \{ */
void GVArray_For_GSpan::get_impl(const int64_t index, void *r_value) const
GVArrayImpl_For_GSpan::GVArrayImpl_For_GSpan(const GSpan span)
: GVArrayImpl(span.type(), span.size()), data_(span.data()), element_size_(span.type().size())
{
}
GVArrayImpl_For_GSpan::GVArrayImpl_For_GSpan(const CPPType &type, const int64_t size)
: GVArrayImpl(type, size), element_size_(type.size())
{
}
void GVArrayImpl_For_GSpan::get(const int64_t index, void *r_value) const
{
type_->copy_assign(POINTER_OFFSET(data_, element_size_ * index), r_value);
}
void GVArray_For_GSpan::get_to_uninitialized_impl(const int64_t index, void *r_value) const
void GVArrayImpl_For_GSpan::get_to_uninitialized(const int64_t index, void *r_value) const
{
type_->copy_construct(POINTER_OFFSET(data_, element_size_ * index), r_value);
}
bool GVArray_For_GSpan::is_span_impl() const
bool GVArrayImpl_For_GSpan::is_span() const
{
return true;
}
GSpan GVArray_For_GSpan::get_internal_span_impl() const
GSpan GVArrayImpl_For_GSpan::get_internal_span() const
{
return GSpan(*type_, data_, size_);
}
class GVArrayImpl_For_GSpan_final final : public GVArrayImpl_For_GSpan {
public:
using GVArrayImpl_For_GSpan::GVArrayImpl_For_GSpan;
private:
bool may_have_ownership() const override
{
return false;
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name #GVMutableArray_For_GMutableSpan
/** \name #GVMutableArrayImpl_For_GMutableSpan
* \{ */
void GVMutableArray_For_GMutableSpan::get_impl(const int64_t index, void *r_value) const
GVMutableArrayImpl_For_GMutableSpan::GVMutableArrayImpl_For_GMutableSpan(const GMutableSpan span)
: GVMutableArrayImpl(span.type(), span.size()),
data_(span.data()),
element_size_(span.type().size())
{
}
GVMutableArrayImpl_For_GMutableSpan::GVMutableArrayImpl_For_GMutableSpan(const CPPType &type,
const int64_t size)
: GVMutableArrayImpl(type, size), element_size_(type.size())
{
}
void GVMutableArrayImpl_For_GMutableSpan::get(const int64_t index, void *r_value) const
{
type_->copy_assign(POINTER_OFFSET(data_, element_size_ * index), r_value);
}
void GVMutableArray_For_GMutableSpan::get_to_uninitialized_impl(const int64_t index,
void *r_value) const
void GVMutableArrayImpl_For_GMutableSpan::get_to_uninitialized(const int64_t index,
void *r_value) const
{
type_->copy_construct(POINTER_OFFSET(data_, element_size_ * index), r_value);
}
void GVMutableArray_For_GMutableSpan::set_by_copy_impl(const int64_t index, const void *value)
void GVMutableArrayImpl_For_GMutableSpan::set_by_copy(const int64_t index, const void *value)
{
type_->copy_assign(value, POINTER_OFFSET(data_, element_size_ * index));
}
void GVMutableArray_For_GMutableSpan::set_by_move_impl(const int64_t index, void *value)
void GVMutableArrayImpl_For_GMutableSpan::set_by_move(const int64_t index, void *value)
{
type_->move_construct(value, POINTER_OFFSET(data_, element_size_ * index));
}
void GVMutableArray_For_GMutableSpan::set_by_relocate_impl(const int64_t index, void *value)
void GVMutableArrayImpl_For_GMutableSpan::set_by_relocate(const int64_t index, void *value)
{
type_->relocate_assign(value, POINTER_OFFSET(data_, element_size_ * index));
}
bool GVMutableArray_For_GMutableSpan::is_span_impl() const
bool GVMutableArrayImpl_For_GMutableSpan::is_span() const
{
return true;
}
GSpan GVMutableArray_For_GMutableSpan::get_internal_span_impl() const
GSpan GVMutableArrayImpl_For_GMutableSpan::get_internal_span() const
{
return GSpan(*type_, data_, size_);
}
/** \} */
class GVMutableArrayImpl_For_GMutableSpan_final final
: public GVMutableArrayImpl_For_GMutableSpan {
public:
using GVMutableArrayImpl_For_GMutableSpan::GVMutableArrayImpl_For_GMutableSpan;
/* -------------------------------------------------------------------- */
/** \name #GVArray_For_SingleValueRef
* \{ */
void GVArray_For_SingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const
{
type_->copy_assign(value_, r_value);
}
void GVArray_For_SingleValueRef::get_to_uninitialized_impl(const int64_t UNUSED(index),
void *r_value) const
{
type_->copy_construct(value_, r_value);
}
bool GVArray_For_SingleValueRef::is_span_impl() const
{
return size_ == 1;
}
GSpan GVArray_For_SingleValueRef::get_internal_span_impl() const
{
return GSpan{*type_, value_, 1};
}
bool GVArray_For_SingleValueRef::is_single_impl() const
{
return true;
}
void GVArray_For_SingleValueRef::get_internal_single_impl(void *r_value) const
{
type_->copy_assign(value_, r_value);
}
private:
bool may_have_ownership() const override
{
return false;
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name #GVArray_For_SingleValue
/** \name #GVArrayImpl_For_SingleValueRef
* \{ */
GVArray_For_SingleValue::GVArray_For_SingleValue(const CPPType &type,
const int64_t size,
const void *value)
: GVArray_For_SingleValueRef(type, size)
{
value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
type.copy_construct(value, (void *)value_);
}
/* Generic virtual array where each element has the same value. The value is not owned. */
class GVArrayImpl_For_SingleValueRef : public GVArrayImpl {
protected:
const void *value_ = nullptr;
GVArray_For_SingleValue::~GVArray_For_SingleValue()
{
type_->destruct((void *)value_);
MEM_freeN((void *)value_);
}
public:
GVArrayImpl_For_SingleValueRef(const CPPType &type, const int64_t size, const void *value)
: GVArrayImpl(type, size), value_(value)
{
}
protected:
GVArrayImpl_For_SingleValueRef(const CPPType &type, const int64_t size) : GVArrayImpl(type, size)
{
}
void get(const int64_t UNUSED(index), void *r_value) const override
{
type_->copy_assign(value_, r_value);
}
void get_to_uninitialized(const int64_t UNUSED(index), void *r_value) const override
{
type_->copy_construct(value_, r_value);
}
bool is_span() const override
{
return size_ == 1;
}
GSpan get_internal_span() const override
{
return GSpan{*type_, value_, 1};
}
bool is_single() const override
{
return true;
}
void get_internal_single(void *r_value) const override
{
type_->copy_assign(value_, r_value);
}
};
class GVArrayImpl_For_SingleValueRef_final final : public GVArrayImpl_For_SingleValueRef {
public:
using GVArrayImpl_For_SingleValueRef::GVArrayImpl_For_SingleValueRef;
private:
bool may_have_ownership() const override
{
return false;
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name #GVArrayImpl_For_SingleValue
* \{ */
/* Same as GVArrayImpl_For_SingleValueRef, but the value is owned. */
class GVArrayImpl_For_SingleValue : public GVArrayImpl_For_SingleValueRef,
NonCopyable,
NonMovable {
public:
GVArrayImpl_For_SingleValue(const CPPType &type, const int64_t size, const void *value)
: GVArrayImpl_For_SingleValueRef(type, size)
{
value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
type.copy_construct(value, (void *)value_);
}
~GVArrayImpl_For_SingleValue() override
{
type_->destruct((void *)value_);
MEM_freeN((void *)value_);
}
};
/** \} */
@@ -326,7 +339,7 @@ GVArray_For_SingleValue::~GVArray_For_SingleValue()
/** \name #GVArray_GSpan
* \{ */
GVArray_GSpan::GVArray_GSpan(const GVArray &varray) : GSpan(varray.type()), varray_(varray)
GVArray_GSpan::GVArray_GSpan(GVArray varray) : GSpan(varray.type()), varray_(std::move(varray))
{
size_ = varray_.size();
if (varray_.is_span()) {
@@ -353,8 +366,8 @@ GVArray_GSpan::~GVArray_GSpan()
/** \name #GVMutableArray_GSpan
* \{ */
GVMutableArray_GSpan::GVMutableArray_GSpan(GVMutableArray &varray, const bool copy_values_to_span)
: GMutableSpan(varray.type()), varray_(varray)
GVMutableArray_GSpan::GVMutableArray_GSpan(GVMutableArray varray, const bool copy_values_to_span)
: GMutableSpan(varray.type()), varray_(std::move(varray))
{
size_ = varray_.size();
if (varray_.is_span()) {
@@ -405,48 +418,314 @@ void GVMutableArray_GSpan::disable_not_applied_warning()
/** \} */
/* -------------------------------------------------------------------- */
/** \name #GVArray_For_SlicedGVArray
/** \name #GVArrayImpl_For_SlicedGVArray
* \{ */
void GVArray_For_SlicedGVArray::get_impl(const int64_t index, void *r_value) const
class GVArrayImpl_For_SlicedGVArray : public GVArrayImpl {
protected:
GVArray varray_;
int64_t offset_;
public:
GVArrayImpl_For_SlicedGVArray(GVArray varray, const IndexRange slice)
: GVArrayImpl(varray.type(), slice.size()),
varray_(std::move(varray)),
offset_(slice.start())
{
BLI_assert(slice.one_after_last() <= varray_.size());
}
void get(const int64_t index, void *r_value) const override
{
varray_.get(index + offset_, r_value);
}
void get_to_uninitialized(const int64_t index, void *r_value) const override
{
varray_.get_to_uninitialized(index + offset_, r_value);
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name #GVArrayCommon
* \{ */
GVArrayCommon::GVArrayCommon() = default;
GVArrayCommon::GVArrayCommon(const GVArrayCommon &other) : storage_(other.storage_)
{
varray_.get(index + offset_, r_value);
impl_ = this->impl_from_storage();
}
void GVArray_For_SlicedGVArray::get_to_uninitialized_impl(const int64_t index, void *r_value) const
GVArrayCommon::GVArrayCommon(GVArrayCommon &&other) noexcept : storage_(std::move(other.storage_))
{
varray_.get_to_uninitialized(index + offset_, r_value);
impl_ = this->impl_from_storage();
other.storage_.reset();
other.impl_ = nullptr;
}
GVArrayCommon::GVArrayCommon(const GVArrayImpl *impl) : impl_(impl)
{
storage_ = impl_;
}
GVArrayCommon::GVArrayCommon(std::shared_ptr<const GVArrayImpl> impl) : impl_(impl.get())
{
if (impl) {
storage_ = std::move(impl);
}
}
GVArrayCommon::~GVArrayCommon() = default;
void GVArrayCommon::materialize(void *dst) const
{
this->materialize(IndexMask(impl_->size()), dst);
}
void GVArrayCommon::materialize(const IndexMask mask, void *dst) const
{
impl_->materialize(mask, dst);
}
void GVArrayCommon::materialize_to_uninitialized(void *dst) const
{
this->materialize_to_uninitialized(IndexMask(impl_->size()), dst);
}
void GVArrayCommon::materialize_to_uninitialized(const IndexMask mask, void *dst) const
{
BLI_assert(mask.min_array_size() <= impl_->size());
impl_->materialize_to_uninitialized(mask, dst);
}
bool GVArrayCommon::may_have_ownership() const
{
return impl_->may_have_ownership();
}
void GVArrayCommon::copy_from(const GVArrayCommon &other)
{
if (this == &other) {
return;
}
storage_ = other.storage_;
impl_ = this->impl_from_storage();
}
void GVArrayCommon::move_from(GVArrayCommon &&other) noexcept
{
if (this == &other) {
return;
}
storage_ = std::move(other.storage_);
impl_ = this->impl_from_storage();
other.storage_.reset();
other.impl_ = nullptr;
}
/* Returns true when the virtual array is stored as a span internally. */
bool GVArrayCommon::is_span() const
{
if (this->is_empty()) {
return true;
}
return impl_->is_span();
}
/* Returns the internally used span of the virtual array. This invokes undefined behavior is the
* virtual array is not stored as a span internally. */
GSpan GVArrayCommon::get_internal_span() const
{
BLI_assert(this->is_span());
if (this->is_empty()) {
return GSpan(impl_->type());
}
return impl_->get_internal_span();
}
/* Returns true when the virtual array returns the same value for every index. */
bool GVArrayCommon::is_single() const
{
if (impl_->size() == 1) {
return true;
}
return impl_->is_single();
}
/* Copies the value that is used for every element into `r_value`, which is expected to point to
* initialized memory. This invokes undefined behavior if the virtual array would not return the
* same value for every index. */
void GVArrayCommon::get_internal_single(void *r_value) const
{
BLI_assert(this->is_single());
if (impl_->size() == 1) {
impl_->get(0, r_value);
return;
}
impl_->get_internal_single(r_value);
}
/* Same as `get_internal_single`, but `r_value` points to initialized memory. */
void GVArrayCommon::get_internal_single_to_uninitialized(void *r_value) const
{
impl_->type().default_construct(r_value);
this->get_internal_single(r_value);
}
const GVArrayImpl *GVArrayCommon::impl_from_storage() const
{
return storage_.extra_info().get_varray(storage_.get());
}
IndexRange GVArrayCommon::index_range() const
{
return IndexRange(this->size());
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name #GVArray_Slice
/** \name #GVArray
* \{ */
GVArray_Slice::GVArray_Slice(const GVArray &varray, const IndexRange slice)
GVArray::GVArray(const GVArray &other) = default;
GVArray::GVArray(GVArray &&other) noexcept = default;
GVArray::GVArray(const GVArrayImpl *impl) : GVArrayCommon(impl)
{
if (varray.is_span()) {
/* Create a new virtual for the sliced span. */
const GSpan span = varray.get_internal_span();
const GSpan sliced_span = span.slice(slice.start(), slice.size());
varray_span_.emplace(sliced_span);
varray_ = &*varray_span_;
}
else if (varray.is_single()) {
/* Can just use the existing virtual array, because it's the same value for the indices in the
* slice anyway. */
varray_ = &varray;
}
else {
/* Generic version when none of the above method works.
* We don't necessarily want to materialize the input varray because there might be
* large distances between the required indices. Then we would materialize many elements that
* are not accessed later on.
*/
varray_any_.emplace(varray, slice);
varray_ = &*varray_any_;
}
GVArray::GVArray(std::shared_ptr<const GVArrayImpl> impl) : GVArrayCommon(std::move(impl))
{
}
GVArray GVArray::ForSingle(const CPPType &type, const int64_t size, const void *value)
{
return GVArray::For<GVArrayImpl_For_SingleValue>(type, size, value);
}
GVArray GVArray::ForSingleRef(const CPPType &type, const int64_t size, const void *value)
{
return GVArray::For<GVArrayImpl_For_SingleValueRef_final>(type, size, value);
}
GVArray GVArray::ForSingleDefault(const CPPType &type, const int64_t size)
{
return GVArray::ForSingleRef(type, size, type.default_value());
}
GVArray GVArray::ForSpan(GSpan span)
{
return GVArray::For<GVArrayImpl_For_GSpan_final>(span);
}
class GVArrayImpl_For_GArray : public GVArrayImpl_For_GSpan {
protected:
GArray<> array_;
public:
GVArrayImpl_For_GArray(GArray<> array)
: GVArrayImpl_For_GSpan(array.as_span()), array_(std::move(array))
{
}
};
GVArray GVArray::ForGArray(GArray<> array)
{
return GVArray::For<GVArrayImpl_For_GArray>(array);
}
GVArray GVArray::ForEmpty(const CPPType &type)
{
return GVArray::ForSpan(GSpan(type));
}
GVArray GVArray::slice(IndexRange slice) const
{
return GVArray::For<GVArrayImpl_For_SlicedGVArray>(*this, slice);
}
GVArray &GVArray::operator=(const GVArray &other)
{
this->copy_from(other);
return *this;
}
GVArray &GVArray::operator=(GVArray &&other) noexcept
{
this->move_from(std::move(other));
return *this;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name #GVMutableArray
* \{ */
GVMutableArray::GVMutableArray(const GVMutableArray &other) = default;
GVMutableArray::GVMutableArray(GVMutableArray &&other) noexcept = default;
GVMutableArray::GVMutableArray(GVMutableArrayImpl *impl) : GVArrayCommon(impl)
{
}
GVMutableArray::GVMutableArray(std::shared_ptr<GVMutableArrayImpl> impl)
: GVArrayCommon(std::move(impl))
{
}
GVMutableArray GVMutableArray::ForSpan(GMutableSpan span)
{
return GVMutableArray::For<GVMutableArrayImpl_For_GMutableSpan_final>(span);
}
GVMutableArray::operator GVArray() const &
{
GVArray varray;
varray.copy_from(*this);
return varray;
}
GVMutableArray::operator GVArray() &&noexcept
{
GVArray varray;
varray.move_from(std::move(*this));
return varray;
}
GVMutableArray &GVMutableArray::operator=(const GVMutableArray &other)
{
this->copy_from(other);
return *this;
}
GVMutableArray &GVMutableArray::operator=(GVMutableArray &&other) noexcept
{
this->move_from(std::move(other));
return *this;
}
GVMutableArrayImpl *GVMutableArray::get_implementation() const
{
return this->get_impl();
}
/* Copy the values from the source buffer to all elements in the virtual array. */
void GVMutableArray::set_all(const void *src)
{
this->get_impl()->set_all(src);
}
GMutableSpan GVMutableArray::get_internal_span() const
{
BLI_assert(this->is_span());
const GSpan span = impl_->get_internal_span();
return GMutableSpan(span.type(), const_cast<void *>(span.data()), span.size());
}
/** \} */

View File

@@ -18,13 +18,13 @@
namespace blender::fn {
void GVArray_For_GVVectorArrayIndex::get_impl(const int64_t index_in_vector, void *r_value) const
void GVArray_For_GVVectorArrayIndex::get(const int64_t index_in_vector, void *r_value) const
{
vector_array_.get_vector_element(index_, index_in_vector, r_value);
}
void GVArray_For_GVVectorArrayIndex::get_to_uninitialized_impl(const int64_t index_in_vector,
void *r_value) const
void GVArray_For_GVVectorArrayIndex::get_to_uninitialized(const int64_t index_in_vector,
void *r_value) const
{
type_->default_construct(r_value);
vector_array_.get_vector_element(index_, index_in_vector, r_value);
@@ -32,14 +32,14 @@ void GVArray_For_GVVectorArrayIndex::get_to_uninitialized_impl(const int64_t ind
int64_t GVVectorArray_For_SingleGVArray::get_vector_size_impl(const int64_t UNUSED(index)) const
{
return array_.size();
return varray_.size();
}
void GVVectorArray_For_SingleGVArray::get_vector_element_impl(const int64_t UNUSED(index),
const int64_t index_in_vector,
void *r_value) const
{
array_.get(index_in_vector, r_value);
varray_.get(index_in_vector, r_value);
}
bool GVVectorArray_For_SingleGVArray::is_single_vector_impl() const

View File

@@ -54,7 +54,6 @@ void ParallelMultiFunction::call(IndexMask full_mask, MFParams params, MFContext
const IndexRange input_slice_range{input_slice_start, input_slice_size};
MFParamsBuilder sub_params{fn_, sub_mask.min_array_size()};
ResourceScope &scope = sub_params.resource_scope();
/* All parameters are sliced so that the wrapped multi-function does not have to take care of
* the index offset. */
@@ -63,8 +62,7 @@ void ParallelMultiFunction::call(IndexMask full_mask, MFParams params, MFContext
switch (param_type.category()) {
case MFParamType::SingleInput: {
const GVArray &varray = params.readonly_single_input(param_index);
const GVArray &sliced_varray = scope.construct<GVArray_Slice>(varray, input_slice_range);
sub_params.add_readonly_single_input(sliced_varray);
sub_params.add_readonly_single_input(varray.slice(input_slice_range));
break;
}
case MFParamType::SingleMutable: {

View File

@@ -66,6 +66,7 @@ struct VariableValue_GVArray : public VariableValue {
VariableValue_GVArray(const GVArray &data) : VariableValue(static_type), data(data)
{
BLI_assert(data);
}
};
@@ -756,7 +757,7 @@ class VariableState : NonCopyable, NonMovable {
switch (value_->type) {
case ValueType::GVArray: {
const GVArray_Typed<bool> varray{this->value_as<VariableValue_GVArray>()->data};
const VArray<bool> varray = this->value_as<VariableValue_GVArray>()->data.typed<bool>();
for (const int i : mask) {
r_indices[varray[i]].append(i);
}

View File

@@ -34,14 +34,12 @@ class IndexFieldInput final : public FieldInput {
{
}
const GVArray *get_varray_for_context(const FieldContext &UNUSED(context),
IndexMask mask,
ResourceScope &scope) const final
GVArray get_varray_for_context(const FieldContext &UNUSED(context),
IndexMask mask,
ResourceScope &UNUSED(scope)) const final
{
auto index_func = [](int i) { return i; };
return &scope.construct<
GVArray_For_EmbeddedVArray<int, VArray_For_Func<int, decltype(index_func)>>>(
mask.min_array_size(), mask.min_array_size(), index_func);
return VArray<int>::ForFunc(mask.min_array_size(), index_func);
}
};
@@ -240,20 +238,20 @@ TEST(field, TwoFunctionsTwoOutputs)
FieldContext field_context;
FieldEvaluator field_evaluator{field_context, &mask};
const VArray<int> *result_1 = nullptr;
const VArray<int> *result_2 = nullptr;
VArray<int> result_1;
VArray<int> result_2;
field_evaluator.add(result_field_1, &result_1);
field_evaluator.add(result_field_2, &result_2);
field_evaluator.evaluate();
EXPECT_EQ(result_1->get(2), 4);
EXPECT_EQ(result_1->get(4), 8);
EXPECT_EQ(result_1->get(6), 12);
EXPECT_EQ(result_1->get(8), 16);
EXPECT_EQ(result_2->get(2), 24);
EXPECT_EQ(result_2->get(4), 28);
EXPECT_EQ(result_2->get(6), 32);
EXPECT_EQ(result_2->get(8), 36);
EXPECT_EQ(result_1.get(2), 4);
EXPECT_EQ(result_1.get(4), 8);
EXPECT_EQ(result_1.get(6), 12);
EXPECT_EQ(result_1.get(8), 16);
EXPECT_EQ(result_2.get(2), 24);
EXPECT_EQ(result_2.get(4), 28);
EXPECT_EQ(result_2.get(6), 32);
EXPECT_EQ(result_2.get(8), 36);
}
TEST(field, SameFieldTwice)
@@ -264,16 +262,16 @@ TEST(field, SameFieldTwice)
FieldContext field_context;
IndexMask mask{IndexRange(2)};
ResourceScope scope;
Vector<const GVArray *> results = evaluate_fields(
Vector<GVArray> results = evaluate_fields(
scope, {constant_field, constant_field}, mask, field_context);
GVArray_Typed<int> varray1{*results[0]};
GVArray_Typed<int> varray2{*results[1]};
VArray<int> varray1 = results[0].typed<int>();
VArray<int> varray2 = results[1].typed<int>();
EXPECT_EQ(varray1->get(0), 10);
EXPECT_EQ(varray1->get(1), 10);
EXPECT_EQ(varray2->get(0), 10);
EXPECT_EQ(varray2->get(1), 10);
EXPECT_EQ(varray1.get(0), 10);
EXPECT_EQ(varray1.get(1), 10);
EXPECT_EQ(varray2.get(0), 10);
EXPECT_EQ(varray2.get(1), 10);
}
TEST(field, IgnoredOutput)
@@ -283,12 +281,12 @@ TEST(field, IgnoredOutput)
FieldContext field_context;
FieldEvaluator field_evaluator{field_context, 10};
const VArray<int> *results = nullptr;
VArray<int> results;
field_evaluator.add(field, &results);
field_evaluator.evaluate();
EXPECT_EQ(results->get(0), 5);
EXPECT_EQ(results->get(3), 5);
EXPECT_EQ(results.get(0), 5);
EXPECT_EQ(results.get(3), 5);
}
} // namespace blender::fn::tests

View File

@@ -9,6 +9,43 @@
namespace blender::fn::tests {
TEST(multi_function_procedure, ConstantOutput)
{
/**
* procedure(int *var2) {
* var1 = 5;
* var2 = var1 + var1;
* }
*/
CustomMF_Constant<int> constant_fn{5};
CustomMF_SI_SI_SO<int, int, int> add_fn{"Add", [](int a, int b) { return a + b; }};
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
auto [var1] = builder.add_call<1>(constant_fn);
auto [var2] = builder.add_call<1>(add_fn, {var1, var1});
builder.add_destruct(*var1);
builder.add_return();
builder.add_output_parameter(*var2);
EXPECT_TRUE(procedure.validate());
MFProcedureExecutor executor{"My Procedure", procedure};
MFParamsBuilder params{executor, 2};
MFContextBuilder context;
Array<int> output_array(2);
params.add_uninitialized_single_output(output_array.as_mutable_span());
executor.call(IndexRange(2), params, context);
EXPECT_EQ(output_array[0], 10);
EXPECT_EQ(output_array[1], 10);
}
TEST(multi_function_procedure, SimpleTest)
{
/**

View File

@@ -50,23 +50,23 @@ static void copy_attributes_to_points(CurveEval &curve,
/* Copy builtin control point attributes. */
if (source_attribute_ids.contains("tilt")) {
const fn::GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>(
const VArray<float> tilt_attribute = mesh_component.attribute_get_for_read<float>(
"tilt", ATTR_DOMAIN_POINT, 0.0f);
threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) {
for (const int i : range) {
copy_attribute_to_points<float>(
*tilt_attribute, point_to_vert_maps[i], splines[i]->tilts());
tilt_attribute, point_to_vert_maps[i], splines[i]->tilts());
}
});
source_attribute_ids.remove_contained("tilt");
}
if (source_attribute_ids.contains("radius")) {
const fn::GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>(
const VArray<float> radius_attribute = mesh_component.attribute_get_for_read<float>(
"radius", ATTR_DOMAIN_POINT, 1.0f);
threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) {
for (const int i : range) {
copy_attribute_to_points<float>(
*radius_attribute, point_to_vert_maps[i], splines[i]->radii());
radius_attribute, point_to_vert_maps[i], splines[i]->radii());
}
});
source_attribute_ids.remove_contained("radius");
@@ -82,7 +82,7 @@ static void copy_attributes_to_points(CurveEval &curve,
continue;
}
const fn::GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(
const fn::GVArray mesh_attribute = mesh_component.attribute_try_get_for_read(
attribute_id, ATTR_DOMAIN_POINT);
/* Some attributes might not exist if they were builtin attribute on domains that don't
* have any elements, i.e. a face attribute on the output of the line primitive node. */
@@ -90,7 +90,7 @@ static void copy_attributes_to_points(CurveEval &curve,
continue;
}
const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type());
const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute.type());
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
@@ -101,10 +101,10 @@ static void copy_attributes_to_points(CurveEval &curve,
BLI_assert(spline_attribute);
/* Copy attribute based on the map for this spline. */
attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) {
attribute_math::convert_to_static_type(mesh_attribute.type(), [&](auto dummy) {
using T = decltype(dummy);
copy_attribute_to_points<T>(
mesh_attribute->typed<T>(), point_to_vert_maps[i], spline_attribute->typed<T>());
mesh_attribute.typed<T>(), point_to_vert_maps[i], spline_attribute->typed<T>());
});
}
});

View File

@@ -19,7 +19,6 @@
# ***** END GPL LICENSE BLOCK *****
add_subdirectory(common)
add_subdirectory(wavefront_obj)
if(WITH_ALEMBIC)
add_subdirectory(alembic)

View File

@@ -1,104 +0,0 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# The Original Code is Copyright (C) 2020, Blender Foundation
# All rights reserved.
# ***** END GPL LICENSE BLOCK *****
set(INC
.
./exporter
./importer
../../blenkernel
../../blenlib
../../bmesh
../../bmesh/intern
../../depsgraph
../../editors/include
../../makesdna
../../makesrna
../../nodes
../../windowmanager
../../../../intern/guardedalloc
)
set(INC_SYS
)
set(SRC
IO_wavefront_obj.cc
exporter/obj_exporter.cc
exporter/obj_export_file_writer.cc
exporter/obj_export_mesh.cc
exporter/obj_export_mtl.cc
exporter/obj_export_nurbs.cc
importer/importer_mesh_utils.cc
importer/obj_import_file_reader.cc
importer/obj_importer.cc
importer/obj_import_mesh.cc
importer/obj_import_mtl.cc
importer/obj_import_nurbs.cc
importer/obj_import_objects.cc
importer/parser_string_utils.cc
IO_wavefront_obj.h
exporter/obj_exporter.hh
exporter/obj_export_file_writer.hh
exporter/obj_export_io.hh
exporter/obj_export_mesh.hh
exporter/obj_export_mtl.hh
exporter/obj_export_nurbs.hh
importer/importer_mesh_utils.hh
importer/obj_import_file_reader.hh
importer/obj_importer.hh
importer/obj_import_mesh.hh
importer/obj_import_mtl.hh
importer/obj_import_nurbs.hh
importer/obj_import_objects.hh
importer/parser_string_utils.hh
)
set(LIB
bf_blenkernel
)
blender_add_lib(bf_wavefront_obj "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
if(WITH_GTESTS)
set(TEST_SRC
tests/obj_exporter_tests.cc
tests/obj_exporter_tests.hh
)
set(TEST_INC
${INC}
../../blenloader
../../../../tests/gtests
)
set(TEST_LIB
${LIB}
bf_blenloader_tests
bf_wavefront_obj
)
include(GTestTesting)
blender_add_test_lib(bf_wavefront_obj_tests "${TEST_SRC}" "${TEST_INC}" "${INC_SYS}" "${TEST_LIB}")
add_dependencies(bf_wavefront_obj_tests bf_wavefront_obj)
endif()

View File

@@ -1,47 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#include "BLI_timeit.hh"
#include "IO_wavefront_obj.h"
#include "obj_exporter.hh"
#include "obj_importer.hh"
/**
* C-interface for the exporter.
*/
void OBJ_export(bContext *C, const OBJExportParams *export_params)
{
SCOPED_TIMER("OBJ export");
blender::io::obj::exporter_main(C, *export_params);
}
/**
* Time the full import process.
*/
void OBJ_import(bContext *C, const OBJImportParams *import_params)
{
SCOPED_TIMER(__func__);
blender::io::obj::importer_main(C, *import_params);
}

View File

@@ -1,111 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#pragma once
#include "BKE_context.h"
#include "BLI_path_util.h"
#include "DEG_depsgraph.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
OBJ_AXIS_X_UP = 0,
OBJ_AXIS_Y_UP = 1,
OBJ_AXIS_Z_UP = 2,
OBJ_AXIS_NEGATIVE_X_UP = 3,
OBJ_AXIS_NEGATIVE_Y_UP = 4,
OBJ_AXIS_NEGATIVE_Z_UP = 5,
} eTransformAxisUp;
typedef enum {
OBJ_AXIS_X_FORWARD = 0,
OBJ_AXIS_Y_FORWARD = 1,
OBJ_AXIS_Z_FORWARD = 2,
OBJ_AXIS_NEGATIVE_X_FORWARD = 3,
OBJ_AXIS_NEGATIVE_Y_FORWARD = 4,
OBJ_AXIS_NEGATIVE_Z_FORWARD = 5,
} eTransformAxisForward;
const int TOTAL_AXES = 3;
struct OBJExportParams {
/** Full path to the destination .OBJ file. */
char filepath[FILE_MAX];
/** Full path to current blender file (used for comments in output). */
const char *blen_filepath;
/** Whether multiple frames should be exported. */
bool export_animation;
/** The first frame to be exported. */
int start_frame;
/** The last frame to be exported. */
int end_frame;
/* Geometry Transform options. */
eTransformAxisForward forward_axis;
eTransformAxisUp up_axis;
float scaling_factor;
/* File Write Options. */
bool export_selected_objects;
eEvaluationMode export_eval_mode;
bool export_uv;
bool export_normals;
bool export_materials;
bool export_triangulated_mesh;
bool export_curves_as_nurbs;
/* Grouping options. */
bool export_object_groups;
bool export_material_groups;
bool export_vertex_groups;
/**
* Calculate smooth groups from sharp edges.
*/
bool export_smooth_groups;
/**
* Create bitflags instead of the default "0"/"1" group IDs.
*/
bool smooth_groups_bitflags;
};
struct OBJImportParams {
/** Full path to the source OBJ file to import. */
char filepath[FILE_MAX];
/* Value 0 disables clamping. */
float clamp_size;
eTransformAxisForward forward_axis;
eTransformAxisUp up_axis;
};
void OBJ_import(bContext *C, const struct OBJImportParams *import_params);
void OBJ_export(bContext *C, const struct OBJExportParams *export_params);
#ifdef __cplusplus
}
#endif

View File

@@ -1,629 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#include <algorithm>
#include <cstdio>
#include "BKE_blender_version.h"
#include "BLI_path_util.h"
#include "obj_export_mesh.hh"
#include "obj_export_mtl.hh"
#include "obj_export_nurbs.hh"
#include "obj_export_file_writer.hh"
namespace blender::io::obj {
/**
* "To turn off smoothing
* groups, use a value of 0 or off. Polygonal elements use group
* numbers to put elements in different smoothing groups. For
* free-form surfaces, smoothing groups are either turned on or off;
* there is no difference between values greater than 0."
* http://www.martinreddy.net/gfx/3d/OBJ.spec
*/
const int SMOOTH_GROUP_DISABLED = 0;
const int SMOOTH_GROUP_DEFAULT = 1;
const char *DEFORM_GROUP_DISABLED = "off";
/* There is no deform group default name. Use what the user set in the UI. */
/* "Once a material is assigned, it cannot be turned off; it can only be changed.
* If a material name is not specified, a white material is used."
* http://www.martinreddy.net/gfx/3d/OBJ.spec
* So an empty material name is written. */
const char *MATERIAL_GROUP_DISABLED = "";
/**
* Write one line of polygon indices as "f v1/vt1/vn1 v2/vt2/vn2 ...".
*/
void OBJWriter::write_vert_uv_normal_indices(Span<int> vert_indices,
Span<int> uv_indices,
Span<int> normal_indices) const
{
BLI_assert(vert_indices.size() == uv_indices.size() &&
vert_indices.size() == normal_indices.size());
file_handler_->write<eOBJSyntaxElement::poly_element_begin>();
for (int j = 0; j < vert_indices.size(); j++) {
file_handler_->write<eOBJSyntaxElement::vertex_uv_normal_indices>(
vert_indices[j] + index_offsets_.vertex_offset + 1,
uv_indices[j] + index_offsets_.uv_vertex_offset + 1,
normal_indices[j] + index_offsets_.normal_offset + 1);
}
file_handler_->write<eOBJSyntaxElement::poly_element_end>();
}
/**
* Write one line of polygon indices as "f v1//vn1 v2//vn2 ...".
*/
void OBJWriter::write_vert_normal_indices(Span<int> vert_indices,
Span<int> /*uv_indices*/,
Span<int> normal_indices) const
{
BLI_assert(vert_indices.size() == normal_indices.size());
file_handler_->write<eOBJSyntaxElement::poly_element_begin>();
for (int j = 0; j < vert_indices.size(); j++) {
file_handler_->write<eOBJSyntaxElement::vertex_normal_indices>(
vert_indices[j] + index_offsets_.vertex_offset + 1,
normal_indices[j] + index_offsets_.normal_offset + 1);
}
file_handler_->write<eOBJSyntaxElement::poly_element_end>();
}
/**
* Write one line of polygon indices as "f v1/vt1 v2/vt2 ...".
*/
void OBJWriter::write_vert_uv_indices(Span<int> vert_indices,
Span<int> uv_indices,
Span<int> /*normal_indices*/) const
{
BLI_assert(vert_indices.size() == uv_indices.size());
file_handler_->write<eOBJSyntaxElement::poly_element_begin>();
for (int j = 0; j < vert_indices.size(); j++) {
file_handler_->write<eOBJSyntaxElement::vertex_uv_indices>(
vert_indices[j] + index_offsets_.vertex_offset + 1,
uv_indices[j] + index_offsets_.uv_vertex_offset + 1);
}
file_handler_->write<eOBJSyntaxElement::poly_element_end>();
}
/**
* Write one line of polygon indices as "f v1 v2 ...".
*/
void OBJWriter::write_vert_indices(Span<int> vert_indices,
Span<int> /*uv_indices*/,
Span<int> /*normal_indices*/) const
{
file_handler_->write<eOBJSyntaxElement::poly_element_begin>();
for (const int vert_index : vert_indices) {
file_handler_->write<eOBJSyntaxElement::vertex_indices>(vert_index +
index_offsets_.vertex_offset + 1);
}
file_handler_->write<eOBJSyntaxElement::poly_element_end>();
}
void OBJWriter::write_header() const
{
using namespace std::string_literals;
file_handler_->write<eOBJSyntaxElement::string>("# Blender "s + BKE_blender_version_string() +
"\n");
file_handler_->write<eOBJSyntaxElement::string>("# www.blender.org\n");
}
/**
* Write file name of Material Library in .OBJ file.
*/
void OBJWriter::write_mtllib_name(const StringRefNull mtl_filepath) const
{
/* Split .MTL file path into parent directory and filename. */
char mtl_file_name[FILE_MAXFILE];
char mtl_dir_name[FILE_MAXDIR];
BLI_split_dirfile(mtl_filepath.data(), mtl_dir_name, mtl_file_name, FILE_MAXDIR, FILE_MAXFILE);
file_handler_->write<eOBJSyntaxElement::mtllib>(mtl_file_name);
}
/**
* Write an object's group with mesh and/or material name appended conditionally.
*/
void OBJWriter::write_object_group(const OBJMesh &obj_mesh_data) const
{
/* "o object_name" is not mandatory. A valid .OBJ file may contain neither
* "o name" nor "g group_name". */
BLI_assert(export_params_.export_object_groups);
if (!export_params_.export_object_groups) {
return;
}
const std::string object_name = obj_mesh_data.get_object_name();
const char *object_mesh_name = obj_mesh_data.get_object_mesh_name();
const char *object_material_name = obj_mesh_data.get_object_material_name(0);
if (export_params_.export_materials && export_params_.export_material_groups &&
object_material_name) {
file_handler_->write<eOBJSyntaxElement::object_group>(object_name + "_" + object_mesh_name +
"_" + object_material_name);
return;
}
file_handler_->write<eOBJSyntaxElement::object_group>(object_name + "_" + object_mesh_name);
}
/**
* Write object's name or group.
*/
void OBJWriter::write_object_name(const OBJMesh &obj_mesh_data) const
{
const char *object_name = obj_mesh_data.get_object_name();
if (export_params_.export_object_groups) {
write_object_group(obj_mesh_data);
return;
}
file_handler_->write<eOBJSyntaxElement::object_name>(object_name);
}
/**
* Write vertex coordinates for all vertices as "v x y z".
*/
void OBJWriter::write_vertex_coords(const OBJMesh &obj_mesh_data) const
{
const int tot_vertices = obj_mesh_data.tot_vertices();
for (int i = 0; i < tot_vertices; i++) {
float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor);
file_handler_->write<eOBJSyntaxElement::vertex_coords>(vertex[0], vertex[1], vertex[2]);
}
}
/**
* Write UV vertex coordinates for all vertices as "vt u v".
* \note UV indices are stored here, but written later.
*/
void OBJWriter::write_uv_coords(OBJMesh &r_obj_mesh_data) const
{
Vector<std::array<float, 2>> uv_coords;
/* UV indices are calculated and stored in an OBJMesh member here. */
r_obj_mesh_data.store_uv_coords_and_indices(uv_coords);
for (const std::array<float, 2> &uv_vertex : uv_coords) {
file_handler_->write<eOBJSyntaxElement::uv_vertex_coords>(uv_vertex[0], uv_vertex[1]);
}
}
/**
* Write loop normals for smooth-shaded polygons, and polygon normals otherwise, as "vn x y z".
*/
void OBJWriter::write_poly_normals(const OBJMesh &obj_mesh_data) const
{
obj_mesh_data.ensure_mesh_normals();
Vector<float3> lnormals;
const int tot_polygons = obj_mesh_data.tot_polygons();
for (int i = 0; i < tot_polygons; i++) {
if (obj_mesh_data.is_ith_poly_smooth(i)) {
obj_mesh_data.calc_loop_normals(i, lnormals);
for (const float3 &lnormal : lnormals) {
file_handler_->write<eOBJSyntaxElement::normal>(lnormal[0], lnormal[1], lnormal[2]);
}
}
else {
float3 poly_normal = obj_mesh_data.calc_poly_normal(i);
file_handler_->write<eOBJSyntaxElement::normal>(
poly_normal[0], poly_normal[1], poly_normal[2]);
}
}
}
/**
* Write smooth group if polygon at the given index is shaded smooth else "s 0"
*/
int OBJWriter::write_smooth_group(const OBJMesh &obj_mesh_data,
const int poly_index,
const int last_poly_smooth_group) const
{
int current_group = SMOOTH_GROUP_DISABLED;
if (!export_params_.export_smooth_groups && obj_mesh_data.is_ith_poly_smooth(poly_index)) {
/* Smooth group calculation is disabled, but polygon is smooth-shaded. */
current_group = SMOOTH_GROUP_DEFAULT;
}
else if (obj_mesh_data.is_ith_poly_smooth(poly_index)) {
/* Smooth group calc is enabled and polygon is smoothshaded, so find the group. */
current_group = obj_mesh_data.ith_smooth_group(poly_index);
}
if (current_group == last_poly_smooth_group) {
/* Group has already been written, even if it is "s 0". */
return current_group;
}
file_handler_->write<eOBJSyntaxElement::smooth_group>(current_group);
return current_group;
}
/**
* Write material name and material group of a polygon in the .OBJ file.
* \return #mat_nr of the polygon at the given index.
* \note It doesn't write to the material library.
*/
int16_t OBJWriter::write_poly_material(const OBJMesh &obj_mesh_data,
const int poly_index,
const int16_t last_poly_mat_nr,
std::function<const char *(int)> matname_fn) const
{
if (!export_params_.export_materials || obj_mesh_data.tot_materials() <= 0) {
return last_poly_mat_nr;
}
const int16_t current_mat_nr = obj_mesh_data.ith_poly_matnr(poly_index);
/* Whenever a polygon with a new material is encountered, write its material
* and/or group, otherwise pass. */
if (last_poly_mat_nr == current_mat_nr) {
return current_mat_nr;
}
if (current_mat_nr == NOT_FOUND) {
file_handler_->write<eOBJSyntaxElement::poly_usemtl>(MATERIAL_GROUP_DISABLED);
return current_mat_nr;
}
if (export_params_.export_object_groups) {
write_object_group(obj_mesh_data);
}
const char *mat_name = matname_fn(current_mat_nr);
if (!mat_name) {
mat_name = MATERIAL_GROUP_DISABLED;
}
file_handler_->write<eOBJSyntaxElement::poly_usemtl>(mat_name);
return current_mat_nr;
}
/**
* Write the name of the deform group of a polygon.
*/
int16_t OBJWriter::write_vertex_group(const OBJMesh &obj_mesh_data,
const int poly_index,
const int16_t last_poly_vertex_group) const
{
if (!export_params_.export_vertex_groups) {
return last_poly_vertex_group;
}
const int16_t current_group = obj_mesh_data.get_poly_deform_group_index(poly_index);
if (current_group == last_poly_vertex_group) {
/* No vertex group found in this polygon, just like in the last iteration. */
return current_group;
}
if (current_group == NOT_FOUND) {
file_handler_->write<eOBJSyntaxElement::object_group>(DEFORM_GROUP_DISABLED);
return current_group;
}
file_handler_->write<eOBJSyntaxElement::object_group>(
obj_mesh_data.get_poly_deform_group_name(current_group));
return current_group;
}
/**
* \return Writer function with appropriate polygon-element syntax.
*/
OBJWriter::func_vert_uv_normal_indices OBJWriter::get_poly_element_writer(
const int total_uv_vertices) const
{
if (export_params_.export_normals) {
if (export_params_.export_uv && (total_uv_vertices > 0)) {
/* Write both normals and UV indices. */
return &OBJWriter::write_vert_uv_normal_indices;
}
/* Write normals indices. */
return &OBJWriter::write_vert_normal_indices;
}
/* Write UV indices. */
if (export_params_.export_uv && (total_uv_vertices > 0)) {
return &OBJWriter::write_vert_uv_indices;
}
/* Write neither normals nor UV indices. */
return &OBJWriter::write_vert_indices;
}
/**
* Write polygon elements with at least vertex indices, and conditionally with UV vertex
* indices and polygon 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 OBJWriter::write_poly_elements(const OBJMesh &obj_mesh_data,
std::function<const char *(int)> matname_fn)
{
int last_poly_smooth_group = NEGATIVE_INIT;
int16_t last_poly_vertex_group = NEGATIVE_INIT;
int16_t last_poly_mat_nr = NEGATIVE_INIT;
const func_vert_uv_normal_indices poly_element_writer = get_poly_element_writer(
obj_mesh_data.tot_uv_vertices());
/* Number of normals may not be equal to number of polygons due to smooth shading. */
int per_object_tot_normals = 0;
const int tot_polygons = obj_mesh_data.tot_polygons();
for (int i = 0; i < tot_polygons; i++) {
Vector<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);
/* For an Object, a normal index depends on how many of its normals have been written before
* it. This is unknown because of smooth shading. So pass "per object total normals"
* and update it after each call. */
int new_normals = 0;
Vector<int> poly_normal_indices;
std::tie(new_normals, poly_normal_indices) = obj_mesh_data.calc_poly_normal_indices(
i, per_object_tot_normals);
per_object_tot_normals += new_normals;
last_poly_smooth_group = write_smooth_group(obj_mesh_data, i, last_poly_smooth_group);
last_poly_vertex_group = write_vertex_group(obj_mesh_data, i, last_poly_vertex_group);
last_poly_mat_nr = write_poly_material(obj_mesh_data, i, last_poly_mat_nr, matname_fn);
(this->*poly_element_writer)(poly_vertex_indices, poly_uv_indices, poly_normal_indices);
}
/* Unusual: Other indices are updated in #OBJWriter::update_index_offsets. */
index_offsets_.normal_offset += per_object_tot_normals;
}
/**
* Write loose edges of a mesh as "l v1 v2".
*/
void OBJWriter::write_edges_indices(const OBJMesh &obj_mesh_data) const
{
obj_mesh_data.ensure_mesh_edges();
const int tot_edges = obj_mesh_data.tot_edges();
for (int edge_index = 0; edge_index < tot_edges; edge_index++) {
const std::optional<std::array<int, 2>> vertex_indices =
obj_mesh_data.calc_loose_edge_vert_indices(edge_index);
if (!vertex_indices) {
continue;
}
file_handler_->write<eOBJSyntaxElement::edge>(
(*vertex_indices)[0] + index_offsets_.vertex_offset + 1,
(*vertex_indices)[1] + index_offsets_.vertex_offset + 1);
}
}
/**
* Write a NURBS curve to the .OBJ file in parameter form.
*/
void OBJWriter::write_nurbs_curve(const OBJCurve &obj_nurbs_data) const
{
const int total_splines = obj_nurbs_data.total_splines();
for (int spline_idx = 0; spline_idx < total_splines; spline_idx++) {
const int total_vertices = obj_nurbs_data.total_spline_vertices(spline_idx);
for (int vertex_idx = 0; vertex_idx < total_vertices; vertex_idx++) {
const float3 vertex_coords = obj_nurbs_data.vertex_coordinates(
spline_idx, vertex_idx, export_params_.scaling_factor);
file_handler_->write<eOBJSyntaxElement::vertex_coords>(
vertex_coords[0], vertex_coords[1], vertex_coords[2]);
}
const char *nurbs_name = obj_nurbs_data.get_curve_name();
const int nurbs_degree = obj_nurbs_data.get_nurbs_degree(spline_idx);
file_handler_->write<eOBJSyntaxElement::object_group>(nurbs_name);
file_handler_->write<eOBJSyntaxElement::cstype>();
file_handler_->write<eOBJSyntaxElement::nurbs_degree>(nurbs_degree);
/**
* The numbers written here are indices into the vertex coordinates written
* earlier, relative to the line that is going to be written.
* [0.0 - 1.0] is the curve parameter range.
* 0.0 1.0 -1 -2 -3 -4 for a non-cyclic curve with 4 vertices.
* 0.0 1.0 -1 -2 -3 -4 -1 -2 -3 for a cyclic curve with 4 vertices.
*/
const int total_control_points = obj_nurbs_data.total_spline_control_points(spline_idx);
file_handler_->write<eOBJSyntaxElement::curve_element_begin>();
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. */
file_handler_->write<eOBJSyntaxElement::vertex_indices>(-((i % total_vertices) + 1));
}
file_handler_->write<eOBJSyntaxElement::curve_element_end>();
/**
* In "parm u 0 0.1 .." line:, (total control points + 2) equidistant numbers in the
* parameter range are inserted.
*/
file_handler_->write<eOBJSyntaxElement::nurbs_parameter_begin>();
for (int i = 1; i <= total_control_points + 2; i++) {
file_handler_->write<eOBJSyntaxElement::nurbs_parameters>(1.0f * i /
(total_control_points + 2 + 1));
}
file_handler_->write<eOBJSyntaxElement::nurbs_parameter_end>();
file_handler_->write<eOBJSyntaxElement::nurbs_group_end>();
}
}
/**
* When there are multiple objects in a frame, the indices of previous objects' coordinates or
* normals add up.
*/
void OBJWriter::update_index_offsets(const OBJMesh &obj_mesh_data)
{
index_offsets_.vertex_offset += obj_mesh_data.tot_vertices();
index_offsets_.uv_vertex_offset += obj_mesh_data.tot_uv_vertices();
/* Normal index is updated right after writing the normals. */
}
/* -------------------------------------------------------------------- */
/** \name .MTL writers.
* \{ */
/**
* Convert #float3 to string of space-separated numbers, with no leading or trailing space.
* Only to be used in NON-performance-critical code.
*/
static std::string float3_to_string(const float3 &numbers)
{
std::ostringstream r_string;
r_string << numbers[0] << " " << numbers[1] << " " << numbers[2];
return r_string.str();
};
/*
* Create the .MTL file.
*/
MTLWriter::MTLWriter(const char *obj_filepath) noexcept(false)
{
mtl_filepath_ = obj_filepath;
const bool ok = BLI_path_extension_replace(mtl_filepath_.data(), FILE_MAX, ".mtl");
if (!ok) {
throw std::system_error(ENAMETOOLONG, std::system_category(), "");
}
file_handler_ = std::make_unique<FileHandler<eFileType::MTL>>(mtl_filepath_);
}
void MTLWriter::write_header(const char *blen_filepath) const
{
using namespace std::string_literals;
const char *blen_basename = (blen_filepath && blen_filepath[0] != '\0') ?
BLI_path_basename(blen_filepath) :
"None";
file_handler_->write<eMTLSyntaxElement::string>("# Blender "s + BKE_blender_version_string() +
" MTL File: '" + blen_basename + "'\n");
file_handler_->write<eMTLSyntaxElement::string>("# www.blender.org\n");
}
StringRefNull MTLWriter::mtl_file_path() const
{
return mtl_filepath_;
}
/**
* Write properties sourced from p-BSDF node or #Object.Material.
*/
void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl_material)
{
file_handler_->write<eMTLSyntaxElement::Ns>(mtl_material.Ns);
file_handler_->write<eMTLSyntaxElement::Ka>(
mtl_material.Ka.x, mtl_material.Ka.y, mtl_material.Ka.z);
file_handler_->write<eMTLSyntaxElement::Kd>(
mtl_material.Kd.x, mtl_material.Kd.y, mtl_material.Kd.z);
file_handler_->write<eMTLSyntaxElement::Ks>(
mtl_material.Ks.x, mtl_material.Ks.y, mtl_material.Ks.z);
file_handler_->write<eMTLSyntaxElement::Ke>(
mtl_material.Ke.x, mtl_material.Ke.y, mtl_material.Ke.z);
file_handler_->write<eMTLSyntaxElement::Ni>(mtl_material.Ni);
file_handler_->write<eMTLSyntaxElement::d>(mtl_material.d);
file_handler_->write<eMTLSyntaxElement::illum>(mtl_material.illum);
}
/**
* Write a texture map in the form "map_XX -s 1. 1. 1. -o 0. 0. 0. [-bm 1.] path/to/image".
*/
void MTLWriter::write_texture_map(
const MTLMaterial &mtl_material,
const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map)
{
std::string translation;
std::string scale;
std::string map_bump_strength;
/* Optional strings should have their own leading spaces. */
if (texture_map.value.translation != float3{0.0f, 0.0f, 0.0f}) {
translation.append(" -s ").append(float3_to_string(texture_map.value.translation));
}
if (texture_map.value.scale != float3{1.0f, 1.0f, 1.0f}) {
scale.append(" -o ").append(float3_to_string(texture_map.value.scale));
}
if (texture_map.key == eMTLSyntaxElement::map_Bump && mtl_material.map_Bump_strength > 0.0001f) {
map_bump_strength.append(" -bm ").append(std::to_string(mtl_material.map_Bump_strength));
}
#define SYNTAX_DISPATCH(eMTLSyntaxElement) \
if (texture_map.key == eMTLSyntaxElement) { \
file_handler_->write<eMTLSyntaxElement>(translation + scale + map_bump_strength, \
texture_map.value.image_path); \
return; \
}
SYNTAX_DISPATCH(eMTLSyntaxElement::map_Kd);
SYNTAX_DISPATCH(eMTLSyntaxElement::map_Ks);
SYNTAX_DISPATCH(eMTLSyntaxElement::map_Ns);
SYNTAX_DISPATCH(eMTLSyntaxElement::map_d);
SYNTAX_DISPATCH(eMTLSyntaxElement::map_refl);
SYNTAX_DISPATCH(eMTLSyntaxElement::map_Ke);
SYNTAX_DISPATCH(eMTLSyntaxElement::map_Bump);
BLI_assert(!"This map type was not written to the file.");
}
/**
* Write all of the material specifications to the MTL file.
* For consistency of output from run to run (useful for testing),
* the materials are sorted by name before writing.
*/
void MTLWriter::write_materials()
{
if (mtlmaterials_.size() == 0) {
return;
}
std::sort(mtlmaterials_.begin(),
mtlmaterials_.end(),
[](const MTLMaterial &a, const MTLMaterial &b) { return a.name < b.name; });
for (const MTLMaterial &mtlmat : mtlmaterials_) {
file_handler_->write<eMTLSyntaxElement::string>("\n");
file_handler_->write<eMTLSyntaxElement::newmtl>(mtlmat.name);
write_bsdf_properties(mtlmat);
for (const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map :
mtlmat.texture_maps.items()) {
if (!texture_map.value.image_path.empty()) {
write_texture_map(mtlmat, texture_map);
}
}
}
}
/**
* Add the materials of the given object to MTLWriter, deduping
* against ones that are already there.
* Return a Vector of indices into mtlmaterials_ that hold the MTLMaterial
* that corresponds to each material slot, in order, of the given Object.
* Indexes are returned rather than pointers to the MTLMaterials themselves
* because the mtlmaterials_ Vector may move around when resized.
*/
Vector<int> MTLWriter::add_materials(const OBJMesh &mesh_to_export)
{
Vector<int> r_mtl_indices;
r_mtl_indices.resize(mesh_to_export.tot_materials());
for (int16_t i = 0; i < mesh_to_export.tot_materials(); i++) {
const Material *material = mesh_to_export.get_object_material(i);
if (!material) {
r_mtl_indices[i] = -1;
continue;
}
int mtlmat_index = material_map_.lookup_default(material, -1);
if (mtlmat_index != -1) {
r_mtl_indices[i] = mtlmat_index;
}
else {
mtlmaterials_.append(mtlmaterial_for_material(material));
r_mtl_indices[i] = mtlmaterials_.size() - 1;
material_map_.add_new(material, r_mtl_indices[i]);
}
}
return r_mtl_indices;
}
const char *MTLWriter::mtlmaterial_name(int index)
{
if (index < 0 || index >= mtlmaterials_.size()) {
return nullptr;
}
return mtlmaterials_[index].name.c_str();
}
/** \} */
} // namespace blender::io::obj

View File

@@ -1,135 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#pragma once
#include "DNA_meshdata_types.h"
#include "BLI_map.hh"
#include "BLI_vector.hh"
#include "IO_wavefront_obj.h"
#include "obj_export_io.hh"
#include "obj_export_mtl.hh"
namespace blender::io::obj {
class OBJCurve;
class OBJMesh;
/**
* Total vertices/ UV vertices/ normals of previous Objects
* should be added to the current Object's indices.
*/
struct IndexOffsets {
int vertex_offset;
int uv_vertex_offset;
int normal_offset;
};
/**
* Responsible for writing a .OBJ file.
*/
class OBJWriter : NonMovable, NonCopyable {
private:
const OBJExportParams &export_params_;
std::unique_ptr<FileHandler<eFileType::OBJ>> file_handler_ = nullptr;
IndexOffsets index_offsets_{0, 0, 0};
public:
OBJWriter(const char *filepath, const OBJExportParams &export_params) noexcept(false)
: export_params_(export_params)
{
file_handler_ = std::make_unique<FileHandler<eFileType::OBJ>>(filepath);
}
void write_header() const;
void write_object_name(const OBJMesh &obj_mesh_data) const;
void write_object_group(const OBJMesh &obj_mesh_data) const;
void write_mtllib_name(const StringRefNull mtl_filepath) const;
void write_vertex_coords(const OBJMesh &obj_mesh_data) const;
void write_uv_coords(OBJMesh &obj_mesh_data) const;
void write_poly_normals(const OBJMesh &obj_mesh_data) const;
int write_smooth_group(const OBJMesh &obj_mesh_data,
int poly_index,
const int last_poly_smooth_group) const;
int16_t write_poly_material(const OBJMesh &obj_mesh_data,
const int poly_index,
const int16_t last_poly_mat_nr,
std::function<const char *(int)> matname_fn) const;
int16_t write_vertex_group(const OBJMesh &obj_mesh_data,
const int poly_index,
const int16_t last_poly_vertex_group) const;
void write_poly_elements(const OBJMesh &obj_mesh_data,
std::function<const char *(int)> matname_fn);
void write_edges_indices(const OBJMesh &obj_mesh_data) const;
void write_nurbs_curve(const OBJCurve &obj_nurbs_data) const;
void update_index_offsets(const OBJMesh &obj_mesh_data);
private:
using func_vert_uv_normal_indices = void (OBJWriter::*)(Span<int> vert_indices,
Span<int> uv_indices,
Span<int> normal_indices) const;
func_vert_uv_normal_indices get_poly_element_writer(const int total_uv_vertices) const;
void write_vert_uv_normal_indices(Span<int> vert_indices,
Span<int> uv_indices,
Span<int> normal_indices) const;
void write_vert_normal_indices(Span<int> vert_indices,
Span<int> /*uv_indices*/,
Span<int> normal_indices) const;
void write_vert_uv_indices(Span<int> vert_indices,
Span<int> uv_indices,
Span<int> /*normal_indices*/) const;
void write_vert_indices(Span<int> vert_indices,
Span<int> /*uv_indices*/,
Span<int> /*normal_indices*/) const;
};
/**
* Responsible for writing a .MTL file.
*/
class MTLWriter : NonMovable, NonCopyable {
private:
std::unique_ptr<FileHandler<eFileType::MTL>> file_handler_ = nullptr;
std::string mtl_filepath_;
Vector<MTLMaterial> mtlmaterials_;
/* Map from a Material* to an index into mtlmaterials_. */
Map<const Material *, int> material_map_;
public:
MTLWriter(const char *obj_filepath) noexcept(false);
void write_header(const char *blen_filepath) const;
void write_materials();
StringRefNull mtl_file_path() const;
Vector<int> add_materials(const OBJMesh &mesh_to_export);
const char *mtlmaterial_name(int index);
private:
void write_bsdf_properties(const MTLMaterial &mtl_material);
void write_texture_map(const MTLMaterial &mtl_material,
const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map);
};
} // namespace blender::io::obj

View File

@@ -1,343 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#pragma once
#include <cstdio>
#include <string>
#include <system_error>
#include <type_traits>
#include "BLI_compiler_attrs.h"
#include "BLI_string_ref.hh"
#include "BLI_utility_mixins.hh"
namespace blender::io::obj {
enum class eFileType {
OBJ,
MTL,
};
enum class eOBJSyntaxElement {
vertex_coords,
uv_vertex_coords,
normal,
poly_element_begin,
vertex_uv_normal_indices,
vertex_normal_indices,
vertex_uv_indices,
vertex_indices,
poly_element_end,
poly_usemtl,
edge,
cstype,
nurbs_degree,
curve_element_begin,
curve_element_end,
nurbs_parameter_begin,
nurbs_parameters,
nurbs_parameter_end,
nurbs_group_end,
new_line,
mtllib,
smooth_group,
object_group,
object_name,
/* Use rarely. New line is NOT included for string. */
string,
};
enum class eMTLSyntaxElement {
newmtl,
Ni,
d,
Ns,
illum,
Ka,
Kd,
Ks,
Ke,
map_Kd,
map_Ks,
map_Ns,
map_d,
map_refl,
map_Ke,
map_Bump,
/* Use rarely. New line is NOT included for string. */
string,
};
template<eFileType filetype> struct FileTypeTraits;
template<> struct FileTypeTraits<eFileType::OBJ> {
using SyntaxType = eOBJSyntaxElement;
};
template<> struct FileTypeTraits<eFileType::MTL> {
using SyntaxType = eMTLSyntaxElement;
};
template<eFileType type> struct Formatting {
const char *fmt = nullptr;
const int total_args = 0;
/* Fail to compile by default. */
const bool is_type_valid = false;
};
/**
* Type dependent but always false. Use to add a conditional compile-time error.
*/
template<typename T> struct always_false : std::false_type {
};
template<typename... T>
constexpr bool is_type_float = (... && std::is_floating_point_v<std::decay_t<T>>);
template<typename... T>
constexpr bool is_type_integral = (... && std::is_integral_v<std::decay_t<T>>);
template<typename... T>
constexpr bool is_type_string_related = (... && std::is_constructible_v<std::string, T>);
template<eFileType filetype, typename... T>
constexpr std::enable_if_t<filetype == eFileType::OBJ, Formatting<filetype>>
syntax_elem_to_formatting(const eOBJSyntaxElement key)
{
switch (key) {
case eOBJSyntaxElement::vertex_coords: {
return {"v %f %f %f\n", 3, is_type_float<T...>};
}
case eOBJSyntaxElement::uv_vertex_coords: {
return {"vt %f %f\n", 2, is_type_float<T...>};
}
case eOBJSyntaxElement::normal: {
return {"vn %f %f %f\n", 3, is_type_float<T...>};
}
case eOBJSyntaxElement::poly_element_begin: {
return {"f", 0, is_type_string_related<T...>};
}
case eOBJSyntaxElement::vertex_uv_normal_indices: {
return {" %d/%d/%d", 3, is_type_integral<T...>};
}
case eOBJSyntaxElement::vertex_normal_indices: {
return {" %d//%d", 2, is_type_integral<T...>};
}
case eOBJSyntaxElement::vertex_uv_indices: {
return {" %d/%d", 2, is_type_integral<T...>};
}
case eOBJSyntaxElement::vertex_indices: {
return {" %d", 1, is_type_integral<T...>};
}
case eOBJSyntaxElement::poly_usemtl: {
return {"usemtl %s\n", 1, is_type_string_related<T...>};
}
case eOBJSyntaxElement::edge: {
return {"l %d %d\n", 2, is_type_integral<T...>};
}
case eOBJSyntaxElement::cstype: {
return {"cstype bspline\n", 0, is_type_string_related<T...>};
}
case eOBJSyntaxElement::nurbs_degree: {
return {"deg %d\n", 1, is_type_integral<T...>};
}
case eOBJSyntaxElement::curve_element_begin: {
return {"curv 0.0 1.0", 0, is_type_string_related<T...>};
}
case eOBJSyntaxElement::nurbs_parameter_begin: {
return {"parm 0.0", 0, is_type_string_related<T...>};
}
case eOBJSyntaxElement::nurbs_parameters: {
return {" %f", 1, is_type_float<T...>};
}
case eOBJSyntaxElement::nurbs_parameter_end: {
return {" 1.0\n", 0, is_type_string_related<T...>};
}
case eOBJSyntaxElement::nurbs_group_end: {
return {"end\n", 0, is_type_string_related<T...>};
}
case eOBJSyntaxElement::poly_element_end: {
ATTR_FALLTHROUGH;
}
case eOBJSyntaxElement::curve_element_end: {
ATTR_FALLTHROUGH;
}
case eOBJSyntaxElement::new_line: {
return {"\n", 0, is_type_string_related<T...>};
}
case eOBJSyntaxElement::mtllib: {
return {"mtllib %s\n", 1, is_type_string_related<T...>};
}
case eOBJSyntaxElement::smooth_group: {
return {"s %d\n", 1, is_type_integral<T...>};
}
case eOBJSyntaxElement::object_group: {
return {"g %s\n", 1, is_type_string_related<T...>};
}
case eOBJSyntaxElement::object_name: {
return {"o %s\n", 1, is_type_string_related<T...>};
}
case eOBJSyntaxElement::string: {
return {"%s", 1, is_type_string_related<T...>};
}
}
}
template<eFileType filetype, typename... T>
constexpr std::enable_if_t<filetype == eFileType::MTL, Formatting<filetype>>
syntax_elem_to_formatting(const eMTLSyntaxElement key)
{
switch (key) {
case eMTLSyntaxElement::newmtl: {
return {"newmtl %s\n", 1, is_type_string_related<T...>};
}
case eMTLSyntaxElement::Ni: {
return {"Ni %.6f\n", 1, is_type_float<T...>};
}
case eMTLSyntaxElement::d: {
return {"d %.6f\n", 1, is_type_float<T...>};
}
case eMTLSyntaxElement::Ns: {
return {"Ns %.6f\n", 1, is_type_float<T...>};
}
case eMTLSyntaxElement::illum: {
return {"illum %d\n", 1, is_type_integral<T...>};
}
case eMTLSyntaxElement::Ka: {
return {"Ka %.6f %.6f %.6f\n", 3, is_type_float<T...>};
}
case eMTLSyntaxElement::Kd: {
return {"Kd %.6f %.6f %.6f\n", 3, is_type_float<T...>};
}
case eMTLSyntaxElement::Ks: {
return {"Ks %.6f %.6f %.6f\n", 3, is_type_float<T...>};
}
case eMTLSyntaxElement::Ke: {
return {"Ke %.6f %.6f %.6f\n", 3, is_type_float<T...>};
}
/* Keep only one space between options since filepaths may have leading spaces too. */
case eMTLSyntaxElement::map_Kd: {
return {"map_Kd %s %s\n", 2, is_type_string_related<T...>};
}
case eMTLSyntaxElement::map_Ks: {
return {"map_Ks %s %s\n", 2, is_type_string_related<T...>};
}
case eMTLSyntaxElement::map_Ns: {
return {"map_Ns %s %s\n", 2, is_type_string_related<T...>};
}
case eMTLSyntaxElement::map_d: {
return {"map_d %s %s\n", 2, is_type_string_related<T...>};
}
case eMTLSyntaxElement::map_refl: {
return {"map_refl %s %s\n", 2, is_type_string_related<T...>};
}
case eMTLSyntaxElement::map_Ke: {
return {"map_Ke %s %s\n", 2, is_type_string_related<T...>};
}
case eMTLSyntaxElement::map_Bump: {
return {"map_Bump %s %s\n", 2, is_type_string_related<T...>};
}
case eMTLSyntaxElement::string: {
return {"%s", 1, is_type_string_related<T...>};
}
}
}
template<eFileType filetype> class FileHandler : NonCopyable, NonMovable {
private:
FILE *outfile_ = nullptr;
std::string outfile_path_;
public:
FileHandler(std::string outfile_path) noexcept(false) : outfile_path_(std::move(outfile_path))
{
outfile_ = std::fopen(outfile_path_.c_str(), "w");
if (!outfile_) {
throw std::system_error(errno, std::system_category(), "Cannot open file");
}
}
~FileHandler()
{
if (outfile_ && std::fclose(outfile_)) {
std::cerr << "Error: could not close the file '" << outfile_path_
<< "' properly, it may be corrupted." << std::endl;
}
}
template<typename FileTypeTraits<filetype>::SyntaxType key, typename... T>
constexpr void write(T &&...args) const
{
constexpr Formatting<filetype> fmt_nargs_valid = syntax_elem_to_formatting<filetype, T...>(
key);
write__impl<fmt_nargs_valid.total_args>(fmt_nargs_valid.fmt, std::forward<T>(args)...);
/* Types of all arguments and the number of arguments should match
* what the formatting specifies. */
return std::enable_if_t < fmt_nargs_valid.is_type_valid &&
(sizeof...(T) == fmt_nargs_valid.total_args),
void > ();
}
private:
/* Remove this after upgrading to C++20. */
template<typename T> using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
/**
* Make #std::string etc., usable for fprintf-family.
* \return: `const char *` or the original argument if the argument is
* not related to #std::string.
*/
template<typename T> constexpr auto string_to_primitive(T &&arg) const
{
if constexpr (std::is_same_v<remove_cvref_t<T>, std::string> ||
std::is_same_v<remove_cvref_t<T>, blender::StringRefNull>) {
return arg.c_str();
}
else if constexpr (std::is_same_v<remove_cvref_t<T>, blender::StringRef>) {
BLI_STATIC_ASSERT(
(always_false<T>::value),
"Null-terminated string not present. Please use blender::StringRefNull instead.");
/* Another trick to cause a compile-time error: returning nothing to #std::printf. */
return;
}
else {
return std::forward<T>(arg);
}
}
template<int total_args, typename... T>
constexpr std::enable_if_t<(total_args != 0), void> write__impl(const char *fmt,
T &&...args) const
{
std::fprintf(outfile_, fmt, string_to_primitive(std::forward<T>(args))...);
}
template<int total_args, typename... T>
constexpr std::enable_if_t<(total_args == 0), void> write__impl(const char *fmt,
T &&...args) const
{
std::fputs(fmt, outfile_);
}
};
} // namespace blender::io::obj

View File

@@ -1,490 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#include "BKE_customdata.h"
#include "BKE_lib_id.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_object.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "DEG_depsgraph_query.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "obj_export_mesh.hh"
namespace blender::io::obj {
/**
* Store evaluated Object and Mesh pointers. Conditionally triangulate a mesh, or
* create a new Mesh from a Curve.
*/
OBJMesh::OBJMesh(Depsgraph *depsgraph, const OBJExportParams &export_params, Object *mesh_object)
{
export_object_eval_ = DEG_get_evaluated_object(depsgraph, mesh_object);
export_mesh_eval_ = BKE_object_get_evaluated_mesh(export_object_eval_);
mesh_eval_needs_free_ = false;
if (!export_mesh_eval_) {
/* Curves and NURBS surfaces need a new mesh when they're
* exported in the form of vertices and edges.
*/
export_mesh_eval_ = BKE_mesh_new_from_object(depsgraph, export_object_eval_, true, true);
/* Since a new mesh been allocated, it needs to be freed in the destructor. */
mesh_eval_needs_free_ = true;
}
if (export_params.export_triangulated_mesh &&
ELEM(export_object_eval_->type, OB_MESH, OB_SURF)) {
std::tie(export_mesh_eval_, mesh_eval_needs_free_) = triangulate_mesh_eval();
}
set_world_axes_transform(export_params.forward_axis, export_params.up_axis);
}
/**
* Free new meshes allocated for triangulated meshes, or Curve converted to Mesh.
*/
OBJMesh::~OBJMesh()
{
free_mesh_if_needed();
if (poly_smooth_groups_) {
MEM_freeN(poly_smooth_groups_);
}
}
/**
* Free the mesh if _the exporter_ created it.
*/
void OBJMesh::free_mesh_if_needed()
{
if (mesh_eval_needs_free_ && export_mesh_eval_) {
BKE_id_free(nullptr, export_mesh_eval_);
}
}
/**
* Allocate a new Mesh with triangulated polygons.
*
* The returned mesh can be the same as the old one.
* \return Owning pointer to the new Mesh, and whether a new Mesh was created.
*/
std::pair<Mesh *, bool> OBJMesh::triangulate_mesh_eval()
{
if (export_mesh_eval_->totpoly <= 0) {
return {export_mesh_eval_, false};
}
const struct BMeshCreateParams bm_create_params = {0u};
const struct BMeshFromMeshParams bm_convert_params = {1u, 0, 0, 0};
/* Lower threshold where triangulation of a polygon starts, i.e. a quadrilateral will be
* triangulated here. */
const int triangulate_min_verts = 4;
unique_bmesh_ptr bmesh(
BKE_mesh_to_bmesh_ex(export_mesh_eval_, &bm_create_params, &bm_convert_params));
BM_mesh_triangulate(bmesh.get(),
MOD_TRIANGULATE_NGON_BEAUTY,
MOD_TRIANGULATE_QUAD_SHORTEDGE,
triangulate_min_verts,
false,
nullptr,
nullptr,
nullptr);
Mesh *triangulated = BKE_mesh_from_bmesh_for_eval_nomain(
bmesh.get(), nullptr, export_mesh_eval_);
free_mesh_if_needed();
return {triangulated, true};
}
/**
* Set the final transform after applying axes settings and an Object's world transform.
*/
void OBJMesh::set_world_axes_transform(const eTransformAxisForward forward,
const eTransformAxisUp up)
{
float axes_transform[3][3];
unit_m3(axes_transform);
/* +Y-forward and +Z-up are the default Blender axis settings. */
mat3_from_axis_conversion(OBJ_AXIS_Y_FORWARD, OBJ_AXIS_Z_UP, forward, up, axes_transform);
/* mat3_from_axis_conversion returns a transposed matrix! */
transpose_m3(axes_transform);
mul_m4_m3m4(world_and_axes_transform_, axes_transform, export_object_eval_->obmat);
/* mul_m4_m3m4 does not transform last row of obmat, i.e. location data. */
mul_v3_m3v3(world_and_axes_transform_[3], axes_transform, export_object_eval_->obmat[3]);
world_and_axes_transform_[3][3] = export_object_eval_->obmat[3][3];
}
int OBJMesh::tot_vertices() const
{
return export_mesh_eval_->totvert;
}
int OBJMesh::tot_polygons() const
{
return export_mesh_eval_->totpoly;
}
int OBJMesh::tot_uv_vertices() const
{
return tot_uv_vertices_;
}
int OBJMesh::tot_edges() const
{
return export_mesh_eval_->totedge;
}
/**
* \return Total materials in the object.
*/
int16_t OBJMesh::tot_materials() const
{
return export_mesh_eval_->totcol;
}
/**
* \return Smooth group of the polygon at the given index.
*/
int OBJMesh::ith_smooth_group(const int poly_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_[poly_index];
}
void OBJMesh::ensure_mesh_normals() const
{
BKE_mesh_ensure_normals(export_mesh_eval_);
BKE_mesh_calc_normals_split(export_mesh_eval_);
}
void OBJMesh::ensure_mesh_edges() const
{
BKE_mesh_calc_edges(export_mesh_eval_, true, false);
BKE_mesh_calc_edges_loose(export_mesh_eval_);
}
/**
* Calculate smooth groups of a smooth-shaded object.
* \return A polygon aligned array of smooth group numbers.
*/
void OBJMesh::calc_smooth_groups(const bool use_bitflags)
{
poly_smooth_groups_ = BKE_mesh_calc_smoothgroups(export_mesh_eval_->medge,
export_mesh_eval_->totedge,
export_mesh_eval_->mpoly,
export_mesh_eval_->totpoly,
export_mesh_eval_->mloop,
export_mesh_eval_->totloop,
&tot_smooth_groups_,
use_bitflags);
}
/**
* Return mat_nr-th material of the object. The given index should be zero-based.
*/
const Material *OBJMesh::get_object_material(const int16_t mat_nr) const
{
/* "+ 1" as material getter needs one-based indices. */
const Material *r_mat = BKE_object_material_get(export_object_eval_, mat_nr + 1);
#ifdef DEBUG
if (!r_mat) {
std::cerr << "Material not found for mat_nr = " << mat_nr << std::endl;
}
#endif
return r_mat;
}
bool OBJMesh::is_ith_poly_smooth(const int poly_index) const
{
return export_mesh_eval_->mpoly[poly_index].flag & ME_SMOOTH;
}
/**
* Returns a zero-based index of a polygon's material indexing into
* the Object's material slots.
*/
int16_t OBJMesh::ith_poly_matnr(const int poly_index) const
{
BLI_assert(poly_index < export_mesh_eval_->totpoly);
const int16_t r_mat_nr = export_mesh_eval_->mpoly[poly_index].mat_nr;
return r_mat_nr >= 0 ? r_mat_nr : NOT_FOUND;
}
/**
* Get object name as it appears in the outliner.
*/
const char *OBJMesh::get_object_name() const
{
return export_object_eval_->id.name + 2;
}
/**
* Get Object's Mesh's name.
*/
const char *OBJMesh::get_object_mesh_name() const
{
return export_mesh_eval_->id.name + 2;
}
/**
* Get object's material (at the given index) name. The given index should be zero-based.
*/
const char *OBJMesh::get_object_material_name(const int16_t mat_nr) const
{
const Material *mat = get_object_material(mat_nr);
if (!mat) {
return nullptr;
}
return mat->id.name + 2;
}
/**
* Calculate coordinates of the vertex at the given index.
*/
float3 OBJMesh::calc_vertex_coords(const int vert_index, const float scaling_factor) const
{
float3 r_coords;
copy_v3_v3(r_coords, export_mesh_eval_->mvert[vert_index].co);
mul_v3_fl(r_coords, scaling_factor);
mul_m4_v3(world_and_axes_transform_, r_coords);
return r_coords;
}
/**
* Calculate vertex indices of all vertices of the polygon at the given index.
*/
Vector<int> OBJMesh::calc_poly_vertex_indices(const int poly_index) const
{
const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
const MLoop *mloop = &export_mesh_eval_->mloop[mpoly.loopstart];
const int totloop = mpoly.totloop;
Vector<int> r_poly_vertex_indices(totloop);
for (int loop_index = 0; loop_index < totloop; loop_index++) {
r_poly_vertex_indices[loop_index] = mloop[loop_index].v;
}
return r_poly_vertex_indices;
}
/**
* Calculate UV vertex coordinates of an Object.
*
* \note Also store the UV vertex indices in the member variable.
*/
void OBJMesh::store_uv_coords_and_indices(Vector<std::array<float, 2>> &r_uv_coords)
{
const MPoly *mpoly = export_mesh_eval_->mpoly;
const MLoop *mloop = export_mesh_eval_->mloop;
const int totpoly = export_mesh_eval_->totpoly;
const int totvert = export_mesh_eval_->totvert;
const MLoopUV *mloopuv = static_cast<MLoopUV *>(
CustomData_get_layer(&export_mesh_eval_->ldata, CD_MLOOPUV));
if (!mloopuv) {
tot_uv_vertices_ = 0;
return;
}
const float limit[2] = {STD_UV_CONNECT_LIMIT, STD_UV_CONNECT_LIMIT};
UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create(
mpoly, mloop, mloopuv, totpoly, totvert, limit, false, false);
uv_indices_.resize(totpoly);
/* At least total vertices of a mesh will be present in its texture map. So
* reserve minimum space early. */
r_uv_coords.reserve(totvert);
tot_uv_vertices_ = 0;
for (int vertex_index = 0; vertex_index < totvert; vertex_index++) {
const UvMapVert *uv_vert = BKE_mesh_uv_vert_map_get_vert(uv_vert_map, vertex_index);
for (; uv_vert; uv_vert = uv_vert->next) {
if (uv_vert->separate) {
tot_uv_vertices_ += 1;
}
const int vertices_in_poly = mpoly[uv_vert->poly_index].totloop;
/* Store UV vertex coordinates. */
r_uv_coords.resize(tot_uv_vertices_);
const int loopstart = mpoly[uv_vert->poly_index].loopstart;
Span<float> vert_uv_coords(mloopuv[loopstart + uv_vert->loop_of_poly_index].uv, 2);
r_uv_coords[tot_uv_vertices_ - 1][0] = vert_uv_coords[0];
r_uv_coords[tot_uv_vertices_ - 1][1] = vert_uv_coords[1];
/* Store UV vertex indices. */
uv_indices_[uv_vert->poly_index].resize(vertices_in_poly);
/* Keep indices zero-based and let the writer handle the "+ 1" as per OBJ spec. */
uv_indices_[uv_vert->poly_index][uv_vert->loop_of_poly_index] = tot_uv_vertices_ - 1;
}
}
BKE_mesh_uv_vert_map_free(uv_vert_map);
}
Span<int> OBJMesh::calc_poly_uv_indices(const int poly_index) const
{
if (uv_indices_.size() <= 0) {
return {};
}
BLI_assert(poly_index < export_mesh_eval_->totpoly);
BLI_assert(poly_index < uv_indices_.size());
return uv_indices_[poly_index];
}
/**
* Calculate polygon normal of a polygon at given index.
*
* Should be used for flat-shaded polygons.
*/
float3 OBJMesh::calc_poly_normal(const int poly_index) const
{
float3 r_poly_normal;
const MPoly &poly = export_mesh_eval_->mpoly[poly_index];
const MLoop &mloop = export_mesh_eval_->mloop[poly.loopstart];
const MVert &mvert = *(export_mesh_eval_->mvert);
BKE_mesh_calc_poly_normal(&poly, &mloop, &mvert, r_poly_normal);
mul_mat3_m4_v3(world_and_axes_transform_, r_poly_normal);
return r_poly_normal;
}
/**
* Calculate loop normals of a polygon at the given index.
*
* Should be used for smooth-shaded polygons.
*/
void OBJMesh::calc_loop_normals(const int poly_index, Vector<float3> &r_loop_normals) const
{
r_loop_normals.clear();
const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
const float(
*lnors)[3] = (const float(*)[3])(CustomData_get_layer(&export_mesh_eval_->ldata, CD_NORMAL));
for (int loop_of_poly = 0; loop_of_poly < mpoly.totloop; loop_of_poly++) {
float3 loop_normal;
copy_v3_v3(loop_normal, lnors[mpoly.loopstart + loop_of_poly]);
mul_mat3_m4_v3(world_and_axes_transform_, loop_normal);
r_loop_normals.append(loop_normal);
}
}
/**
* Calculate a polygon's polygon/loop normal indices.
* \param object_tot_prev_normals Number of normals of this Object written so far.
* \return Number of distinct normal indices.
*/
std::pair<int, Vector<int>> OBJMesh::calc_poly_normal_indices(
const int poly_index, const int object_tot_prev_normals) const
{
const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
const int totloop = mpoly.totloop;
Vector<int> r_poly_normal_indices(totloop);
if (is_ith_poly_smooth(poly_index)) {
for (int poly_loop_index = 0; poly_loop_index < totloop; poly_loop_index++) {
/* Using polygon loop index is fine because polygon/loop normals and their normal indices are
* written by looping over #Mesh.mpoly /#Mesh.mloop in the same order. */
r_poly_normal_indices[poly_loop_index] = object_tot_prev_normals + poly_loop_index;
}
/* For a smooth-shaded polygon, #Mesh.totloop -many loop normals are written. */
return {totloop, r_poly_normal_indices};
}
for (int poly_loop_index = 0; poly_loop_index < totloop; poly_loop_index++) {
r_poly_normal_indices[poly_loop_index] = object_tot_prev_normals;
}
/* For a flat-shaded polygon, one polygon normal is written. */
return {1, r_poly_normal_indices};
}
/**
* Find the index of the vertex group with the maximum number of vertices in a polygon.
* The index indices into the #Object.defbase.
*
* If two or more groups have the same number of vertices (maximum), group name depends on the
* implementation of #std::max_element.
*/
int16_t OBJMesh::get_poly_deform_group_index(const int poly_index) const
{
BLI_assert(poly_index < export_mesh_eval_->totpoly);
const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
const MLoop *mloop = &export_mesh_eval_->mloop[mpoly.loopstart];
const int tot_deform_groups = BLI_listbase_count(&export_object_eval_->defbase);
/* Indices of the vector index into deform groups of an object; values are the]
* number of vertex members in one deform group. */
Vector<int16_t> deform_group_members(tot_deform_groups, 0);
/* Whether at least one vertex in the polygon belongs to any group. */
bool found_group = false;
const MDeformVert *dvert_orig = static_cast<MDeformVert *>(
CustomData_get_layer(&export_mesh_eval_->vdata, CD_MDEFORMVERT));
if (!dvert_orig) {
return NOT_FOUND;
}
const MDeformWeight *curr_weight = nullptr;
const MDeformVert *dvert = nullptr;
for (int loop_index = 0; loop_index < mpoly.totloop; loop_index++) {
dvert = &dvert_orig[(mloop + loop_index)->v];
curr_weight = dvert->dw;
if (curr_weight) {
bDeformGroup *vertex_group = static_cast<bDeformGroup *>(
BLI_findlink((&export_object_eval_->defbase), curr_weight->def_nr));
if (vertex_group) {
deform_group_members[curr_weight->def_nr] += 1;
found_group = true;
}
}
}
if (!found_group) {
return NOT_FOUND;
}
/* Index of the group with maximum vertices. */
int16_t max_idx = std::max_element(deform_group_members.begin(), deform_group_members.end()) -
deform_group_members.begin();
return max_idx;
}
/**
* Find the name of the vertex deform group at the given index.
* The index indices into the #Object.defbase.
*/
const char *OBJMesh::get_poly_deform_group_name(const int16_t def_group_index) const
{
const bDeformGroup &vertex_group = *(
static_cast<bDeformGroup *>(BLI_findlink(&export_object_eval_->defbase, def_group_index)));
return vertex_group.name;
}
/**
* Calculate vertex indices of an edge's corners if it is a loose edge.
*/
std::optional<std::array<int, 2>> OBJMesh::calc_loose_edge_vert_indices(const int edge_index) const
{
const MEdge &edge = export_mesh_eval_->medge[edge_index];
if (edge.flag & ME_LOOSEEDGE) {
return std::array<int, 2>{static_cast<int>(edge.v1), static_cast<int>(edge.v2)};
}
return std::nullopt;
}
} // namespace blender::io::obj

View File

@@ -1,134 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#pragma once
#include <optional>
#include "BLI_float3.hh"
#include "BLI_utility_mixins.hh"
#include "BLI_vector.hh"
#include "bmesh.h"
#include "bmesh_tools.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "IO_wavefront_obj.h"
namespace blender::io::obj {
/* Denote absence for usually non-negative numbers. */
const int NOT_FOUND = -1;
/* Any negative number other than `NOT_FOUND` to initialise usually non-negative numbers. */
const int NEGATIVE_INIT = -10;
/**
* #std::unique_ptr deleter for BMesh.
*/
struct CustomBMeshDeleter {
void operator()(BMesh *bmesh)
{
if (bmesh) {
BM_mesh_free(bmesh);
}
}
};
using unique_bmesh_ptr = std::unique_ptr<BMesh, CustomBMeshDeleter>;
class OBJMesh : NonCopyable {
private:
Object *export_object_eval_;
Mesh *export_mesh_eval_;
/**
* For curves which are converted to mesh, and triangulated meshes, a new mesh is allocated.
*/
bool mesh_eval_needs_free_ = false;
/**
* Final transform of an object obtained from export settings (up_axis, forward_axis) and the
* object's world transform matrix.
*/
float world_and_axes_transform_[4][4];
/**
* Total UV vertices in a mesh's texture map.
*/
int tot_uv_vertices_ = 0;
/**
* Per-polygon-per-vertex UV vertex indices.
*/
Vector<Vector<int>> uv_indices_;
/**
* Total smooth groups in an object.
*/
int tot_smooth_groups_ = NEGATIVE_INIT;
/**
* Polygon aligned array of their smooth groups.
*/
int *poly_smooth_groups_ = nullptr;
public:
OBJMesh(Depsgraph *depsgraph, const OBJExportParams &export_params, Object *mesh_object);
~OBJMesh();
int tot_vertices() const;
int tot_polygons() const;
int tot_uv_vertices() const;
int tot_edges() const;
int16_t tot_materials() const;
const Material *get_object_material(const int16_t mat_nr) const;
int16_t ith_poly_matnr(const int poly_index) const;
void ensure_mesh_normals() const;
void ensure_mesh_edges() const;
void calc_smooth_groups(const bool use_bitflags);
int ith_smooth_group(const int poly_index) const;
bool is_ith_poly_smooth(const int poly_index) const;
const char *get_object_name() const;
const char *get_object_mesh_name() const;
const char *get_object_material_name(const int16_t mat_nr) const;
float3 calc_vertex_coords(const int vert_index, const float scaling_factor) const;
Vector<int> calc_poly_vertex_indices(const int poly_index) const;
void store_uv_coords_and_indices(Vector<std::array<float, 2>> &r_uv_coords);
Span<int> calc_poly_uv_indices(const int poly_index) const;
float3 calc_poly_normal(const int poly_index) const;
std::pair<int, Vector<int>> calc_poly_normal_indices(const int poly_index,
const int object_tot_prev_normals) const;
void calc_loop_normals(const int poly_index, Vector<float3> &r_loop_normals) const;
int16_t get_poly_deform_group_index(const int poly_index) const;
const char *get_poly_deform_group_name(const int16_t def_group_index) const;
std::optional<std::array<int, 2>> calc_loose_edge_vert_indices(const int edge_index) const;
private:
void free_mesh_if_needed();
std::pair<Mesh *, bool> triangulate_mesh_eval();
void set_world_axes_transform(const eTransformAxisForward forward, const eTransformAxisUp up);
};
} // namespace blender::io::obj

View File

@@ -1,365 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#include "BKE_image.h"
#include "BKE_node.h"
#include "BLI_float3.hh"
#include "BLI_map.hh"
#include "BLI_path_util.h"
#include "DNA_material_types.h"
#include "DNA_node_types.h"
#include "NOD_node_tree_ref.hh"
#include "obj_export_mesh.hh"
#include "obj_export_mtl.hh"
namespace blender::io::obj {
/**
* Copy a float property of the given type from the bNode to given buffer.
*/
static void copy_property_from_node(const eNodeSocketDatatype property_type,
const bNode *node,
const char *identifier,
MutableSpan<float> r_property)
{
if (!node) {
return;
}
bNodeSocket *socket{nodeFindSocket(node, SOCK_IN, identifier)};
BLI_assert(socket && socket->type == property_type);
if (!socket) {
return;
}
switch (property_type) {
case SOCK_FLOAT: {
BLI_assert(r_property.size() == 1);
bNodeSocketValueFloat *socket_def_value = static_cast<bNodeSocketValueFloat *>(
socket->default_value);
r_property[0] = socket_def_value->value;
break;
}
case SOCK_RGBA: {
BLI_assert(r_property.size() == 3);
bNodeSocketValueRGBA *socket_def_value = static_cast<bNodeSocketValueRGBA *>(
socket->default_value);
copy_v3_v3(r_property.data(), socket_def_value->value);
break;
}
case SOCK_VECTOR: {
BLI_assert(r_property.size() == 3);
bNodeSocketValueVector *socket_def_value = static_cast<bNodeSocketValueVector *>(
socket->default_value);
copy_v3_v3(r_property.data(), socket_def_value->value);
break;
}
default: {
/* Other socket types are not handled here. */
BLI_assert(0);
break;
}
}
}
/**
* Collect all the source sockets linked to the destination socket in a destination node.
*/
static void linked_sockets_to_dest_id(const bNode *dest_node,
const nodes::NodeTreeRef &node_tree,
StringRefNull dest_socket_id,
Vector<const nodes::OutputSocketRef *> &r_linked_sockets)
{
r_linked_sockets.clear();
if (!dest_node) {
return;
}
Span<const nodes::NodeRef *> object_dest_nodes = node_tree.nodes_by_type(dest_node->idname);
Span<const nodes::InputSocketRef *> dest_inputs = object_dest_nodes.first()->inputs();
const nodes::InputSocketRef *dest_socket = nullptr;
for (const nodes::InputSocketRef *curr_socket : dest_inputs) {
if (STREQ(curr_socket->bsocket()->identifier, dest_socket_id.c_str())) {
dest_socket = curr_socket;
break;
}
}
if (dest_socket) {
Span<const nodes::OutputSocketRef *> linked_sockets = dest_socket->directly_linked_sockets();
r_linked_sockets.resize(linked_sockets.size());
r_linked_sockets = linked_sockets;
}
}
/**
* From a list of sockets, get the parent node which is of the given node type.
*/
static const bNode *get_node_of_type(Span<const nodes::OutputSocketRef *> sockets_list,
const int node_type)
{
for (const nodes::SocketRef *socket : sockets_list) {
const bNode *parent_node = socket->bnode();
if (parent_node->typeinfo->type == node_type) {
return parent_node;
}
}
return nullptr;
}
/**
* From a texture image shader node, get the image's filepath.
* Returned filepath is stripped of initial "//". If packed image is found,
* only the file "name" is returned.
*/
static const char *get_image_filepath(const bNode *tex_node)
{
if (!tex_node) {
return nullptr;
}
Image *tex_image = reinterpret_cast<Image *>(tex_node->id);
if (!tex_image || !BKE_image_has_filepath(tex_image)) {
return nullptr;
}
const char *path = tex_image->filepath;
if (BKE_image_has_packedfile(tex_image)) {
/* Put image in the same directory as the .MTL file. */
path = BLI_path_slash_rfind(path) + 1;
fprintf(stderr,
"Packed image found:'%s'. Unpack and place the image in the same "
"directory as the .MTL file.\n",
path);
}
if (path[0] == '/' && path[1] == '/') {
path += 2;
}
return path;
}
/**
* Find the Principled-BSDF Node in nodetree.
* We only want one that feeds directly into a Material Output node
* (that is the behavior of the legacy Python exporter).
*/
static const nodes::NodeRef *find_bsdf_node(const nodes::NodeTreeRef *nodetree)
{
if (!nodetree) {
return nullptr;
}
for (const nodes::NodeRef *node : nodetree->nodes_by_type("ShaderNodeOutputMaterial")) {
const nodes::InputSocketRef *node_input_socket0 = node->inputs()[0];
for (const nodes::OutputSocketRef *out_sock : node_input_socket0->directly_linked_sockets()) {
const nodes::NodeRef &in_node = out_sock->node();
if (in_node.typeinfo()->type == SH_NODE_BSDF_PRINCIPLED) {
return &in_node;
}
}
}
return nullptr;
}
/**
* Store properties found either in bNode or material into r_mtl_mat.
*/
static void store_bsdf_properties(const nodes::NodeRef *bsdf_node,
const Material *material,
MTLMaterial &r_mtl_mat)
{
const bNode *bnode = nullptr;
if (bsdf_node) {
bnode = bsdf_node->bnode();
}
/* If p-BSDF is not present, fallback to #Object.Material. */
float roughness = material->roughness;
if (bnode) {
copy_property_from_node(SOCK_FLOAT, bnode, "Roughness", {&roughness, 1});
}
/* Emperical approximation. Importer should use the inverse of this method. */
float spec_exponent = (1.0f - roughness) * 30;
spec_exponent *= spec_exponent;
float specular = material->spec;
if (bnode) {
copy_property_from_node(SOCK_FLOAT, bnode, "Specular", {&specular, 1});
}
float metallic = material->metallic;
if (bnode) {
copy_property_from_node(SOCK_FLOAT, bnode, "Metallic", {&metallic, 1});
}
float refraction_index = 1.0f;
if (bnode) {
copy_property_from_node(SOCK_FLOAT, bnode, "IOR", {&refraction_index, 1});
}
float dissolved = material->a;
if (bnode) {
copy_property_from_node(SOCK_FLOAT, bnode, "Alpha", {&dissolved, 1});
}
const bool transparent = dissolved != 1.0f;
float3 diffuse_col = {material->r, material->g, material->b};
if (bnode) {
copy_property_from_node(SOCK_RGBA, bnode, "Base Color", {diffuse_col, 3});
}
float3 emission_col{0.0f};
float emission_strength = 0.0f;
if (bnode) {
copy_property_from_node(SOCK_FLOAT, bnode, "Emission Strength", {&emission_strength, 1});
copy_property_from_node(SOCK_RGBA, bnode, "Emission", {emission_col, 3});
}
mul_v3_fl(emission_col, emission_strength);
/* See https://wikipedia.org/wiki/Wavefront_.obj_file for all possible values of illum. */
/* Highlight on. */
int illum = 2;
if (specular == 0.0f) {
/* Color on and Ambient on. */
illum = 1;
}
else if (metallic > 0.0f) {
/* Metallic ~= Reflection. */
if (transparent) {
/* Transparency: Refraction on, Reflection: ~~Fresnel off and Ray trace~~ on. */
illum = 6;
}
else {
/* Reflection on and Ray trace on. */
illum = 3;
}
}
else if (transparent) {
/* Transparency: Glass on, Reflection: Ray trace off */
illum = 9;
}
r_mtl_mat.Ns = spec_exponent;
if (metallic != 0.0f) {
r_mtl_mat.Ka = {metallic, metallic, metallic};
}
else {
r_mtl_mat.Ka = {1.0f, 1.0f, 1.0f};
}
r_mtl_mat.Kd = diffuse_col;
r_mtl_mat.Ks = {specular, specular, specular};
r_mtl_mat.Ke = emission_col;
r_mtl_mat.Ni = refraction_index;
r_mtl_mat.d = dissolved;
r_mtl_mat.illum = illum;
}
/**
* Store image texture options and filepaths in r_mtl_mat.
*/
static void store_image_textures(const nodes::NodeRef *bsdf_node,
const nodes::NodeTreeRef *node_tree,
const Material *material,
MTLMaterial &r_mtl_mat)
{
if (!material || !node_tree || !bsdf_node) {
/* No nodetree, no images, or no Principled BSDF node. */
return;
}
const bNode *bnode = bsdf_node->bnode();
/* Normal Map Texture has two extra tasks of:
* - finding a Normal Map node before finding a texture node.
* - finding "Strength" property of the node for `-bm` option.
*/
for (Map<const eMTLSyntaxElement, tex_map_XX>::MutableItem texture_map :
r_mtl_mat.texture_maps.items()) {
Vector<const nodes::OutputSocketRef *> linked_sockets;
const bNode *normal_map_node{nullptr};
if (texture_map.key == eMTLSyntaxElement::map_Bump) {
/* Find sockets linked to destination "Normal" socket in p-bsdf node. */
linked_sockets_to_dest_id(bnode, *node_tree, "Normal", linked_sockets);
/* Among the linked sockets, find Normal Map shader node. */
normal_map_node = get_node_of_type(linked_sockets, SH_NODE_NORMAL_MAP);
/* Find sockets linked to "Color" socket in normal map node. */
linked_sockets_to_dest_id(normal_map_node, *node_tree, "Color", linked_sockets);
}
else if (texture_map.key == eMTLSyntaxElement::map_Ke) {
float emission_strength = 0.0f;
copy_property_from_node(SOCK_FLOAT, bnode, "Emission Strength", {&emission_strength, 1});
if (emission_strength == 0.0f) {
continue;
}
}
else {
/* Find sockets linked to the destination socket of interest, in p-bsdf node. */
linked_sockets_to_dest_id(
bnode, *node_tree, texture_map.value.dest_socket_id, linked_sockets);
}
/* Among the linked sockets, find Image Texture shader node. */
const bNode *tex_node{get_node_of_type(linked_sockets, SH_NODE_TEX_IMAGE)};
if (!tex_node) {
continue;
}
const char *tex_image_filepath = get_image_filepath(tex_node);
if (!tex_image_filepath) {
continue;
}
/* Find "Mapping" node if connected to texture node. */
linked_sockets_to_dest_id(tex_node, *node_tree, "Vector", linked_sockets);
const bNode *mapping = get_node_of_type(linked_sockets, SH_NODE_MAPPING);
if (normal_map_node) {
copy_property_from_node(
SOCK_FLOAT, normal_map_node, "Strength", {&r_mtl_mat.map_Bump_strength, 1});
}
/* Texture transform options. Only translation (origin offset, "-o") and scale
* ("-o") are supported. */
copy_property_from_node(SOCK_VECTOR, mapping, "Location", {texture_map.value.translation, 3});
copy_property_from_node(SOCK_VECTOR, mapping, "Scale", {texture_map.value.scale, 3});
texture_map.value.image_path = tex_image_filepath;
}
}
MTLMaterial mtlmaterial_for_material(const Material *material)
{
BLI_assert(material != nullptr);
MTLMaterial mtlmat;
mtlmat.name = std::string(material->id.name + 2);
std::replace(mtlmat.name.begin(), mtlmat.name.end(), ' ', '_');
const nodes::NodeTreeRef *nodetree = nullptr;
if (material->nodetree) {
nodetree = new nodes::NodeTreeRef(material->nodetree);
}
const nodes::NodeRef *bsdf_node = find_bsdf_node(nodetree);
store_bsdf_properties(bsdf_node, material, mtlmat);
store_image_textures(bsdf_node, nodetree, material, mtlmat);
if (nodetree) {
delete nodetree;
}
return mtlmat;
}
} // namespace blender::io::obj

View File

@@ -1,107 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#pragma once
#include "BLI_float3.hh"
#include "BLI_map.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "DNA_node_types.h"
#include "obj_export_io.hh"
namespace blender {
template<> struct DefaultHash<io::obj::eMTLSyntaxElement> {
uint64_t operator()(const io::obj::eMTLSyntaxElement value) const
{
return static_cast<uint64_t>(value);
}
};
} // namespace blender
namespace blender::io::obj {
class OBJMesh;
/**
* Generic container for texture node properties.
*/
struct tex_map_XX {
tex_map_XX(StringRef to_socket_id) : dest_socket_id(to_socket_id){};
/** Target socket which this texture node connects to. */
const std::string dest_socket_id;
float3 translation{0.0f};
float3 scale{1.0f};
/* Only Flat and Smooth projections are supported. */
int projection_type = SHD_PROJ_FLAT;
std::string image_path;
std::string mtl_dir_path;
};
/**
* Container suited for storing Material data for/from a .MTL file.
*/
struct MTLMaterial {
MTLMaterial()
{
texture_maps.add(eMTLSyntaxElement::map_Kd, tex_map_XX("Base Color"));
texture_maps.add(eMTLSyntaxElement::map_Ks, tex_map_XX("Specular"));
texture_maps.add(eMTLSyntaxElement::map_Ns, tex_map_XX("Roughness"));
texture_maps.add(eMTLSyntaxElement::map_d, tex_map_XX("Alpha"));
texture_maps.add(eMTLSyntaxElement::map_refl, tex_map_XX("Metallic"));
texture_maps.add(eMTLSyntaxElement::map_Ke, tex_map_XX("Emission"));
texture_maps.add(eMTLSyntaxElement::map_Bump, tex_map_XX("Normal"));
}
/**
* Caller must ensure that the given lookup key exists in the Map.
* \return Texture map corresponding to the given ID.
*/
tex_map_XX &tex_map_of_type(const eMTLSyntaxElement key)
{
{
BLI_assert(texture_maps.contains_as(key));
return texture_maps.lookup_as(key);
}
}
std::string name;
/* Always check for negative values while importing or exporting. Use defaults if
* any value is negative. */
float Ns{-1.0f};
float3 Ka{-1.0f};
float3 Kd{-1.0f};
float3 Ks{-1.0f};
float3 Ke{-1.0f};
float Ni{-1.0f};
float d{-1.0f};
int illum{-1};
Map<const eMTLSyntaxElement, tex_map_XX> texture_maps;
/** Only used for Normal Map node: "map_Bump". */
float map_Bump_strength{-1.0f};
};
MTLMaterial mtlmaterial_for_material(const Material *material);
} // namespace blender::io::obj

View File

@@ -1,125 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#include "BLI_float3.hh"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "IO_wavefront_obj.h"
#include "obj_export_nurbs.hh"
namespace blender::io::obj {
OBJCurve::OBJCurve(const Depsgraph *depsgraph,
const OBJExportParams &export_params,
Object *curve_object)
: export_object_eval_(curve_object)
{
export_object_eval_ = DEG_get_evaluated_object(depsgraph, curve_object);
export_curve_ = static_cast<Curve *>(export_object_eval_->data);
set_world_axes_transform(export_params.forward_axis, export_params.up_axis);
}
/**
* Set the final transform after applying axes settings and an Object's world transform.
*/
void OBJCurve::set_world_axes_transform(const eTransformAxisForward forward,
const eTransformAxisUp up)
{
float axes_transform[3][3];
unit_m3(axes_transform);
/* +Y-forward and +Z-up are the Blender's default axis settings. */
mat3_from_axis_conversion(OBJ_AXIS_Y_FORWARD, OBJ_AXIS_Z_UP, forward, up, axes_transform);
/* mat3_from_axis_conversion returns a transposed matrix! */
transpose_m3(axes_transform);
mul_m4_m3m4(world_axes_transform_, axes_transform, export_object_eval_->obmat);
/* #mul_m4_m3m4 does not transform last row of #Object.obmat, i.e. location data. */
mul_v3_m3v3(world_axes_transform_[3], axes_transform, export_object_eval_->obmat[3]);
world_axes_transform_[3][3] = export_object_eval_->obmat[3][3];
}
const char *OBJCurve::get_curve_name() const
{
return export_object_eval_->id.name + 2;
}
int OBJCurve::total_splines() const
{
return BLI_listbase_count(&export_curve_->nurb);
}
/**
* \param spline_index: Zero-based index of spline of interest.
* \return: Total vertices in a spline.
*/
int OBJCurve::total_spline_vertices(const int spline_index) const
{
const Nurb *const nurb = static_cast<Nurb *>(BLI_findlink(&export_curve_->nurb, spline_index));
return nurb->pntsu * nurb->pntsv;
}
/**
* Get coordinates of the vertex at the given index on the given spline.
*/
float3 OBJCurve::vertex_coordinates(const int spline_index,
const int vertex_index,
const float scaling_factor) const
{
const Nurb *const nurb = static_cast<Nurb *>(BLI_findlink(&export_curve_->nurb, spline_index));
float3 r_coord;
const BPoint &bpoint = nurb->bp[vertex_index];
copy_v3_v3(r_coord, bpoint.vec);
mul_m4_v3(world_axes_transform_, r_coord);
mul_v3_fl(r_coord, scaling_factor);
return r_coord;
}
/**
* Get total control points of the NURBS spline at the given index. This is different than total
* vertices of a spline.
*/
int OBJCurve::total_spline_control_points(const int spline_index) const
{
const Nurb *const nurb = static_cast<Nurb *>(BLI_findlink(&export_curve_->nurb, spline_index));
const int r_nurbs_degree = nurb->orderu - 1;
/* Total control points = Number of points in the curve (+ degree of the
* curve if it is cyclic). */
int r_tot_control_points = nurb->pntsv * nurb->pntsu;
if (nurb->flagu & CU_NURB_CYCLIC) {
r_tot_control_points += r_nurbs_degree;
}
return r_tot_control_points;
}
/**
* Get the degree of the NURBS spline at the given index.
*/
int OBJCurve::get_nurbs_degree(const int spline_index) const
{
const Nurb *const nurb = static_cast<Nurb *>(BLI_findlink(&export_curve_->nurb, spline_index));
return nurb->orderu - 1;
}
} // namespace blender::io::obj

View File

@@ -1,60 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#pragma once
#include "BLI_utility_mixins.hh"
#include "DNA_curve_types.h"
namespace blender::io::obj {
/**
* Provides access to the a Curve Object's properties.
* Only #CU_NURBS type is supported.
*
* \note Used for Curves to be exported in parameter form, and not converted to meshes.
*/
class OBJCurve : NonCopyable {
private:
const Object *export_object_eval_;
const Curve *export_curve_;
float world_axes_transform_[4][4];
public:
OBJCurve(const Depsgraph *depsgraph, const OBJExportParams &export_params, Object *curve_object);
const char *get_curve_name() const;
int total_splines() const;
int total_spline_vertices(const int spline_index) const;
float3 vertex_coordinates(const int spline_index,
const int vertex_index,
const float scaling_factor) const;
int total_spline_control_points(const int spline_index) const;
int get_nurbs_degree(const int spline_index) const;
private:
void set_world_axes_transform(const eTransformAxisForward forward, const eTransformAxisUp up);
};
} // namespace blender::io::obj

View File

@@ -1,310 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#include <cstdio>
#include <exception>
#include <memory>
#include "BKE_scene.h"
#include "BLI_path_util.h"
#include "BLI_vector.hh"
#include "DEG_depsgraph_query.h"
#include "DNA_scene_types.h"
#include "ED_object.h"
#include "obj_export_mesh.hh"
#include "obj_export_nurbs.hh"
#include "obj_exporter.hh"
#include "obj_export_file_writer.hh"
namespace blender::io::obj {
OBJDepsgraph::OBJDepsgraph(const bContext *C, const eEvaluationMode eval_mode)
{
Scene *scene = CTX_data_scene(C);
Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
if (eval_mode == DAG_EVAL_RENDER) {
depsgraph_ = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER);
needs_free_ = true;
DEG_graph_build_for_all_objects(depsgraph_);
BKE_scene_graph_evaluated_ensure(depsgraph_, bmain);
}
else {
depsgraph_ = CTX_data_ensure_evaluated_depsgraph(C);
needs_free_ = false;
}
}
OBJDepsgraph::~OBJDepsgraph()
{
if (needs_free_) {
DEG_graph_free(depsgraph_);
}
}
Depsgraph *OBJDepsgraph::get()
{
return depsgraph_;
}
void OBJDepsgraph::update_for_newframe()
{
BKE_scene_graph_update_for_newframe(depsgraph_);
}
static void print_exception_error(const std::system_error &ex)
{
std::cerr << ex.code().category().name() << ": " << ex.what() << ": " << ex.code().message()
<< std::endl;
}
/**
* Filter supported objects from the Scene.
*
* \note Curves are also stored with Meshes if export settings specify so.
*/
std::pair<Vector<std::unique_ptr<OBJMesh>>, Vector<std::unique_ptr<OBJCurve>>>
filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_params)
{
Vector<std::unique_ptr<OBJMesh>> r_exportable_meshes;
Vector<std::unique_ptr<OBJCurve>> r_exportable_nurbs;
const ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
LISTBASE_FOREACH (const Base *, base, &view_layer->object_bases) {
Object *object_in_layer = base->object;
if (export_params.export_selected_objects && !(object_in_layer->base_flag & BASE_SELECTED)) {
continue;
}
switch (object_in_layer->type) {
case OB_SURF:
/* Export in mesh form: vertices and polygons. */
ATTR_FALLTHROUGH;
case OB_MESH: {
r_exportable_meshes.append(
std::make_unique<OBJMesh>(depsgraph, export_params, object_in_layer));
break;
}
case OB_CURVE: {
Curve *curve = static_cast<Curve *>(object_in_layer->data);
Nurb *nurb{static_cast<Nurb *>(curve->nurb.first)};
if (!nurb) {
/* An empty curve. Not yet supported to export these as meshes. */
if (export_params.export_curves_as_nurbs) {
r_exportable_nurbs.append(
std::make_unique<OBJCurve>(depsgraph, export_params, object_in_layer));
}
break;
}
switch (nurb->type) {
case CU_NURBS: {
if (export_params.export_curves_as_nurbs) {
/* Export in parameter form: control points. */
r_exportable_nurbs.append(
std::make_unique<OBJCurve>(depsgraph, export_params, object_in_layer));
}
else {
/* Export in mesh form: edges and vertices. */
r_exportable_meshes.append(
std::make_unique<OBJMesh>(depsgraph, export_params, object_in_layer));
}
break;
}
case CU_BEZIER: {
/* Always export in mesh form: edges and vertices. */
r_exportable_meshes.append(
std::make_unique<OBJMesh>(depsgraph, export_params, object_in_layer));
break;
}
default: {
/* Other curve types are not supported. */
break;
}
}
break;
}
default: {
/* Other object types are not supported. */
break;
}
}
}
return {std::move(r_exportable_meshes), std::move(r_exportable_nurbs)};
}
static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_mesh,
OBJWriter &obj_writer,
MTLWriter *mtl_writer,
const OBJExportParams &export_params)
{
if (mtl_writer) {
obj_writer.write_mtllib_name(mtl_writer->mtl_file_path());
}
/* Smooth groups and UV vertex indices may make huge memory allocations, so they should be freed
* right after they're written, instead of waiting for #blender::Vector to clean them up after
* all the objects are exported. */
for (StealUniquePtr<OBJMesh> obj_mesh : exportable_as_mesh) {
obj_writer.write_object_name(*obj_mesh);
obj_writer.write_vertex_coords(*obj_mesh);
Vector<int> obj_mtlindices;
if (obj_mesh->tot_polygons() > 0) {
if (export_params.export_smooth_groups) {
obj_mesh->calc_smooth_groups(export_params.smooth_groups_bitflags);
}
if (export_params.export_normals) {
obj_writer.write_poly_normals(*obj_mesh);
}
if (export_params.export_uv) {
obj_writer.write_uv_coords(*obj_mesh);
}
if (mtl_writer) {
obj_mtlindices = mtl_writer->add_materials(*obj_mesh);
}
/* This function takes a 0-indexed slot index for the obj_mesh object and
* returns the material name that we are using in the .obj file for it. */
std::function<const char *(int)> matname_fn = [&](int s) -> const char * {
if (!mtl_writer || s < 0 || s >= obj_mtlindices.size()) {
return nullptr;
}
return mtl_writer->mtlmaterial_name(obj_mtlindices[s]);
};
obj_writer.write_poly_elements(*obj_mesh, matname_fn);
}
obj_writer.write_edges_indices(*obj_mesh);
obj_writer.update_index_offsets(*obj_mesh);
}
}
/**
* Export NURBS Curves in parameter form, not as vertices and edges.
*/
static void write_nurbs_curve_objects(const Vector<std::unique_ptr<OBJCurve>> &exportable_as_nurbs,
const OBJWriter &obj_writer)
{
/* #OBJCurve doesn't have any dynamically allocated memory, so it's fine
* to wait for #blender::Vector to clean the objects up. */
for (const std::unique_ptr<OBJCurve> &obj_curve : exportable_as_nurbs) {
obj_writer.write_nurbs_curve(*obj_curve);
}
}
/**
* Export a single frame to a .OBJ file.
*
* Conditionally write a .MTL file also.
*/
void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, const char *filepath)
{
std::unique_ptr<OBJWriter> frame_writer = nullptr;
try {
frame_writer = std::make_unique<OBJWriter>(filepath, export_params);
}
catch (const std::system_error &ex) {
print_exception_error(ex);
return;
}
if (!frame_writer) {
BLI_assert(!"File should be writable by now.");
return;
}
std::unique_ptr<MTLWriter> mtl_writer = nullptr;
if (export_params.export_materials) {
try {
mtl_writer = std::make_unique<MTLWriter>(export_params.filepath);
}
catch (const std::system_error &ex) {
print_exception_error(ex);
}
}
frame_writer->write_header();
auto [exportable_as_mesh, exportable_as_nurbs] = filter_supported_objects(depsgraph,
export_params);
write_mesh_objects(
std::move(exportable_as_mesh), *frame_writer, mtl_writer.get(), export_params);
if (mtl_writer) {
mtl_writer->write_header(export_params.blen_filepath);
mtl_writer->write_materials();
}
write_nurbs_curve_objects(std::move(exportable_as_nurbs), *frame_writer);
}
/**
* Append the current frame number in the .OBJ file name.
*
* \return Whether the filepath is in #FILE_MAX limits.
*/
bool append_frame_to_filename(const char *filepath, const int frame, char *r_filepath_with_frames)
{
BLI_strncpy(r_filepath_with_frames, filepath, FILE_MAX);
BLI_path_extension_replace(r_filepath_with_frames, FILE_MAX, "");
const int digits = frame == 0 ? 1 : integer_digits_i(abs(frame));
BLI_path_frame(r_filepath_with_frames, frame, digits);
return BLI_path_extension_replace(r_filepath_with_frames, FILE_MAX, ".obj");
}
/**
* Central internal function to call Scene update & writer functions.
*/
void exporter_main(bContext *C, const OBJExportParams &export_params)
{
ED_object_mode_set(C, OB_MODE_OBJECT);
OBJDepsgraph obj_depsgraph(C, export_params.export_eval_mode);
Scene *scene = DEG_get_input_scene(obj_depsgraph.get());
const char *filepath = export_params.filepath;
/* Single frame export, i.e. no animation. */
if (!export_params.export_animation) {
fprintf(stderr, "Writing to %s\n", filepath);
export_frame(obj_depsgraph.get(), export_params, filepath);
return;
}
char filepath_with_frames[FILE_MAX];
/* Used to reset the Scene to its original state. */
const int original_frame = CFRA;
for (int frame = export_params.start_frame; frame <= export_params.end_frame; frame++) {
const bool filepath_ok = append_frame_to_filename(filepath, frame, filepath_with_frames);
if (!filepath_ok) {
fprintf(stderr, "Error: File Path too long.\n%s\n", filepath_with_frames);
return;
}
CFRA = frame;
obj_depsgraph.update_for_newframe();
fprintf(stderr, "Writing to %s\n", filepath_with_frames);
export_frame(obj_depsgraph.get(), export_params, filepath_with_frames);
}
CFRA = original_frame;
}
} // namespace blender::io::obj

View File

@@ -1,82 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#pragma once
#include "BLI_utility_mixins.hh"
#include "BLI_vector.hh"
#include "IO_wavefront_obj.h"
namespace blender::io::obj {
/**
* Steal elements' ownership in a range-based for-loop.
*/
template<typename T> struct StealUniquePtr {
std::unique_ptr<T> owning;
StealUniquePtr(std::unique_ptr<T> &owning) : owning(std::move(owning))
{
}
T *operator->()
{
return owning.operator->();
}
T &operator*()
{
return owning.operator*();
}
};
/**
* Behaves like `std::unique_ptr<Depsgraph, custom_deleter>`.
* Needed to free a new Depsgraph created for #DAG_EVAL_RENDER.
*/
class OBJDepsgraph : NonMovable, NonCopyable {
private:
Depsgraph *depsgraph_ = nullptr;
bool needs_free_ = false;
public:
OBJDepsgraph(const bContext *C, const eEvaluationMode eval_mode);
~OBJDepsgraph();
Depsgraph *get();
void update_for_newframe();
};
void exporter_main(bContext *C, const OBJExportParams &export_params);
class OBJMesh;
class OBJCurve;
void export_frame(Depsgraph *depsgraph,
const OBJExportParams &export_params,
const char *filepath);
std::pair<Vector<std::unique_ptr<OBJMesh>>, Vector<std::unique_ptr<OBJCurve>>>
filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_params);
bool append_frame_to_filename(const char *filepath, const int frame, char *r_filepath_with_frames);
} // namespace blender::io::obj

View File

@@ -1,368 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#include <array>
#include "BKE_displist.h"
#include "BKE_mesh.h"
#include "BLI_set.hh"
#include "DNA_object_types.h"
#include "IO_wavefront_obj.h"
#include "importer_mesh_utils.hh"
namespace blender::io::obj {
static float manhatten_len(const float3 coord)
{
return std::abs(coord[0]) + std::abs(coord[1]) + std::abs(coord[2]);
}
/**
* Keeps original index of the vertex as well as manhatten length for future use.
*/
struct vert_index_mlen {
const float3 v;
const int i;
const float mlen;
vert_index_mlen(float3 v, int i) : v(v), i(i), mlen(manhatten_len(v))
{
}
friend bool operator==(const vert_index_mlen &one, const vert_index_mlen &other)
{
return other.v == one.v;
}
friend bool operator!=(const vert_index_mlen &one, const vert_index_mlen &other)
{
return !(one == other);
}
};
/**
* Reorder vertices `v1` and `v2` so that edges like (v1,v2) and (v2,v2) are processed as the same.
*/
static std::pair<float3, float3> ed_key_mlen(const vert_index_mlen &v1, const vert_index_mlen &v2)
{
if (v2.mlen < v1.mlen) {
return {v2.v, v1.v};
}
return {v1.v, v2.v};
}
/**
* Join segments which have same starting or ending points.
* Caller should ensure non-empty segments.
*/
static bool join_segments(Vector<vert_index_mlen> *r_seg1, Vector<vert_index_mlen> *r_seg2)
{
if ((*r_seg1)[0].v == r_seg2->last().v) {
Vector<vert_index_mlen> *temp = r_seg1;
r_seg1 = r_seg2;
r_seg2 = temp;
}
else if (r_seg1->last().v == (*r_seg2)[0].v) {
}
else {
return false;
}
r_seg1->remove_last();
r_seg1->extend(*r_seg2);
if (r_seg1->last().v == (*r_seg1)[0].v) {
r_seg1->remove_last();
}
r_seg2->clear();
return true;
}
/**
* A simplified version of `M_Geometry_tessellate_polygon`.
*
* \param polyLineSeq List of polylines.
* \param r_new_line_seq Empty vector that fill be filled with indices of corners of triangles.
*/
static void tessellate_polygon(const Vector<Vector<float3>> &polyLineSeq,
Vector<Vector<int>> &r_new_line_seq)
{
int64_t totpoints = 0;
/* Display #ListBase. */
ListBase dispbase = {nullptr, nullptr};
const int64_t len_polylines{polyLineSeq.size()};
for (int i = 0; i < len_polylines; i++) {
Span<float3> polyLine = polyLineSeq[i];
const int64_t len_polypoints{polyLine.size()};
totpoints += len_polypoints;
if (len_polypoints <= 0) { /* don't bother adding edges as polylines */
continue;
}
DispList *dl = static_cast<DispList *>(MEM_callocN(sizeof(DispList), __func__));
BLI_addtail(&dispbase, dl);
dl->type = DL_INDEX3;
dl->nr = len_polypoints;
dl->type = DL_POLY;
dl->parts = 1; /* no faces, 1 edge loop */
dl->col = 0; /* no material */
dl->verts = static_cast<float *>(MEM_mallocN(sizeof(float[3]) * len_polypoints, "dl verts"));
dl->index = static_cast<int *>(MEM_callocN(sizeof(int[3]) * len_polypoints, "dl index"));
float *fp_verts = dl->verts;
for (int j = 0; j < len_polypoints; j++, fp_verts += 3) {
copy_v3_v3(fp_verts, polyLine[j]);
}
}
if (totpoints) {
/* now make the list to fill */
BKE_displist_fill(&dispbase, &dispbase, nullptr, false);
/* The faces are stored in a new DisplayList
* that's added to the head of the #ListBase. */
const DispList *dl = static_cast<DispList *>(dispbase.first);
for (int index = 0, *dl_face = dl->index; index < dl->parts; index++, dl_face += 3) {
r_new_line_seq.append({dl_face[0], dl_face[1], dl_face[2]});
}
BKE_displist_free(&dispbase);
}
}
/**
* Tessellate an ngon with holes to triangles.
*
* \param face_vertex_indices A polygon's indices that index into the given vertex coordinate list.
* \return List of polygons with each element containing indices of one polygon.
*/
Vector<Vector<int>> ngon_tessellate(Span<float3> vertex_coords, Span<int> face_vertex_indices)
{
if (face_vertex_indices.is_empty()) {
return {};
}
Vector<vert_index_mlen> verts;
verts.reserve(face_vertex_indices.size());
for (int i = 0; i < face_vertex_indices.size(); i++) {
verts.append({vertex_coords[face_vertex_indices[i]], i});
}
Vector<std::array<int, 2>> edges;
for (int i = 0; i < face_vertex_indices.size(); i++) {
edges.append({i, i - 1});
}
edges[0] = {0, static_cast<int>(face_vertex_indices.size() - 1)};
Set<std::pair<float3, float3>> edges_double;
{
Set<std::pair<float3, float3>> edges_used;
for (Span<int> edge : edges) {
std::pair<float3, float3> edge_key{ed_key_mlen(verts[edge[0]], verts[edge[1]])};
if (edges_used.contains(edge_key)) {
edges_double.add(edge_key);
}
else {
edges_used.add(edge_key);
}
}
}
Vector<Vector<vert_index_mlen>> loop_segments;
{
const vert_index_mlen *vert_prev = &verts[0];
Vector<vert_index_mlen> context_loop{1, *vert_prev};
loop_segments.append(context_loop);
for (const vert_index_mlen &vertex : verts) {
if (vertex == *vert_prev) {
continue;
}
if (edges_double.contains(ed_key_mlen(vertex, *vert_prev))) {
context_loop = {vertex};
loop_segments.append(context_loop);
}
else {
if (!context_loop.is_empty() && context_loop.last() == vertex) {
}
else {
loop_segments.last().append(vertex);
context_loop.append(vertex);
}
}
vert_prev = &vertex;
}
}
bool joining_segements = true;
while (joining_segements) {
joining_segements = false;
for (int j = loop_segments.size() - 1; j >= 0; j--) {
Vector<vert_index_mlen> &seg_j = loop_segments[j];
if (seg_j.is_empty()) {
continue;
}
for (int k = j - 1; k >= 0; k--) {
if (seg_j.is_empty()) {
break;
}
Vector<vert_index_mlen> &seg_k = loop_segments[k];
if (!seg_k.is_empty() && join_segments(&seg_j, &seg_k)) {
joining_segements = true;
}
}
}
}
for (Vector<vert_index_mlen> &loop : loop_segments) {
while (!loop.is_empty() && loop[0].v == loop.last().v) {
loop.remove_last();
}
}
Vector<Vector<vert_index_mlen>> loop_list;
for (Vector<vert_index_mlen> &loop : loop_segments) {
if (loop.size() > 2) {
loop_list.append(loop);
}
}
// Done with loop fixing.
Vector<int> vert_map(face_vertex_indices.size(), 0);
int ii = 0;
for (Span<vert_index_mlen> verts : loop_list) {
if (verts.size() <= 2) {
continue;
}
for (int i = 0; i < verts.size(); i++) {
vert_map[i + ii] = verts[i].i;
}
ii += verts.size();
}
Vector<Vector<int>> fill;
{
Vector<Vector<float3>> coord_list;
for (Span<vert_index_mlen> loop : loop_list) {
Vector<float3> coord;
for (const vert_index_mlen &vert : loop) {
coord.append(vert.v);
}
coord_list.append(coord);
}
tessellate_polygon(coord_list, fill);
}
Vector<Vector<int>> fill_indices;
Vector<Vector<int>> fill_indices_reversed;
for (Span<int> f : fill) {
Vector<int> tri;
for (const int i : f) {
tri.append(vert_map[i]);
}
fill_indices.append(tri);
}
if (fill_indices.is_empty()) {
std::cerr << "Warning: could not scanfill, fallback on triangle fan" << std::endl;
for (int i = 2; i < face_vertex_indices.size(); i++) {
fill_indices.append({0, i - 1, i});
}
}
else {
int flip = -1;
for (Span<int> fi : fill_indices) {
if (flip != -1) {
break;
}
for (int i = 0; i < fi.size(); i++) {
if (fi[i] == 0 && fi[i - 1] == 1) {
flip = 0;
break;
}
if (fi[i] == 1 && fi[i - 1] == 0) {
flip = 1;
break;
}
}
}
if (flip == 1) {
for (Span<int> fill_index : fill_indices) {
Vector<int> rev_face(fill_index.size());
for (int j = 0; j < rev_face.size(); j++) {
rev_face[j] = fill_index[rev_face.size() - 1 - j];
}
fill_indices_reversed.append(rev_face);
}
}
}
if (!fill_indices_reversed.is_empty()) {
return fill_indices_reversed;
}
return fill_indices;
}
/**
* Apply axes transform to the Object, and clamp object dimensions to the specified value.
*
* Ideally, this should be a member of a base class which `MeshFromGeometry` and
* `CurveFromGeometry` derive from.
*/
void transform_object(Object *object, const OBJImportParams &import_params)
{
float axes_transform[3][3];
unit_m3(axes_transform);
unit_m4(object->obmat);
/* Location shift should be 0. */
copy_v3_fl(object->obmat[3], 0.0f);
/* +Y-forward and +Z-up are the default Blender axis settings. */
mat3_from_axis_conversion(OBJ_AXIS_Y_FORWARD,
OBJ_AXIS_Z_UP,
import_params.forward_axis,
import_params.up_axis,
axes_transform);
/* mat3_from_axis_conversion returns a transposed matrix! */
transpose_m3(axes_transform);
mul_m4_m3m4(object->obmat, axes_transform, object->obmat);
if (import_params.clamp_size != 0.0f) {
float3 max_coord(-INT_MAX);
float3 min_coord(INT_MAX);
BoundBox *bb = BKE_mesh_boundbox_get(object);
for (const float(&vertex)[3] : bb->vec) {
for (int axis = 0; axis < 3; axis++) {
max_coord[axis] = max_ff(max_coord[axis], vertex[axis]);
min_coord[axis] = min_ff(min_coord[axis], vertex[axis]);
}
}
const float max_diff = max_fff(
max_coord[0] - min_coord[0], max_coord[1] - min_coord[1], max_coord[2] - min_coord[2]);
float scale = 1.0f;
while (import_params.clamp_size < max_diff * scale) {
scale = scale / 10;
}
copy_v3_fl(object->scale, scale);
}
}
} // namespace blender::io::obj

View File

@@ -1,37 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#pragma once
#include "BLI_float3.hh"
#include "BLI_span.hh"
#include "BLI_vector.hh"
struct Object;
struct OBJImportParams;
namespace blender::io::obj {
Vector<Vector<int>> ngon_tessellate(Span<float3> vertex_coords, Span<int> face_vertex_indices);
void transform_object(Object *object, const OBJImportParams &import_params);
} // namespace blender::io::obj

View File

@@ -1,603 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#include <fstream>
#include <iostream>
#include "BLI_map.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "parser_string_utils.hh"
#include "obj_import_file_reader.hh"
namespace blender::io::obj {
using std::string;
/**
* Based on the properties of the given Geometry instance, create a new Geometry instance
* or return the previous one.
*
* Also update index offsets which should always happen if a new Geometry instance is created.
*/
static Geometry *create_geometry(Geometry *const prev_geometry,
const eGeometryType new_type,
StringRef name,
const GlobalVertices &global_vertices,
Vector<std::unique_ptr<Geometry>> &r_all_geometries,
VertexIndexOffset &r_offset)
{
auto new_geometry = [&]() {
if (name.is_empty()) {
r_all_geometries.append(std::make_unique<Geometry>(new_type, "New object"));
}
else {
r_all_geometries.append(std::make_unique<Geometry>(new_type, name));
}
r_offset.set_index_offset(global_vertices.vertices.size());
return r_all_geometries.last().get();
};
if (prev_geometry && prev_geometry->get_geom_type() == GEOM_MESH) {
/* After the creation of a Geometry instance, at least one element has been found in the OBJ
* file that indicates that it is a mesh. */
if (prev_geometry->total_verts() || prev_geometry->total_face_elems() ||
prev_geometry->total_normals() || prev_geometry->total_edges()) {
return new_geometry();
}
if (new_type == GEOM_MESH) {
/* A Geometry created initially with a default name now found its name. */
prev_geometry->set_geometry_name(name);
return prev_geometry;
}
if (new_type == GEOM_CURVE) {
/* The object originally created is not a mesh now that curve data
* follows the vertex coordinates list. */
prev_geometry->set_geom_type(GEOM_CURVE);
return prev_geometry;
}
}
if (prev_geometry && prev_geometry->get_geom_type() == GEOM_CURVE) {
return new_geometry();
}
return new_geometry();
}
void OBJStorer::add_vertex(const StringRef rest_line, GlobalVertices &r_global_vertices)
{
float3 curr_vert;
Vector<StringRef> str_vert_split;
split_by_char(rest_line, ' ', str_vert_split);
copy_string_to_float(str_vert_split, FLT_MAX, {curr_vert, 3});
r_global_vertices.vertices.append(curr_vert);
r_geom_.vertex_indices_.append(r_global_vertices.vertices.size() - 1);
}
void OBJStorer::add_vertex_normal(const StringRef rest_line, GlobalVertices &r_global_vertices)
{
float3 curr_vert_normal;
Vector<StringRef> str_vert_normal_split;
split_by_char(rest_line, ' ', str_vert_normal_split);
copy_string_to_float(str_vert_normal_split, FLT_MAX, {curr_vert_normal, 2});
r_global_vertices.vertex_normals.append(curr_vert_normal);
r_geom_.vertex_normal_indices_.append(r_global_vertices.vertex_normals.size() - 1);
}
void OBJStorer::add_uv_vertex(const StringRef rest_line, GlobalVertices &r_global_vertices)
{
float2 curr_uv_vert;
Vector<StringRef> str_uv_vert_split;
split_by_char(rest_line, ' ', str_uv_vert_split);
copy_string_to_float(str_uv_vert_split, FLT_MAX, {curr_uv_vert, 2});
r_global_vertices.uv_vertices.append(curr_uv_vert);
}
void OBJStorer::add_edge(const StringRef rest_line,
const VertexIndexOffset &offsets,
GlobalVertices &r_global_vertices)
{
int edge_v1 = -1, edge_v2 = -1;
Vector<StringRef> str_edge_split;
split_by_char(rest_line, ' ', str_edge_split);
copy_string_to_int(str_edge_split[0], -1, edge_v1);
copy_string_to_int(str_edge_split[1], -1, edge_v2);
/* Always keep stored indices non-negative and zero-based. */
edge_v1 += edge_v1 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1;
edge_v2 += edge_v2 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1;
BLI_assert(edge_v1 >= 0 && edge_v2 >= 0);
r_geom_.edges_.append({static_cast<uint>(edge_v1), static_cast<uint>(edge_v2)});
}
void OBJStorer::add_polygon(const StringRef rest_line,
const GlobalVertices &global_vertices,
const VertexIndexOffset &offsets,
const StringRef state_material_name,
const StringRef state_object_group,
const bool state_shaded_smooth)
{
PolyElem curr_face;
curr_face.shaded_smooth = state_shaded_smooth;
if (!state_material_name.is_empty()) {
curr_face.material_name = state_material_name;
}
if (!state_object_group.is_empty()) {
curr_face.vertex_group = state_object_group;
/* Yes it repeats several times, but another if-check will not reduce steps either. */
r_geom_.use_vertex_groups_ = true;
}
Vector<StringRef> str_corners_split;
split_by_char(rest_line, ' ', str_corners_split);
for (StringRef str_corner : str_corners_split) {
PolyCorner corner;
const size_t n_slash = std::count(str_corner.begin(), str_corner.end(), '/');
if (n_slash == 0) {
/* Case: "f v1 v2 v3". */
copy_string_to_int(str_corner, INT32_MAX, corner.vert_index);
}
else if (n_slash == 1) {
/* Case: "f v1/vt1 v2/vt2 v3/vt3". */
Vector<StringRef> vert_uv_split;
split_by_char(str_corner, '/', vert_uv_split);
copy_string_to_int(vert_uv_split[0], INT32_MAX, corner.vert_index);
if (vert_uv_split.size() == 2) {
copy_string_to_int(vert_uv_split[1], INT32_MAX, corner.uv_vert_index);
}
}
else if (n_slash == 2) {
/* Case: "f v1//vn1 v2//vn2 v3//vn3". */
/* Case: "f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3". */
Vector<StringRef> vert_uv_normal_split;
split_by_char(str_corner, '/', vert_uv_normal_split);
copy_string_to_int(vert_uv_normal_split[0], INT32_MAX, corner.vert_index);
copy_string_to_int(vert_uv_normal_split[1], INT32_MAX, corner.uv_vert_index);
if (vert_uv_normal_split.size() == 3) {
copy_string_to_int(vert_uv_normal_split[2], INT32_MAX, corner.vertex_normal_index);
}
}
/* Always keep stored indices non-negative and zero-based. */
corner.vert_index += corner.vert_index < 0 ? global_vertices.vertices.size() :
-offsets.get_index_offset() - 1;
corner.uv_vert_index += corner.uv_vert_index < 0 ? global_vertices.uv_vertices.size() : -1;
corner.vertex_normal_index += corner.vertex_normal_index < 0 ?
global_vertices.vertex_normals.size() :
-1;
curr_face.face_corners.append(corner);
}
r_geom_.face_elements_.append(curr_face);
r_geom_.total_loops_ += curr_face.face_corners.size();
}
void OBJStorer::set_curve_type(const StringRef rest_line,
const GlobalVertices &global_vertices,
const StringRef state_object_group,
VertexIndexOffset &r_offsets,
Vector<std::unique_ptr<Geometry>> &r_all_geometries)
{
if (rest_line.find("bspline") != StringRef::not_found) {
r_geom_ = *create_geometry(
&r_geom_, GEOM_CURVE, state_object_group, global_vertices, r_all_geometries, r_offsets);
r_geom_.nurbs_element_.group_ = state_object_group;
}
else {
std::cerr << "Curve type not supported:'" << rest_line << "'" << std::endl;
}
}
void OBJStorer::set_curve_degree(const StringRef rest_line)
{
copy_string_to_int(rest_line, 3, r_geom_.nurbs_element_.degree);
}
void OBJStorer::add_curve_vertex_indices(const StringRef rest_line,
const GlobalVertices &global_vertices)
{
Vector<StringRef> str_curv_split;
split_by_char(rest_line, ' ', str_curv_split);
/* Remove "0.0" and "1.0" from the strings. They are hardcoded. */
str_curv_split.remove(0);
str_curv_split.remove(0);
r_geom_.nurbs_element_.curv_indices.resize(str_curv_split.size());
copy_string_to_int(str_curv_split, INT32_MAX, r_geom_.nurbs_element_.curv_indices);
for (int &curv_index : r_geom_.nurbs_element_.curv_indices) {
/* Always keep stored indices non-negative and zero-based. */
curv_index += curv_index < 0 ? global_vertices.vertices.size() : -1;
}
}
void OBJStorer::add_curve_parameters(const StringRef rest_line)
{
Vector<StringRef> str_parm_split;
split_by_char(rest_line, ' ', str_parm_split);
if (str_parm_split[0] == "u" || str_parm_split[0] == "v") {
str_parm_split.remove(0);
r_geom_.nurbs_element_.parm.resize(str_parm_split.size());
copy_string_to_float(str_parm_split, FLT_MAX, r_geom_.nurbs_element_.parm);
}
else {
std::cerr << "Surfaces are not supported:'" << str_parm_split[0] << "'" << std::endl;
}
}
void OBJStorer::update_object_group(const StringRef rest_line,
std::string &r_state_object_group) const
{
if (rest_line.find("off") != string::npos || rest_line.find("null") != string::npos ||
rest_line.find("default") != string::npos) {
/* Set group for future elements like faces or curves to empty. */
r_state_object_group = "";
return;
}
r_state_object_group = rest_line;
}
void OBJStorer::update_polygon_material(const StringRef rest_line,
std::string &r_state_material_name) const
{
/* Materials may repeat if faces are written without sorting. */
r_geom_.material_names_.add(string(rest_line));
r_state_material_name = rest_line;
}
void OBJStorer::update_smooth_group(const StringRef rest_line, bool &r_state_shaded_smooth) const
{
/* Some implementations use "0" and "null" too, in addition to "off". */
if (rest_line != "0" && rest_line.find("off") == StringRef::not_found &&
rest_line.find("null") == StringRef::not_found) {
int smooth = 0;
copy_string_to_int(rest_line, 0, smooth);
r_state_shaded_smooth = smooth != 0;
}
else {
/* The OBJ file explicitly set shading to off. */
r_state_shaded_smooth = false;
}
}
/**
* Open OBJ file at the path given in import parameters.
*/
OBJParser::OBJParser(const OBJImportParams &import_params) : import_params_(import_params)
{
obj_file_.open(import_params_.filepath);
if (!obj_file_.good()) {
fprintf(stderr, "Cannot read from OBJ file:'%s'.\n", import_params_.filepath);
return;
}
fprintf(stderr, "Reading OBJ file from '%s'\n", import_params.filepath);
}
/**
* Read the OBJ file line by line and create OBJ Geometry instances. Also store all the vertex
* and UV vertex coordinates in a struct accessible by all objects.
*/
void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
GlobalVertices &r_global_vertices)
{
if (!obj_file_.good()) {
return;
}
string line;
/* Store vertex coordinates that belong to other Geometry instances. */
VertexIndexOffset offsets;
/* Non owning raw pointer to a Geometry. To be updated while creating a new Geometry. */
Geometry *current_geometry = create_geometry(
nullptr, GEOM_MESH, "", r_global_vertices, r_all_geometries, offsets);
/* State-setting variables: if set, they remain the same for the remaining
* elements in the object. */
bool state_shaded_smooth = false;
string state_object_group;
string state_material_name;
while (std::getline(obj_file_, line)) {
/* Keep reading new lines if the last character is `\`. */
/* Another way is to make a getline wrapper and use it in the while condition. */
read_next_line(obj_file_, line);
StringRef line_key, rest_line;
split_line_key_rest(line, line_key, rest_line);
if (line.empty() || rest_line.is_empty()) {
continue;
}
OBJStorer storer(*current_geometry);
switch (line_key_str_to_enum(line_key)) {
case eOBJLineKey::V: {
storer.add_vertex(rest_line, r_global_vertices);
break;
}
case eOBJLineKey::VN: {
storer.add_vertex_normal(rest_line, r_global_vertices);
break;
}
case eOBJLineKey::VT: {
storer.add_uv_vertex(rest_line, r_global_vertices);
break;
}
case eOBJLineKey::F: {
storer.add_polygon(rest_line,
r_global_vertices,
offsets,
state_material_name,
state_material_name,
state_shaded_smooth);
break;
}
case eOBJLineKey::L: {
storer.add_edge(rest_line, offsets, r_global_vertices);
break;
}
case eOBJLineKey::CSTYPE: {
storer.set_curve_type(
rest_line, r_global_vertices, state_object_group, offsets, r_all_geometries);
break;
}
case eOBJLineKey::DEG: {
storer.set_curve_degree(rest_line);
break;
}
case eOBJLineKey::CURV: {
storer.add_curve_vertex_indices(rest_line, r_global_vertices);
break;
}
case eOBJLineKey::PARM: {
storer.add_curve_parameters(rest_line);
break;
}
case eOBJLineKey::O: {
state_shaded_smooth = false;
state_object_group = "";
state_material_name = "";
current_geometry = create_geometry(
current_geometry, GEOM_MESH, rest_line, r_global_vertices, r_all_geometries, offsets);
break;
}
case eOBJLineKey::G: {
storer.update_object_group(rest_line, state_object_group);
break;
}
case eOBJLineKey::S: {
storer.update_smooth_group(rest_line, state_shaded_smooth);
break;
}
case eOBJLineKey::USEMTL: {
storer.update_polygon_material(rest_line, state_material_name);
break;
}
case eOBJLineKey::MTLLIB: {
mtl_libraries_.append(string(rest_line));
break;
}
case eOBJLineKey::COMMENT:
break;
default:
std::cout << "Element not recognised: '" << line_key << "'" << std::endl;
break;
}
}
}
/**
* Skip all texture map options and get the filepath from a "map_" line.
*/
static StringRef skip_unsupported_options(StringRef line)
{
TextureMapOptions map_options;
StringRef last_option;
int64_t last_option_pos = 0;
/* Find the last texture map option. */
for (StringRef option : map_options.all_options()) {
const int64_t pos{line.find(option)};
/* Equality (>=) takes care of finding an option in the beginning of the line. Avoid messing
* with signed-unsigned int comparison. */
if (pos != StringRef::not_found && pos >= last_option_pos) {
last_option = option;
last_option_pos = pos;
}
}
if (last_option.is_empty()) {
/* No option found, line is the filepath */
return line;
}
/* Remove upto start of the last option + size of the last option + space after it. */
line = line.drop_prefix(last_option_pos + last_option.size() + 1);
for (int i = 0; i < map_options.number_of_args(last_option); i++) {
const int64_t pos_space{line.find_first_of(' ')};
if (pos_space != StringRef::not_found) {
BLI_assert(pos_space + 1 < line.size());
line = line.drop_prefix(pos_space + 1);
}
}
return line;
}
/**
* Fix incoming texture map line keys for variations due to other exporters.
*/
static string fix_bad_map_keys(StringRef map_key)
{
string new_map_key(map_key);
if (map_key == "refl") {
new_map_key = "map_refl";
}
if (map_key.find("bump") != StringRef::not_found) {
/* Handles both "bump" and "map_Bump" */
new_map_key = "map_Bump";
}
return new_map_key;
}
/**
* Return a list of all material library filepaths referenced by the OBJ file.
*/
Span<std::string> OBJParser::mtl_libraries() const
{
return mtl_libraries_;
}
/**
* Open material library file.
*/
MTLParser::MTLParser(StringRef mtl_library, StringRefNull obj_filepath)
{
char obj_file_dir[FILE_MAXDIR];
BLI_split_dir_part(obj_filepath.data(), obj_file_dir, FILE_MAXDIR);
BLI_path_join(mtl_file_path_, FILE_MAX, obj_file_dir, mtl_library.data(), NULL);
BLI_split_dir_part(mtl_file_path_, mtl_dir_path_, FILE_MAXDIR);
mtl_file_.open(mtl_file_path_);
if (!mtl_file_.good()) {
fprintf(stderr, "Cannot read from MTL file:'%s'\n", mtl_file_path_);
return;
}
fprintf(stderr, "Reading MTL file from:'%s'\n", mtl_file_path_);
}
/**
* Read MTL file(s) and add MTLMaterial instances to the given Map reference.
*/
void MTLParser::parse_and_store(Map<string, std::unique_ptr<MTLMaterial>> &r_mtl_materials)
{
if (!mtl_file_.good()) {
return;
}
string line;
MTLMaterial *current_mtlmaterial = nullptr;
while (std::getline(mtl_file_, line)) {
StringRef line_key, rest_line;
split_line_key_rest(line, line_key, rest_line);
if (line.empty() || rest_line.is_empty()) {
continue;
}
/* Fix lower case/ incomplete texture map identifiers. */
const string fixed_key = fix_bad_map_keys(line_key);
line_key = fixed_key;
if (line_key == "newmtl") {
if (r_mtl_materials.remove_as(rest_line)) {
std::cerr << "Duplicate material found:'" << rest_line
<< "', using the last encountered Material definition." << std::endl;
}
current_mtlmaterial =
r_mtl_materials.lookup_or_add(string(rest_line), std::make_unique<MTLMaterial>()).get();
}
else if (line_key == "Ns") {
copy_string_to_float(rest_line, 324.0f, current_mtlmaterial->Ns);
}
else if (line_key == "Ka") {
Vector<StringRef> str_ka_split;
split_by_char(rest_line, ' ', str_ka_split);
copy_string_to_float(str_ka_split, 0.0f, {current_mtlmaterial->Ka, 3});
}
else if (line_key == "Kd") {
Vector<StringRef> str_kd_split;
split_by_char(rest_line, ' ', str_kd_split);
copy_string_to_float(str_kd_split, 0.8f, {current_mtlmaterial->Kd, 3});
}
else if (line_key == "Ks") {
Vector<StringRef> str_ks_split;
split_by_char(rest_line, ' ', str_ks_split);
copy_string_to_float(str_ks_split, 0.5f, {current_mtlmaterial->Ks, 3});
}
else if (line_key == "Ke") {
Vector<StringRef> str_ke_split;
split_by_char(rest_line, ' ', str_ke_split);
copy_string_to_float(str_ke_split, 0.0f, {current_mtlmaterial->Ke, 3});
}
else if (line_key == "Ni") {
copy_string_to_float(rest_line, 1.45f, current_mtlmaterial->Ni);
}
else if (line_key == "d") {
copy_string_to_float(rest_line, 1.0f, current_mtlmaterial->d);
}
else if (line_key == "illum") {
copy_string_to_int(rest_line, 2, current_mtlmaterial->illum);
}
/* Parse image textures. */
else if (line_key.find("map_") != StringRef::not_found) {
/* TODO howardt: fix this */
eMTLSyntaxElement line_key_enum = mtl_line_key_str_to_enum(line_key);
if (line_key_enum == eMTLSyntaxElement::string ||
!current_mtlmaterial->texture_maps.contains_as(line_key_enum)) {
/* No supported texture map found. */
std::cerr << "Texture map type not supported:'" << line_key << "'" << std::endl;
continue;
}
tex_map_XX &tex_map = current_mtlmaterial->texture_maps.lookup(line_key_enum);
Vector<StringRef> str_map_xx_split;
split_by_char(rest_line, ' ', str_map_xx_split);
/* TODO ankitm: use `skip_unsupported_options` for parsing these options too? */
const int64_t pos_o{str_map_xx_split.first_index_of_try("-o")};
if (pos_o != -1 && pos_o + 3 < str_map_xx_split.size()) {
copy_string_to_float({str_map_xx_split[pos_o + 1],
str_map_xx_split[pos_o + 2],
str_map_xx_split[pos_o + 3]},
0.0f,
{tex_map.translation, 3});
}
const int64_t pos_s{str_map_xx_split.first_index_of_try("-s")};
if (pos_s != -1 && pos_s + 3 < str_map_xx_split.size()) {
copy_string_to_float({str_map_xx_split[pos_s + 1],
str_map_xx_split[pos_s + 2],
str_map_xx_split[pos_s + 3]},
1.0f,
{tex_map.scale, 3});
}
/* Only specific to Normal Map node. */
const int64_t pos_bm{str_map_xx_split.first_index_of_try("-bm")};
if (pos_bm != -1 && pos_bm + 1 < str_map_xx_split.size()) {
copy_string_to_float(
str_map_xx_split[pos_bm + 1], 0.0f, current_mtlmaterial->map_Bump_strength);
}
const int64_t pos_projection{str_map_xx_split.first_index_of_try("-type")};
if (pos_projection != -1 && pos_projection + 1 < str_map_xx_split.size()) {
/* Only Sphere is supported, so whatever the type is, set it to Sphere. */
tex_map.projection_type = SHD_PROJ_SPHERE;
if (str_map_xx_split[pos_projection + 1] != "sphere") {
std::cerr << "Using projection type 'sphere', not:'"
<< str_map_xx_split[pos_projection + 1] << "'." << std::endl;
}
}
/* Skip all unsupported options and arguments. */
tex_map.image_path = string(skip_unsupported_options(rest_line));
tex_map.mtl_dir_path = mtl_dir_path_;
}
}
}
} // namespace blender::io::obj

View File

@@ -1,203 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#pragma once
#include <fstream>
#include "IO_wavefront_obj.h"
#include "obj_import_mtl.hh"
#include "obj_import_objects.hh"
namespace blender::io::obj {
class OBJParser {
private:
const OBJImportParams &import_params_;
std::ifstream obj_file_;
Vector<std::string> mtl_libraries_;
public:
OBJParser(const OBJImportParams &import_params);
void parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
GlobalVertices &r_global_vertices);
Span<std::string> mtl_libraries() const;
};
class OBJStorer {
private:
Geometry &r_geom_;
public:
OBJStorer(Geometry &r_geom) : r_geom_(r_geom)
{
}
void add_vertex(const StringRef rest_line, GlobalVertices &r_global_vertices);
void add_vertex_normal(const StringRef rest_line, GlobalVertices &r_global_vertices);
void add_uv_vertex(const StringRef rest_line, GlobalVertices &r_global_vertices);
void add_edge(const StringRef rest_line,
const VertexIndexOffset &offsets,
GlobalVertices &r_global_vertices);
void add_polygon(const StringRef rest_line,
const GlobalVertices &global_vertices,
const VertexIndexOffset &offsets,
const StringRef state_material_name,
const StringRef state_object_group,
const bool state_shaded_smooth);
void set_curve_type(const StringRef rest_line,
const GlobalVertices &global_vertices,
const StringRef state_object_group,
VertexIndexOffset &r_offsets,
Vector<std::unique_ptr<Geometry>> &r_all_geometries);
void set_curve_degree(const StringRef rest_line);
void add_curve_vertex_indices(const StringRef rest_line, const GlobalVertices &global_vertices);
void add_curve_parameters(const StringRef rest_line);
void update_object_group(const StringRef rest_line, std::string &r_state_object_group) const;
void update_polygon_material(const StringRef rest_line,
std::string &r_state_material_name) const;
void update_smooth_group(const StringRef rest_line, bool &r_state_shaded_smooth) const;
};
enum class eOBJLineKey {
V,
VN,
VT,
F,
L,
CSTYPE,
DEG,
CURV,
PARM,
O,
G,
S,
USEMTL,
MTLLIB,
COMMENT
};
constexpr eOBJLineKey line_key_str_to_enum(const std::string_view key_str)
{
if (key_str == "v" || key_str == "V") {
return eOBJLineKey::V;
}
if (key_str == "vn" || key_str == "VN") {
return eOBJLineKey::VN;
}
if (key_str == "vt" || key_str == "VT") {
return eOBJLineKey::VT;
}
if (key_str == "f" || key_str == "F") {
return eOBJLineKey::F;
}
if (key_str == "l" || key_str == "L") {
return eOBJLineKey::L;
}
if (key_str == "cstype" || key_str == "CSTYPE") {
return eOBJLineKey::CSTYPE;
}
if (key_str == "deg" || key_str == "DEG") {
return eOBJLineKey::DEG;
}
if (key_str == "curv" || key_str == "CURV") {
return eOBJLineKey::CURV;
}
if (key_str == "parm" || key_str == "PARM") {
return eOBJLineKey::PARM;
}
if (key_str == "o" || key_str == "O") {
return eOBJLineKey::O;
}
if (key_str == "g" || key_str == "G") {
return eOBJLineKey::G;
}
if (key_str == "s" || key_str == "S") {
return eOBJLineKey::S;
}
if (key_str == "usemtl" || key_str == "USEMTL") {
return eOBJLineKey::USEMTL;
}
if (key_str == "mtllib" || key_str == "MTLLIB") {
return eOBJLineKey::MTLLIB;
}
if (key_str == "#") {
return eOBJLineKey::COMMENT;
}
return eOBJLineKey::COMMENT;
}
/**
* All texture map options with number of arguments they accept.
*/
class TextureMapOptions {
private:
Map<const std::string, int> tex_map_options;
public:
TextureMapOptions()
{
tex_map_options.add_new("-blendu", 1);
tex_map_options.add_new("-blendv", 1);
tex_map_options.add_new("-boost", 1);
tex_map_options.add_new("-mm", 2);
tex_map_options.add_new("-o", 3);
tex_map_options.add_new("-s", 3);
tex_map_options.add_new("-t", 3);
tex_map_options.add_new("-texres", 1);
tex_map_options.add_new("-clamp", 1);
tex_map_options.add_new("-bm", 1);
tex_map_options.add_new("-imfchan", 1);
}
/**
* All valid option strings.
*/
Map<const std::string, int>::KeyIterator all_options() const
{
return tex_map_options.keys();
}
int number_of_args(StringRef option) const
{
return tex_map_options.lookup_as(std::string(option));
}
};
class MTLParser {
private:
char mtl_file_path_[FILE_MAX];
/**
* Directory in which the MTL file is found.
*/
char mtl_dir_path_[FILE_MAX];
std::ifstream mtl_file_;
public:
MTLParser(StringRef mtl_library_, StringRefNull obj_filepath);
void parse_and_store(Map<std::string, std::unique_ptr<MTLMaterial>> &r_mtl_materials);
};
} // namespace blender::io::obj

View File

@@ -1,413 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#include <array>
#include "DNA_scene_types.h" /* For eVGroupSelect. */
#include "BKE_customdata.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_object_deform.h"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_vector_set.hh"
#include "bmesh.h"
#include "bmesh_operator_api.h"
#include "bmesh_tools.h"
#include "DNA_customdata_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "importer_mesh_utils.hh"
#include "obj_import_mesh.hh"
namespace blender::io::obj {
MeshFromGeometry::~MeshFromGeometry()
{
if (mesh_object_ || blender_mesh_) {
/* Move the object to own it. */
mesh_object_.reset();
blender_mesh_.reset();
BLI_assert(0);
}
}
void MeshFromGeometry::create_mesh(Main *bmain,
const Map<std::string, std::unique_ptr<MTLMaterial>> &materials,
const OBJImportParams &import_params)
{
std::string ob_name{mesh_geometry_.get_geometry_name()};
if (ob_name.empty()) {
ob_name = "Untitled";
}
Vector<PolyElem> new_faces;
Set<std::pair<int, int>> fgon_edges;
const auto [removed_faces, removed_loops]{tessellate_polygons(new_faces, fgon_edges)};
const int64_t tot_verts_object{mesh_geometry_.total_verts()};
/* Total explicitly imported edges, not the ones belonging the polygons to be created. */
const int64_t tot_edges{mesh_geometry_.total_edges()};
const int64_t tot_face_elems{mesh_geometry_.total_face_elems() - removed_faces +
new_faces.size()};
const int64_t tot_loops{mesh_geometry_.total_loops() - removed_loops + 3 * new_faces.size()};
blender_mesh_.reset(
BKE_mesh_new_nomain(tot_verts_object, tot_edges, 0, tot_loops, tot_face_elems));
mesh_object_.reset(BKE_object_add_only_object(bmain, OB_MESH, ob_name.c_str()));
mesh_object_->data = BKE_object_obdata_add_from_type(bmain, OB_MESH, ob_name.c_str());
create_vertices();
new_faces.extend(mesh_geometry_.face_elements());
create_polys_loops(new_faces);
create_edges();
create_uv_verts();
create_materials(bmain, materials);
bool verbose_validate = false;
#ifdef DEBUG
verbose_validate = true;
#endif
BKE_mesh_validate(blender_mesh_.get(), verbose_validate, false);
#if 0
/* TODO ankitm Check if it should be executed or not. */
add_custom_normals();
#endif
/* Un-tessellate unnecesarily triangulated n-gons. */
dissolve_edges(fgon_edges);
transform_object(mesh_object_.get(), import_params);
BKE_mesh_nomain_to_mesh(blender_mesh_.release(),
static_cast<Mesh *>(mesh_object_->data),
mesh_object_.get(),
&CD_MASK_EVERYTHING,
true);
}
/**
* Tessellate potentially invalid polygons and fill the
*/
std::pair<int64_t, int64_t> MeshFromGeometry::tessellate_polygons(
Vector<PolyElem> &r_new_faces, Set<std::pair<int, int>> &fgon_edges)
{
int64_t removed_faces = 0;
int64_t removed_loops = 0;
for (const PolyElem &curr_face : mesh_geometry_.face_elements()) {
if (curr_face.shaded_smooth || true) { // should be valid/invalid
return {removed_faces, removed_loops};
}
Vector<int> face_vert_indices;
Vector<int> face_uv_indices;
Vector<int> face_normal_indices;
face_vert_indices.reserve(curr_face.face_corners.size());
face_uv_indices.reserve(curr_face.face_corners.size());
face_normal_indices.reserve(curr_face.face_corners.size());
for (const PolyCorner &corner : curr_face.face_corners) {
face_vert_indices.append(corner.vert_index);
face_normal_indices.append(corner.vertex_normal_index);
face_uv_indices.append(corner.uv_vert_index);
removed_loops++;
}
Vector<Vector<int>> new_polygon_indices = ngon_tessellate(global_vertices_.vertices,
face_vert_indices);
for (Span<int> triangle : new_polygon_indices) {
r_new_faces.append({curr_face.vertex_group,
curr_face.material_name,
curr_face.shaded_smooth,
{{face_vert_indices[triangle[0]],
face_uv_indices[triangle[0]],
face_normal_indices[triangle[0]]},
{face_vert_indices[triangle[1]],
face_uv_indices[triangle[1]],
face_normal_indices[triangle[1]]},
{face_vert_indices[triangle[2]],
face_uv_indices[triangle[2]],
face_normal_indices[triangle[2]]}},
false});
}
if (new_polygon_indices.size() > 1) {
Set<std::pair<int, int>> edge_users;
for (Span<int> triangle : new_polygon_indices) {
int prev_vidx = face_vert_indices[triangle.last()];
for (const int ngidx : triangle) {
int vidx = face_vert_indices[ngidx];
if (vidx == prev_vidx) {
continue;
}
std::pair<int, int> edge_key = {min_ii(prev_vidx, vidx), max_ii(prev_vidx, vidx)};
prev_vidx = vidx;
if (edge_users.contains(edge_key)) {
fgon_edges.add(edge_key);
}
else {
edge_users.add(edge_key);
}
}
}
}
removed_faces++;
}
return {removed_faces, removed_loops};
}
void MeshFromGeometry::dissolve_edges(const Set<std::pair<int, int>> &fgon_edges)
{
if (fgon_edges.is_empty()) {
return;
}
const struct BMeshCreateParams bm_create_params = {1u};
/* If calc_face_normal is false, it triggers BLI_assert(BM_face_is_normal_valid(f)). */
const struct BMeshFromMeshParams bm_convert_params = {1u, 0, 0, 0};
BMesh *bmesh = BKE_mesh_to_bmesh_ex(blender_mesh_.get(), &bm_create_params, &bm_convert_params);
Vector<std::array<BMVert *, 2>> edges;
edges.reserve(fgon_edges.size());
BM_mesh_elem_table_ensure(bmesh, BM_VERT);
for (const std::pair<int, int> &edge : fgon_edges) {
edges.append({BM_vert_at_index(bmesh, edge.first), BM_vert_at_index(bmesh, edge.second)});
}
BMO_op_callf(bmesh,
BMO_FLAG_DEFAULTS,
"dissolve_edges edges=%eb use_verts=%b use_face_split=%b",
edges.data(),
0,
0);
unique_mesh_ptr to_free = std::move(blender_mesh_);
blender_mesh_.reset(BKE_mesh_from_bmesh_for_eval_nomain(bmesh, nullptr, to_free.get()));
to_free.reset();
BM_mesh_free(bmesh);
}
void MeshFromGeometry::create_vertices()
{
const int64_t tot_verts_object{mesh_geometry_.total_verts()};
for (int i = 0; i < tot_verts_object; ++i) {
if (mesh_geometry_.vertex_index(i) < global_vertices_.vertices.size()) {
copy_v3_v3(blender_mesh_->mvert[i].co,
global_vertices_.vertices[mesh_geometry_.vertex_index(i)]);
if (i >= mesh_geometry_.total_normals()) {
/* Silence debug warning in mesh validate. */
const float3 normals = {1.0f, 1.0f, 1.0f};
normal_float_to_short_v3(blender_mesh_->mvert[i].no, normals);
}
}
else {
std::cerr << "Vertex index:" << mesh_geometry_.vertex_index(i)
<< " larger than total vertices:" << global_vertices_.vertices.size() << " ."
<< std::endl;
}
}
}
/**
* Create polygons for the Mesh, set smooth shading flag, deform group name, assigned material
* also.
*
* It must receive all polygons to be added to the mesh. Remove holes from polygons before
* calling this.
*/
void MeshFromGeometry::create_polys_loops(Span<PolyElem> all_faces)
{
/* Will not be used if vertex groups are not imported. */
blender_mesh_->dvert = nullptr;
float weight = 0.0f;
if (mesh_geometry_.total_verts() && mesh_geometry_.use_vertex_groups()) {
blender_mesh_->dvert = static_cast<MDeformVert *>(CustomData_add_layer(
&blender_mesh_->vdata, CD_MDEFORMVERT, CD_CALLOC, nullptr, mesh_geometry_.total_verts()));
weight = 1.0f / mesh_geometry_.total_verts();
}
else {
UNUSED_VARS(weight);
}
/* Do not remove elements from the VectorSet since order of insertion is required.
* StringRef is fine since per-face deform group name outlives the VectorSet. */
VectorSet<StringRef> group_names;
const int64_t tot_face_elems{blender_mesh_->totpoly};
int tot_loop_idx = 0;
for (int poly_idx = 0; poly_idx < tot_face_elems; ++poly_idx) {
const PolyElem &curr_face = all_faces[poly_idx];
if (curr_face.face_corners.size() < 3) {
/* Don't add single vertex face, or edges. */
std::cerr << "Face with less than 3 vertices found, skipping." << std::endl;
continue;
}
MPoly &mpoly = blender_mesh_->mpoly[poly_idx];
mpoly.totloop = curr_face.face_corners.size();
mpoly.loopstart = tot_loop_idx;
if (curr_face.shaded_smooth) {
mpoly.flag |= ME_SMOOTH;
}
mpoly.mat_nr = mesh_geometry_.material_names().index_of_try(curr_face.material_name);
for (const PolyCorner &curr_corner : curr_face.face_corners) {
MLoop &mloop = blender_mesh_->mloop[tot_loop_idx];
tot_loop_idx++;
mloop.v = curr_corner.vert_index;
/* Set normals to silence mesh validate zero normals warnings. */
if (curr_corner.vertex_normal_index >= 0 &&
curr_corner.vertex_normal_index < global_vertices_.vertex_normals.size()) {
normal_float_to_short_v3(blender_mesh_->mvert[mloop.v].no,
global_vertices_.vertex_normals[curr_corner.vertex_normal_index]);
}
if (blender_mesh_->dvert) {
/* Iterating over mloop results in finding the same vertex multiple times.
* Another way is to allocate memory for dvert while creating vertices and fill them here.
*/
MDeformVert &def_vert = blender_mesh_->dvert[mloop.v];
if (!def_vert.dw) {
def_vert.dw = static_cast<MDeformWeight *>(
MEM_callocN(sizeof(MDeformWeight), "OBJ Import Deform Weight"));
}
/* Every vertex in a face is assigned the same deform group. */
int64_t pos_name{group_names.index_of_try(curr_face.vertex_group)};
if (pos_name == -1) {
group_names.add_new(curr_face.vertex_group);
pos_name = group_names.size() - 1;
}
BLI_assert(pos_name >= 0);
/* Deform group number (def_nr) must behave like an index into the names' list. */
*(def_vert.dw) = {static_cast<unsigned int>(pos_name), weight};
}
}
}
if (!blender_mesh_->dvert) {
return;
}
/* Add deform group(s) to the object's defbase. */
for (StringRef name : group_names) {
/* Adding groups in this order assumes that def_nr is an index into the names' list. */
BKE_object_defgroup_add_name(mesh_object_.get(), name.data());
}
}
/**
* Add explicitly imported OBJ edges to the mesh.
*/
void MeshFromGeometry::create_edges()
{
const int64_t tot_edges{mesh_geometry_.total_edges()};
for (int i = 0; i < tot_edges; ++i) {
const MEdge &src_edge = mesh_geometry_.edges()[i];
MEdge &dst_edge = blender_mesh_->medge[i];
BLI_assert(src_edge.v1 < mesh_geometry_.total_verts() &&
src_edge.v2 < mesh_geometry_.total_verts());
dst_edge.v1 = src_edge.v1;
dst_edge.v2 = src_edge.v2;
dst_edge.flag = ME_LOOSEEDGE;
}
/* Set argument `update` to true so that existing, explicitly imported edges can be merged
* with the new ones created from polygons. */
BKE_mesh_calc_edges(blender_mesh_.get(), true, false);
BKE_mesh_calc_edges_loose(blender_mesh_.get());
}
/**
* Add UV layer and vertices to the Mesh.
*/
void MeshFromGeometry::create_uv_verts()
{
if (global_vertices_.uv_vertices.size() <= 0) {
return;
}
MLoopUV *mluv_dst = static_cast<MLoopUV *>(CustomData_add_layer(
&blender_mesh_->ldata, CD_MLOOPUV, CD_DEFAULT, nullptr, mesh_geometry_.total_loops()));
int tot_loop_idx = 0;
for (const PolyElem &curr_face : mesh_geometry_.face_elements()) {
for (const PolyCorner &curr_corner : curr_face.face_corners) {
if (curr_corner.uv_vert_index >= 0 &&
curr_corner.uv_vert_index < global_vertices_.uv_vertices.size()) {
const float2 &mluv_src = global_vertices_.uv_vertices[curr_corner.uv_vert_index];
copy_v2_v2(mluv_dst[tot_loop_idx].uv, mluv_src);
tot_loop_idx++;
}
}
}
}
/**
* Add materials and the nodetree to the Mesh Object.
*/
void MeshFromGeometry::create_materials(
Main *bmain, const Map<std::string, std::unique_ptr<MTLMaterial>> &materials)
{
for (StringRef material_name : mesh_geometry_.material_names()) {
if (!materials.contains_as(material_name)) {
std::cerr << "Material named '" << material_name << "' not found in material library."
<< std::endl;
continue;
}
BKE_object_material_slot_add(bmain, mesh_object_.get());
Material *mat = BKE_material_add(bmain, material_name.data());
BKE_object_material_assign(
bmain, mesh_object_.get(), mat, mesh_object_->totcol, BKE_MAT_ASSIGN_USERPREF);
const MTLMaterial &curr_mat = *materials.lookup_as(material_name);
ShaderNodetreeWrap mat_wrap{bmain, curr_mat};
mat->use_nodes = true;
mat->nodetree = mat_wrap.get_nodetree();
ntreeUpdateTree(bmain, mat->nodetree);
}
}
/**
* Needs more clarity about what is expected in the viewport if the function works.
*/
void MeshFromGeometry::add_custom_normals()
{
const int64_t tot_loop_normals{mesh_geometry_.total_normals()};
float(*loop_normals)[3] = static_cast<float(*)[3]>(
MEM_malloc_arrayN(tot_loop_normals, sizeof(float[3]), __func__));
for (int index = 0; index < tot_loop_normals; index++) {
copy_v3_v3(loop_normals[index],
global_vertices_.vertex_normals[mesh_geometry_.vertex_normal_index(index)]);
}
blender_mesh_->flag |= ME_AUTOSMOOTH;
BKE_mesh_set_custom_normals(blender_mesh_.get(), loop_normals);
for (int i = 0; i < tot_loop_normals; i++) {
print_v3("", loop_normals[i]);
}
MEM_freeN(loop_normals);
}
} // namespace blender::io::obj

View File

@@ -1,94 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#pragma once
#include "BKE_lib_id.h"
#include "BLI_utility_mixins.hh"
#include "obj_import_mtl.hh"
#include "obj_import_objects.hh"
namespace blender::io::obj {
/**
* An custom unique_ptr deleter for a Mesh object.
*/
struct UniqueMeshDeleter {
void operator()(Mesh *mesh)
{
BKE_id_free(nullptr, mesh);
}
};
/**
* An unique_ptr to a Mesh with a custom deleter.
*/
using unique_mesh_ptr = std::unique_ptr<Mesh, UniqueMeshDeleter>;
/**
* Make a Blender Mesh Object from a Geometry of GEOM_MESH type.
* Use the mover function to own the mesh after creation.
*/
class MeshFromGeometry : NonMovable, NonCopyable {
private:
/**
* Mesh datablock made from OBJ data.
*/
unique_mesh_ptr blender_mesh_{nullptr};
/**
* An Object of type OB_MESH. Use the mover function to own it.
*/
unique_object_ptr mesh_object_{nullptr};
const Geometry &mesh_geometry_;
const GlobalVertices &global_vertices_;
public:
MeshFromGeometry(const Geometry &mesh_geometry, const GlobalVertices &global_vertices)
: mesh_geometry_(mesh_geometry), global_vertices_(global_vertices)
{
}
~MeshFromGeometry();
void create_mesh(Main *bmain,
const Map<std::string, std::unique_ptr<MTLMaterial>> &materials,
const OBJImportParams &import_params);
unique_object_ptr mover()
{
return std::move(mesh_object_);
}
private:
std::pair<int64_t, int64_t> tessellate_polygons(Vector<PolyElem> &new_faces,
Set<std::pair<int, int>> &fgon_edges);
void create_vertices();
void create_polys_loops(Span<PolyElem> all_faces);
void create_edges();
void create_uv_verts();
void create_materials(Main *bmain,
const Map<std::string, std::unique_ptr<MTLMaterial>> &materials);
void add_custom_normals();
void dissolve_edges(const Set<std::pair<int, int>> &fgon_edges);
};
} // namespace blender::io::obj

View File

@@ -1,377 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#include "BKE_image.h"
#include "BKE_node.h"
#include "BLI_map.hh"
#include "DNA_node_types.h"
#include "NOD_shader.h"
/* TODO: move eMTLSyntaxElement out of following file into a more neutral place */
#include "obj_export_io.hh"
#include "obj_import_mtl.hh"
#include "parser_string_utils.hh"
namespace blender::io::obj {
/**
* Set the socket's (of given ID) value to the given number(s).
* Only float value(s) can be set using this method.
*/
static void set_property_of_socket(eNodeSocketDatatype property_type,
StringRef socket_id,
Span<float> value,
bNode *r_node)
{
BLI_assert(r_node);
bNodeSocket *socket{nodeFindSocket(r_node, SOCK_IN, socket_id.data())};
BLI_assert(socket && socket->type == property_type);
switch (property_type) {
case SOCK_FLOAT: {
BLI_assert(value.size() == 1);
static_cast<bNodeSocketValueFloat *>(socket->default_value)->value = value[0];
break;
}
case SOCK_RGBA: {
/* Alpha will be added manually. It is not read from the MTL file either. */
BLI_assert(value.size() == 3);
copy_v3_v3(static_cast<bNodeSocketValueRGBA *>(socket->default_value)->value, value.data());
static_cast<bNodeSocketValueRGBA *>(socket->default_value)->value[3] = 1.0f;
break;
}
case SOCK_VECTOR: {
BLI_assert(value.size() == 3);
copy_v4_v4(static_cast<bNodeSocketValueVector *>(socket->default_value)->value,
value.data());
break;
}
default: {
BLI_assert(0);
break;
}
}
}
/**
* Load image for Image Texture node and set the node properties.
* Return success if Image can be loaded successfully.
*/
static bool load_texture_image(Main *bmain, const tex_map_XX &tex_map, bNode *r_node)
{
BLI_assert(r_node && r_node->type == SH_NODE_TEX_IMAGE);
std::string tex_file_path{tex_map.mtl_dir_path + tex_map.image_path};
Image *tex_image = BKE_image_load(bmain, tex_file_path.c_str());
if (!tex_image) {
/* Could be absolute, so load the image directly. */
fprintf(stderr, "Cannot load image file:'%s'\n", tex_file_path.c_str());
tex_image = BKE_image_load(bmain, tex_map.image_path.c_str());
}
if (!tex_image) {
fprintf(stderr, "Cannot load image file:'%s'\n", tex_map.image_path.c_str());
/* Remove quotes from the filepath. */
std::string no_quote_path{tex_map.mtl_dir_path +
replace_all_occurences(tex_map.image_path, "\"", "")};
tex_image = BKE_image_load(nullptr, no_quote_path.c_str());
if (!tex_image) {
fprintf(stderr, "Cannot load image file:'%s'\n", no_quote_path.data());
std::string no_underscore_path{replace_all_occurences(no_quote_path, "_", " ")};
tex_image = BKE_image_load(nullptr, no_underscore_path.c_str());
if (!tex_image) {
fprintf(stderr, "Cannot load image file:'%s'\n", no_underscore_path.data());
}
}
}
BLI_assert(tex_image);
if (tex_image) {
fprintf(stderr, "Loaded image from:'%s'\n", tex_image->filepath);
r_node->id = reinterpret_cast<ID *>(tex_image);
NodeTexImage *image = static_cast<NodeTexImage *>(r_node->storage);
image->projection = tex_map.projection_type;
return true;
}
return false;
}
/**
* Initializes a nodetree with a p-BSDF node's BSDF socket connected to shader output node's
* surface socket.
*/
ShaderNodetreeWrap::ShaderNodetreeWrap(Main *bmain, const MTLMaterial &mtl_mat) : mtl_mat_(mtl_mat)
{
nodetree_.reset(ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname));
bsdf_.reset(add_node_to_tree(SH_NODE_BSDF_PRINCIPLED));
shader_output_.reset(add_node_to_tree(SH_NODE_OUTPUT_MATERIAL));
set_bsdf_socket_values();
add_image_textures(bmain);
link_sockets(std::move(bsdf_), "BSDF", shader_output_.get(), "Surface", 4);
nodeSetActive(nodetree_.get(), shader_output_.get());
}
/**
* Assert if caller hasn't acquired nodetree.
*/
ShaderNodetreeWrap::~ShaderNodetreeWrap()
{
if (nodetree_) {
/* nodetree's ownership must be acquired by the caller. */
nodetree_.reset();
BLI_assert(0);
}
}
/**
* Release nodetree for materials to own it. nodetree has its unique deleter
* if destructor is not reached for some reason.
*/
bNodeTree *ShaderNodetreeWrap::get_nodetree()
{
/* If this function has been reached, we know that nodes and the nodetree
* can be added to the scene safely. */
static_cast<void>(shader_output_.release());
return nodetree_.release();
}
/**
* Add a new static node to the tree.
* No two nodes are linked here.
*/
bNode *ShaderNodetreeWrap::add_node_to_tree(const int node_type)
{
return nodeAddStaticNode(nullptr, nodetree_.get(), node_type);
}
/**
* Return x-y coordinates for a node where y is determined by other nodes present in
* the same vertical column.
*/
std::pair<float, float> ShaderNodetreeWrap::set_node_locations(const int pos_x)
{
int pos_y = 0;
bool found = false;
while (true) {
for (Span<int> location : node_locations) {
if (location[0] == pos_x && location[1] == pos_y) {
pos_y += 1;
found = true;
}
else {
found = false;
}
}
if (!found) {
node_locations.append({pos_x, pos_y});
return {pos_x * node_size_, pos_y * node_size_ * 2.0 / 3.0};
}
}
}
/**
* Link two nodes by the sockets of given IDs.
* Also releases the ownership of the "from" node for nodetree to free it.
* \param from_node_pos_x 0 to 4 value as per nodetree arrangement.
*/
void ShaderNodetreeWrap::link_sockets(unique_node_ptr from_node,
StringRef from_node_id,
bNode *to_node,
StringRef to_node_id,
const int from_node_pos_x)
{
std::tie(from_node->locx, from_node->locy) = set_node_locations(from_node_pos_x);
std::tie(to_node->locx, to_node->locy) = set_node_locations(from_node_pos_x + 1);
bNodeSocket *from_sock{nodeFindSocket(from_node.get(), SOCK_OUT, from_node_id.data())};
bNodeSocket *to_sock{nodeFindSocket(to_node, SOCK_IN, to_node_id.data())};
BLI_assert(from_sock && to_sock);
nodeAddLink(nodetree_.get(), from_node.get(), from_sock, to_node, to_sock);
static_cast<void>(from_node.release());
}
/**
* Set values of sockets in p-BSDF node of the nodetree.
*/
void ShaderNodetreeWrap::set_bsdf_socket_values()
{
const int illum = mtl_mat_.illum;
bool do_highlight = false;
bool do_tranparency = false;
bool do_reflection = false;
bool do_glass = false;
switch (illum) {
case 1: {
/* Base color on, ambient on. */
break;
}
case 2: {
/* Highlight on. */
do_highlight = true;
break;
}
case 3: {
/* Reflection on and Ray trace on. */
do_reflection = true;
break;
}
case 4: {
/* Transparency: Glass on, Reflection: Ray trace on. */
do_glass = true;
do_reflection = true;
do_tranparency = true;
break;
}
case 5: {
/* Reflection: Fresnel on and Ray trace on. */
do_reflection = true;
break;
}
case 6: {
/* Transparency: Refraction on, Reflection: Fresnel off and Ray trace on. */
do_reflection = true;
do_tranparency = true;
break;
}
case 7: {
/* Transparency: Refraction on, Reflection: Fresnel on and Ray trace on. */
do_reflection = true;
do_tranparency = true;
break;
}
case 8: {
/* Reflection on and Ray trace off. */
do_reflection = true;
break;
}
case 9: {
/* Transparency: Glass on, Reflection: Ray trace off. */
do_glass = true;
do_reflection = false;
do_tranparency = true;
break;
}
default: {
std::cerr << "Warning! illum value = " << illum
<< "is not supported by the Principled-BSDF shader." << std::endl;
break;
}
}
float specular = (mtl_mat_.Ks[0] + mtl_mat_.Ks[1] + mtl_mat_.Ks[2]) / 3;
float roughness = 1.0f - 1.0f / 30 * sqrt(std::max(0.0f, std::min(900.0f, mtl_mat_.Ns)));
float metallic = (mtl_mat_.Ka[0] + mtl_mat_.Ka[1] + mtl_mat_.Ka[2]) / 3;
float ior = mtl_mat_.Ni;
float alpha = mtl_mat_.d;
if (specular < 0.0f) {
specular = static_cast<float>(do_highlight);
}
if (mtl_mat_.Ns < 0.0f) {
roughness = static_cast<float>(!do_highlight);
}
if (metallic < 0.0f) {
if (do_reflection) {
metallic = 1.0f;
}
}
else {
metallic = 0.0f;
}
if (ior < 0) {
if (do_tranparency) {
ior = 1.0f;
}
if (do_glass) {
ior = 1.5f;
}
}
if (alpha < 0) {
if (do_tranparency) {
alpha = 1.0f;
}
}
float3 base_color = {std::max(0.0f, mtl_mat_.Kd[0]),
std::max(0.0f, mtl_mat_.Kd[1]),
std::max(0.0f, mtl_mat_.Kd[2])};
float3 emission_color = {std::max(0.0f, mtl_mat_.Ke[0]),
std::max(0.0f, mtl_mat_.Ke[1]),
std::max(0.0f, mtl_mat_.Ke[2])};
set_property_of_socket(SOCK_RGBA, "Base Color", {base_color, 3}, bsdf_.get());
set_property_of_socket(SOCK_RGBA, "Emission", {emission_color, 3}, bsdf_.get());
if (mtl_mat_.texture_maps.contains_as(eMTLSyntaxElement::map_Ke)) {
set_property_of_socket(SOCK_FLOAT, "Emission Strength", {1.0f}, bsdf_.get());
}
set_property_of_socket(SOCK_FLOAT, "Specular", {specular}, bsdf_.get());
set_property_of_socket(SOCK_FLOAT, "Roughness", {roughness}, bsdf_.get());
set_property_of_socket(SOCK_FLOAT, "Metallic", {metallic}, bsdf_.get());
set_property_of_socket(SOCK_FLOAT, "IOR", {ior}, bsdf_.get());
set_property_of_socket(SOCK_FLOAT, "Alpha", {alpha}, bsdf_.get());
}
/**
* Create image texture, vector and normal mapping nodes from MTL materials and link the
* nodes to p-BSDF node.
*/
void ShaderNodetreeWrap::add_image_textures(Main *bmain)
{
for (const Map<const eMTLSyntaxElement, tex_map_XX>::Item texture_map :
mtl_mat_.texture_maps.items()) {
if (texture_map.value.image_path.empty()) {
/* No Image texture node of this map type can be added to this material. */
continue;
}
unique_node_ptr image_texture{add_node_to_tree(SH_NODE_TEX_IMAGE)};
unique_node_ptr mapping{add_node_to_tree(SH_NODE_MAPPING)};
unique_node_ptr texture_coordinate(add_node_to_tree(SH_NODE_TEX_COORD));
unique_node_ptr normal_map = nullptr;
if (texture_map.key == eMTLSyntaxElement::map_Bump) {
normal_map.reset(add_node_to_tree(SH_NODE_NORMAL_MAP));
const float bump = std::max(0.0f, mtl_mat_.map_Bump_strength);
set_property_of_socket(SOCK_FLOAT, "Strength", {bump}, normal_map.get());
}
if (!load_texture_image(bmain, texture_map.value, image_texture.get())) {
/* Image could not be added, so don't link image texture, vector, normal map nodes. */
continue;
}
set_property_of_socket(
SOCK_VECTOR, "Location", {texture_map.value.translation, 3}, mapping.get());
set_property_of_socket(SOCK_VECTOR, "Scale", {texture_map.value.scale, 3}, mapping.get());
link_sockets(std::move(texture_coordinate), "UV", mapping.get(), "Vector", 0);
link_sockets(std::move(mapping), "Vector", image_texture.get(), "Vector", 1);
if (normal_map) {
link_sockets(std::move(image_texture), "Color", normal_map.get(), "Color", 2);
link_sockets(std::move(normal_map), "Normal", bsdf_.get(), "Normal", 3);
}
else {
link_sockets(
std::move(image_texture), "Color", bsdf_.get(), texture_map.value.dest_socket_id, 2);
}
}
}
} // namespace blender::io::obj

View File

@@ -1,115 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#pragma once
#include <array>
#include "BLI_float3.hh"
#include "BLI_map.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "DNA_node_types.h"
#include "MEM_guardedalloc.h"
#include "obj_export_mtl.hh"
namespace blender::io::obj {
struct UniqueNodeDeleter {
void operator()(bNode *node)
{
MEM_freeN(node);
}
};
struct UniqueNodetreeDeleter {
void operator()(bNodeTree *node)
{
MEM_freeN(node);
}
};
using unique_node_ptr = std::unique_ptr<bNode, UniqueNodeDeleter>;
using unique_nodetree_ptr = std::unique_ptr<bNodeTree, UniqueNodetreeDeleter>;
class ShaderNodetreeWrap {
private:
/* Node arrangement:
* Texture Coordinates -> Mapping -> Image Texture -> (optional) Normal Map -> p-BSDF -> Material
* Output. */
unique_nodetree_ptr nodetree_;
unique_node_ptr bsdf_;
unique_node_ptr shader_output_;
const MTLMaterial &mtl_mat_;
/* List of all locations occupied by nodes. */
Vector<std::array<int, 2>> node_locations;
const float node_size_{300.f};
public:
ShaderNodetreeWrap(Main *bmain, const MTLMaterial &mtl_mat);
~ShaderNodetreeWrap();
bNodeTree *get_nodetree();
private:
bNode *add_node_to_tree(const int node_type);
std::pair<float, float> set_node_locations(const int pos_x);
void link_sockets(unique_node_ptr from_node,
StringRef from_node_id,
bNode *to_node,
StringRef to_node_id,
const int from_node_pos_x);
void set_bsdf_socket_values();
void add_image_textures(Main *bmain);
};
constexpr eMTLSyntaxElement mtl_line_key_str_to_enum(const std::string_view key_str)
{
if (key_str == "map_Kd") {
return eMTLSyntaxElement::map_Kd;
}
if (key_str == "map_Ks") {
return eMTLSyntaxElement::map_Ks;
}
if (key_str == "map_Ns") {
return eMTLSyntaxElement::map_Ns;
}
if (key_str == "map_d") {
return eMTLSyntaxElement::map_d;
}
if (key_str == "refl" || key_str == "map_refl") {
return eMTLSyntaxElement::map_refl;
}
if (key_str == "map_Ke") {
return eMTLSyntaxElement::map_Ke;
}
if (key_str == "map_Bump" || key_str == "bump") {
return eMTLSyntaxElement::map_Bump;
}
return eMTLSyntaxElement::string;
}
} // namespace blender::io::obj

View File

@@ -1,123 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#include "BKE_object.h"
#include "DNA_curve_types.h"
#include "importer_mesh_utils.hh"
#include "obj_import_nurbs.hh"
#include "obj_import_objects.hh"
namespace blender::io::obj {
CurveFromGeometry::~CurveFromGeometry()
{
if (curve_object_ || blender_curve_) {
/* Move the object to own it. */
curve_object_.reset();
blender_curve_.reset();
BLI_assert(0);
}
}
void CurveFromGeometry::create_curve(Main *bmain, const OBJImportParams &import_params)
{
std::string ob_name{curve_geometry_.get_geometry_name()};
if (ob_name.empty() && !curve_geometry_.group().empty()) {
ob_name = curve_geometry_.group();
}
else {
ob_name = "Untitled";
}
blender_curve_.reset(BKE_curve_add(bmain, ob_name.c_str(), OB_CURVE));
curve_object_.reset(BKE_object_add_only_object(bmain, OB_CURVE, ob_name.c_str()));
blender_curve_->flag = CU_3D;
blender_curve_->resolu = blender_curve_->resolv = 12;
/* Only one NURBS spline will be created in the curve object. */
blender_curve_->actnu = 0;
Nurb *nurb = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), "OBJ import NURBS curve"));
BLI_addtail(BKE_curve_nurbs_get(blender_curve_.get()), nurb);
create_nurbs(import_params);
curve_object_->data = blender_curve_.release();
transform_object(curve_object_.get(), import_params);
}
/**
* Create a NURBS spline for the Curve converted from Geometry.
*/
void CurveFromGeometry::create_nurbs(const OBJImportParams & /*import_params */)
{
const NurbsElement &nurbs_geometry = curve_geometry_.nurbs_elem();
Nurb *nurb = static_cast<Nurb *>(blender_curve_->nurb.first);
nurb->type = CU_NURBS;
nurb->flag = CU_3D;
nurb->next = nurb->prev = nullptr;
/* BKE_nurb_points_add later on will update pntsu. If this were set to total curv points,
* we get double the total points in viewport. */
nurb->pntsu = 0;
/* Total points = pntsu * pntsv. */
nurb->pntsv = 1;
nurb->orderu = nurb->orderv = (nurbs_geometry.degree + 1 > SHRT_MAX) ? 4 :
nurbs_geometry.degree + 1;
nurb->resolu = nurb->resolv = blender_curve_->resolu;
const int64_t tot_vert{curve_geometry_.nurbs_elem().curv_indices.size()};
BKE_nurb_points_add(nurb, tot_vert);
for (int i = 0; i < tot_vert; i++) {
BPoint &bpoint = nurb->bp[i];
copy_v3_v3(bpoint.vec, global_vertices_.vertices[nurbs_geometry.curv_indices[i]]);
bpoint.vec[3] = 1.0f;
bpoint.weight = 1.0f;
}
BKE_nurb_knot_calc_u(nurb);
bool do_endpoints = true;
if (nurbs_geometry.curv_indices.size() &&
nurbs_geometry.parm.size() > nurbs_geometry.degree + 1) {
for (int i = 0; i < nurbs_geometry.degree + 1; i++) {
if (abs(nurbs_geometry.parm[i] - nurbs_geometry.curv_indices[0]) > 0.0001) {
do_endpoints = false;
break;
}
if (abs(nurbs_geometry.parm[-(i + 1)] - nurbs_geometry.curv_indices[1]) > 0.0001) {
do_endpoints = false;
break;
}
}
}
else {
do_endpoints = false;
}
if (do_endpoints) {
nurb->flagu = CU_NURB_ENDPOINT;
}
}
} // namespace blender::io::obj

View File

@@ -1,86 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#pragma once
#include <memory>
#include "BKE_curve.h"
#include "BLI_utility_mixins.hh"
#include "DNA_curve_types.h"
#include "obj_import_objects.hh"
namespace blender::io::obj {
/** Free a curve's memory using Blender's memory management. */
struct UniqueCurveDeleter {
void operator()(Curve *curve)
{
if (curve) {
BKE_nurbList_free(&curve->nurb);
}
}
};
/** An unique_ptr to a Curve with a custom deleter. Don't let unique_ptr free a curve with a
* different deallocator.
*/
using unique_curve_ptr = std::unique_ptr<Curve, UniqueCurveDeleter>;
/**
* Make a Blender NURBS Curve block from a Geometry of GEOM_CURVE type.
* Use the mover function to own the curve.
*/
class CurveFromGeometry : NonMovable, NonCopyable {
private:
/**
* Curve datablock of type CU_NURBS made from OBJ data..
*/
unique_curve_ptr blender_curve_;
/**
* Object of type OB_CURVE. Use the mover function to own it.
*/
unique_object_ptr curve_object_;
const Geometry &curve_geometry_;
const GlobalVertices &global_vertices_;
public:
CurveFromGeometry(const Geometry &geometry, const GlobalVertices &global_vertices)
: curve_geometry_(geometry), global_vertices_(global_vertices)
{
}
~CurveFromGeometry();
void create_curve(Main *bmain, const OBJImportParams &import_params);
unique_object_ptr mover()
{
return std::move(curve_object_);
}
private:
void create_nurbs(const OBJImportParams &import_params);
};
} // namespace blender::io::obj

View File

@@ -1,155 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#include "BKE_collection.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "DNA_collection_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "obj_import_objects.hh"
namespace blender::io::obj {
eGeometryType Geometry::get_geom_type() const
{
return geom_type_;
}
/**
* Use very rarely. Only when it is guaranteed that the
* type originally set is wrong.
*/
void Geometry::set_geom_type(const eGeometryType new_type)
{
geom_type_ = new_type;
}
StringRef Geometry::get_geometry_name() const
{
return geometry_name_;
}
void Geometry::set_geometry_name(StringRef new_name)
{
geometry_name_ = std::string(new_name);
}
/**
* Returns an index that ranges from zero to total coordinates in the
* global list of vertices.
*/
int64_t Geometry::vertex_index(const int64_t index) const
{
return vertex_indices_[index];
}
int64_t Geometry::total_verts() const
{
return vertex_indices_.size();
}
Span<PolyElem> Geometry::face_elements() const
{
return face_elements_;
}
const PolyElem &Geometry::ith_face_element(const int64_t index) const
{
return face_elements_[index];
}
int64_t Geometry::total_face_elems() const
{
return face_elements_.size();
}
bool Geometry::use_vertex_groups() const
{
return use_vertex_groups_;
}
Span<MEdge> Geometry::edges() const
{
return edges_;
}
int64_t Geometry::total_edges() const
{
return edges_.size();
}
int Geometry::total_loops() const
{
return total_loops_;
}
int64_t Geometry::vertex_normal_index(const int64_t vertex_index) const
{
return vertex_normal_indices_[vertex_index];
}
int64_t Geometry::total_normals() const
{
return vertex_normal_indices_.size();
}
const VectorSet<std::string> &Geometry::material_names() const
{
return material_names_;
}
const NurbsElement &Geometry::nurbs_elem() const
{
return nurbs_element_;
}
const std::string &Geometry::group() const
{
return nurbs_element_.group_;
}
/**
* Create a collection to store all imported objects.
*/
OBJImportCollection::OBJImportCollection(Main *bmain, Scene *scene) : bmain_(bmain), scene_(scene)
{
obj_import_collection_ = BKE_collection_add(
bmain_, scene_->master_collection, "OBJ import collection");
}
/**
* Add the given Mesh/Curve object to the OBJ import collection.
*/
void OBJImportCollection::add_object_to_collection(unique_object_ptr b_object)
{
BKE_collection_object_add(bmain_, obj_import_collection_, b_object.release());
id_fake_user_set(&obj_import_collection_->id);
DEG_id_tag_update(&obj_import_collection_->id, ID_RECALC_COPY_ON_WRITE);
DEG_relations_tag_update(bmain_);
}
} // namespace blender::io::obj

View File

@@ -1,183 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#pragma once
#include "BKE_lib_id.h"
#include "BLI_float2.hh"
#include "BLI_float3.hh"
#include "BLI_vector.hh"
#include "BLI_vector_set.hh"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
namespace blender::io::obj {
/**
* List of all vertex and UV vertex coordinates in an OBJ file accessible to any
* Geometry instance at any time.
*/
struct GlobalVertices {
Vector<float3> vertices;
Vector<float2> uv_vertices;
Vector<float3> vertex_normals;
};
/**
* Keeps track of the vertices that belong to other Geometries.
* Needed only for MLoop.v and MEdge.v1 which needs vertex indices ranging from (0 to total
* vertices in the mesh) as opposed to the other OBJ indices ranging from (0 to total vertices
* in the global list).
*/
struct VertexIndexOffset {
private:
int offset_ = 0;
public:
void set_index_offset(const int64_t total_vertices)
{
offset_ = total_vertices;
}
int64_t get_index_offset() const
{
return offset_;
}
};
/**
* A face's corner in an OBJ file. In Blender, it translates to a mloop vertex.
*/
struct PolyCorner {
/* 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
* it can be less than -1 too. */
int uv_vert_index = -1;
int vertex_normal_index;
};
struct PolyElem {
std::string vertex_group;
std::string material_name;
bool shaded_smooth = false;
Vector<PolyCorner> face_corners;
/* Not read from the OBJ file. Set to true for potentially invalid polygons. */
bool invalid = false;
};
/**
* Contains data for one single NURBS curve in the OBJ file.
*/
struct NurbsElement {
/**
* For curves, groups may be used to specify multiple splines in the same curve object.
* It may also serve as the name of the curve if not specified explicitly.
*/
std::string group_;
int degree = 0;
/**
* Indices into the global list of vertex coordinates. Must be non-negative.
*/
Vector<int> curv_indices;
/* Values in the parm u/v line in a curve definition. */
Vector<float> parm;
};
enum eGeometryType {
GEOM_MESH = OB_MESH,
GEOM_CURVE = OB_CURVE,
};
class Geometry {
private:
eGeometryType geom_type_ = GEOM_MESH;
std::string geometry_name_;
VectorSet<std::string> material_names_;
/**
* Indices in the vector range from zero to total vertices in a geometry.
* Values range from zero to total coordinates in the global list.
*/
Vector<int> vertex_indices_;
Vector<int> vertex_normal_indices_;
/** Edges written in the file in addition to (or even without polygon) elements. */
Vector<MEdge> edges_;
Vector<PolyElem> face_elements_;
bool use_vertex_groups_ = false;
NurbsElement nurbs_element_;
int total_loops_ = 0;
public:
Geometry(eGeometryType type, StringRef ob_name)
: geom_type_(type), geometry_name_(std::string(ob_name)){};
eGeometryType get_geom_type() const;
void set_geom_type(const eGeometryType new_type);
StringRef get_geometry_name() const;
void set_geometry_name(StringRef new_name);
int64_t vertex_index(const int64_t index) const;
int64_t total_verts() const;
Span<PolyElem> face_elements() const;
const PolyElem &ith_face_element(const int64_t index) const;
int64_t total_face_elems() const;
bool use_vertex_groups() const;
Span<MEdge> edges() const;
int64_t total_edges() const;
int total_loops() const;
int64_t vertex_normal_index(const int64_t vertex_index) const;
int64_t total_normals() const;
const VectorSet<std::string> &material_names() const;
const NurbsElement &nurbs_elem() const;
const std::string &group() const;
friend class OBJStorer;
};
struct UniqueObjectDeleter {
void operator()(Object *object)
{
BKE_id_free(nullptr, object);
}
};
using unique_object_ptr = std::unique_ptr<Object, UniqueObjectDeleter>;
class OBJImportCollection {
private:
Main *bmain_;
Scene *scene_;
/**
* The collection that holds all the imported objects.
*/
Collection *obj_import_collection_;
public:
OBJImportCollection(Main *bmain, Scene *scene);
void add_object_to_collection(unique_object_ptr b_object);
};
} // namespace blender::io::obj

View File

@@ -1,91 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#include <string>
#include "BLI_float2.hh"
#include "BLI_float3.hh"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_string_ref.hh"
#include "BKE_scene.h"
#include "obj_import_file_reader.hh"
#include "obj_import_mesh.hh"
#include "obj_import_nurbs.hh"
#include "obj_import_objects.hh"
#include "obj_importer.hh"
namespace blender::io::obj {
/**
* Make Blender Mesh, Curve etc from Geometry and add them to the import collection.
*/
static void geometry_to_blender_objects(
Main *bmain,
Scene *scene,
const OBJImportParams &import_params,
Vector<std::unique_ptr<Geometry>> &all_geometries,
const GlobalVertices &global_vertices,
const Map<std::string, std::unique_ptr<MTLMaterial>> &materials)
{
OBJImportCollection import_collection{bmain, scene};
for (const std::unique_ptr<Geometry> &geometry : all_geometries) {
if (geometry->get_geom_type() == GEOM_MESH) {
MeshFromGeometry mesh_ob_from_geometry{*geometry, global_vertices};
mesh_ob_from_geometry.create_mesh(bmain, materials, import_params);
import_collection.add_object_to_collection(mesh_ob_from_geometry.mover());
}
else if (geometry->get_geom_type() == GEOM_CURVE) {
CurveFromGeometry curve_ob_from_geometry(*geometry, global_vertices);
curve_ob_from_geometry.create_curve(bmain, import_params);
import_collection.add_object_to_collection(curve_ob_from_geometry.mover());
}
}
}
void importer_main(bContext *C, const OBJImportParams &import_params)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
/* List of Geometry instances to be parsed from OBJ file. */
Vector<std::unique_ptr<Geometry>> all_geometries;
/* Container for vertex and UV vertex coordinates. */
GlobalVertices global_vertices;
/* List of MTLMaterial instances to be parsed from MTL file. */
Map<std::string, std::unique_ptr<MTLMaterial>> materials;
OBJParser obj_parser{import_params};
obj_parser.parse(all_geometries, global_vertices);
for (StringRef mtl_library : obj_parser.mtl_libraries()) {
MTLParser mtl_parser{mtl_library, import_params.filepath};
mtl_parser.parse_and_store(materials);
}
geometry_to_blender_objects(
bmain, scene, import_params, all_geometries, global_vertices, materials);
static_cast<void>(CTX_data_ensure_evaluated_depsgraph(C));
}
} // namespace blender::io::obj

View File

@@ -1,31 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup obj
*/
#pragma once
#include "IO_wavefront_obj.h"
namespace blender::io::obj {
void importer_main(bContext *C, const OBJImportParams &import_params);
} // namespace blender::io::obj

View File

@@ -1,217 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
#include <fstream>
#include <iostream>
#include <sstream>
#include "BLI_float3.hh"
#include "BLI_span.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "parser_string_utils.hh"
namespace blender::io::obj {
using std::string;
/**
* Store multiple lines separated by an escaped newline character: `\\n`.
* Use this before doing any parse operations on the read string.
*/
void read_next_line(std::ifstream &file, string &r_line)
{
std::string new_line;
while (file.good() && !r_line.empty() && r_line.back() == '\\') {
new_line.clear();
const bool ok = static_cast<bool>(std::getline(file, new_line));
/* Remove the last backslash character. */
r_line.pop_back();
r_line.append(new_line);
if (!ok || new_line.empty()) {
return;
}
}
}
/**
* Split a line string into the first word (key) and the rest of the line.
* Also remove leading & trailing spaces as well as `\r` carriage return
* character if present.
*/
void split_line_key_rest(const StringRef line, StringRef &r_line_key, StringRef &r_rest_line)
{
if (line.is_empty()) {
return;
}
const int64_t pos_split{line.find_first_of(' ')};
if (pos_split == StringRef::not_found) {
/* Use the first character if no space is found in the line. It's usually a comment like:
* #This is a comment. */
r_line_key = line.substr(0, 1);
}
else {
r_line_key = line.substr(0, pos_split);
}
/* Eat the delimiter also using "+ 1". */
r_rest_line = line.drop_prefix(r_line_key.size() + 1);
if (r_rest_line.is_empty()) {
return;
}
/* Remove any leading spaces, trailing spaces & \r character, if any. */
const int64_t leading_space{r_rest_line.find_first_not_of(' ')};
if (leading_space != StringRef::not_found) {
r_rest_line = r_rest_line.drop_prefix(leading_space);
}
/* Another way is to do a test run before the actual parsing to find the newline
* character and use it in the getline. */
const int64_t carriage_return{r_rest_line.find_first_of('\r')};
if (carriage_return != StringRef::not_found) {
r_rest_line = r_rest_line.substr(0, carriage_return + 1);
}
const int64_t trailing_space{r_rest_line.find_last_not_of(' ')};
if (trailing_space != StringRef::not_found) {
/* The position is of a character that is not ' ', so count of characters is position + 1. */
r_rest_line = r_rest_line.substr(0, trailing_space + 1);
}
}
/**
* Split the given string by the delimiter and fill the given vector.
* If an intermediate string is empty, or space or null character, it is not appended to the
* vector.
*/
void split_by_char(StringRef in_string, const char delimiter, Vector<StringRef> &r_out_list)
{
r_out_list.clear();
while (!in_string.is_empty()) {
const int64_t pos_delim{in_string.find_first_of(delimiter)};
const int64_t word_len = pos_delim == StringRef::not_found ? in_string.size() : pos_delim;
StringRef word{in_string.data(), word_len};
if (!word.is_empty() && !(word == " " && !(word[0] == '\0'))) {
r_out_list.append(word);
}
if (pos_delim == StringRef::not_found) {
return;
}
/* Skip the word already stored. */
in_string = in_string.drop_prefix(word_len);
/* Skip all delimiters. */
in_string = in_string.drop_prefix(
std::min(in_string.find_first_not_of(delimiter), in_string.size()));
}
}
/**
* Convert the given string to float and assign it to the destination value.
*
* Catches exception if the string cannot be converted to a float. The destination value
* is set to the given fallback value in that case.
*/
void copy_string_to_float(StringRef src, const float fallback_value, float &r_dst)
{
try {
r_dst = std::stof(string(src));
}
catch (const std::invalid_argument &inv_arg) {
std::cerr << "Bad conversion to float:'" << inv_arg.what() << "':'" << src << "'" << std::endl;
r_dst = fallback_value;
}
catch (const std::out_of_range &out_of_range) {
std::cerr << "Out of range for float:'" << out_of_range.what() << ":'" << src << "'"
<< std::endl;
r_dst = fallback_value;
}
}
/**
* Convert all members of the Span of strings to floats and assign them to the float
* array members. Usually used for values like coordinates.
*
* Catches exception if any string cannot be converted to a float. The destination
* float is set to the given fallback value in that case.
*/
void copy_string_to_float(Span<StringRef> src,
const float fallback_value,
MutableSpan<float> r_dst)
{
BLI_assert(src.size() >= r_dst.size());
for (int i = 0; i < r_dst.size(); ++i) {
copy_string_to_float(src[i], fallback_value, r_dst[i]);
}
}
/**
* Convert the given string to int and assign it to the destination value.
*
* Catches exception if the string cannot be converted to an integer. The destination
* int is set to the given fallback value in that case.
*/
void copy_string_to_int(StringRef src, const int fallback_value, int &r_dst)
{
try {
r_dst = std::stoi(string(src));
}
catch (const std::invalid_argument &inv_arg) {
std::cerr << "Bad conversion to int:'" << inv_arg.what() << "':'" << src << "'" << std::endl;
r_dst = fallback_value;
}
catch (const std::out_of_range &out_of_range) {
std::cerr << "Out of range for int:'" << out_of_range.what() << ":'" << src << "'"
<< std::endl;
r_dst = fallback_value;
}
}
/**
* Convert the given strings to ints and fill the destination int buffer.
*
* Catches exception if any string cannot be converted to an integer. The destination
* int is set to the given fallback value in that case.
*/
void copy_string_to_int(Span<StringRef> src, const int fallback_value, MutableSpan<int> r_dst)
{
BLI_assert(src.size() >= r_dst.size());
for (int i = 0; i < r_dst.size(); ++i) {
copy_string_to_int(src[i], fallback_value, r_dst[i]);
}
}
std::string replace_all_occurences(StringRef original, StringRef to_remove, StringRef to_add)
{
std::string clean{original};
while (true) {
const std::string::size_type pos = clean.find(to_remove);
if (pos == std::string::npos) {
break;
}
clean.replace(pos, to_add.size(), to_add);
}
return clean;
}
} // namespace blender::io::obj

View File

@@ -1,33 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
namespace blender::io::obj {
void read_next_line(std::ifstream &file, std::string &r_line);
void split_line_key_rest(StringRef line, StringRef &r_line_key, StringRef &r_rest_line);
void split_by_char(StringRef in_string, const char delimiter, Vector<StringRef> &r_out_list);
void copy_string_to_float(StringRef src, const float fallback_value, float &r_dst);
void copy_string_to_float(Span<StringRef> src,
const float fallback_value,
MutableSpan<float> r_dst);
void copy_string_to_int(StringRef src, const int fallback_value, int &r_dst);
void copy_string_to_int(Span<StringRef> src, const int fallback_value, MutableSpan<int> r_dst);
std::string replace_all_occurences(StringRef original, StringRef to_remove, StringRef to_add);
} // namespace blender::io::obj

View File

@@ -1,416 +0,0 @@
/* Apache License, Version 2.0 */
#include <fstream>
#include <gtest/gtest.h>
#include <ios>
#include <memory>
#include <sstream>
#include <string>
#include <system_error>
#include "testing/testing.h"
#include "tests/blendfile_loading_base_test.h"
#include "BKE_appdir.h"
#include "BKE_blender_version.h"
#include "BLI_fileops.h"
#include "BLI_index_range.hh"
#include "BLI_string_utf8.h"
#include "BLI_vector.hh"
#include "DEG_depsgraph.h"
#include "obj_export_file_writer.hh"
#include "obj_export_mesh.hh"
#include "obj_export_nurbs.hh"
#include "obj_exporter.hh"
#include "obj_exporter_tests.hh"
namespace blender::io::obj {
/* This is also the test name. */
class obj_exporter_test : public BlendfileLoadingBaseTest {
public:
/**
* \param filepath: relative to "tests" directory.
*/
bool load_file_and_depsgraph(const std::string &filepath,
const eEvaluationMode eval_mode = DAG_EVAL_VIEWPORT)
{
if (!blendfile_load(filepath.c_str())) {
return false;
}
depsgraph_create(eval_mode);
return true;
}
};
const std::string all_objects_file = "io_tests/blend_scene/all_objects.blend";
const std::string all_curve_objects_file = "io_tests/blend_scene/all_curves.blend";
TEST_F(obj_exporter_test, filter_objects_curves_as_mesh)
{
OBJExportParamsDefault _export;
if (!load_file_and_depsgraph(all_objects_file)) {
ADD_FAILURE();
return;
}
auto [objmeshes, objcurves]{filter_supported_objects(depsgraph, _export.params)};
EXPECT_EQ(objmeshes.size(), 17);
EXPECT_EQ(objcurves.size(), 0);
}
TEST_F(obj_exporter_test, filter_objects_curves_as_nurbs)
{
OBJExportParamsDefault _export;
if (!load_file_and_depsgraph(all_objects_file)) {
ADD_FAILURE();
return;
}
_export.params.export_curves_as_nurbs = true;
auto [objmeshes, objcurves]{filter_supported_objects(depsgraph, _export.params)};
EXPECT_EQ(objmeshes.size(), 16);
EXPECT_EQ(objcurves.size(), 2);
}
TEST_F(obj_exporter_test, filter_objects_selected)
{
OBJExportParamsDefault _export;
if (!load_file_and_depsgraph(all_objects_file)) {
ADD_FAILURE();
return;
}
_export.params.export_selected_objects = true;
_export.params.export_curves_as_nurbs = true;
auto [objmeshes, objcurves]{filter_supported_objects(depsgraph, _export.params)};
EXPECT_EQ(objmeshes.size(), 1);
EXPECT_EQ(objcurves.size(), 0);
}
TEST(obj_exporter_utils, append_negative_frame_to_filename)
{
const char path_original[FILE_MAX] = "/my_file.obj";
const char path_truth[FILE_MAX] = "/my_file-123.obj";
const int frame = -123;
char path_with_frame[FILE_MAX] = {0};
const bool ok = append_frame_to_filename(path_original, frame, path_with_frame);
EXPECT_TRUE(ok);
EXPECT_EQ_ARRAY(path_with_frame, path_truth, BLI_strlen_utf8(path_truth));
}
TEST(obj_exporter_utils, append_positive_frame_to_filename)
{
const char path_original[FILE_MAX] = "/my_file.obj";
const char path_truth[FILE_MAX] = "/my_file123.obj";
const int frame = 123;
char path_with_frame[FILE_MAX] = {0};
const bool ok = append_frame_to_filename(path_original, frame, path_with_frame);
EXPECT_TRUE(ok);
EXPECT_EQ_ARRAY(path_with_frame, path_truth, BLI_strlen_utf8(path_truth));
}
TEST_F(obj_exporter_test, curve_nurbs_points)
{
if (!load_file_and_depsgraph(all_curve_objects_file)) {
ADD_FAILURE();
return;
}
OBJExportParamsDefault _export;
_export.params.export_curves_as_nurbs = true;
auto [objmeshes_unused, objcurves]{filter_supported_objects(depsgraph, _export.params)};
for (StealUniquePtr<OBJCurve> objcurve : objcurves) {
if (all_nurbs_truth.count(objcurve->get_curve_name()) != 1) {
ADD_FAILURE();
return;
}
const NurbsObject *const nurbs_truth = all_nurbs_truth.at(objcurve->get_curve_name()).get();
EXPECT_EQ(objcurve->total_splines(), nurbs_truth->total_splines());
for (int spline_index : IndexRange(objcurve->total_splines())) {
EXPECT_EQ(objcurve->total_spline_vertices(spline_index),
nurbs_truth->total_spline_vertices(spline_index));
EXPECT_EQ(objcurve->get_nurbs_degree(spline_index),
nurbs_truth->get_nurbs_degree(spline_index));
EXPECT_EQ(objcurve->total_spline_control_points(spline_index),
nurbs_truth->total_spline_control_points(spline_index));
}
}
}
TEST_F(obj_exporter_test, curve_coordinates)
{
if (!load_file_and_depsgraph(all_curve_objects_file)) {
ADD_FAILURE();
return;
}
OBJExportParamsDefault _export;
_export.params.export_curves_as_nurbs = true;
auto [objmeshes_unused, objcurves]{filter_supported_objects(depsgraph, _export.params)};
for (StealUniquePtr<OBJCurve> objcurve : objcurves) {
if (all_nurbs_truth.count(objcurve->get_curve_name()) != 1) {
ADD_FAILURE();
return;
}
const NurbsObject *const nurbs_truth = all_nurbs_truth.at(objcurve->get_curve_name()).get();
EXPECT_EQ(objcurve->total_splines(), nurbs_truth->total_splines());
for (int spline_index : IndexRange(objcurve->total_splines())) {
for (int vertex_index : IndexRange(objcurve->total_spline_vertices(spline_index))) {
EXPECT_V3_NEAR(objcurve->vertex_coordinates(
spline_index, vertex_index, _export.params.scaling_factor),
nurbs_truth->vertex_coordinates(spline_index, vertex_index),
0.000001f);
}
}
}
}
static std::unique_ptr<OBJWriter> init_writer(const OBJExportParams &params,
const std::string out_filepath)
{
try {
auto writer = std::make_unique<OBJWriter>(out_filepath.c_str(), params);
return writer;
}
catch (const std::system_error &ex) {
std::cerr << ex.code().category().name() << ": " << ex.what() << ": " << ex.code().message()
<< std::endl;
return nullptr;
}
}
/* The following is relative to BKE_tempdir_base. */
const char *const temp_file_path = "output.OBJ";
static std::string read_temp_file_in_string(const std::string &file_path)
{
std::ifstream temp_stream(file_path);
std::ostringstream input_ss;
input_ss << temp_stream.rdbuf();
return input_ss.str();
}
TEST(obj_exporter_writer, header)
{
/* Because testing doesn't fully initialize Blender, we need the following. */
BKE_tempdir_init(NULL);
std::string out_file_path = blender::tests::flags_test_release_dir() + "/" + temp_file_path;
{
OBJExportParamsDefault _export;
std::unique_ptr<OBJWriter> writer = init_writer(_export.params, out_file_path);
if (!writer) {
ADD_FAILURE();
return;
}
writer->write_header();
}
const std::string result = read_temp_file_in_string(out_file_path);
using namespace std::string_literals;
ASSERT_EQ(result, "# Blender "s + BKE_blender_version_string() + "\n" + "# www.blender.org\n");
BLI_delete(out_file_path.c_str(), false, false);
}
TEST(obj_exporter_writer, mtllib)
{
std::string out_file_path = blender::tests::flags_test_release_dir() + "/" + temp_file_path;
{
OBJExportParamsDefault _export;
std::unique_ptr<OBJWriter> writer = init_writer(_export.params, out_file_path);
if (!writer) {
ADD_FAILURE();
return;
}
writer->write_mtllib_name("/Users/blah.mtl");
writer->write_mtllib_name("\\C:\\blah.mtl");
}
const std::string result = read_temp_file_in_string(out_file_path);
ASSERT_EQ(result, "mtllib blah.mtl\nmtllib blah.mtl\n");
}
/* Return true if string #a and string #b are equal after their first newline. */
static bool strings_equal_after_first_lines(const std::string &a, const std::string &b)
{
bool dbg_level = 0;
size_t a_len = a.size();
size_t b_len = b.size();
size_t a_next = a.find_first_of('\n');
size_t b_next = b.find_first_of('\n');
if (a_next == std::string::npos || b_next == std::string::npos) {
if (dbg_level > 0) {
std::cout << "Couldn't find newline in one of args\n";
}
return false;
}
if (dbg_level > 0) {
if (a.compare(a_next, a_len - a_next, b, b_next, b_len - b_next) != 0) {
for (int i = 0; i < a_len - a_next && i < b_len - b_next; ++i) {
if (a[a_next + i] != b[b_next + i]) {
std::cout << "Difference found at pos " << a_next + i << " of a\n";
std::cout << "a: " << a.substr(a_next + i, 100) << " ...\n";
std::cout << "b: " << b.substr(b_next + i, 100) << " ... \n";
return false;
}
}
}
else {
return true;
}
}
return a.compare(a_next, a_len - a_next, b, b_next, b_len - b_next) == 0;
}
/* From here on, tests are whole file tests, testing for golden output. */
class obj_exporter_regression_test : public obj_exporter_test {
public:
/**
* Export the given blend file with the given parameters and
* test to see if it matches a golden file (ignoring any difference in Blender version number).
* \param blendfile: input, relative to "tests" directory.
* \param golden_obj: expected output, relative to "tests" directory.
* \param params: the parameters to be used for export.
*/
void compare_obj_export_to_golden(const std::string &blendfile,
const std::string &golden_obj,
const std::string &golden_mtl,
OBJExportParams &params)
{
if (!load_file_and_depsgraph(blendfile)) {
return;
}
/* Because testing doesn't fully initialize Blender, we need the following. */
BKE_tempdir_init(NULL);
std::string tempdir = std::string(BKE_tempdir_base());
std::string out_file_path = tempdir + BLI_path_basename(golden_obj.c_str());
strncpy(params.filepath, out_file_path.c_str(), FILE_MAX);
params.blen_filepath = blendfile.c_str();
export_frame(depsgraph, params, out_file_path.c_str());
std::string output_str = read_temp_file_in_string(out_file_path);
std::string golden_file_path = blender::tests::flags_test_asset_dir() + "/" + golden_obj;
std::string golden_str = read_temp_file_in_string(golden_file_path);
ASSERT_TRUE(strings_equal_after_first_lines(output_str, golden_str));
BLI_delete(out_file_path.c_str(), false, false);
if (!golden_mtl.empty()) {
std::string out_mtl_file_path = tempdir + BLI_path_basename(golden_mtl.c_str());
std::string output_mtl_str = read_temp_file_in_string(out_mtl_file_path);
std::string golden_mtl_file_path = blender::tests::flags_test_asset_dir() + "/" + golden_mtl;
std::string golden_mtl_str = read_temp_file_in_string(golden_mtl_file_path);
ASSERT_TRUE(strings_equal_after_first_lines(output_mtl_str, golden_mtl_str));
BLI_delete(out_mtl_file_path.c_str(), false, false);
}
}
};
TEST_F(obj_exporter_regression_test, all_tris)
{
OBJExportParamsDefault _export;
compare_obj_export_to_golden("io_tests/blend_geometry/all_tris.blend",
"io_tests/obj/all_tris.obj",
"io_tests/obj/all_tris.mtl",
_export.params);
}
TEST_F(obj_exporter_regression_test, all_quads)
{
OBJExportParamsDefault _export;
_export.params.scaling_factor = 2.0f;
_export.params.export_materials = false;
compare_obj_export_to_golden(
"io_tests/blend_geometry/all_quads.blend", "io_tests/obj/all_quads.obj", "", _export.params);
}
TEST_F(obj_exporter_regression_test, fgons)
{
OBJExportParamsDefault _export;
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
_export.params.up_axis = OBJ_AXIS_Z_UP;
_export.params.export_materials = false;
compare_obj_export_to_golden(
"io_tests/blend_geometry/fgons.blend", "io_tests/obj/fgons.obj", "", _export.params);
}
TEST_F(obj_exporter_regression_test, edges)
{
OBJExportParamsDefault _export;
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
_export.params.up_axis = OBJ_AXIS_Z_UP;
_export.params.export_materials = false;
compare_obj_export_to_golden(
"io_tests/blend_geometry/edges.blend", "io_tests/obj/edges.obj", "", _export.params);
}
TEST_F(obj_exporter_regression_test, vertices)
{
OBJExportParamsDefault _export;
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
_export.params.up_axis = OBJ_AXIS_Z_UP;
_export.params.export_materials = false;
compare_obj_export_to_golden(
"io_tests/blend_geometry/vertices.blend", "io_tests/obj/vertices.obj", "", _export.params);
}
TEST_F(obj_exporter_regression_test, nurbs_as_nurbs)
{
OBJExportParamsDefault _export;
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
_export.params.up_axis = OBJ_AXIS_Z_UP;
_export.params.export_materials = false;
_export.params.export_curves_as_nurbs = true;
compare_obj_export_to_golden(
"io_tests/blend_geometry/nurbs.blend", "io_tests/obj/nurbs.obj", "", _export.params);
}
TEST_F(obj_exporter_regression_test, nurbs_as_mesh)
{
OBJExportParamsDefault _export;
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
_export.params.up_axis = OBJ_AXIS_Z_UP;
_export.params.export_materials = false;
_export.params.export_curves_as_nurbs = false;
compare_obj_export_to_golden(
"io_tests/blend_geometry/nurbs.blend", "io_tests/obj/nurbs_mesh.obj", "", _export.params);
}
TEST_F(obj_exporter_regression_test, cube_all_data_triangulated)
{
OBJExportParamsDefault _export;
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
_export.params.up_axis = OBJ_AXIS_Z_UP;
_export.params.export_materials = false;
_export.params.export_triangulated_mesh = true;
compare_obj_export_to_golden("io_tests/blend_geometry/cube_all_data.blend",
"io_tests/obj/cube_all_data_triangulated.obj",
"",
_export.params);
}
TEST_F(obj_exporter_regression_test, suzanne_all_data)
{
OBJExportParamsDefault _export;
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
_export.params.up_axis = OBJ_AXIS_Z_UP;
_export.params.export_materials = false;
_export.params.export_smooth_groups = true;
compare_obj_export_to_golden("io_tests/blend_geometry/suzanne_all_data.blend",
"io_tests/obj/suzanne_all_data.obj",
"",
_export.params);
}
TEST_F(obj_exporter_regression_test, all_objects)
{
OBJExportParamsDefault _export;
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
_export.params.up_axis = OBJ_AXIS_Z_UP;
_export.params.export_smooth_groups = true;
compare_obj_export_to_golden("io_tests/blend_scene/all_objects.blend",
"io_tests/obj/all_objects.obj",
"io_tests/obj/all_objects.mtl",
_export.params);
}
} // namespace blender::io::obj

View File

@@ -1,149 +0,0 @@
/* Apache License, Version 2.0 */
/**
* This file contains default values for several items like
* vertex coordinates, export parameters, MTL values etc.
*/
#pragma once
#include <array>
#include <gtest/gtest.h>
#include <string>
#include <vector>
#include "IO_wavefront_obj.h"
namespace blender::io::obj {
using array_float_3 = std::array<float, 3>;
/**
* This matches #OBJCurve's member functions, except that all the numbers and names are known
* constants. Used to store expected values of NURBS Curve sobjects.
*/
class NurbsObject {
private:
std::string nurbs_name_;
/* The indices in these vectors are spline indices. */
std::vector<std::vector<array_float_3>> coordinates_;
std::vector<int> degrees_;
std::vector<int> control_points_;
public:
NurbsObject(const std::string nurbs_name,
const std::vector<std::vector<array_float_3>> coordinates,
const std::vector<int> degrees,
const std::vector<int> control_points)
: nurbs_name_(nurbs_name),
coordinates_(coordinates),
degrees_(degrees),
control_points_(control_points)
{
}
int total_splines() const
{
return coordinates_.size();
}
int total_spline_vertices(const int spline_index) const
{
if (spline_index >= coordinates_.size()) {
ADD_FAILURE();
return 0;
}
return coordinates_[spline_index].size();
}
const float *vertex_coordinates(const int spline_index, const int vertex_index) const
{
return coordinates_[spline_index][vertex_index].data();
}
int get_nurbs_degree(const int spline_index) const
{
return degrees_[spline_index];
}
int total_spline_control_points(const int spline_index) const
{
return control_points_[spline_index];
}
};
struct OBJExportParamsDefault {
OBJExportParams params;
OBJExportParamsDefault()
{
params.filepath[0] = '\0';
params.blen_filepath = "";
params.export_animation = false;
params.start_frame = 0;
params.end_frame = 1;
params.forward_axis = OBJ_AXIS_NEGATIVE_Z_FORWARD;
params.up_axis = OBJ_AXIS_Y_UP;
params.scaling_factor = 1.f;
params.export_eval_mode = DAG_EVAL_VIEWPORT;
params.export_selected_objects = false;
params.export_uv = true;
params.export_normals = true;
params.export_materials = true;
params.export_triangulated_mesh = false;
params.export_curves_as_nurbs = false;
params.export_object_groups = false;
params.export_material_groups = false;
params.export_vertex_groups = false;
params.export_smooth_groups = true;
params.smooth_groups_bitflags = false;
}
};
const std::vector<std::vector<array_float_3>> coordinates_NurbsCurve{
{{6.94742, 0.000000, 0.000000},
{7.44742, 0.000000, -1.000000},
{9.44742, 0.000000, -1.000000},
{9.94742, 0.000000, 0.000000}}};
const std::vector<std::vector<array_float_3>> coordinates_NurbsCircle{
{{11.463165, 0.000000, 1.000000},
{10.463165, 0.000000, 1.000000},
{10.463165, 0.000000, 0.000000},
{10.463165, 0.000000, -1.000000},
{11.463165, 0.000000, -1.000000},
{12.463165, 0.000000, -1.000000},
{12.463165, 0.000000, 0.000000},
{12.463165, 0.000000, 1.000000}}};
const std::vector<std::vector<array_float_3>> coordinates_NurbsPathCurve{
{{13.690557, 0.000000, 0.000000},
{14.690557, 0.000000, 0.000000},
{15.690557, 0.000000, 0.000000},
{16.690557, 0.000000, 0.000000},
{17.690557, 0.000000, 0.000000}},
{{14.192808, 0.000000, 0.000000},
{14.692808, 0.000000, -1.000000},
{16.692808, 0.000000, -1.000000},
{17.192808, 0.000000, 0.000000}}};
const std::map<std::string, std::unique_ptr<NurbsObject>> all_nurbs_truth = []() {
std::map<std::string, std::unique_ptr<NurbsObject>> all_nurbs;
all_nurbs.emplace(
"NurbsCurve",
/* Name, coordinates, degrees of splines, control points of splines. */
std::make_unique<NurbsObject>(
"NurbsCurve", coordinates_NurbsCurve, std::vector<int>{3}, std::vector<int>{4}));
all_nurbs.emplace(
"NurbsCircle",
std::make_unique<NurbsObject>(
"NurbsCircle", coordinates_NurbsCircle, std::vector<int>{3}, std::vector<int>{11}));
/* This is actually an Object containing a NurbsPath and a NurbsCurve spline. */
all_nurbs.emplace("NurbsPathCurve",
std::make_unique<NurbsObject>("NurbsPathCurve",
coordinates_NurbsPathCurve,
std::vector<int>{3, 3},
std::vector<int>{5, 4}));
return all_nurbs;
}();
} // namespace blender::io::obj

View File

@@ -57,13 +57,8 @@ using fn::GPointer;
using fn::GSpan;
using fn::GVArray;
using fn::GVArray_GSpan;
using fn::GVArray_Span;
using fn::GVArray_Typed;
using fn::GVArrayPtr;
using fn::GVMutableArray;
using fn::GVMutableArray_GSpan;
using fn::GVMutableArray_Typed;
using fn::GVMutableArrayPtr;
using geometry_nodes_eval_log::NodeWarningType;
/**
@@ -316,21 +311,21 @@ class GeoNodeExecParams {
* \note This will add an error message if the string socket is active and
* the input attribute does not exist.
*/
GVArrayPtr get_input_attribute(const StringRef name,
const GeometryComponent &component,
const AttributeDomain domain,
const CustomDataType type,
const void *default_value) const;
GVArray get_input_attribute(const StringRef name,
const GeometryComponent &component,
const AttributeDomain domain,
const CustomDataType type,
const void *default_value) const;
template<typename T>
GVArray_Typed<T> get_input_attribute(const StringRef name,
const GeometryComponent &component,
const AttributeDomain domain,
const T &default_value) const
VArray<T> get_input_attribute(const StringRef name,
const GeometryComponent &component,
const AttributeDomain domain,
const T &default_value) const
{
const CustomDataType type = bke::cpp_type_to_custom_data_type(CPPType::get<T>());
GVArrayPtr varray = this->get_input_attribute(name, component, domain, type, &default_value);
return GVArray_Typed<T>(std::move(varray));
GVArray varray = this->get_input_attribute(name, component, domain, type, &default_value);
return varray.typed<T>();
}
/**

View File

@@ -21,7 +21,6 @@
namespace blender::nodes {
using fn::CPPType;
using fn::GVArray;
struct ConversionFunctions {
const fn::MultiFunction *multi_function;
@@ -73,9 +72,9 @@ class DataTypeConversions {
const void *from_value,
void *to_value) const;
fn::GVArrayPtr try_convert(fn::GVArrayPtr varray, const CPPType &to_type) const;
fn::GVArray try_convert(fn::GVArray varray, const CPPType &to_type) const;
fn::GVMutableArrayPtr try_convert(fn::GVMutableArrayPtr varray, const CPPType &to_type) const;
fn::GVMutableArray try_convert(fn::GVMutableArray varray, const CPPType &to_type) const;
};
const DataTypeConversions &get_implicit_type_conversions();

View File

@@ -179,9 +179,9 @@ static void align_rotations_on_component(GeometryComponent &component,
return;
}
GVArray_Typed<float> factors = params.get_input_attribute<float>(
VArray<float> factors = params.get_input_attribute<float>(
"Factor", component, ATTR_DOMAIN_POINT, 1.0f);
GVArray_Typed<float3> vectors = params.get_input_attribute<float3>(
VArray<float3> vectors = params.get_input_attribute<float3>(
"Vector", component, ATTR_DOMAIN_POINT, {0, 0, 1});
float3 local_main_axis{0, 0, 0};

View File

@@ -156,7 +156,7 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
const AttributeDomain domain = get_result_domain(component, attribute_name, result_name);
const int operation = static_cast<int>(storage.operation);
GVArrayPtr attribute_input = component.attribute_try_get_for_read(
GVArray attribute_input = component.attribute_try_get_for_read(
attribute_name, domain, data_type);
OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
@@ -185,7 +185,7 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
}
}
MutableSpan<float3> results = attribute_result.as_span<float3>();
clamp_attribute<float3>(attribute_input->typed<float3>(), results, min, max);
clamp_attribute<float3>(attribute_input.typed<float3>(), results, min, max);
break;
}
case CD_PROP_FLOAT: {
@@ -193,10 +193,10 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
const float max = params.get_input<float>("Max_001");
MutableSpan<float> results = attribute_result.as_span<float>();
if (operation == NODE_CLAMP_RANGE && min > max) {
clamp_attribute<float>(attribute_input->typed<float>(), results, max, min);
clamp_attribute<float>(attribute_input.typed<float>(), results, max, min);
}
else {
clamp_attribute<float>(attribute_input->typed<float>(), results, min, max);
clamp_attribute<float>(attribute_input.typed<float>(), results, min, max);
}
break;
}
@@ -205,10 +205,10 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
const int max = params.get_input<int>("Max_002");
MutableSpan<int> results = attribute_result.as_span<int>();
if (operation == NODE_CLAMP_RANGE && min > max) {
clamp_attribute<int>(attribute_input->typed<int>(), results, max, min);
clamp_attribute<int>(attribute_input.typed<int>(), results, max, min);
}
else {
clamp_attribute<int>(attribute_input->typed<int>(), results, min, max);
clamp_attribute<int>(attribute_input.typed<int>(), results, min, max);
}
break;
}
@@ -231,7 +231,7 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
}
MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>();
clamp_attribute<ColorGeometry4f>(
attribute_input->typed<ColorGeometry4f>(), results, min, max);
attribute_input.typed<ColorGeometry4f>(), results, min, max);
break;
}
default: {

View File

@@ -85,7 +85,7 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
return;
}
GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>(
VArray<float> attribute_in = component.attribute_get_for_read<float>(
input_name, result_domain, 0.0f);
MutableSpan<ColorGeometry4f> results = attribute_result.as_span();

View File

@@ -95,11 +95,11 @@ static void combine_attributes(GeometryComponent &component, const GeoNodeExecPa
if (!attribute_result) {
return;
}
GVArray_Typed<float> attribute_x = params.get_input_attribute<float>(
VArray<float> attribute_x = params.get_input_attribute<float>(
"X", component, result_domain, 0.0f);
GVArray_Typed<float> attribute_y = params.get_input_attribute<float>(
VArray<float> attribute_y = params.get_input_attribute<float>(
"Y", component, result_domain, 0.0f);
GVArray_Typed<float> attribute_z = params.get_input_attribute<float>(
VArray<float> attribute_z = params.get_input_attribute<float>(
"Z", component, result_domain, 0.0f);
for (const int i : IndexRange(attribute_result->size())) {

View File

@@ -257,9 +257,9 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
const CustomDataType input_data_type = get_data_type(component, params, *node_storage);
GVArrayPtr attribute_a = params.get_input_attribute(
GVArray attribute_a = params.get_input_attribute(
"A", component, result_domain, input_data_type, nullptr);
GVArrayPtr attribute_b = params.get_input_attribute(
GVArray attribute_b = params.get_input_attribute(
"B", component, result_domain, input_data_type, nullptr);
if (!attribute_a || !attribute_b) {
@@ -276,47 +276,47 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
if (operation == NODE_FLOAT_COMPARE_EQUAL) {
if (input_data_type == CD_PROP_FLOAT) {
do_equal_operation_float(
attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
attribute_a.typed<float>(), attribute_b.typed<float>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_FLOAT3) {
do_equal_operation_float3(
attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
attribute_a.typed<float3>(), attribute_b.typed<float3>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_COLOR) {
do_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(),
attribute_b->typed<ColorGeometry4f>(),
do_equal_operation_color4f(attribute_a.typed<ColorGeometry4f>(),
attribute_b.typed<ColorGeometry4f>(),
threshold,
result_span);
}
else if (input_data_type == CD_PROP_BOOL) {
do_equal_operation_bool(
attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
attribute_a.typed<bool>(), attribute_b.typed<bool>(), threshold, result_span);
}
}
else if (operation == NODE_FLOAT_COMPARE_NOT_EQUAL) {
if (input_data_type == CD_PROP_FLOAT) {
do_not_equal_operation_float(
attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
attribute_a.typed<float>(), attribute_b.typed<float>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_FLOAT3) {
do_not_equal_operation_float3(
attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
attribute_a.typed<float3>(), attribute_b.typed<float3>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_COLOR) {
do_not_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(),
attribute_b->typed<ColorGeometry4f>(),
do_not_equal_operation_color4f(attribute_a.typed<ColorGeometry4f>(),
attribute_b.typed<ColorGeometry4f>(),
threshold,
result_span);
}
else if (input_data_type == CD_PROP_BOOL) {
do_not_equal_operation_bool(
attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
attribute_a.typed<bool>(), attribute_b.typed<bool>(), threshold, result_span);
}
}
}
else {
do_math_operation(
attribute_a->typed<float>(), attribute_b->typed<float>(), operation, result_span);
attribute_a.typed<float>(), attribute_b.typed<float>(), operation, result_span);
}
attribute_result.save();

View File

@@ -104,7 +104,7 @@ static void attribute_convert_calc(GeometryComponent &component,
return;
}
GVArrayPtr source_attribute = component.attribute_try_get_for_read(
GVArray source_attribute = component.attribute_try_get_for_read(
source_name, result_domain, result_type);
if (!source_attribute) {
params.error_message_add(NodeWarningType::Error,
@@ -118,7 +118,7 @@ static void attribute_convert_calc(GeometryComponent &component,
return;
}
GVArray_GSpan source_span{*source_attribute};
GVArray_GSpan source_span{source_attribute};
GMutableSpan result_span = result_attribute.as_span();
BLI_assert(source_span.size() == result_span.size());

View File

@@ -136,10 +136,10 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
switch (result_type) {
case CD_PROP_FLOAT: {
const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec;
GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>(
VArray<float> attribute_in = component.attribute_get_for_read<float>(
input_name, result_domain, float(0.0f));
MutableSpan<float> results = attribute_result.as_span<float>();
threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
threading::parallel_for(attribute_in.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
results[i] = BKE_curvemapping_evaluateF(cumap, 3, attribute_in[i]);
}
@@ -148,10 +148,10 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
}
case CD_PROP_FLOAT3: {
const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec;
GVArray_Typed<float3> attribute_in = component.attribute_get_for_read<float3>(
VArray<float3> attribute_in = component.attribute_get_for_read<float3>(
input_name, result_domain, float3(0.0f));
MutableSpan<float3> results = attribute_result.as_span<float3>();
threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
threading::parallel_for(attribute_in.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
BKE_curvemapping_evaluate3F(cumap, results[i], attribute_in[i]);
}
@@ -160,11 +160,10 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
}
case CD_PROP_COLOR: {
const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb;
GVArray_Typed<ColorGeometry4f> attribute_in =
component.attribute_get_for_read<ColorGeometry4f>(
input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
VArray<ColorGeometry4f> attribute_in = component.attribute_get_for_read<ColorGeometry4f>(
input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>();
threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
threading::parallel_for(attribute_in.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]);
}

View File

@@ -362,7 +362,7 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP
const AttributeDomain domain = get_result_domain(component, input_name, result_name);
GVArrayPtr attribute_input = component.attribute_try_get_for_read(input_name, domain, data_type);
GVArray attribute_input = component.attribute_try_get_for_read(input_name, domain, data_type);
if (!attribute_input) {
params.error_message_add(NodeWarningType::Error,
@@ -381,12 +381,12 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP
switch (data_type) {
case CD_PROP_FLOAT: {
map_range_float(attribute_input->typed<float>(), attribute_result.as_span<float>(), params);
map_range_float(attribute_input.typed<float>(), attribute_result.as_span<float>(), params);
break;
}
case CD_PROP_FLOAT3: {
map_range_float3(
attribute_input->typed<float3>(), attribute_result.as_span<float3>(), params);
attribute_input.typed<float3>(), attribute_result.as_span<float3>(), params);
break;
}
default:

View File

@@ -250,7 +250,7 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP
return;
}
GVArray_Typed<float> attribute_a = params.get_input_attribute<float>(
VArray<float> attribute_a = params.get_input_attribute<float>(
"A", component, result_domain, 0.0f);
MutableSpan<float> result_span = attribute_result.as_span();
@@ -258,10 +258,10 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP
/* Note that passing the data with `get_internal_span<float>()` works
* because the attributes were accessed with #CD_PROP_FLOAT. */
if (operation_use_input_b(operation)) {
GVArray_Typed<float> attribute_b = params.get_input_attribute<float>(
VArray<float> attribute_b = params.get_input_attribute<float>(
"B", component, result_domain, 0.0f);
if (operation_use_input_c(operation)) {
GVArray_Typed<float> attribute_c = params.get_input_attribute<float>(
VArray<float> attribute_c = params.get_input_attribute<float>(
"C", component, result_domain, 0.0f);
do_math_operation(attribute_a, attribute_b, attribute_c, result_span, operation);
}

View File

@@ -144,25 +144,28 @@ static void do_mix_operation(const CustomDataType result_type,
GVMutableArray &attribute_result)
{
if (result_type == CD_PROP_FLOAT) {
VMutableArray<float> result = attribute_result.typed<float>();
do_mix_operation_float(blend_mode,
attribute_factor,
attribute_a.typed<float>(),
attribute_b.typed<float>(),
attribute_result.typed<float>());
result);
}
else if (result_type == CD_PROP_FLOAT3) {
VMutableArray<float3> result = attribute_result.typed<float3>();
do_mix_operation_float3(blend_mode,
attribute_factor,
attribute_a.typed<float3>(),
attribute_b.typed<float3>(),
attribute_result.typed<float3>());
result);
}
else if (result_type == CD_PROP_COLOR) {
VMutableArray<ColorGeometry4f> result = attribute_result.typed<ColorGeometry4f>();
do_mix_operation_color4f(blend_mode,
attribute_factor,
attribute_a.typed<ColorGeometry4f>(),
attribute_b.typed<ColorGeometry4f>(),
attribute_result.typed<ColorGeometry4f>());
result);
}
}
@@ -203,19 +206,19 @@ static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecPa
return;
}
GVArray_Typed<float> attribute_factor = params.get_input_attribute<float>(
VArray<float> attribute_factor = params.get_input_attribute<float>(
"Factor", component, result_domain, 0.5f);
GVArrayPtr attribute_a = params.get_input_attribute(
GVArray attribute_a = params.get_input_attribute(
"A", component, result_domain, result_type, nullptr);
GVArrayPtr attribute_b = params.get_input_attribute(
GVArray attribute_b = params.get_input_attribute(
"B", component, result_domain, result_type, nullptr);
do_mix_operation(result_type,
node_storage->blend_type,
attribute_factor,
*attribute_a,
*attribute_b,
*attribute_result);
attribute_a,
attribute_b,
attribute_result.varray());
attribute_result.save();
}

View File

@@ -153,7 +153,7 @@ static void attribute_calc_proximity(GeometryComponent &component,
if (!position_attribute || (!distance_attribute && !location_attribute)) {
return;
}
GVArray_Typed<float3> positions{*position_attribute.varray};
VArray<float3> positions = position_attribute.varray.typed<float3>();
const NodeGeometryAttributeProximity &storage =
*(const NodeGeometryAttributeProximity *)params.node().storage;

View File

@@ -180,13 +180,13 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo
const int domain_size = component.attribute_domain_size(domain);
/* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */
GVArrayPtr hash_attribute = component.attribute_try_get_for_read("id", domain);
GVArray hash_attribute = component.attribute_try_get_for_read("id", domain);
Array<uint32_t> hashes(domain_size);
if (hash_attribute) {
BLI_assert(hashes.size() == hash_attribute->size());
const CPPType &cpp_type = hash_attribute->type();
BLI_assert(hashes.size() == hash_attribute.size());
const CPPType &cpp_type = hash_attribute.type();
BLI_assert(cpp_type.is_hashable());
GVArray_GSpan items{*hash_attribute};
GVArray_GSpan items{hash_attribute};
threading::parallel_for(hashes.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
hashes[i] = cpp_type.hash(items[i]);

View File

@@ -82,7 +82,7 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec
return;
}
GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>(
VArray<float3> mapping_attribute = component.attribute_get_for_read<float3>(
mapping_name, result_domain, {0, 0, 0});
MutableSpan<ColorGeometry4f> colors = attribute_out.as_span();

View File

@@ -106,9 +106,9 @@ static void separate_attribute(GeometryComponent &component, const GeoNodeExecPa
const AttributeDomain result_domain = get_result_domain(
component, params, result_name_x, result_name_y, result_name_z);
GVArray_Typed<float3> attribute_input = params.get_input_attribute<float3>(
VArray<float3> attribute_input = params.get_input_attribute<float3>(
"Vector", component, result_domain, {0, 0, 0});
VArray_Span<float3> input_span{*attribute_input};
VArray_Span<float3> input_span{attribute_input};
OutputAttribute_Typed<float> attribute_result_x =
component.attribute_try_get_for_output_only<float>(result_name_x, result_domain);

View File

@@ -407,13 +407,13 @@ static void transfer_attribute_nearest(const GeometrySet &src_geometry,
if (pointcloud_distances_sq[i] < mesh_distances_sq[i]) {
/* Point-cloud point is closer. */
const int index = pointcloud_indices[i];
pointcloud_src_attribute.varray->get(index, buffer);
pointcloud_src_attribute.varray.get(index, buffer);
dst_attribute->set_by_relocate(i, buffer);
}
else {
/* Mesh element is closer. */
const int index = mesh_indices[i];
mesh_src_attribute.varray->get(index, buffer);
mesh_src_attribute.varray.get(index, buffer);
dst_attribute->set_by_relocate(i, buffer);
}
}
@@ -424,7 +424,7 @@ static void transfer_attribute_nearest(const GeometrySet &src_geometry,
src_name, data_type);
for (const int i : IndexRange(tot_samples)) {
const int index = pointcloud_indices[i];
src_attribute.varray->get(index, buffer);
src_attribute.varray.get(index, buffer);
dst_attribute->set_by_relocate(i, buffer);
}
}
@@ -434,7 +434,7 @@ static void transfer_attribute_nearest(const GeometrySet &src_geometry,
data_type);
for (const int i : IndexRange(tot_samples)) {
const int index = mesh_indices[i];
src_attribute.varray->get(index, buffer);
src_attribute.varray.get(index, buffer);
dst_attribute->set_by_relocate(i, buffer);
}
}
@@ -460,7 +460,7 @@ static void transfer_attribute(const GeoNodeExecParams &params,
const AttributeDomain dst_domain = (input_domain == ATTR_DOMAIN_AUTO) ? auto_domain :
input_domain;
GVArray_Typed<float3> dst_positions = dst_component.attribute_get_for_read<float3>(
VArray<float3> dst_positions = dst_component.attribute_get_for_read<float3>(
"position", dst_domain, {0, 0, 0});
switch (mapping) {

View File

@@ -187,7 +187,7 @@ static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNod
static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a,
const VArray<float3> &input_b,
VMutableArray<float3> &result,
const VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
@@ -218,7 +218,7 @@ static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a,
static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a,
const VArray<float3> &input_b,
const VArray<float3> &input_c,
VMutableArray<float3> &result,
const VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
@@ -251,7 +251,7 @@ static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a,
static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a,
const VArray<float3> &input_b,
const VArray<float> &input_c,
VMutableArray<float3> &result,
const VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
@@ -283,7 +283,7 @@ static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a,
static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a,
const VArray<float3> &input_b,
VMutableArray<float> &result,
const VMutableArray<float> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
@@ -313,7 +313,7 @@ static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a,
static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a,
const VArray<float> &input_b,
VMutableArray<float3> &result,
const VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
@@ -342,7 +342,7 @@ static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a,
}
static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a,
VMutableArray<float3> &result,
const VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
@@ -369,7 +369,7 @@ static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a,
}
static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a,
VMutableArray<float> &result,
const VMutableArray<float> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
@@ -437,13 +437,13 @@ static void attribute_vector_math_calc(GeometryComponent &component,
const AttributeDomain result_domain = get_result_domain(
component, params, operation, result_name);
GVArrayPtr attribute_a = params.get_input_attribute(
GVArray attribute_a = params.get_input_attribute(
"A", component, result_domain, read_type_a, nullptr);
if (!attribute_a) {
return;
}
GVArrayPtr attribute_b;
GVArrayPtr attribute_c;
GVArray attribute_b;
GVArray attribute_c;
if (use_input_b) {
attribute_b = params.get_input_attribute("B", component, result_domain, read_type_b, nullptr);
if (!attribute_b) {
@@ -476,26 +476,26 @@ static void attribute_vector_math_calc(GeometryComponent &component,
case NODE_VECTOR_MATH_MODULO:
case NODE_VECTOR_MATH_MINIMUM:
case NODE_VECTOR_MATH_MAXIMUM:
do_math_operation_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
attribute_b->typed<float3>(),
attribute_result->typed<float3>(),
do_math_operation_fl3_fl3_to_fl3(attribute_a.typed<float3>(),
attribute_b.typed<float3>(),
attribute_result.varray().typed<float3>(),
operation);
break;
case NODE_VECTOR_MATH_DOT_PRODUCT:
case NODE_VECTOR_MATH_DISTANCE:
do_math_operation_fl3_fl3_to_fl(attribute_a->typed<float3>(),
attribute_b->typed<float3>(),
attribute_result->typed<float>(),
do_math_operation_fl3_fl3_to_fl(attribute_a.typed<float3>(),
attribute_b.typed<float3>(),
attribute_result.varray().typed<float>(),
operation);
break;
case NODE_VECTOR_MATH_LENGTH:
do_math_operation_fl3_to_fl(
attribute_a->typed<float3>(), attribute_result->typed<float>(), operation);
attribute_a.typed<float3>(), attribute_result.varray().typed<float>(), operation);
break;
case NODE_VECTOR_MATH_SCALE:
do_math_operation_fl3_fl_to_fl3(attribute_a->typed<float3>(),
attribute_b->typed<float>(),
attribute_result->typed<float3>(),
do_math_operation_fl3_fl_to_fl3(attribute_a.typed<float3>(),
attribute_b.typed<float>(),
attribute_result.varray().typed<float3>(),
operation);
break;
case NODE_VECTOR_MATH_NORMALIZE:
@@ -507,22 +507,22 @@ static void attribute_vector_math_calc(GeometryComponent &component,
case NODE_VECTOR_MATH_COSINE:
case NODE_VECTOR_MATH_TANGENT:
do_math_operation_fl3_to_fl3(
attribute_a->typed<float3>(), attribute_result->typed<float3>(), operation);
attribute_a.typed<float3>(), attribute_result.varray().typed<float3>(), operation);
break;
case NODE_VECTOR_MATH_WRAP:
case NODE_VECTOR_MATH_FACEFORWARD:
case NODE_VECTOR_MATH_MULTIPLY_ADD:
do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
attribute_b->typed<float3>(),
attribute_c->typed<float3>(),
attribute_result->typed<float3>(),
do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a.typed<float3>(),
attribute_b.typed<float3>(),
attribute_c.typed<float3>(),
attribute_result.varray().typed<float3>(),
operation);
break;
case NODE_VECTOR_MATH_REFRACT:
do_math_operation_fl3_fl3_fl_to_fl3(attribute_a->typed<float3>(),
attribute_b->typed<float3>(),
attribute_c->typed<float>(),
attribute_result->typed<float3>(),
do_math_operation_fl3_fl3_fl_to_fl3(attribute_a.typed<float3>(),
attribute_b.typed<float3>(),
attribute_c.typed<float>(),
attribute_result.varray().typed<float3>(),
operation);
break;
}

View File

@@ -220,12 +220,12 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
const bool invert = params.get_input<bool>("Invert");
GVArrayPtr attribute_vector = params.get_input_attribute(
GVArray attribute_vector = params.get_input_attribute(
"Vector", component, result_domain, CD_PROP_FLOAT3, nullptr);
if (!attribute_vector) {
return;
}
GVArrayPtr attribute_center = params.get_input_attribute(
GVArray attribute_center = params.get_input_attribute(
"Center", component, result_domain, CD_PROP_FLOAT3, nullptr);
if (!attribute_center) {
return;
@@ -238,21 +238,21 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
}
if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
GVArrayPtr attribute_rotation = params.get_input_attribute(
GVArray attribute_rotation = params.get_input_attribute(
"Rotation", component, result_domain, CD_PROP_FLOAT3, nullptr);
if (!attribute_rotation) {
return;
}
do_vector_rotate_euler(attribute_vector->typed<float3>(),
attribute_center->typed<float3>(),
attribute_rotation->typed<float3>(),
do_vector_rotate_euler(attribute_vector.typed<float3>(),
attribute_center.typed<float3>(),
attribute_rotation.typed<float3>(),
attribute_result.as_span<float3>(),
invert);
attribute_result.save();
return;
}
GVArrayPtr attribute_angle = params.get_input_attribute(
GVArray attribute_angle = params.get_input_attribute(
"Angle", component, result_domain, CD_PROP_FLOAT, nullptr);
if (!attribute_angle) {
return;
@@ -260,40 +260,40 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
switch (mode) {
case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS: {
GVArrayPtr attribute_axis = params.get_input_attribute(
GVArray attribute_axis = params.get_input_attribute(
"Axis", component, result_domain, CD_PROP_FLOAT3, nullptr);
if (!attribute_axis) {
return;
}
do_vector_rotate_around_axis(attribute_vector->typed<float3>(),
attribute_center->typed<float3>(),
attribute_axis->typed<float3>(),
attribute_angle->typed<float>(),
do_vector_rotate_around_axis(attribute_vector.typed<float3>(),
attribute_center.typed<float3>(),
attribute_axis.typed<float3>(),
attribute_angle.typed<float>(),
attribute_result.as_span<float3>(),
invert);
} break;
case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X:
do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
attribute_center->typed<float3>(),
do_vector_rotate_around_fixed_axis(attribute_vector.typed<float3>(),
attribute_center.typed<float3>(),
float3(1.0f, 0.0f, 0.0f),
attribute_angle->typed<float>(),
attribute_angle.typed<float>(),
attribute_result.as_span<float3>(),
invert);
break;
case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y:
do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
attribute_center->typed<float3>(),
do_vector_rotate_around_fixed_axis(attribute_vector.typed<float3>(),
attribute_center.typed<float3>(),
float3(0.0f, 1.0f, 0.0f),
attribute_angle->typed<float>(),
attribute_angle.typed<float>(),
attribute_result.as_span<float3>(),
invert);
break;
case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z:
do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
attribute_center->typed<float3>(),
do_vector_rotate_around_fixed_axis(attribute_vector.typed<float3>(),
attribute_center.typed<float3>(),
float3(0.0f, 0.0f, 1.0f),
attribute_angle->typed<float>(),
attribute_angle.typed<float>(),
attribute_result.as_span<float3>(),
invert);

View File

@@ -61,7 +61,7 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component,
if (meta_data.domain != ATTR_DOMAIN_CURVE) {
return true;
}
GVArrayPtr spline_attribute = curve_component.attribute_get_for_read(
GVArray spline_attribute = curve_component.attribute_get_for_read(
attribute_id, ATTR_DOMAIN_CURVE, meta_data.data_type);
OutputAttribute result_attribute = points.attribute_try_get_for_output_only(
@@ -70,7 +70,7 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component,
/* Only copy the attributes of splines in the offsets. */
for (const int i : offsets.index_range()) {
spline_attribute->get(offsets[i], result[i]);
spline_attribute.get(offsets[i], result[i]);
}
result_attribute.save();
@@ -130,7 +130,7 @@ static void copy_endpoint_attributes(Span<SplinePtr> splines,
BLI_assert(spline.attributes.get_for_read(attribute_id));
GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
blender::fn::GVArray_For_GSpan(spline_span).get(0, point_span[i]);
spline_span.type().copy_assign(spline_span[0], point_span[i]);
}
for (const auto item : end_data.point_attributes.items()) {
@@ -139,7 +139,7 @@ static void copy_endpoint_attributes(Span<SplinePtr> splines,
BLI_assert(spline.attributes.get_for_read(attribute_id));
GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
blender::fn::GVArray_For_GSpan(spline_span).get(spline.size() - 1, point_span[i]);
spline_span.type().copy_assign(spline_span[spline.size() - 1], point_span[i]);
}
}
});

View File

@@ -44,7 +44,7 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
MutableSpan<SplinePtr> splines = curve.splines();
const std::string selection_name = params.extract_input<std::string>("Selection");
GVArray_Typed<bool> selection = curve_component.attribute_get_for_read(
VArray<bool> selection = curve_component.attribute_get_for_read(
selection_name, ATTR_DOMAIN_CURVE, true);
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {

Some files were not shown because too many files have changed in this diff Show More