1
1

Compare commits

...

15 Commits

Author SHA1 Message Date
b3a7e47c9e remove unnecessary mutex 2020-03-22 16:16:19 +01:00
f55198da42 use multithreading for obj parsing 2020-03-22 16:14:02 +01:00
95e71dad88 initial mesh generation 2020-03-22 15:47:51 +01:00
6a61e86b43 count total amounts 2020-03-22 14:57:05 +01:00
7ae5433779 prepare function for object generation 2020-03-22 14:30:46 +01:00
4be9d4541e collect all segments 2020-03-22 14:24:08 +01:00
01416b7e8d cleanup 2020-03-22 14:16:48 +01:00
df984b1dd6 parse face information 2020-03-22 14:12:45 +01:00
a4d1acf1ce parse more stuff 2020-03-22 13:19:12 +01:00
a40bdbb5fc convert stringref to float 2020-03-22 11:33:49 +01:00
0f2147bf52 parse object name 2020-03-22 11:01:38 +01:00
d4a3adf045 strip methods for stringref 2020-03-22 10:56:15 +01:00
c7d9070ccb tests for new stringref functions 2020-03-21 14:04:08 +01:00
3512be55e9 improved file name parsing 2020-03-20 12:41:27 +01:00
4a4a36627e initial obj import experiments 2020-03-19 21:32:40 +01:00
9 changed files with 1096 additions and 0 deletions

View File

@@ -257,6 +257,14 @@ template<typename T, typename Allocator = GuardedAllocator> class StringMap {
return const_cast<T &>(const_cast<const StringMap *>(this)->lookup(key));
}
T &lookup_or_add_default(StringRef key)
{
if (!this->contains(key)) {
this->add_new(key, T());
}
return this->lookup(key);
}
/**
* Get a pointer to the value corresponding to the key. Return nullptr, if the key does not
* exist.

View File

@@ -33,6 +33,7 @@
#include <string>
#include "BLI_array_ref.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
namespace BLI {
@@ -104,13 +105,28 @@ class StringRefBase {
* Returns true when the string begins with the given prefix. Otherwise false.
*/
bool startswith(StringRef prefix) const;
bool startswith(char c) const;
bool startswith_lower_ascii(StringRef prefix) const;
/**
* Returns true when the string ends with the given suffix. Otherwise false.
*/
bool endswith(StringRef suffix) const;
bool endswith(char c) const;
StringRef substr(uint start, uint size) const;
StringRef lstrip(ArrayRef<char> chars = {' ', '\t', '\n', '\r'}) const;
StringRef rstrip(ArrayRef<char> chars = {' ', '\t', '\n', '\r'}) const;
StringRef strip(ArrayRef<char> chars = {' ', '\t', '\n', '\r'}) const;
float to_float(bool *r_success = nullptr) const;
int to_int(bool *r_success = nullptr) const;
bool contains(char c) const;
uint first_index_of(char c, uint start = 0) const;
int try_first_index_of(char c, uint start = 0) const;
};
/**
@@ -137,6 +153,17 @@ class StringRefNull : public StringRefBase {
StringRefNull(const std::string &str) : StringRefNull(str.data())
{
}
StringRefNull lstrip(ArrayRef<char> chars = {' ', '\t', '\r', '\n'}) const
{
for (uint i = 0; i < m_size; i++) {
char c = m_data[i];
if (!chars.contains(c)) {
return StringRefNull(m_data + i, m_size - i);
}
}
return "";
}
};
/**
@@ -182,6 +209,18 @@ class StringRef : public StringRefBase {
BLI_assert(this->startswith(prefix));
return this->drop_prefix(prefix.size());
}
StringRef drop_suffix(uint n) const
{
BLI_assert(n <= m_size);
return StringRef(m_data, m_size - n);
}
StringRef drop_suffix(StringRef suffix) const
{
BLI_assert(this->endswith(suffix));
return this->drop_suffix(suffix.size());
}
};
/* More inline functions
@@ -230,6 +269,42 @@ inline bool StringRefBase::startswith(StringRef prefix) const
return true;
}
inline char tolower_ascii(char c)
{
if (c >= 'A' && c <= 'Z') {
return c + ('a' - 'A');
}
return c;
}
inline bool StringRefBase::startswith_lower_ascii(StringRef prefix) const
{
#ifdef DEBUG
for (char c : prefix) {
char lower_c = tolower_ascii(c);
BLI_assert(c == lower_c);
}
#endif
if (m_size < prefix.m_size) {
return false;
}
for (uint i = 0; i < prefix.m_size; i++) {
char c = tolower_ascii(m_data[i]);
if (c != prefix.m_data[i]) {
return false;
}
}
return true;
}
inline bool StringRefBase::startswith(char c) const
{
if (m_size == 0) {
return false;
}
return m_data[0] == c;
}
inline bool StringRefBase::endswith(StringRef suffix) const
{
if (m_size < suffix.m_size) {
@@ -244,12 +319,95 @@ inline bool StringRefBase::endswith(StringRef suffix) const
return true;
}
inline bool StringRefBase::endswith(char c) const
{
if (m_size == 0) {
return false;
}
return m_data[m_size - 1] == c;
}
inline StringRef StringRefBase::substr(uint start, uint size) const
{
BLI_assert(start + size <= m_size);
return StringRef(m_data + start, size);
}
inline StringRef StringRefBase::lstrip(ArrayRef<char> chars) const
{
for (uint i = 0; i < m_size; i++) {
char c = m_data[i];
if (!chars.contains(c)) {
return StringRef(m_data + i, m_size - i);
}
}
return "";
}
inline StringRef StringRefBase::rstrip(ArrayRef<char> chars) const
{
for (int i = m_size - 1; i >= 0; i--) {
char c = m_data[i];
if (!chars.contains(c)) {
return StringRef(m_data, i + 1);
}
}
return "";
}
inline StringRef StringRefBase::strip(ArrayRef<char> chars) const
{
StringRef lstripped = this->lstrip(chars);
StringRef stripped = lstripped.rstrip(chars);
return stripped;
}
inline float StringRefBase::to_float(bool *r_success) const
{
char *str_with_null = (char *)alloca(m_size + 1);
this->copy_to__with_null(str_with_null);
char *end;
float value = std::strtof(str_with_null, &end);
if (r_success) {
*r_success = str_with_null != end;
}
return value;
}
inline int StringRefBase::to_int(bool *r_success) const
{
char *str_with_null = (char *)alloca(m_size + 1);
this->copy_to__with_null(str_with_null);
char *end;
int value = std::strtol(str_with_null, &end, 10);
if (r_success) {
*r_success = str_with_null != end;
}
return value;
}
inline bool StringRefBase::contains(char c) const
{
return this->try_first_index_of(c) >= 0;
}
inline uint StringRefBase::first_index_of(char c, uint start) const
{
int index = this->try_first_index_of(c, start);
BLI_assert(index >= 0);
return (uint)index;
}
inline int StringRefBase::try_first_index_of(char c, uint start) const
{
for (uint i = start; i < m_size; i++) {
if (m_data[i] == c) {
return i;
}
}
return -1;
}
} // namespace BLI
#endif /* __BLI_STRING_REF_H__ */

View File

@@ -0,0 +1,53 @@
#pragma once
/* This file contains utilities to make timing of
* code segments easy.
*/
#include "BLI_sys_types.h"
#include <chrono>
#include <iostream>
namespace BLI {
namespace Timers {
using Clock = std::chrono::high_resolution_clock;
using TimePoint = Clock::time_point;
using Nanoseconds = std::chrono::nanoseconds;
inline void print_duration(Nanoseconds duration)
{
if (duration.count() < 100000) {
std::cout << duration.count() << " ns";
}
else {
std::cout << duration.count() / 1000000.0 << " ms";
}
}
class ScopedTimer {
private:
const char *m_name;
TimePoint m_start;
public:
ScopedTimer(const char *name = "") : m_name(name)
{
m_start = Clock::now();
}
~ScopedTimer()
{
TimePoint end = Clock::now();
Nanoseconds duration = end - m_start;
std::cout << "Timer '" << m_name << "' took ";
print_duration(duration);
std::cout << "\n";
}
};
} // namespace Timers
} // namespace BLI
#define SCOPED_TIMER(name) BLI::Timers::ScopedTimer t(name);

View File

@@ -253,6 +253,8 @@ set(SRC
BLI_winstuff.h
PIL_time.h
PIL_time_utildefines.h
BLI_timeit.h
)
set(LIB

View File

@@ -55,6 +55,7 @@ set(SRC
object_hook.c
object_modes.c
object_modifier.c
object_obj_import.cc
object_ops.c
object_random.c
object_relations.c

View File

@@ -297,6 +297,9 @@ void OBJECT_OT_quadriflow_remesh(struct wmOperatorType *ot);
void OBJECT_OT_data_transfer(struct wmOperatorType *ot);
void OBJECT_OT_datalayout_transfer(struct wmOperatorType *ot);
/* object_obj_import.cc */
void OBJECT_OT_obj_import_test(struct wmOperatorType *ot);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,776 @@
#include <fstream>
#include <iostream>
#include <mutex>
#include "BKE_collection.h"
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_mesh.h"
#include "BKE_object.h"
#include "BLI_array_ref.h"
#include "BLI_math_vector.h"
#include "BLI_set.h"
#include "BLI_string.h"
#include "BLI_string_map.h"
#include "BLI_string_ref.h"
#include "BLI_timeit.h"
#include "BLI_vector.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_space_types.h"
#include "ED_object.h"
#include "RNA_access.h"
#include "WM_api.h"
#include "WM_types.h"
#include "object_intern.h"
#include "tbb/task_group.h"
struct float3 {
float x, y, z;
};
struct float2 {
float x, y;
};
using BLI::ArrayRef;
using BLI::IndexRange;
using BLI::Set;
using BLI::StringMap;
using BLI::StringRef;
using BLI::StringRefNull;
using BLI::Vector;
class TextLinesReader {
private:
std::istream &m_istream;
std::mutex m_mutex;
Set<const char *> m_chunks;
public:
TextLinesReader(std::istream &istream) : m_istream(istream)
{
}
~TextLinesReader()
{
for (const char *ptr : m_chunks) {
MEM_freeN((void *)ptr);
}
}
bool eof() const
{
return m_istream.eof();
}
/* The returned string does not necessarily contain the final newline. */
BLI_NOINLINE StringRef read_next_line_chunk(uint approximate_size)
{
SCOPED_TIMER(__func__);
std::lock_guard<std::mutex> lock(m_mutex);
StringRef chunk = this->read_next_line_chunk_internal(approximate_size);
m_chunks.add_new(chunk.data());
return chunk;
}
void free_chunk(StringRef chunk)
{
std::lock_guard<std::mutex> lock(m_mutex);
const char *ptr = chunk.data();
m_chunks.remove(ptr);
MEM_freeN((void *)ptr);
}
private:
StringRef read_next_line_chunk_internal(uint approximate_size)
{
approximate_size = std::max<uint>(1, approximate_size);
std::streampos start_pos = m_istream.tellg();
char *buffer = (char *)MEM_mallocN(approximate_size, __func__);
m_istream.read(buffer, approximate_size);
int extracted_amount = m_istream.gcount();
/* Buffer goes to end of file. So return the entire remaining buffer. */
if (m_istream.eof()) {
return StringRef(buffer, extracted_amount);
}
/* Search the last line ending. */
/* TODO: multi-line handling */
char *buffer_end = buffer + extracted_amount;
while (buffer_end > buffer && *buffer_end != '\n') {
buffer_end--;
}
/* The buffer contains part of a single line. Try again with a larger buffer. */
if (buffer == buffer_end) {
MEM_freeN(buffer);
m_istream.seekg(start_pos);
return this->read_next_line_chunk_internal(approximate_size * 2);
}
int chunk_size = buffer_end - buffer;
/* Seek to start of a new line. */
m_istream.seekg(chunk_size - extracted_amount + 1, std::ios::cur);
return StringRef(buffer, chunk_size);
}
};
enum class ObjFileSegmentType {
mtllib,
o,
v,
vt,
vn,
usemtl,
s,
f,
};
struct ObjFileSegment {
ObjFileSegmentType type;
ObjFileSegment(ObjFileSegmentType type) : type(type)
{
}
virtual ~ObjFileSegment()
{
}
};
struct ObjFileSegment_mtllib : public ObjFileSegment {
Vector<std::string> file_names;
ObjFileSegment_mtllib() : ObjFileSegment(ObjFileSegmentType::mtllib)
{
}
};
struct ObjFileSegment_o : public ObjFileSegment {
std::string object_name;
ObjFileSegment_o() : ObjFileSegment(ObjFileSegmentType::o)
{
}
};
struct ObjFileSegment_v : public ObjFileSegment {
Vector<float3> positions;
ObjFileSegment_v() : ObjFileSegment(ObjFileSegmentType::v)
{
}
};
struct ObjFileSegment_vt : public ObjFileSegment {
Vector<float2> uvs;
ObjFileSegment_vt() : ObjFileSegment(ObjFileSegmentType::vt)
{
}
};
struct ObjFileSegment_vn : public ObjFileSegment {
Vector<float3> normals;
ObjFileSegment_vn() : ObjFileSegment(ObjFileSegmentType::vn)
{
}
};
struct ObjFileSegment_usemtl : public ObjFileSegment {
std::string material_name;
ObjFileSegment_usemtl() : ObjFileSegment(ObjFileSegmentType::usemtl)
{
}
};
struct ObjFileSegment_s : public ObjFileSegment {
std::string smoothing_group;
ObjFileSegment_s() : ObjFileSegment(ObjFileSegmentType::s)
{
}
};
struct ObjFileSegment_f : public ObjFileSegment {
Vector<uint> face_offsets;
Vector<uint> vertex_counts;
Vector<int> v_indices;
Vector<int> vt_indices;
Vector<int> vn_indices;
ObjFileSegment_f() : ObjFileSegment(ObjFileSegmentType::f)
{
}
};
template<typename FuncT> static uint count_while(StringRef str, const FuncT &func)
{
uint count = 0;
for (uint c : str) {
if (func(c)) {
count++;
}
else {
break;
}
}
return count;
}
class StringRefStream {
private:
const char *m_current;
const char *m_end;
public:
StringRefStream(StringRef str) : m_current(str.begin()), m_end(str.end())
{
}
bool has_remaining_chars() const
{
return m_current < m_end;
}
char peek_next() const
{
BLI_assert(this->has_remaining_chars());
return m_current[0];
}
StringRef peek_word() const
{
const char *word_end = m_current;
while (word_end < m_end && !ELEM(*word_end, ' ', '\r', '\n', '\t')) {
word_end++;
}
return StringRef(m_current, word_end - m_current);
}
StringRef remaining_str() const
{
return StringRef(m_current, m_end - m_current);
}
bool startswith(StringRef other) const
{
return this->remaining_str().startswith(other);
}
bool startswith_lower_ascii(StringRef other) const
{
return this->remaining_str().startswith_lower_ascii(other);
}
bool startswith_and_forward_over(StringRef other)
{
if (this->startswith(other)) {
m_current += other.size();
return true;
}
else {
return false;
}
}
/* Might not end with a newline character. */
StringRef extract_line()
{
const char *start = m_current;
while (m_current < m_end && *m_current != '\n') {
m_current++;
}
if (m_current < m_end) {
m_current++;
}
return StringRef(start, m_current - start);
}
StringRef extract_until(char c)
{
const char *start = m_current;
while (m_current < m_end && *m_current != c) {
m_current++;
}
return StringRef(start, m_current - start);
}
StringRef extract_until(ArrayRef<char> chars)
{
const char *start = m_current;
while (m_current < m_end && !chars.contains(*m_current)) {
m_current++;
}
return StringRef(start, m_current - start);
}
StringRef extract_quoted_string(char quote)
{
BLI_assert(this->peek_next() == quote);
m_current++;
StringRef str = this->extract_until(quote);
if (m_current < m_end) {
m_current++;
}
return str;
}
StringRef extract_next_word()
{
this->forward_over_whitespace();
return this->extract_until({' ', '\n', '\t', '\r'});
}
float extract_next_float(bool *r_success = nullptr)
{
StringRef str = this->extract_next_word();
float value = str.to_float(r_success);
return value;
}
int extract_next_int(bool *r_success = nullptr)
{
StringRef str = this->extract_next_word();
int value = str.to_int(r_success);
return value;
}
void forward_over_whitespace()
{
while (m_current < m_end && ELEM(*m_current, ' ', '\t', '\r')) {
m_current++;
}
}
void forward(uint i)
{
m_current += i;
BLI_assert(m_current <= m_end);
}
StringRef extract_including_ext(StringRef ext)
{
const char *start = m_current;
while (m_current < m_end) {
if (this->startswith_lower_ascii(ext)) {
m_current += ext.size();
if (m_current == m_end || ELEM(*m_current, ' ', '\t', '\r', '\n')) {
return StringRef(start, m_current - start);
}
}
else {
m_current++;
}
}
return "";
}
};
static void parse_file_names(StringRef str, StringRef ext, Vector<std::string> &r_names)
{
if (str.endswith('\n')) {
str = str.drop_suffix("\n");
}
StringRefStream stream(str);
while (true) {
stream.forward_over_whitespace();
if (!stream.has_remaining_chars()) {
return;
}
if (stream.peek_next() == '"') {
StringRef name = stream.extract_quoted_string('"');
r_names.append(name);
}
else {
StringRef name = stream.extract_including_ext(ext);
r_names.append(name);
}
}
}
static StringRef parse_object_name(StringRef str)
{
return str.strip();
}
static StringRef parse_material_name(StringRef str)
{
return str.strip();
}
static StringRef parse_smoothing_group_name(StringRef str)
{
return str.strip();
}
BLI_NOINLINE static void parse_positions(StringRefStream &stream, Vector<float3> &r_positions)
{
while (stream.peek_word() == "v") {
StringRefStream line = stream.extract_line().drop_prefix("v");
float3 position;
position.x = line.extract_next_float();
position.y = line.extract_next_float();
position.z = line.extract_next_float();
r_positions.append(position);
}
}
BLI_NOINLINE static void parse_normals(StringRefStream &stream, Vector<float3> &r_normals)
{
while (stream.peek_word() == "vn") {
StringRefStream line = stream.extract_line().drop_prefix("vn");
float3 normal;
normal.x = line.extract_next_float();
normal.y = line.extract_next_float();
normal.z = line.extract_next_float();
r_normals.append(normal);
}
}
BLI_NOINLINE static void parse_uvs(StringRefStream &stream, Vector<float2> &r_uvs)
{
while (stream.peek_word() == "vt") {
StringRefStream line = stream.extract_line().drop_prefix("vt");
float2 uv;
uv.x = line.extract_next_float();
uv.y = line.extract_next_float();
r_uvs.append(uv);
}
}
BLI_NOINLINE static void parse_faces(StringRefStream &stream, ObjFileSegment_f &segment)
{
while (stream.peek_word() == "f") {
StringRefStream line = stream.extract_line().drop_prefix("f");
uint count = 0;
segment.face_offsets.append(segment.v_indices.size());
while (true) {
StringRef face_corner = line.extract_next_word();
if (face_corner.size() == 0) {
break;
}
int v_index, vt_index, vn_index;
if (face_corner.contains('/')) {
uint index1 = face_corner.first_index_of('/');
StringRef first_str = face_corner.substr(0, index1);
v_index = first_str.to_int();
StringRef remaining_str = face_corner.drop_prefix(index1 + 1);
int index2 = remaining_str.try_first_index_of('/');
if (index2 == -1) {
vt_index = remaining_str.to_int();
vn_index = -1;
}
else if (index2 == 0) {
StringRef second_str = remaining_str.drop_prefix('/');
vt_index = -1;
vn_index = second_str.to_int();
}
else {
StringRef second_str = remaining_str.substr(0, index2);
StringRef third_str = remaining_str.drop_prefix(index2 + 1);
vt_index = second_str.to_int();
vn_index = third_str.to_int();
}
}
else {
v_index = face_corner.to_int();
vt_index = -1;
vn_index = -1;
}
segment.v_indices.append(v_index);
segment.vt_indices.append(vt_index);
segment.vn_indices.append(vn_index);
count++;
}
segment.vertex_counts.append(count);
}
}
BLI_NOINLINE static void parse_obj_lines(StringRef orig_str,
Vector<std::unique_ptr<ObjFileSegment>> &r_segments)
{
SCOPED_TIMER(__func__);
StringRefStream stream(orig_str);
while (stream.has_remaining_chars()) {
StringRef first_word = stream.peek_word();
if (first_word == "mtllib") {
StringRef line = stream.extract_line();
auto segment = BLI::make_unique<ObjFileSegment_mtllib>();
parse_file_names(line.drop_prefix("mtllib"), ".mtl", segment->file_names);
r_segments.append(std::move(segment));
}
else if (first_word == "o") {
StringRef line = stream.extract_line();
auto segment = BLI::make_unique<ObjFileSegment_o>();
segment->object_name = parse_object_name(line.drop_prefix("o"));
r_segments.append(std::move(segment));
}
else if (first_word == "v") {
auto segment = BLI::make_unique<ObjFileSegment_v>();
parse_positions(stream, segment->positions);
r_segments.append(std::move(segment));
}
else if (first_word == "vn") {
auto segment = BLI::make_unique<ObjFileSegment_vn>();
parse_normals(stream, segment->normals);
r_segments.append(std::move(segment));
}
else if (first_word == "vt") {
auto segment = BLI::make_unique<ObjFileSegment_vt>();
parse_uvs(stream, segment->uvs);
r_segments.append(std::move(segment));
}
else if (first_word == "usemtl") {
StringRef line = stream.extract_line();
auto segment = BLI::make_unique<ObjFileSegment_usemtl>();
segment->material_name = parse_material_name(line.drop_prefix("usemtl"));
r_segments.append(std::move(segment));
}
else if (first_word == "s") {
StringRef line = stream.extract_line();
auto segment = BLI::make_unique<ObjFileSegment_s>();
segment->smoothing_group = parse_smoothing_group_name(line.drop_prefix("s"));
r_segments.append(std::move(segment));
}
else if (first_word == "f") {
auto segment = BLI::make_unique<ObjFileSegment_f>();
parse_faces(stream, *segment);
r_segments.append(std::move(segment));
}
else {
stream.extract_line();
}
}
}
struct ObjectData {
uint vertex_amount = 0;
uint normal_amount = 0;
uint uv_amount = 0;
uint face_amount = 0;
uint loop_amount = 0;
Mesh *mesh = nullptr;
uint assigned_vertex_amount = 0;
uint assigned_loop_amount = 0;
uint assigned_face_amount = 0;
};
BLI_NOINLINE static void generate_objects_from_segments(bContext *C,
ArrayRef<const ObjFileSegment *> segments)
{
SCOPED_TIMER(__func__);
StringMap<ObjectData> object_data_by_name;
StringRef current_object_name = "My Object";
for (const ObjFileSegment *segment : segments) {
switch (segment->type) {
case ObjFileSegmentType::o: {
auto segment_o = (const ObjFileSegment_o *)segment;
current_object_name = segment_o->object_name;
break;
}
case ObjFileSegmentType::f: {
auto segment_f = (const ObjFileSegment_f *)segment;
ObjectData &object_data = object_data_by_name.lookup_or_add_default(current_object_name);
object_data.face_amount += segment_f->face_offsets.size();
object_data.loop_amount += segment_f->v_indices.size();
break;
}
case ObjFileSegmentType::v: {
auto segment_v = (const ObjFileSegment_v *)segment;
ObjectData &object_data = object_data_by_name.lookup_or_add_default(current_object_name);
object_data.vertex_amount += segment_v->positions.size();
break;
}
case ObjFileSegmentType::vn: {
auto segment_vn = (const ObjFileSegment_vn *)segment;
ObjectData &object_data = object_data_by_name.lookup_or_add_default(current_object_name);
object_data.normal_amount += segment_vn->normals.size();
break;
}
case ObjFileSegmentType::vt: {
auto segment_vt = (const ObjFileSegment_vt *)segment;
ObjectData &object_data = object_data_by_name.lookup_or_add_default(current_object_name);
object_data.uv_amount += segment_vt->uvs.size();
break;
}
case ObjFileSegmentType::mtllib:
case ObjFileSegmentType::s:
case ObjFileSegmentType::usemtl: {
break;
}
}
}
// Main *bmain = CTX_data_main(C);
// Collection *collection = CTX_data_collection(C);
// Mesh *mesh = BKE_mesh_add(bmain, "My Mesh");
// Object *object = BKE_object_add_only_object(bmain, OB_MESH, "My Object");
// object->data = mesh;
// BKE_collection_object_add(bmain, collection, object);
object_data_by_name.foreach_item([&](StringRefNull key, ObjectData &object_data) {
std::cout << key << ":\n";
std::cout << " Vertex Amount: " << object_data.vertex_amount << '\n';
std::cout << " Normal Amount: " << object_data.normal_amount << '\n';
std::cout << " UV Amount: " << object_data.uv_amount << '\n';
std::cout << " Face Amount: " << object_data.face_amount << '\n';
std::cout << " Loop Amount: " << object_data.loop_amount << '\n';
Mesh *mesh = BKE_mesh_new_nomain(
object_data.vertex_amount, 0, 0, object_data.loop_amount, object_data.face_amount);
object_data.mesh = mesh;
});
current_object_name = "My Object";
for (const ObjFileSegment *segment : segments) {
switch (segment->type) {
case ObjFileSegmentType::o: {
auto segment_o = (const ObjFileSegment_o *)segment;
current_object_name = segment_o->object_name;
break;
}
case ObjFileSegmentType::v: {
auto segment_v = (const ObjFileSegment_v *)segment;
ObjectData &object_data = object_data_by_name.lookup(current_object_name);
Mesh *mesh = object_data.mesh;
uint &index = object_data.assigned_vertex_amount;
for (float3 pos : segment_v->positions) {
copy_v3_v3(mesh->mvert[index].co, &pos.x);
index++;
}
break;
}
case ObjFileSegmentType::f: {
auto segment_f = (const ObjFileSegment_f *)segment;
ObjectData &object_data = object_data_by_name.lookup(current_object_name);
Mesh *mesh = object_data.mesh;
uint &loop_index = object_data.assigned_loop_amount;
uint &face_index = object_data.assigned_face_amount;
for (uint i : IndexRange(segment_f->face_offsets.size())) {
MPoly *face = &mesh->mpoly[face_index];
face->loopstart = loop_index;
face->totloop = segment_f->vertex_counts[i];
uint start = segment_f->face_offsets[i];
for (uint j : IndexRange(face->totloop)) {
MLoop *loop = &mesh->mloop[loop_index];
/* Indices start counting at 1 in .obj format. */
loop->v = segment_f->v_indices[start + j] - 1;
loop_index++;
}
face_index++;
}
break;
}
case ObjFileSegmentType::vn:
case ObjFileSegmentType::vt:
case ObjFileSegmentType::mtllib:
case ObjFileSegmentType::s:
case ObjFileSegmentType::usemtl: {
break;
}
}
}
object_data_by_name.foreach_item([&](StringRefNull key, ObjectData &object_data) {
float loc[3] = {0};
float rot[3] = {0};
{
SCOPED_TIMER("validate");
BKE_mesh_validate(object_data.mesh, false, true);
}
Object *object = ED_object_add_type(C, OB_MESH, key.data(), loc, rot, false, 0);
BKE_mesh_nomain_to_mesh(
object_data.mesh, (Mesh *)object->data, object, &CD_MASK_EVERYTHING, true);
});
}
BLI_NOINLINE static void import_obj(bContext *C, StringRef file_path)
{
SCOPED_TIMER(__func__);
std::ifstream input_stream;
input_stream.open(file_path, std::ios::binary);
TextLinesReader reader(input_stream);
Vector<std::unique_ptr<Vector<std::unique_ptr<ObjFileSegment>>>> all_segments;
tbb::task_group tasks;
while (!reader.eof()) {
StringRef text = reader.read_next_line_chunk(20 * 1024 * 1024);
auto segments = BLI::make_unique<Vector<std::unique_ptr<ObjFileSegment>>>();
auto segments_ptr = segments.get();
all_segments.append(std::move(segments));
tasks.run([segments_ptr, text, &reader]() {
parse_obj_lines(text, *segments_ptr);
reader.free_chunk(text);
});
}
tasks.wait();
Vector<const ObjFileSegment *> flattened_segments;
for (auto &segments : all_segments) {
for (auto &segment : *segments) {
flattened_segments.append(segment.get());
}
}
generate_objects_from_segments(C, flattened_segments);
}
static int obj_import_exec(bContext *C, wmOperator *UNUSED(op))
{
char filepath[FILE_MAX];
strcpy(filepath, "/home/jacques/Documents/subdiv_cube.obj");
// RNA_string_get(op->ptr, "filepath", filepath);
std::cout << "Open: " << filepath << '\n';
import_obj(C, filepath);
return OPERATOR_FINISHED;
}
static int obj_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
// return WM_operator_filesel(C, op, event);
return obj_import_exec(C, op);
}
void OBJECT_OT_obj_import_test(wmOperatorType *ot)
{
ot->name = "Obj Import Test";
ot->description = "Obj Import test";
ot->idname = "OBJECT_OT_obj_import_test";
ot->invoke = obj_import_invoke;
ot->exec = obj_import_exec;
/* Properties.*/
WM_operator_properties_filesel(ot,
FILE_TYPE_OBJECT_IO,
FILE_SPECIAL,
FILE_OPENFILE,
WM_FILESEL_FILEPATH,
FILE_DEFAULTDISPLAY,
FILE_SORT_ALPHA);
}

View File

@@ -266,6 +266,8 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_voxel_remesh);
WM_operatortype_append(OBJECT_OT_quadriflow_remesh);
WM_operatortype_append(OBJECT_OT_obj_import_test);
}
void ED_operatormacros_object(void)

View File

@@ -237,3 +237,96 @@ TEST(string_ref, Substr)
EXPECT_EQ(ref.substr(3, 4), "lo w");
EXPECT_EQ(ref.substr(6, 5), "world");
}
TEST(string_ref, StartsWithChar)
{
StringRef ref("hello world");
EXPECT_TRUE(ref.startswith('h'));
EXPECT_FALSE(ref.startswith('a'));
}
TEST(string_ref, EndsWithChar)
{
StringRef ref("hello world");
EXPECT_TRUE(ref.endswith('d'));
EXPECT_FALSE(ref.endswith('a'));
}
TEST(string_ref, StartsWithLowerAscii)
{
{
StringRef ref("hello");
EXPECT_TRUE(ref.startswith_lower_ascii("hel"));
EXPECT_FALSE(ref.startswith_lower_ascii("el"));
}
{
StringRef ref("HELLO");
EXPECT_TRUE(ref.startswith_lower_ascii("hel"));
EXPECT_FALSE(ref.startswith_lower_ascii("el"));
}
{
StringRef ref("Hello");
EXPECT_TRUE(ref.startswith_lower_ascii("hel"));
EXPECT_FALSE(ref.startswith_lower_ascii("el"));
}
}
TEST(string_ref, DropSuffixN)
{
StringRef ref1("hello world");
StringRef ref2 = ref1.drop_suffix(4);
StringRef ref3 = ref2.drop_suffix(7);
EXPECT_EQ(ref2, "hello w");
EXPECT_EQ(ref3, "");
}
TEST(string_ref, DropSuffix)
{
StringRef ref1("hello world");
StringRef ref2 = ref1.drop_suffix("orld");
StringRef ref3 = ref2.drop_suffix("hello w");
EXPECT_EQ(ref2, "hello w");
EXPECT_EQ(ref3, "");
}
TEST(string_ref, Strip)
{
StringRef ref(" \ttest \n ");
EXPECT_EQ(ref.lstrip(), "test \n ");
EXPECT_EQ(ref.rstrip(), " \ttest");
EXPECT_EQ(ref.strip(), "test");
EXPECT_EQ(ref.lstrip({' ', '\t', 't'}), "est \n ");
EXPECT_EQ(ref.strip({' ', '\t', '\n', 't'}), "es");
}
TEST(string_ref_null, Strip)
{
StringRefNull ref1(" test ");
StringRefNull ref2 = ref1.lstrip();
EXPECT_EQ(ref2, "test ");
}
static void test_valid_float_conversion(StringRef str, float expected)
{
bool success = false;
float value = str.to_float(&success);
EXPECT_TRUE(success);
EXPECT_EQ(value, expected);
}
static void test_invalid_float_conversion(StringRef str)
{
bool success = true;
float value = str.to_float(&success);
EXPECT_FALSE(success);
EXPECT_EQ(value, 0.0f);
}
TEST(string_ref, ToFloat)
{
test_valid_float_conversion("25", 25.0f);
test_valid_float_conversion("2e3", 2000.0f);
test_valid_float_conversion("12.25", 12.25f);
test_invalid_float_conversion("abc");
test_invalid_float_conversion("");
}