1 | // Copyright 2009-2021 Intel Corporation |
2 | // SPDX-License-Identifier: Apache-2.0 |
3 | |
4 | #pragma once |
5 | |
6 | #include "geometry.h" |
7 | #include "accel.h" |
8 | |
9 | namespace embree |
10 | { |
11 | struct MotionDerivativeCoefficients; |
12 | |
13 | /*! Instanced acceleration structure */ |
14 | struct Instance : public Geometry |
15 | { |
16 | ALIGNED_STRUCT_(16); |
17 | static const Geometry::GTypeMask geom_type = Geometry::MTY_INSTANCE; |
18 | |
19 | public: |
20 | Instance (Device* device, Accel* object = nullptr, unsigned int numTimeSteps = 1); |
21 | ~Instance(); |
22 | |
23 | private: |
24 | Instance (const Instance& other) DELETED; // do not implement |
25 | Instance& operator= (const Instance& other) DELETED; // do not implement |
26 | |
27 | private: |
28 | LBBox3fa nonlinearBounds(const BBox1f& time_range_in, |
29 | const BBox1f& geom_time_range, |
30 | float geom_time_segments) const; |
31 | |
32 | BBox3fa boundSegment(size_t itime, |
33 | BBox3fa const& obbox0, BBox3fa const& obbox1, |
34 | BBox3fa const& bbox0, BBox3fa const& bbox1, |
35 | float t_min, float t_max) const; |
36 | |
37 | /* calculates the (correct) interpolated bounds */ |
38 | __forceinline BBox3fa bounds(size_t itime0, size_t itime1, float f) const |
39 | { |
40 | if (unlikely(gsubtype == GTY_SUBTYPE_INSTANCE_QUATERNION)) |
41 | return xfmBounds(m: slerp(M0: local2world[itime0], M1: local2world[itime1], t: f), |
42 | b: lerp(b0: getObjectBounds(itime: itime0), b1: getObjectBounds(itime: itime1), t: f)); |
43 | return xfmBounds(m: lerp(M0: local2world[itime0], M1: local2world[itime1], t: f), |
44 | b: lerp(b0: getObjectBounds(itime: itime0), b1: getObjectBounds(itime: itime1), t: f)); |
45 | } |
46 | |
47 | public: |
48 | virtual void setNumTimeSteps (unsigned int numTimeSteps) override; |
49 | virtual void setInstancedScene(const Ref<Scene>& scene) override; |
50 | virtual void setTransform(const AffineSpace3fa& local2world, unsigned int timeStep) override; |
51 | virtual void setQuaternionDecomposition(const AffineSpace3ff& qd, unsigned int timeStep) override; |
52 | virtual AffineSpace3fa getTransform(float time) override; |
53 | virtual void setMask (unsigned mask) override; |
54 | virtual void build() {} |
55 | virtual void addElementsToCount (GeometryCounts & counts) const override; |
56 | virtual void commit() override; |
57 | |
58 | public: |
59 | |
60 | /*! calculates the bounds of instance */ |
61 | __forceinline BBox3fa bounds(size_t i) const { |
62 | assert(i == 0); |
63 | if (unlikely(gsubtype == GTY_SUBTYPE_INSTANCE_QUATERNION)) |
64 | return xfmBounds(m: quaternionDecompositionToAffineSpace(qd: local2world[0]),b: object->bounds.bounds()); |
65 | return xfmBounds(m: local2world[0],b: object->bounds.bounds()); |
66 | } |
67 | |
68 | /*! gets the bounds of the instanced scene */ |
69 | __forceinline BBox3fa getObjectBounds(size_t itime) const { |
70 | return object->getBounds(t: timeStep(i: itime)); |
71 | } |
72 | |
73 | /*! calculates the bounds of instance */ |
74 | __forceinline BBox3fa bounds(size_t i, size_t itime) const { |
75 | assert(i == 0); |
76 | if (unlikely(gsubtype == GTY_SUBTYPE_INSTANCE_QUATERNION)) |
77 | return xfmBounds(m: quaternionDecompositionToAffineSpace(qd: local2world[itime]),b: getObjectBounds(itime)); |
78 | return xfmBounds(m: local2world[itime],b: getObjectBounds(itime)); |
79 | } |
80 | |
81 | /*! calculates the linear bounds of the i'th primitive for the specified time range */ |
82 | __forceinline LBBox3fa linearBounds(size_t i, const BBox1f& dt) const { |
83 | assert(i == 0); |
84 | LBBox3fa lbbox = nonlinearBounds(time_range_in: dt, geom_time_range: time_range, geom_time_segments: fnumTimeSegments); |
85 | return lbbox; |
86 | } |
87 | |
88 | /*! calculates the build bounds of the i'th item, if it's valid */ |
89 | __forceinline bool buildBounds(size_t i, BBox3fa* bbox = nullptr) const |
90 | { |
91 | assert(i==0); |
92 | const BBox3fa b = bounds(i); |
93 | if (bbox) *bbox = b; |
94 | return isvalid(v: b); |
95 | } |
96 | |
97 | /*! calculates the build bounds of the i'th item at the itime'th time segment, if it's valid */ |
98 | __forceinline bool buildBounds(size_t i, size_t itime, BBox3fa& bbox) const |
99 | { |
100 | assert(i==0); |
101 | const LBBox3fa bounds = linearBounds(i,dt: itime); |
102 | bbox = bounds.bounds (); |
103 | return isvalid(v: bounds); |
104 | } |
105 | |
106 | /* gets version info of topology */ |
107 | unsigned int getTopologyVersion() const { |
108 | return numPrimitives; |
109 | } |
110 | |
111 | /* returns true if topology changed */ |
112 | bool topologyChanged(unsigned int otherVersion) const { |
113 | return numPrimitives != otherVersion; |
114 | } |
115 | |
116 | /*! check if the i'th primitive is valid between the specified time range */ |
117 | __forceinline bool valid(size_t i, const range<size_t>& itime_range) const |
118 | { |
119 | assert(i == 0); |
120 | for (size_t itime = itime_range.begin(); itime <= itime_range.end(); itime++) |
121 | if (!isvalid(v: bounds(i,itime))) return false; |
122 | |
123 | return true; |
124 | } |
125 | |
126 | __forceinline AffineSpace3fa getLocal2World() const |
127 | { |
128 | if (unlikely(gsubtype == GTY_SUBTYPE_INSTANCE_QUATERNION)) |
129 | return quaternionDecompositionToAffineSpace(qd: local2world[0]); |
130 | return local2world[0]; |
131 | } |
132 | |
133 | __forceinline AffineSpace3fa getLocal2World(float t) const |
134 | { |
135 | float ftime; const unsigned int itime = timeSegment(time: t, ftime); |
136 | if (unlikely(gsubtype == GTY_SUBTYPE_INSTANCE_QUATERNION)) |
137 | return slerp(M0: local2world[itime+0],M1: local2world[itime+1],t: ftime); |
138 | return lerp(M0: local2world[itime+0],M1: local2world[itime+1],t: ftime); |
139 | } |
140 | |
141 | __forceinline AffineSpace3fa getWorld2Local() const { |
142 | return world2local0; |
143 | } |
144 | |
145 | __forceinline AffineSpace3fa getWorld2Local(float t) const { |
146 | return rcp(a: getLocal2World(t)); |
147 | } |
148 | |
149 | template<int K> |
150 | __forceinline AffineSpace3vf<K> getWorld2Local(const vbool<K>& valid, const vfloat<K>& t) const |
151 | { |
152 | if (unlikely(gsubtype == GTY_SUBTYPE_INSTANCE_QUATERNION)) |
153 | return getWorld2LocalSlerp<K>(valid, t); |
154 | return getWorld2LocalLerp<K>(valid, t); |
155 | } |
156 | |
157 | private: |
158 | |
159 | template<int K> |
160 | __forceinline AffineSpace3vf<K> getWorld2LocalSlerp(const vbool<K>& valid, const vfloat<K>& t) const |
161 | { |
162 | vfloat<K> ftime; |
163 | const vint<K> itime_k = timeSegment<K>(t, ftime); |
164 | assert(any(valid)); |
165 | const size_t index = bsf(movemask(valid)); |
166 | const int itime = itime_k[index]; |
167 | if (likely(all(valid, itime_k == vint<K>(itime)))) { |
168 | return rcp(slerp(AffineSpace3vff<K>(local2world[itime+0]), |
169 | AffineSpace3vff<K>(local2world[itime+1]), |
170 | ftime)); |
171 | } |
172 | else { |
173 | AffineSpace3vff<K> space0,space1; |
174 | vbool<K> valid1 = valid; |
175 | while (any(valid1)) { |
176 | vbool<K> valid2; |
177 | const int itime = next_unique(valid1, itime_k, valid2); |
178 | space0 = select(valid2, AffineSpace3vff<K>(local2world[itime+0]), space0); |
179 | space1 = select(valid2, AffineSpace3vff<K>(local2world[itime+1]), space1); |
180 | } |
181 | return rcp(slerp(space0, space1, ftime)); |
182 | } |
183 | } |
184 | |
185 | template<int K> |
186 | __forceinline AffineSpace3vf<K> getWorld2LocalLerp(const vbool<K>& valid, const vfloat<K>& t) const |
187 | { |
188 | vfloat<K> ftime; |
189 | const vint<K> itime_k = timeSegment<K>(t, ftime); |
190 | assert(any(valid)); |
191 | const size_t index = bsf(movemask(valid)); |
192 | const int itime = itime_k[index]; |
193 | if (likely(all(valid, itime_k == vint<K>(itime)))) { |
194 | return rcp(lerp(AffineSpace3vf<K>((AffineSpace3fa)local2world[itime+0]), |
195 | AffineSpace3vf<K>((AffineSpace3fa)local2world[itime+1]), |
196 | ftime)); |
197 | } else { |
198 | AffineSpace3vf<K> space0,space1; |
199 | vbool<K> valid1 = valid; |
200 | while (any(valid1)) { |
201 | vbool<K> valid2; |
202 | const int itime = next_unique(valid1, itime_k, valid2); |
203 | space0 = select(valid2, AffineSpace3vf<K>((AffineSpace3fa)local2world[itime+0]), space0); |
204 | space1 = select(valid2, AffineSpace3vf<K>((AffineSpace3fa)local2world[itime+1]), space1); |
205 | } |
206 | return rcp(lerp(space0, space1, ftime)); |
207 | } |
208 | } |
209 | |
210 | public: |
211 | Accel* object; //!< pointer to instanced acceleration structure |
212 | AffineSpace3ff* local2world; //!< transformation from local space to world space for each timestep (either normal matrix or quaternion decomposition) |
213 | AffineSpace3fa world2local0; //!< transformation from world space to local space for timestep 0 |
214 | }; |
215 | |
216 | namespace isa |
217 | { |
218 | struct InstanceISA : public Instance |
219 | { |
220 | InstanceISA (Device* device) |
221 | : Instance(device) {} |
222 | |
223 | PrimInfo createPrimRefArray(mvector<PrimRef>& prims, const range<size_t>& r, size_t k, unsigned int geomID) const |
224 | { |
225 | assert(r.begin() == 0); |
226 | assert(r.end() == 1); |
227 | |
228 | PrimInfo pinfo(empty); |
229 | BBox3fa b = empty; |
230 | if (!buildBounds(i: 0,bbox: &b)) return pinfo; |
231 | // const BBox3fa b = bounds(0); |
232 | // if (!isvalid(b)) return pinfo; |
233 | |
234 | const PrimRef prim(b,geomID,unsigned(0)); |
235 | pinfo.add_center2(prim); |
236 | prims[k++] = prim; |
237 | return pinfo; |
238 | } |
239 | |
240 | PrimInfo createPrimRefArrayMB(mvector<PrimRef>& prims, size_t itime, const range<size_t>& r, size_t k, unsigned int geomID) const |
241 | { |
242 | assert(r.begin() == 0); |
243 | assert(r.end() == 1); |
244 | |
245 | PrimInfo pinfo(empty); |
246 | BBox3fa b = empty; |
247 | if (!buildBounds(i: 0,bbox: &b)) return pinfo; |
248 | // if (!valid(0,range<size_t>(itime))) return pinfo; |
249 | // const PrimRef prim(linearBounds(0,itime).bounds(),geomID,unsigned(0)); |
250 | const PrimRef prim(b,geomID,unsigned(0)); |
251 | pinfo.add_center2(prim); |
252 | prims[k++] = prim; |
253 | return pinfo; |
254 | } |
255 | |
256 | PrimInfoMB createPrimRefMBArray(mvector<PrimRefMB>& prims, const BBox1f& t0t1, const range<size_t>& r, size_t k, unsigned int geomID) const |
257 | { |
258 | assert(r.begin() == 0); |
259 | assert(r.end() == 1); |
260 | |
261 | PrimInfoMB pinfo(empty); |
262 | if (!valid(i: 0, itime_range: timeSegmentRange(range: t0t1))) return pinfo; |
263 | const PrimRefMB prim(linearBounds(i: 0,dt: t0t1),this->numTimeSegments(),this->time_range,this->numTimeSegments(),geomID,unsigned(0)); |
264 | pinfo.add_primref(prim); |
265 | prims[k++] = prim; |
266 | return pinfo; |
267 | } |
268 | }; |
269 | } |
270 | |
271 | DECLARE_ISA_FUNCTION(Instance*, createInstance, Device*); |
272 | } |
273 | |