csg 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Makefile +41 -0
- data/ext/Rakefile +6 -0
- data/lib/csg.rb +83 -0
- data/src/bsp.c +555 -0
- data/src/bsp.h +40 -0
- data/src/dbg.c +48 -0
- data/src/dbg.h +76 -0
- data/src/export.c +68 -0
- data/src/export.h +16 -0
- data/src/klist.h +121 -0
- data/src/poly.c +206 -0
- data/src/poly.h +45 -0
- data/src/stl.c +333 -0
- data/src/stl.h +60 -0
- data/src/vector.c +55 -0
- data/src/vector.h +35 -0
- metadata +80 -0
data/Makefile
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
#-*- mode:makefile-gmake; -*-
|
2
|
+
ROOT = $(shell pwd)
|
3
|
+
TARGET = csgtool
|
4
|
+
|
5
|
+
INCLUDE += -I$(ROOT)/src
|
6
|
+
SOURCES = $(wildcard $(ROOT)/src/*.c)
|
7
|
+
|
8
|
+
OBJS = $(patsubst %.c,%.o,$(SOURCES))
|
9
|
+
CPPFLAGS = $(OPTCPPFLAGS)
|
10
|
+
LIBS = -lm $(OPTLIBS)
|
11
|
+
CFLAGS = -g -std=c99 $(INCLUDE) -Wall -Werror $(OPTFLAGS)
|
12
|
+
|
13
|
+
ifeq ($(shell uname),Darwin)
|
14
|
+
LIB_TARGET = libcsg.dylib
|
15
|
+
else
|
16
|
+
LIB_TARGET = libcsg.so
|
17
|
+
endif
|
18
|
+
|
19
|
+
.DEFAULT_GOAL = all
|
20
|
+
all: $(TARGET) $(LIB_TARGET)
|
21
|
+
|
22
|
+
clean:
|
23
|
+
make -C tests clean
|
24
|
+
rm -rf $(OBJS) $(TARGET) $(TARGET).o $(TARGET).new $(LIB_TARGET)
|
25
|
+
|
26
|
+
test:
|
27
|
+
@make -C tests clean test
|
28
|
+
|
29
|
+
.PHONY: all clean test libcsg
|
30
|
+
|
31
|
+
$(TARGET): $(TARGET).o $(OBJS)
|
32
|
+
$(CC) $(CFLAGS) $^ $(LIBS) -o $@.new
|
33
|
+
mv $@.new $@
|
34
|
+
|
35
|
+
$(LIB_TARGET): $(OBJS)
|
36
|
+
$(CC) -shared $(OBJS) $(LIBS) -o $(LIB_TARGET)
|
37
|
+
|
38
|
+
libcsg: $(LIB_TARGET)
|
39
|
+
|
40
|
+
%.o: %.c
|
41
|
+
$(CC) -fPIC $(CFLAGS) -o $@ -c $^
|
data/ext/Rakefile
ADDED
data/lib/csg.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module CSG
|
4
|
+
module Native
|
5
|
+
extend FFI::Library
|
6
|
+
|
7
|
+
candidates = ['.bundle', '.so', '.dylib', ''].map do |ext|
|
8
|
+
File.join(File.expand_path("../..", __FILE__), 'libcsg' + ext)
|
9
|
+
end
|
10
|
+
ffi_lib ['csg'] + candidates
|
11
|
+
|
12
|
+
class STLObject < FFI::ManagedStruct
|
13
|
+
layout :header, [:uint8, 80],
|
14
|
+
:facet_count, :uint32,
|
15
|
+
:stl_facet, :pointer
|
16
|
+
|
17
|
+
def self.release(ptr)
|
18
|
+
CSG::Native.stl_free ptr
|
19
|
+
end
|
20
|
+
|
21
|
+
def write_file(path)
|
22
|
+
CSG::Native.stl_write_file self, path
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class BSPNode < FFI::ManagedStruct
|
27
|
+
layout :polygons, :pointer,
|
28
|
+
:divider, :pointer,
|
29
|
+
:front, :pointer,
|
30
|
+
:back, :pointer
|
31
|
+
|
32
|
+
def self.release(ptr)
|
33
|
+
CSG::Native.free_bsp_tree ptr
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
attach_function :stl_read_file, [:string, :bool], :pointer
|
38
|
+
attach_function :stl_write_file, [:pointer, :string], :int
|
39
|
+
|
40
|
+
attach_function :stl_free, [:pointer], :void
|
41
|
+
attach_function :free_bsp_tree, [:pointer], :void
|
42
|
+
|
43
|
+
attach_function :stl_to_bsp, [:pointer], :pointer
|
44
|
+
attach_function :bsp_to_stl, [:pointer], :pointer
|
45
|
+
|
46
|
+
attach_function :bsp_union, [:pointer, :pointer], :pointer
|
47
|
+
attach_function :bsp_subtract, [:pointer, :pointer], :pointer
|
48
|
+
attach_function :bsp_intersect, [:pointer, :pointer], :pointer
|
49
|
+
end
|
50
|
+
|
51
|
+
class Solid
|
52
|
+
attr_reader :tree
|
53
|
+
|
54
|
+
def initialize(opts)
|
55
|
+
if opts[:file]
|
56
|
+
load_from_file opts[:file]
|
57
|
+
elsif opts[:tree]
|
58
|
+
@tree = opts[:tree]
|
59
|
+
end
|
60
|
+
raise ArgumentError.new "Failed to load tree with: #{opts.inspect}" unless @tree
|
61
|
+
end
|
62
|
+
|
63
|
+
def load_from_file(path)
|
64
|
+
File.stat(path) # Stat before load to raise a sane "Does not exist" error
|
65
|
+
stl = CSG::Native::STLObject.new CSG::Native.stl_read_file(path, false)
|
66
|
+
@tree = CSG::Native::BSPNode.new CSG::Native.stl_to_bsp(stl)
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_stl
|
70
|
+
ptr = CSG::Native.bsp_to_stl tree
|
71
|
+
CSG::Native::STLObject.new(ptr)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Build the CSG methods with ManagedStruct wrappers
|
75
|
+
[:intersect, :subtract, :union].each do |name|
|
76
|
+
define_method name do |solid|
|
77
|
+
ptr = CSG::Native.send "bsp_#{name}", tree, solid.tree
|
78
|
+
tree = CSG::Native::BSPNode.new(ptr)
|
79
|
+
CSG::Solid.new :tree => tree
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/src/bsp.c
ADDED
@@ -0,0 +1,555 @@
|
|
1
|
+
#include <assert.h>
|
2
|
+
|
3
|
+
#include "bsp.h"
|
4
|
+
#include "dbg.h"
|
5
|
+
|
6
|
+
bsp_node_t *alloc_bsp_node(void) {
|
7
|
+
bsp_node_t *node = NULL;
|
8
|
+
check_mem(node = calloc(1, sizeof(bsp_node_t)));
|
9
|
+
node->polygons = kl_init(poly);
|
10
|
+
return node;
|
11
|
+
error:
|
12
|
+
return NULL;
|
13
|
+
}
|
14
|
+
|
15
|
+
bsp_node_t *clone_bsp_tree(bsp_node_t *tree) {
|
16
|
+
bsp_node_t *copy = alloc_bsp_node();
|
17
|
+
check_mem(copy);
|
18
|
+
|
19
|
+
kliter_t(poly) *iter = kl_begin(tree->polygons);
|
20
|
+
for(; iter != kl_end(tree->polygons); iter = kl_next(iter)) {
|
21
|
+
poly_t *poly_copy = clone_poly(kl_val(iter));
|
22
|
+
check_mem(poly_copy);
|
23
|
+
*kl_pushp(poly, copy->polygons) = poly_copy;
|
24
|
+
}
|
25
|
+
|
26
|
+
free_poly(copy->divider, 1);
|
27
|
+
copy->divider = clone_poly(tree->divider);
|
28
|
+
check_mem(copy->divider);
|
29
|
+
|
30
|
+
if(tree->front != NULL) {
|
31
|
+
copy->front = clone_bsp_tree(tree->front);
|
32
|
+
check_mem(copy->front);
|
33
|
+
}
|
34
|
+
if(tree->back != NULL) {
|
35
|
+
copy->back = clone_bsp_tree(tree->back);
|
36
|
+
check_mem(copy->back);
|
37
|
+
}
|
38
|
+
|
39
|
+
return copy;
|
40
|
+
error:
|
41
|
+
if(copy != NULL) free_bsp_tree(copy);
|
42
|
+
return NULL;
|
43
|
+
}
|
44
|
+
|
45
|
+
void free_bsp_node(bsp_node_t *node) {
|
46
|
+
if(node == NULL) return;
|
47
|
+
kl_destroy(poly, node->polygons);
|
48
|
+
free_poly(node->divider, 1);
|
49
|
+
free(node);
|
50
|
+
}
|
51
|
+
|
52
|
+
void free_bsp_tree(bsp_node_t *tree) {
|
53
|
+
if(tree == NULL) return;
|
54
|
+
if(tree->front != NULL) free_bsp_tree(tree->front);
|
55
|
+
if(tree->back != NULL) free_bsp_tree(tree->back);
|
56
|
+
free_bsp_node(tree);
|
57
|
+
}
|
58
|
+
|
59
|
+
// Put the polygon in the the appropriate list
|
60
|
+
// and increment the counter assosiated with it.
|
61
|
+
// A polygon can end up in muliple lists, but not
|
62
|
+
// all of them.
|
63
|
+
int bsp_subdivide(poly_t *divider, poly_t *poly,
|
64
|
+
poly_t **coplanar_front, int *n_cp_front,
|
65
|
+
poly_t **coplanar_back, int *n_cp_back,
|
66
|
+
poly_t **front, int *n_front,
|
67
|
+
poly_t **back, int *n_back) {
|
68
|
+
switch(poly_classify_poly(divider, poly)) {
|
69
|
+
case FRONT:
|
70
|
+
front[*n_front] = poly;
|
71
|
+
*n_front += 1;
|
72
|
+
break;
|
73
|
+
case BACK:
|
74
|
+
back[*n_back] = poly;
|
75
|
+
*n_back += 1;
|
76
|
+
break;
|
77
|
+
case COPLANAR:
|
78
|
+
if(f3_dot(divider->normal, poly->normal) > 0) {
|
79
|
+
coplanar_front[*n_cp_front] = poly;
|
80
|
+
*n_cp_front += 1;
|
81
|
+
}
|
82
|
+
else {
|
83
|
+
coplanar_back[*n_cp_back] = poly;
|
84
|
+
*n_cp_back += 1;
|
85
|
+
}
|
86
|
+
break;
|
87
|
+
case SPANNING: {
|
88
|
+
poly_t *f = NULL;
|
89
|
+
poly_t *b = NULL;
|
90
|
+
check(poly_split(divider, poly, &f, &b) == 0,
|
91
|
+
"Failed to split polygon(%p) with divider(%p)", poly, divider);
|
92
|
+
front[*n_front] = f;
|
93
|
+
*n_front += 1;
|
94
|
+
|
95
|
+
back[*n_back] = b;
|
96
|
+
*n_back += 1;
|
97
|
+
break;
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
return 0;
|
102
|
+
error:
|
103
|
+
return -1;
|
104
|
+
}
|
105
|
+
|
106
|
+
bsp_node_t *bsp_build(bsp_node_t *node, klist_t(poly) *polygons, int copy) {
|
107
|
+
poly_t **polys = NULL;
|
108
|
+
check_mem(polys = malloc(sizeof(poly_t*) * polygons->size));
|
109
|
+
|
110
|
+
kliter_t(poly) *iter = NULL;
|
111
|
+
int i = 0;
|
112
|
+
for(iter = kl_begin(polygons); iter != kl_end(polygons); i++, iter = kl_next(iter)) {
|
113
|
+
poly_t *poly = NULL;
|
114
|
+
poly = copy ? clone_poly(kl_val(iter)) : kl_val(iter);
|
115
|
+
check(poly != NULL, "Failed to make poly array. Item %d is NULL in list %p. (Copy: %s)",
|
116
|
+
i, polygons, copy ? "true" : "false");
|
117
|
+
polys[i] = poly;
|
118
|
+
}
|
119
|
+
|
120
|
+
check((node = bsp_build_array(node, polys, polygons->size)),
|
121
|
+
"Failed to build node from list(%p) of %zd polys", polygons, polygons->size);
|
122
|
+
free(polys);
|
123
|
+
|
124
|
+
return node;
|
125
|
+
error:
|
126
|
+
if(polys) free(polys);
|
127
|
+
return NULL;
|
128
|
+
}
|
129
|
+
bsp_node_t *bsp_build_array(bsp_node_t *node, poly_t **polygons, size_t n_polys) {
|
130
|
+
int rc = 0;
|
131
|
+
|
132
|
+
// Polygon lists and counters
|
133
|
+
int n_coplanar = 0;
|
134
|
+
int n_front = 0;
|
135
|
+
int n_back = 0;
|
136
|
+
poly_t **coplanar = NULL;
|
137
|
+
poly_t **front_p = NULL;
|
138
|
+
poly_t **back_p = NULL;
|
139
|
+
|
140
|
+
// Iterators
|
141
|
+
poly_t *poly = NULL;
|
142
|
+
size_t poly_i = 0;
|
143
|
+
|
144
|
+
if(node == NULL) {
|
145
|
+
// Allocate a node if we weren't given one. It's the nice
|
146
|
+
// thing to do for people.
|
147
|
+
node = alloc_bsp_node();
|
148
|
+
check_mem(node);
|
149
|
+
}
|
150
|
+
|
151
|
+
if(n_polys == 0) return node;
|
152
|
+
|
153
|
+
if(node->divider == NULL) {
|
154
|
+
// Add the divider to the list of coplanar polygons
|
155
|
+
// and advance the iterator to the next polygon
|
156
|
+
// if we have not yet picked the divider for this node.
|
157
|
+
// This avoids having to rely on an explicit
|
158
|
+
// test of this node against itself in the loop below.
|
159
|
+
*kl_pushp(poly, node->polygons) = polygons[0];
|
160
|
+
poly_i += 1;
|
161
|
+
|
162
|
+
node->divider = clone_poly(polygons[0]);
|
163
|
+
check_mem(node->divider);
|
164
|
+
}
|
165
|
+
|
166
|
+
|
167
|
+
check_mem(coplanar = malloc(sizeof(poly_t*) * n_polys));
|
168
|
+
check_mem(front_p = malloc(sizeof(poly_t*) * n_polys));
|
169
|
+
check_mem(back_p = malloc(sizeof(poly_t*) * n_polys));
|
170
|
+
for(; poly_i < n_polys; poly_i++) {
|
171
|
+
poly = polygons[poly_i];
|
172
|
+
rc = bsp_subdivide(node->divider, poly,
|
173
|
+
coplanar, &n_coplanar,
|
174
|
+
coplanar, &n_coplanar,
|
175
|
+
front_p, &n_front,
|
176
|
+
back_p, &n_back);
|
177
|
+
check(rc == 0, "Failed to subdivide: %p => %p", node->divider, poly);
|
178
|
+
}
|
179
|
+
|
180
|
+
// Store the coplanar nodes in this node's polygon list
|
181
|
+
// and free the container, letting the list destructor
|
182
|
+
// clean up
|
183
|
+
int i = 0;
|
184
|
+
for(i = 0; i < n_coplanar; i++) {
|
185
|
+
*kl_pushp(poly, node->polygons) = coplanar[i];
|
186
|
+
}
|
187
|
+
free(coplanar);
|
188
|
+
coplanar = NULL;
|
189
|
+
|
190
|
+
if((n_front > 0)) {
|
191
|
+
if(node->front == NULL) node->front = alloc_bsp_node();
|
192
|
+
check_mem(node->front);
|
193
|
+
check(bsp_build_array(node->front, front_p, n_front) != NULL,
|
194
|
+
"Failed to build front tree of bsp_node_array(%p)", node);
|
195
|
+
}
|
196
|
+
if((n_back > 0)) {
|
197
|
+
if(node->back == NULL) node->back = alloc_bsp_node();
|
198
|
+
check_mem(node->back);
|
199
|
+
check(bsp_build_array(node->back, back_p, n_back) != NULL,
|
200
|
+
"Failed to build back tree of bsp_node(%p)", node);
|
201
|
+
}
|
202
|
+
free(front_p);
|
203
|
+
free(back_p);
|
204
|
+
front_p = back_p = NULL;
|
205
|
+
|
206
|
+
return node;
|
207
|
+
error:
|
208
|
+
if(coplanar) free(coplanar);
|
209
|
+
if(back_p) free(back_p);
|
210
|
+
if(front_p) free(front_p);
|
211
|
+
return NULL;
|
212
|
+
}
|
213
|
+
|
214
|
+
int bsp_copy_node_polygons(bsp_node_t *node, int make_triangles, klist_t(poly) *dst) {
|
215
|
+
int copied = 0;
|
216
|
+
kliter_t(poly) *iter = kl_begin(node->polygons);
|
217
|
+
for(;iter != kl_end(node->polygons); iter = kl_next(iter)) {
|
218
|
+
poly_t *poly = kl_val(iter);
|
219
|
+
int vertex_count = poly_vertex_count(poly);
|
220
|
+
if(!make_triangles || vertex_count == 3) {
|
221
|
+
poly_t *copy = clone_poly(poly);
|
222
|
+
check_mem(copy);
|
223
|
+
*kl_pushp(poly, dst) = copy;
|
224
|
+
}
|
225
|
+
else if(vertex_count > 3){
|
226
|
+
// Start with the third vertex and build triangles
|
227
|
+
// in in the form (v0, v_prev, v_cur)
|
228
|
+
float3 *v_cur, *v_prev;
|
229
|
+
for(int i = 2; i < vertex_count; i++) {
|
230
|
+
v_cur = &poly->vertices[i];
|
231
|
+
v_prev = &poly->vertices[i - 1];
|
232
|
+
poly_t *tri = poly_make_triangle(poly->vertices[0], *v_prev, *v_cur);
|
233
|
+
check_mem(tri);
|
234
|
+
*kl_pushp(poly, dst) = tri;
|
235
|
+
}
|
236
|
+
}
|
237
|
+
else {
|
238
|
+
sentinel("polygon(%p) has less than three(%d) vertices.", poly, poly_vertex_count(poly));
|
239
|
+
}
|
240
|
+
copied++;
|
241
|
+
}
|
242
|
+
return copied;
|
243
|
+
error:
|
244
|
+
return -1;
|
245
|
+
}
|
246
|
+
|
247
|
+
klist_t(poly) *bsp_to_polygons(bsp_node_t *tree, int make_triangles, klist_t(poly) *dst) {
|
248
|
+
klist_t(poly) *polygons = dst ? dst : kl_init(poly);
|
249
|
+
|
250
|
+
if(tree->back != NULL) {
|
251
|
+
bsp_to_polygons(tree->back, make_triangles, polygons);
|
252
|
+
}
|
253
|
+
|
254
|
+
int rc = bsp_copy_node_polygons(tree, make_triangles, polygons);
|
255
|
+
check(rc == tree->polygons->size, "bsp_copy_node_polygons() did not copy all polygons");
|
256
|
+
|
257
|
+
if(tree->front != NULL) {
|
258
|
+
bsp_to_polygons(tree->front, make_triangles, polygons);
|
259
|
+
}
|
260
|
+
|
261
|
+
return polygons;
|
262
|
+
error:
|
263
|
+
// Only clean up the polygons list if we initialized it on error
|
264
|
+
if(dst == NULL) kl_destroy(poly, polygons);
|
265
|
+
return NULL;
|
266
|
+
}
|
267
|
+
|
268
|
+
bsp_node_t *bsp_invert(bsp_node_t *tree) {
|
269
|
+
// Invert every polygon in this node
|
270
|
+
poly_t *poly = NULL;
|
271
|
+
kliter_t(poly) *iter = kl_begin(tree->polygons);
|
272
|
+
for(; iter != kl_end(tree->polygons); iter = kl_next(iter)) {
|
273
|
+
poly = poly_invert(kl_val(iter));
|
274
|
+
check(poly != NULL, "Failed to invert polygon %p", kl_val(iter));
|
275
|
+
}
|
276
|
+
|
277
|
+
// Invert the divider
|
278
|
+
if(tree->divider) {
|
279
|
+
poly = poly_invert(tree->divider);
|
280
|
+
check(poly != NULL, "Failed to inverts bsp_node_t(%p)->divider(%p)", tree, tree->divider);
|
281
|
+
}
|
282
|
+
|
283
|
+
// Invert the front and back trees
|
284
|
+
bsp_node_t *node;
|
285
|
+
if(tree->front) {
|
286
|
+
node = bsp_invert(tree->front);
|
287
|
+
check(node != NULL, "Failed to invert back tree of bsp_node_t(%p)", tree->front);
|
288
|
+
}
|
289
|
+
if(tree->back) {
|
290
|
+
node = bsp_invert(tree->back);
|
291
|
+
check(node != NULL, "Failed to invert back tree of bsp_node_t(%p)", tree->back);
|
292
|
+
}
|
293
|
+
|
294
|
+
// Swap front and back trees
|
295
|
+
node = tree->front;
|
296
|
+
tree->front = tree->back;
|
297
|
+
tree->back = node;
|
298
|
+
|
299
|
+
return tree;
|
300
|
+
error:
|
301
|
+
return NULL;
|
302
|
+
}
|
303
|
+
|
304
|
+
klist_t(poly) *bsp_clip_polygon_array(bsp_node_t *node, poly_t **polygons, size_t n_polys, klist_t(poly) *dst) {
|
305
|
+
klist_t(poly) *result = dst != NULL ? dst : kl_init(poly);
|
306
|
+
size_t i;
|
307
|
+
poly_t *p = NULL;
|
308
|
+
int rc = -1;
|
309
|
+
|
310
|
+
poly_t **poly_buffer = NULL;
|
311
|
+
poly_t **front_array = NULL;
|
312
|
+
poly_t **back_array = NULL;
|
313
|
+
int n_front = 0;
|
314
|
+
int n_back = 0;
|
315
|
+
|
316
|
+
// Let's end this quick if there's nothing to do.
|
317
|
+
if(n_polys == 0) return result;
|
318
|
+
|
319
|
+
if(node->divider != NULL) {
|
320
|
+
check_mem(poly_buffer = malloc((sizeof(poly_t*) * n_polys) * 2));
|
321
|
+
front_array = poly_buffer;
|
322
|
+
back_array = poly_buffer + n_polys;
|
323
|
+
// Sort this node's polygons into the front or back
|
324
|
+
for(i = 0; i < n_polys; i++) {
|
325
|
+
p = polygons[i];
|
326
|
+
rc = bsp_subdivide(node->divider, p,
|
327
|
+
front_array, &n_front, back_array, &n_back,
|
328
|
+
front_array, &n_front, back_array, &n_back);
|
329
|
+
check(rc != -1, "Failed to subdivide poly %p", p);
|
330
|
+
}
|
331
|
+
|
332
|
+
int i;
|
333
|
+
poly_t *copy = NULL;
|
334
|
+
// Recur to the front tree, or copy my current front nodes to result.
|
335
|
+
if(node->front) {
|
336
|
+
result = bsp_clip_polygon_array(node->front, front_array, n_front, result);
|
337
|
+
check(result != NULL, "Failed to clip front tree");
|
338
|
+
}
|
339
|
+
else {
|
340
|
+
for(i = 0; i < n_front; i++) {
|
341
|
+
copy = clone_poly(front_array[i]);
|
342
|
+
check_mem(copy);
|
343
|
+
*kl_pushp(poly, result) = copy;
|
344
|
+
}
|
345
|
+
}
|
346
|
+
|
347
|
+
// Repeat for the back tree
|
348
|
+
if(node->back) {
|
349
|
+
result = bsp_clip_polygon_array(node->back, back_array, n_back, result);
|
350
|
+
check(result != NULL, "Failed to clip back tree");
|
351
|
+
}
|
352
|
+
|
353
|
+
if(poly_buffer) free(poly_buffer);
|
354
|
+
// Clean up the result halves, now that they're copied into `result`
|
355
|
+
}
|
356
|
+
else {
|
357
|
+
// If we don't have a divider we just copy out the polygons
|
358
|
+
for(i = 0; i < n_polys; i++) {
|
359
|
+
check_mem(p = clone_poly(polygons[i]));
|
360
|
+
*kl_pushp(poly, result) = p;
|
361
|
+
}
|
362
|
+
}
|
363
|
+
|
364
|
+
return result;
|
365
|
+
error:
|
366
|
+
if(poly_buffer) free(poly_buffer);
|
367
|
+
if(result) kl_destroy(poly, result);
|
368
|
+
return NULL;
|
369
|
+
}
|
370
|
+
|
371
|
+
klist_t(poly) *bsp_clip_polygons(bsp_node_t *node, klist_t(poly) *polygons, klist_t(poly) *dst) {
|
372
|
+
klist_t(poly) *result = dst != NULL ? dst : kl_init(poly);
|
373
|
+
kliter_t(poly) *iter = NULL;
|
374
|
+
poly_t *p = NULL;
|
375
|
+
int rc = -1;
|
376
|
+
|
377
|
+
poly_t **poly_buffer = NULL;
|
378
|
+
poly_t **front_array = NULL;
|
379
|
+
poly_t **back_array = NULL;
|
380
|
+
int n_front = 0;
|
381
|
+
int n_back = 0;
|
382
|
+
|
383
|
+
// Let's end this quick if there's nothing to do.
|
384
|
+
if(polygons->size == 0) return result;
|
385
|
+
|
386
|
+
if(node->divider != NULL) {
|
387
|
+
check_mem(poly_buffer = malloc(sizeof(poly_t*) * polygons->size * 2));
|
388
|
+
front_array = poly_buffer;
|
389
|
+
back_array = poly_buffer + polygons->size;
|
390
|
+
// Sort this node's polygons into the front or back
|
391
|
+
for(iter = kl_begin(polygons); iter != kl_end(polygons); iter = kl_next(iter)) {
|
392
|
+
rc = bsp_subdivide(node->divider, kl_val(iter),
|
393
|
+
front_array, &n_front, back_array, &n_back,
|
394
|
+
front_array, &n_front, back_array, &n_back);
|
395
|
+
check(rc != -1, "Failed to subdivide poly %p", kl_val(iter));
|
396
|
+
}
|
397
|
+
|
398
|
+
int i;
|
399
|
+
poly_t *copy = NULL;
|
400
|
+
// Recur to the front tree, or copy my current front nodes to result.
|
401
|
+
if(node->front) {
|
402
|
+
result = bsp_clip_polygon_array(node->front, front_array, n_front, result);
|
403
|
+
check(result != NULL, "Failed to clip front tree");
|
404
|
+
}
|
405
|
+
else {
|
406
|
+
for(i = 0; i < n_front; i++) {
|
407
|
+
copy = clone_poly(front_array[i]);
|
408
|
+
check_mem(copy);
|
409
|
+
*kl_pushp(poly, result) = copy;
|
410
|
+
}
|
411
|
+
}
|
412
|
+
|
413
|
+
// Repeat for the back tree
|
414
|
+
if(node->back) {
|
415
|
+
result = bsp_clip_polygon_array(node->back, back_array, n_back, result);
|
416
|
+
check(result != NULL, "Failed to clip back tree");
|
417
|
+
}
|
418
|
+
|
419
|
+
if(poly_buffer) free(poly_buffer);
|
420
|
+
// Clean up the result halves, now that they're copied into `result`
|
421
|
+
}
|
422
|
+
else {
|
423
|
+
// If we don't have a divider we just copy out the polygons
|
424
|
+
for(iter = kl_begin(polygons); iter != kl_end(polygons); iter = kl_next(iter)) {
|
425
|
+
check_mem(p = clone_poly(kl_val(iter)));
|
426
|
+
*kl_pushp(poly, result) = p;
|
427
|
+
}
|
428
|
+
}
|
429
|
+
|
430
|
+
return result;
|
431
|
+
error:
|
432
|
+
if(poly_buffer) free(poly_buffer);
|
433
|
+
if(result) kl_destroy(poly, result);
|
434
|
+
return NULL;
|
435
|
+
}
|
436
|
+
|
437
|
+
bsp_node_t *bsp_clip(bsp_node_t *us, bsp_node_t *them) {
|
438
|
+
klist_t(poly) *new = bsp_clip_polygons(them, us->polygons, NULL);
|
439
|
+
kl_destroy(poly, us->polygons);
|
440
|
+
us->polygons = new;
|
441
|
+
|
442
|
+
if(us->front != NULL) bsp_clip(us->front, them);
|
443
|
+
if(us->back != NULL) bsp_clip(us->back, them);
|
444
|
+
|
445
|
+
return us;
|
446
|
+
}
|
447
|
+
|
448
|
+
bsp_node_t *bsp_subtract(bsp_node_t *tree_a, bsp_node_t *tree_b) {
|
449
|
+
bsp_node_t *a = NULL;
|
450
|
+
bsp_node_t *b = NULL;
|
451
|
+
bsp_node_t *result = NULL;
|
452
|
+
klist_t(poly) *b_polys = NULL;
|
453
|
+
|
454
|
+
check_mem(a = clone_bsp_tree(tree_a));
|
455
|
+
check_mem(b = clone_bsp_tree(tree_b));
|
456
|
+
|
457
|
+
check(bsp_invert(a) != NULL, "Failed to invert A");
|
458
|
+
check(bsp_clip(a, b) != NULL, "Failed to clip(A, B)");
|
459
|
+
check(bsp_clip(b, a) != NULL, "Failed clip(B, A)");
|
460
|
+
check(bsp_invert(b) != NULL, "Failed to invert B")
|
461
|
+
check(bsp_clip(b, a) != NULL, "Failed to clio(B, A)");
|
462
|
+
check(bsp_invert(b) != NULL, "Failed to invert B")
|
463
|
+
b_polys = bsp_to_polygons(b, 0, NULL);
|
464
|
+
check(b_polys != NULL, "Failed to get polygons from B");
|
465
|
+
check(bsp_build(a, b_polys, 1) == a, "Failed to add nodes from B into tree A");
|
466
|
+
check(bsp_invert(a) != NULL, "Failed to invert A")
|
467
|
+
|
468
|
+
// TODO: Build a more balanced trees from the polys of
|
469
|
+
// a instead of cloning a tree with potential gaps.
|
470
|
+
result = clone_bsp_tree(a);
|
471
|
+
check_mem(result);
|
472
|
+
|
473
|
+
if(b_polys != NULL) kl_destroy(poly, b_polys);
|
474
|
+
if(a != NULL) free_bsp_tree(a);
|
475
|
+
if(b != NULL) free_bsp_tree(b);
|
476
|
+
return result;
|
477
|
+
error:
|
478
|
+
if(b_polys != NULL) kl_destroy(poly, b_polys);
|
479
|
+
if(a != NULL) free_bsp_tree(a);
|
480
|
+
if(b != NULL) free_bsp_tree(b);
|
481
|
+
if(result != NULL) free_bsp_tree(result);
|
482
|
+
return NULL;
|
483
|
+
}
|
484
|
+
|
485
|
+
bsp_node_t *bsp_union(bsp_node_t *tree_a, bsp_node_t *tree_b) {
|
486
|
+
bsp_node_t *a = NULL;
|
487
|
+
bsp_node_t *b = NULL;
|
488
|
+
bsp_node_t *result = NULL;
|
489
|
+
klist_t(poly) *b_polys = NULL;
|
490
|
+
|
491
|
+
check_mem(a = clone_bsp_tree(tree_a));
|
492
|
+
check_mem(b = clone_bsp_tree(tree_b));
|
493
|
+
|
494
|
+
check(bsp_clip(a, b) != NULL, "Failed to clip(A, B)");
|
495
|
+
check(bsp_clip(b, a) != NULL, "Failed clip(B, A)");
|
496
|
+
check(bsp_invert(b) != NULL, "Failed to invert B")
|
497
|
+
check(bsp_clip(b, a) != NULL, "Failed to clio(B, A)");
|
498
|
+
check(bsp_invert(b) != NULL, "Failed to invert B")
|
499
|
+
b_polys = bsp_to_polygons(b, 0, NULL);
|
500
|
+
check(b_polys != NULL, "Failed to get polygons from B");
|
501
|
+
check(bsp_build(a, b_polys, 1) == a, "Failed to add nodes from B into tree A");
|
502
|
+
|
503
|
+
// TODO: Build a more balanced trees from the polys of
|
504
|
+
// a instead of cloning a tree with potential gaps.
|
505
|
+
result = clone_bsp_tree(a);
|
506
|
+
check_mem(result);
|
507
|
+
|
508
|
+
if(b_polys != NULL) kl_destroy(poly, b_polys);
|
509
|
+
if(a != NULL) free_bsp_tree(a);
|
510
|
+
if(b != NULL) free_bsp_tree(b);
|
511
|
+
return result;
|
512
|
+
error:
|
513
|
+
if(b_polys != NULL) kl_destroy(poly, b_polys);
|
514
|
+
if(a != NULL) free_bsp_tree(a);
|
515
|
+
if(b != NULL) free_bsp_tree(b);
|
516
|
+
if(result != NULL) free_bsp_tree(result);
|
517
|
+
return NULL;
|
518
|
+
}
|
519
|
+
|
520
|
+
bsp_node_t *bsp_intersect(bsp_node_t *tree_a, bsp_node_t *tree_b) {
|
521
|
+
bsp_node_t *a = NULL;
|
522
|
+
bsp_node_t *b = NULL;
|
523
|
+
bsp_node_t *result = NULL;
|
524
|
+
klist_t(poly) *b_polys = NULL;
|
525
|
+
|
526
|
+
check_mem(a = clone_bsp_tree(tree_a));
|
527
|
+
check_mem(b = clone_bsp_tree(tree_b));
|
528
|
+
|
529
|
+
check(bsp_invert(a) != NULL, "Failed to invert A");
|
530
|
+
check(bsp_clip(b, a) != NULL, "Failed clip(B, A)");
|
531
|
+
check(bsp_invert(b) != NULL, "Failed to invert B");
|
532
|
+
check(bsp_clip(a, b) != NULL, "Failed to clip(A, B)");
|
533
|
+
check(bsp_clip(b, a) != NULL, "Failed to clio(B, A)");
|
534
|
+
|
535
|
+
b_polys = bsp_to_polygons(b, 0, NULL);
|
536
|
+
check(b_polys != NULL, "Failed to get polygons from B");
|
537
|
+
check(bsp_build(a, b_polys, 1) == a, "Failed to add nodes from B into tree A");
|
538
|
+
check(bsp_invert(a) == a, "Failed to invert tree A");
|
539
|
+
|
540
|
+
// TODO: Build a more balanced trees from the polys of
|
541
|
+
// a instead of cloning a tree with potential gaps.
|
542
|
+
result = clone_bsp_tree(a);
|
543
|
+
check_mem(result);
|
544
|
+
|
545
|
+
if(b_polys != NULL) kl_destroy(poly, b_polys);
|
546
|
+
if(a != NULL) free_bsp_tree(a);
|
547
|
+
if(b != NULL) free_bsp_tree(b);
|
548
|
+
return result;
|
549
|
+
error:
|
550
|
+
if(b_polys != NULL) kl_destroy(poly, b_polys);
|
551
|
+
if(a != NULL) free_bsp_tree(a);
|
552
|
+
if(b != NULL) free_bsp_tree(b);
|
553
|
+
if(result != NULL) free_bsp_tree(result);
|
554
|
+
return NULL;
|
555
|
+
}
|