-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrapidobj_ext.cpp
More file actions
180 lines (151 loc) · 6.19 KB
/
Copy pathrapidobj_ext.cpp
File metadata and controls
180 lines (151 loc) · 6.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#include "rapidobj.hpp"
#include <nanobind/nanobind.h>
#include <nanobind/ndarray.h>
#include <nanobind/stl/string.h>
#include <nanobind/stl/vector.h>
#include <vector>
namespace nb = nanobind;
// Keep the rapidobj::Result alive - it owns all the data
// We only need to build the flattened face/wedge arrays
struct ObjData {
rapidobj::Result result;
std::vector<int> faces; // flat: [v0,v1,v2, ...]
std::vector<int> wedge_texcoord_indices; // per-wedge index into texcoords
std::vector<int> wedge_material_ids; // per-wedge material ID
std::vector<std::string> texture_paths;
bool ok() const { return !result.error; }
std::string error_message() const {
return result.error ? result.error.code.message() : "";
}
};
static ObjData *parse_obj_internal(const std::string &filename) {
auto *data = new ObjData();
data->result = rapidobj::ParseFile(filename);
if (data->result.error) {
return data;
}
// Collect texture paths from materials
for (const auto &material : data->result.materials) {
data->texture_paths.push_back(material.diffuse_texname);
}
// Triangulate in place
rapidobj::Triangulate(data->result);
// Pre-allocate faces and wedge indices
size_t total_indices = 0;
for (const auto &shape : data->result.shapes) {
total_indices += shape.mesh.indices.size();
}
data->faces.reserve(total_indices);
data->wedge_texcoord_indices.reserve(total_indices);
data->wedge_material_ids.reserve(total_indices);
// Build flattened face, wedge texcoord index, and material ID arrays
for (const auto &shape : data->result.shapes) {
const auto &mesh = shape.mesh;
size_t face_idx = 0;
for (size_t i = 0; i < mesh.indices.size(); ++i) {
const auto &index = mesh.indices[i];
data->faces.push_back(index.position_index);
data->wedge_texcoord_indices.push_back(index.texcoord_index);
// Each face has 3 vertices (after triangulation), so divide by 3 to get
// face index
int material_id =
mesh.material_ids.empty() ? -1 : mesh.material_ids[face_idx];
data->wedge_material_ids.push_back(material_id);
// Move to next face every 3 vertices
if ((i + 1) % 3 == 0) {
face_idx++;
}
}
}
return data;
}
// Python-exposed class that wraps ObjData with capsule ownership
class ObjParseResult {
public:
ObjData *data;
nb::capsule owner;
ObjParseResult(ObjData *d)
: data(d), owner(d, [](void *p) noexcept { delete (ObjData *)p; }) {}
bool ok() const { return data->ok(); }
std::string error_message() const { return data->error_message(); }
int vertex_count() const {
return static_cast<int>(data->result.attributes.positions.size() / 3);
}
int normal_count() const {
return static_cast<int>(data->result.attributes.normals.size() / 3);
}
int uv_count() const {
return static_cast<int>(data->result.attributes.texcoords.size() / 2);
}
int shape_count() const {
return static_cast<int>(data->result.shapes.size());
}
int material_count() const {
return static_cast<int>(data->result.materials.size());
}
const std::vector<std::string> &texture_paths() const {
return data->texture_paths;
}
// Return vertices - ZERO COPY, points directly to rapidobj's Array
nb::ndarray<nb::numpy, float, nb::shape<-1, 3>> vertices() {
size_t n_vertices = data->result.attributes.positions.size() / 3;
return nb::ndarray<nb::numpy, float, nb::shape<-1, 3>>(
data->result.attributes.positions.data(), {n_vertices, 3}, owner);
}
// Return faces - points to our flattened vector
nb::ndarray<nb::numpy, int, nb::shape<-1, 3>> faces() {
size_t n_faces = data->faces.size() / 3;
return nb::ndarray<nb::numpy, int, nb::shape<-1, 3>>(data->faces.data(),
{n_faces, 3}, owner);
}
// Return texcoords - ZERO COPY, points directly to rapidobj's Array
nb::ndarray<nb::numpy, float, nb::shape<-1, 2>> texcoords() {
size_t n_texcoords = data->result.attributes.texcoords.size() / 2;
return nb::ndarray<nb::numpy, float, nb::shape<-1, 2>>(
data->result.attributes.texcoords.data(), {n_texcoords, 2}, owner);
}
// Return wedge texcoord indices - points to our flattened vector
nb::ndarray<nb::numpy, int, nb::shape<-1>> wedge_texcoord_indices() {
return nb::ndarray<nb::numpy, int, nb::shape<-1>>(
data->wedge_texcoord_indices.data(),
{data->wedge_texcoord_indices.size()}, owner);
}
// Return wedge material IDs - points to our flattened vector
nb::ndarray<nb::numpy, int, nb::shape<-1>> wedge_material_ids() {
return nb::ndarray<nb::numpy, int, nb::shape<-1>>(
data->wedge_material_ids.data(), {data->wedge_material_ids.size()},
owner);
}
};
ObjParseResult parse_obj(const std::string &filename) {
ObjData *data = nullptr;
{
nb::gil_scoped_release release;
data = parse_obj_internal(filename);
}
return ObjParseResult(data);
}
NB_MODULE(rapidobj, m) {
m.doc() = "Fast OBJ file parser using rapidobj";
nb::class_<ObjParseResult>(m, "ObjParseResult")
.def_prop_ro("ok", &ObjParseResult::ok)
.def_prop_ro("error_message", &ObjParseResult::error_message)
.def_prop_ro("vertex_count", &ObjParseResult::vertex_count)
.def_prop_ro("normal_count", &ObjParseResult::normal_count)
.def_prop_ro("uv_count", &ObjParseResult::uv_count)
.def_prop_ro("shape_count", &ObjParseResult::shape_count)
.def_prop_ro("material_count", &ObjParseResult::material_count)
.def_prop_ro("texture_paths", &ObjParseResult::texture_paths)
.def_prop_ro("vertices", &ObjParseResult::vertices,
nb::rv_policy::reference)
.def_prop_ro("faces", &ObjParseResult::faces, nb::rv_policy::reference)
.def_prop_ro("texcoords", &ObjParseResult::texcoords,
nb::rv_policy::reference)
.def_prop_ro("wedge_texcoord_indices",
&ObjParseResult::wedge_texcoord_indices,
nb::rv_policy::reference)
.def_prop_ro("wedge_material_ids", &ObjParseResult::wedge_material_ids,
nb::rv_policy::reference);
m.def("parse_obj", &parse_obj, nb::arg("filename"),
"Parse an OBJ file and return the result");
}