-
-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathbounds.lisp
308 lines (258 loc) · 12.7 KB
/
bounds.lisp
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
(in-package #:org.shirakumo.fraf.trial)
(defgeneric location (entity))
(defgeneric orientation (entity))
(defgeneric bsize (entity))
(defgeneric bradius (entity))
(defgeneric compute-bounding-box (entity))
(defgeneric compute-bounding-sphere (entity))
(defgeneric global-transform-matrix (entity &optional target))
(defgeneric global-location (entity &optional target))
(defgeneric global-orientation (entity &optional target))
(defgeneric global-bsize (entity &optional target))
(defgeneric global-bradius (entity))
(defgeneric global-bounding-box (entity &optional center bsize))
(defgeneric global-bounding-sphere (entity &optional center))
(defgeneric invalidate-global-bounds-cache (object))
;;; Defaults
(defmethod bradius ((entity entity))
(vlength (bsize entity)))
(defmethod compute-bounding-box ((entity entity))
(values (vec3 0) (bsize entity)))
(defmethod compute-bounding-sphere ((entity entity))
(values (vec3 0) (bradius entity)))
(defmethod compute-bounding-box ((sequence sequences:sequence))
;; Note: have to offset by location since the bbox is local to the child transform.
(case (length sequence)
(0
(values (vec3) (vec3)))
(1
(let ((entity (elt sequence 0)))
(multiple-value-bind (center bsize) (compute-bounding-box entity)
(values (nv+ center (location entity)) bsize))))
(T
(let ((min (vec3 most-positive-single-float))
(max (vec3 most-negative-single-float))
(tmp (vec3))
(center (vec3))
(bsize (vec3)))
(declare (dynamic-extent min max tmp))
(sequences:dosequence (child sequence)
(multiple-value-bind (center bsize) (compute-bounding-box child)
(nvmin min (!v- tmp (!v+ tmp center (location child)) bsize))
(nvmax max (!v+ tmp (!v+ tmp center (location child)) bsize))))
(nv* (!v- bsize max min) 0.5)
(!v+ center min bsize)
(values center bsize)))))
(defmethod compute-bounding-sphere ((sequence sequences:sequence))
;; Note: have to offset by location since the bbox is local to the child transform.
(case (length sequence)
(0
(values (vec3) 0.0))
(1
(let ((entity (elt sequence 0)))
(multiple-value-bind (center radius) (compute-bounding-sphere entity)
(values (nv+ center (location entity)) radius))))
(T
(let ((scalar (/ (length sequence)))
(center (vec3))
(radius 0.0) (tmp (vec3)))
(declare (dynamic-extent tmp))
;; Note: this is not an optimal bounding sphere but rather a trivial approximation
;; by center average
(v<- center 0)
(sequences:dosequence (child sequence)
(nv+* center (global-bounding-sphere child tmp) scalar))
(sequences:dosequence (child sequence)
(multiple-value-bind (child-center child-radius) (compute-bounding-sphere child)
(!v+ tmp child-center (location child))
(setf radius (max radius (+ child-radius (vdistance tmp center))))))
(values center radius)))))
(defmethod global-transform-matrix ((entity entity) &optional (matrix (meye 4)))
(with-pushed-matrix ((model-matrix matrix))
(!meye matrix)
(apply-transforms entity)
matrix))
(defmethod global-location ((entity entity) &optional (target (vec3)))
(let ((matrix (meye 4)))
(declare (dynamic-extent matrix))
(global-transform-matrix entity matrix)
(mcol3 matrix 3 target)
target))
(defmethod global-orientation ((entity entity) &optional (target (quat)))
(let ((matrix (meye 4)))
(declare (dynamic-extent matrix))
(global-transform-matrix entity matrix)
(!qfrom-mat target matrix)))
(defmethod global-bsize ((entity entity) &optional (target (vec3)))
(let ((mat (mat4)))
(declare (dynamic-extent mat))
(global-transform-matrix entity mat)
(!m*4/3 target matrix (bsize object))))
(defmethod global-bradius ((entity entity))
(let ((mat (mat4)))
(declare (dynamic-extent mat))
(global-transform-matrix entity mat)
(with-fast-matref (m mat)
(* (bradius entity) (max (m 0 0) (m 1 1) (m 2 2))))))
(defmethod global-bounding-box ((entity entity) &optional center bsize)
(values (global-location entity center)
(global-bsize entity bsize)))
(defmethod global-bounding-sphere ((entity entity) &optional center)
(values (global-location entity center)
(global-bradius entity)))
(defmethod global-bounding-box ((sequence sequences:sequence) &optional (center (vec3)) (bsize (vec3)))
(case (length sequence)
(0 (call-next-method))
(1 (global-bounding-box (elt sequence 0) center bsize))
(T
(let ((min (vec3 most-positive-single-float))
(max (vec3 most-negative-single-float))
(tmp (vec3)))
(declare (dynamic-extent min max tmp))
(sequences:dosequence (child sequence)
(global-bounding-box child center bsize)
(!v- tmp center bsize) (nvmin min tmp)
(!v+ tmp center bsize) (nvmax max tmp))
(nv* (!v- bsize max min) 0.5)
(!v+ center min bsize)
(values center bsize)))))
(defmethod global-bounding-sphere ((sequence sequences:sequence) &optional (center (vec3)))
(case (length sequence)
(0 (call-next-method))
(1 (global-bounding-sphere (elt sequence 0) center))
(T
(let ((scalar (/ (length sequence)))
(radius 0.0) (tmp (vec3)))
(declare (dynamic-extent tmp))
;; Note: this is not an optimal bounding sphere but rather a trivial approximation
;; by center average
(v<- center 0)
(sequences:dosequence (child sequence)
(nv+* center (global-bounding-sphere child tmp) scalar))
(sequences:dosequence (child sequence)
(multiple-value-bind (child child-radius) (global-bounding-sphere child tmp)
(setf radius (max radius (+ child-radius (vdistance child center))))))
(values center radius)))))
;; Default empty method to allow for a simple traversal of containers
(defmethod invalidate-global-bounds-cache (object))
(defmethod invalidate-global-bounds-cache :after ((container container))
(sequences:dosequence (child container)
(invalidate-global-bounds-cache child)))
(defstruct (global-bounds-cache
(:constructor %make-global-bounds-cache (&optional generator (radius 0f0) (obb (vec3)) (sphere-offset (vec3)) (box-offset (vec3)))))
(generator NIL :type T)
(sphere-offset (vec3) :type vec3)
(radius 0f0 :type single-float)
(box-offset (vec3) :type vec3)
(obb (vec3) :type vec3)
(location (vec3) :type vec3)
(aabb (vec3) :type vec3)
(dirty-p T :type boolean))
(define-transfer global-bounds-cache
global-bounds-cache-generator
global-bounds-cache-radius
(:eval (v<- (global-bounds-cache-obb target) (global-bounds-cache-obb source))
(v<- (global-bounds-cache-aabb target) (global-bounds-cache-aabb source))
(v<- (global-bounds-cache-location target) (global-bounds-cache-location source))
(v<- (global-bounds-cache-sphere-offset target) (global-bounds-cache-sphere-offset source))
(v<- (global-bounds-cache-box-offset target) (global-bounds-cache-box-offset source))))
(defun make-global-bounds-cache (generator &key radius bsize (sphere-offset (vec3) sphere-offset-p) (box-offset (vec3) box-offset-p))
(unless radius
(multiple-value-bind (center new-radius) (compute-bounding-sphere generator)
(unless sphere-offset-p (v<- sphere-offset center))
(setf radius new-radius)))
(unless bsize
(multiple-value-bind (center new-bsize) (compute-bounding-box generator)
(unless box-offset-p (v<- box-offset center))
(setf bsize new-bsize)))
(%make-global-bounds-cache generator radius (vcopy bsize) (vcopy sphere-offset) (vcopy box-offset)))
(defun update-global-bounds-cache (cache)
(let ((matrix (meye 4)))
(declare (dynamic-extent matrix))
(global-transform-matrix (global-bounds-cache-generator cache) matrix)
(mcol3 matrix 3 (global-bounds-cache-location cache))
(nmapply matrix #'abs)
#+:check-global-bounds-cache-obb
(assert (v/= 0.0 (global-bounds-cache-obb cache)))
(!m*4/3 (global-bounds-cache-aabb cache) matrix (global-bounds-cache-obb cache)))
(setf (global-bounds-cache-dirty-p cache) NIL))
(defmethod global-transform-matrix ((cache global-bounds-cache) &optional target)
(global-transform-matrix (global-bounds-cache-generator cache) target))
(defmethod global-location ((cache global-bounds-cache) &optional target)
(when (global-bounds-cache-dirty-p cache)
(update-global-bounds-cache cache))
(etypecase target
(vec3 (v<- target (global-bounds-cache-location cache)))
(null (global-bounds-cache-location cache))))
(defmethod global-bsize ((cache global-bounds-cache) &optional target)
(when (global-bounds-cache-dirty-p cache)
(update-global-bounds-cache cache))
(etypecase target
(vec3 (v<- target (global-bounds-cache-aabb cache)))
(null (global-bounds-cache-aabb cache))))
(defmethod bsize ((cache global-bounds-cache))
(when (global-bounds-cache-dirty-p cache)
(update-global-bounds-cache cache))
;; This is local to the cached origin, so we need to increase the bounds by the offset
(nv+ (vabs (global-bounds-cache-box-offset cache))
(global-bounds-cache-aabb cache)))
(defmethod bradius ((cache global-bounds-cache))
;; Note: don't need to check for dirty, as it's invariant to rotation
;; This is local to the cached origin, so we need to increase the bounds by the offset
(+ (vlength (global-bounds-cache-sphere-offset cache))
(global-bounds-cache-radius cache)))
(defmethod global-bounding-box ((cache global-bounds-cache) &optional (location (vec3)) bsize)
(when (global-bounds-cache-dirty-p cache)
(update-global-bounds-cache cache))
(values (!v+ location (global-bounds-cache-location cache) (global-bounds-cache-box-offset cache))
(etypecase bsize
(vec3 (v<- bsize (global-bounds-cache-aabb cache)))
(null (global-bounds-cache-aabb cache)))))
(defmethod global-bounding-sphere ((cache global-bounds-cache) &optional (location (vec3)))
(when (global-bounds-cache-dirty-p cache)
(update-global-bounds-cache cache))
(values (!v+ location (global-bounds-cache-location cache) (global-bounds-cache-sphere-offset cache))
(global-bounds-cache-radius cache)))
(defmethod invalidate-global-bounds-cache ((cache global-bounds-cache))
(setf (global-bounds-cache-dirty-p cache) T))
(defmethod 3ds:location ((cache global-bounds-cache))
(when (global-bounds-cache-dirty-p cache)
(update-global-bounds-cache cache))
(global-bounds-cache-location cache))
(defmethod 3ds:bsize ((cache global-bounds-cache))
(bsize cache))
(defmethod 3ds:radius ((cache global-bounds-cache))
(bradius cache))
(defmethod 3ds:bounding-box ((cache global-bounds-cache))
(global-bounding-box cache))
(defmethod 3ds:bounding-sphere ((cache global-bounds-cache))
(global-bounding-sphere cache))
(defclass global-bounds-cached-entity (entity)
((global-bounds-cache :accessor global-bounds-cache)))
(defmethod shared-initialize :after ((entity global-bounds-cached-entity) slots &key)
(unless (slot-boundp entity 'global-bounds-cache)
(setf (global-bounds-cache entity) (make-global-bounds-cache entity))))
(defmethod global-location ((entity global-bounds-cached-entity) &optional target)
(global-location (global-bounds-cache entity) target))
(defmethod global-bsize ((entity global-bounds-cached-entity) &optional target)
(global-bsize (global-bounds-cache entity) target))
(defmethod global-bounding-box ((entity global-bounds-cached-entity) &optional (location (vec3)) bsize)
(global-bounding-box (global-bounds-cache entity) location bsize))
(defmethod global-bounding-sphere ((entity global-bounds-cached-entity) &optional (location (vec3)))
(global-bounding-sphere (global-bounds-cache entity) location))
(defmethod (setf location) :after ((new-value t) (entity global-bounds-cached-entity))
(setf (global-bounds-cache-dirty-p (global-bounds-cache entity)) T))
(defmethod (setf orientation) :after ((new-value t) (entity global-bounds-cached-entity))
(setf (global-bounds-cache-dirty-p (global-bounds-cache entity)) T))
(defmethod invalidate-global-bounds-cache ((cache global-bounds-cached-entity))
(setf (global-bounds-cache-dirty-p (global-bounds-cache cache)) T))
(defmethod 3ds:location ((entity global-bounds-cached-entity))
(global-location (global-bounds-cache entity)))
(defmethod 3ds:bsize ((entity global-bounds-cached-entity))
(global-bsize (global-bounds-cache entity)))
(defmethod 3ds:radius ((entity global-bounds-cached-entity))
(global-bounds-cache-radius (global-bounds-cache entity)))
(defmethod 3ds:bounding-box ((entity global-bounds-cached-entity))
(global-bounding-box (global-bounds-cache entity)))
(defmethod 3ds:bounding-sphere ((entity global-bounds-cached-entity))
(global-bounding-sphere (global-bounds-cache entity)))