1 // Copyright 2009-2021 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3
4 #include "../common/tutorial/tutorial_device.h"
5
6 #include <functional>
7 #include <queue>
8
9 namespace embree {
10
11 struct Point;
12
13 /* scene data */
14 RTCScene g_scene = nullptr;
15 Point* g_points = nullptr;
16 Point* g_points_tmp = nullptr;
17 Vec3fa* g_colors = nullptr;
18 extern "C" Vec3fa g_query_point;
19 const int g_num_colors = 27;
20
21 typedef void (*DrawGUI)(void);
22
23 int g_num_points_current;
24 extern "C" int g_num_points;
25 extern "C" int g_num_knn;
26 extern "C" bool g_show_voronoi;
27 extern "C" bool g_point_repulsion;
28 extern "C" float g_tmax;
29
30 struct Neighbour
31 {
32 unsigned int primID;
33 float d;
34
operator <embree::Neighbour35 bool operator<(Neighbour const& n1) const { return d < n1.d; }
36 };
37
38 struct KNNResult
39 {
KNNResultembree::KNNResult40 KNNResult()
41 {
42 visited.reserve(2 * g_num_knn);
43 }
44
45 unsigned int k;
46 std::priority_queue<Neighbour, std::vector<Neighbour>> knn;
47 std::vector<unsigned int> visited; // primIDs of all visited points
48 };
49
50 // ======================================================================== //
51 // User defined point geometry //
52 // ======================================================================== //
53
54 struct Point
55 {
56 ALIGNED_STRUCT_(16)
57 Vec3fa p; //!< position
58 RTCGeometry geometry;
59 unsigned int geomID;
60 };
61
pointBoundsFunc(const struct RTCBoundsFunctionArguments * args)62 void pointBoundsFunc(const struct RTCBoundsFunctionArguments* args)
63 {
64 const Point* points = (const Point*) args->geometryUserPtr;
65 RTCBounds* bounds_o = args->bounds_o;
66 const Point& point = points[args->primID];
67 bounds_o->lower_x = point.p.x;
68 bounds_o->lower_y = point.p.y;
69 bounds_o->lower_z = point.p.z;
70 bounds_o->upper_x = point.p.x;
71 bounds_o->upper_y = point.p.y;
72 bounds_o->upper_z = point.p.z;
73 }
74
pointQueryFunc(struct RTCPointQueryFunctionArguments * args)75 bool pointQueryFunc(struct RTCPointQueryFunctionArguments* args)
76 {
77 RTCPointQuery* query = (RTCPointQuery*)args->query;
78 const unsigned int primID = args->primID;
79 const Vec3f q(query->x, query->y, query->z);
80 const Point& point = g_points[primID];
81 const float d = distance(point.p, q);
82
83 assert(args->query);
84 KNNResult* result = (KNNResult*)args->userPtr;
85 result->visited.push_back(primID);
86
87 if (d < query->radius && (result->knn.size() < result->k || d < result->knn.top().d))
88 {
89 Neighbour neighbour;
90 neighbour.primID = primID;
91 neighbour.d = d;
92
93 if (result->knn.size() == result->k)
94 result->knn.pop();
95
96 result->knn.push(neighbour);
97
98 if (result->knn.size() == result->k)
99 {
100 const float R = result->knn.top().d;
101 query->radius = R;
102 return true;
103 }
104 }
105 return false;
106 }
107
knnQuery(Vec3f const & q,float radius,KNNResult * result)108 void knnQuery(Vec3f const& q, float radius, KNNResult* result)
109 {
110 RTCPointQuery query;
111 query.x = q.x;
112 query.y = q.y;
113 query.z = q.z;
114 query.radius = radius;
115 query.time = 0.f;
116 RTCPointQueryContext context;
117 rtcInitPointQueryContext(&context);
118 rtcPointQuery(g_scene, &query, &context, pointQueryFunc, (void*)result);
119 }
120
createPoints(RTCScene scene,unsigned int N)121 Point* createPoints (RTCScene scene, unsigned int N)
122 {
123 RTCGeometry geom = rtcNewGeometry(g_device, RTC_GEOMETRY_TYPE_USER);
124 g_points = (Point*) alignedMalloc(N*sizeof(Point), 16);
125 g_points_tmp = (Point*) alignedMalloc(N*sizeof(Point), 16);
126 unsigned int geomID = rtcAttachGeometry(scene, geom);
127 for (unsigned int i=0; i<N; i++) {
128 g_points[i].geometry = geom;
129 g_points[i].geomID = geomID;
130 g_points_tmp[i].geometry = geom;
131 g_points_tmp[i].geomID = geomID;
132 }
133 rtcSetGeometryUserPrimitiveCount(geom, N);
134 rtcSetGeometryUserData(geom, g_points);
135 rtcSetGeometryBoundsFunction(geom, pointBoundsFunc, nullptr);
136 rtcCommitGeometry(geom);
137 rtcReleaseGeometry(geom);
138
139 RandomSampler rs;
140 RandomSampler_init(rs, 42);
141 for (unsigned int i = 0; i < N; ++i)
142 {
143 float xi1 = RandomSampler_getFloat(rs);
144 float xi2 = RandomSampler_getFloat(rs);
145 g_points[i].p = Vec3f(xi1, 0.f, xi2);
146 }
147
148 g_num_points_current = N;
149 return g_points;
150 }
151
152 /* called by the C++ code for initialization */
device_init(char * cfg)153 extern "C" void device_init (char* cfg)
154 {
155 /* create scene */
156 g_scene = rtcNewScene(g_device);
157 g_points = createPoints(g_scene, g_num_points);
158 rtcCommitScene(g_scene);
159
160 g_colors = (Vec3fa*) alignedMalloc(g_num_colors*sizeof(Point), 16);
161 for (int r = 0; r < 3; ++r) for (int g = 0; g < 3; ++g) for (int b = 0; b < 3; ++b)
162 g_colors[r * 9 + g * 3 + b] = Vec3fa(0.2f + 0.3f * r, 0.2f + 0.3f * g, 0.2f + 0.3f * b);
163 }
164
165 /* renders a single screen tile */
renderTileStandard(int taskIndex,int threadIndex,int * pixels,const unsigned int width,const unsigned int height,const float time,const ISPCCamera & camera,const int numTilesX,const int numTilesY)166 void renderTileStandard(int taskIndex,
167 int threadIndex,
168 int* pixels,
169 const unsigned int width,
170 const unsigned int height,
171 const float time,
172 const ISPCCamera& camera,
173 const int numTilesX,
174 const int numTilesY)
175 {
176 const unsigned int tileY = taskIndex / numTilesX;
177 const unsigned int tileX = taskIndex - tileY * numTilesX;
178 const unsigned int x0 = tileX * TILE_SIZE_X;
179 const unsigned int x1 = min(x0+TILE_SIZE_X,width);
180 const unsigned int y0 = tileY * TILE_SIZE_Y;
181 const unsigned int y1 = min(y0+TILE_SIZE_Y,height);
182
183 for (unsigned int y=y0; y<y1; y++) for (unsigned int x=x0; x<x1; x++)
184 {
185 Vec3fa color = Vec3fa(0.f);
186 if (g_show_voronoi)
187 {
188 Vec3fa q = Vec3fa((float(x) + 0.5f) / width, 0.f, (float(y) + 0.5f) / height);
189
190 KNNResult result;
191 result.k = 1;
192 knnQuery(q, g_tmax, &result);
193 unsigned int primID = result.knn.empty() ? RTC_INVALID_GEOMETRY_ID : result.knn.top().primID;
194
195 if (primID != RTC_INVALID_GEOMETRY_ID)
196 color = g_colors[primID % 27];
197 }
198
199 /* write color to framebuffer */
200 unsigned int r = (unsigned int) (255.0f * clamp(color.x,0.0f,1.0f));
201 unsigned int g = (unsigned int) (255.0f * clamp(color.y,0.0f,1.0f));
202 unsigned int b = (unsigned int) (255.0f * clamp(color.z,0.0f,1.0f));
203 pixels[y*width+x] = (b << 16) + (g << 8) + r;
204 }
205 }
206
207 /* task that renders a single screen tile */
renderTileTask(int taskIndex,int threadIndex,int * pixels,const unsigned int width,const unsigned int height,const float time,const ISPCCamera & camera,const int numTilesX,const int numTilesY)208 void renderTileTask (int taskIndex, int threadIndex, int* pixels,
209 const unsigned int width,
210 const unsigned int height,
211 const float time,
212 const ISPCCamera& camera,
213 const int numTilesX,
214 const int numTilesY)
215 {
216 renderTileStandard(taskIndex,threadIndex,pixels,width,height,time,camera,numTilesX,numTilesY);
217 }
218
splat_color(int * pixels,unsigned int width,unsigned int height,int kernel_size,float x,float y,Vec3fa const & color)219 void splat_color(int* pixels,
220 unsigned int width,
221 unsigned int height,
222 int kernel_size,
223 float x, float y, Vec3fa const& color)
224 {
225 for (int dy = -kernel_size; dy <= kernel_size; ++dy)
226 for (int dx = -kernel_size; dx <= kernel_size; ++dx)
227 {
228 unsigned int r = (unsigned int)(255.0f * clamp(color.x, 0.0f, 1.0f));
229 unsigned int g = (unsigned int)(255.0f * clamp(color.y, 0.0f, 1.0f));
230 unsigned int b = (unsigned int)(255.0f * clamp(color.z, 0.0f, 1.0f));
231 const unsigned int px = (unsigned int)min(width - 1.f, max(0.f, x * width + dx));
232 const unsigned int py = (unsigned int)min(height - 1.f, max(0.f, y * height + dy));
233 pixels[py*width + px] = (b << 16) + (g << 8) + r;
234 }
235 }
236
rebuild_bvh()237 void rebuild_bvh()
238 {
239 rtcReleaseScene (g_scene);
240 g_scene = rtcNewScene(g_device);
241 RTCGeometry geom = rtcNewGeometry(g_device, RTC_GEOMETRY_TYPE_USER);
242 rtcAttachGeometry(g_scene, geom);
243 rtcSetGeometryUserPrimitiveCount(geom, g_num_points);
244 rtcSetGeometryUserData(geom, g_points);
245 rtcSetGeometryBoundsFunction(geom, pointBoundsFunc, nullptr);
246 rtcCommitGeometry(geom);
247 rtcReleaseGeometry(geom);
248 rtcCommitScene(g_scene);
249 }
250
renderFrameStandard(int * pixels,const unsigned int width,const unsigned int height,const float time,const ISPCCamera & camera)251 extern "C" void renderFrameStandard (int* pixels,
252 const unsigned int width,
253 const unsigned int height,
254 const float time,
255 const ISPCCamera& camera)
256 {
257 }
258
259 /* called by the C++ code to render */
device_render(int * pixels,const unsigned int width,const unsigned int height,const float time,const ISPCCamera & camera)260 extern "C" void device_render (int* pixels,
261 const unsigned int width,
262 const unsigned int height,
263 const float time,
264 const ISPCCamera& camera)
265 {
266 if (g_num_points != g_num_points_current)
267 {
268 rtcReleaseScene (g_scene);
269 g_scene = rtcNewScene(g_device);
270 alignedFree(g_points);
271 alignedFree(g_points_tmp);
272 g_points = createPoints(g_scene, g_num_points);
273 rtcCommitScene(g_scene);
274 }
275
276 if (g_point_repulsion)
277 {
278 parallel_for(size_t(0), size_t(g_num_points), [&](const range<size_t>& range) {
279 for (size_t i = range.begin(); i < range.end(); i++)
280 {
281 // perform nearest neighbour search for point
282 KNNResult result;
283 result.k = g_num_knn + 1;
284 knnQuery(g_points[i].p, g_tmax, &result);
285 if (result.knn.empty())
286 continue;
287
288 const float D = result.knn.top().d;
289 result.knn.pop();
290
291 // store number of nearest neighbours for normalization later
292 const size_t K = result.knn.size();
293 assert(K >= 1);
294
295 // tusk point repulsion formula
296 Vec3fa dx(0.f);
297 while (!result.knn.empty())
298 {
299 Point const& q = g_points[result.knn.top().primID];
300 dx += (g_points[i].p - q.p) * (D / (result.knn.top().d + 1e-4f) - 1.f);
301 result.knn.pop();
302 }
303 g_points_tmp[i].p = min(Vec3fa(1.f), max(Vec3fa(0.f), g_points[i].p + (1.f / K) * dx));
304 }
305 });
306
307 // copy new point locations and rebuild bvh
308 for (int i = 0; i < g_num_points; ++i)
309 g_points[i] = g_points_tmp[i];
310 rebuild_bvh();
311 }
312
313 // clear image and draw voronoi regions if enabled
314 const int numTilesX = (width + TILE_SIZE_X - 1) / TILE_SIZE_X;
315 const int numTilesY = (height + TILE_SIZE_Y - 1) / TILE_SIZE_Y;
316 parallel_for(size_t(0), size_t(numTilesX*numTilesY), [&](const range<size_t>& range) {
317 const int threadIndex = (int)TaskScheduler::threadIndex();
318 for (size_t i = range.begin(); i < range.end(); i++)
319 renderTileTask((int)i, threadIndex, pixels, width, height, time, camera, numTilesX, numTilesY);
320 });
321
322 // draw points
323 parallel_for(size_t(0), size_t(g_num_points), [&](const range<size_t>& range) {
324 for (size_t i = range.begin(); i < range.end(); i++)
325 {
326 Point const& p = g_points[i];
327 Vec3fa color = Vec3fa(0.9f);
328 if (g_show_voronoi) color = g_colors[i%g_num_colors] / 0.8f;
329 splat_color(pixels, width, height, 2, p.p.x, p.p.z, color);
330 }
331 });
332
333 if (!g_show_voronoi)
334 {
335 // perform nearest neighbour query for query point
336 KNNResult result;
337 result.k = g_num_knn;
338 knnQuery(g_query_point, g_tmax, &result);
339
340 if (!result.knn.empty())
341 {
342 // draw search radius
343 parallel_for(size_t(0), size_t(numTilesX*numTilesY), [&](const range<size_t>& range) {
344 for (size_t i = range.begin(); i < range.end(); i++)
345 {
346 const unsigned int tileY = (unsigned int)i / numTilesX;
347 const unsigned int tileX = (unsigned int)i - tileY * numTilesX;
348 const unsigned int x0 = tileX * TILE_SIZE_X;
349 const unsigned int x1 = min(x0 + TILE_SIZE_X, width);
350 const unsigned int y0 = tileY * TILE_SIZE_Y;
351 const unsigned int y1 = min(y0 + TILE_SIZE_Y, height);
352
353 for (unsigned int y = y0; y < y1; y++)
354 for (unsigned int x = x0; x < x1; x++)
355 {
356 Vec3fa q = Vec3fa((float(x) + 0.5f) / width, 0.f, (float(y) + 0.5f) / height);
357 if ( pixels[y*width + x] > 0)
358 continue;
359 Vec3fa color(0.0f, 0.0f, 0.0f);
360 if (distance(q, g_query_point) < result.knn.top().d)
361 color = Vec3fa(0.2f, 0.2f, 0.8f);
362 else if (distance(q, g_query_point) < g_tmax)
363 color = Vec3fa(0.2f, 0.2f, 0.2f);
364 else {
365 continue;
366 }
367 unsigned int r = (unsigned int)(255.0f * clamp(color.x, 0.0f, 1.0f));
368 unsigned int g = (unsigned int)(255.0f * clamp(color.y, 0.0f, 1.0f));
369 unsigned int b = (unsigned int)(255.0f * clamp(color.z, 0.0f, 1.0f));
370 pixels[y*width + x] = (b << 16) + (g << 8) + r;
371 }
372 }
373 });
374 }
375
376 // draw all visited points
377 for (unsigned int primID : result.visited)
378 {
379 Point const& p = g_points[primID];
380 splat_color(pixels, width, height, 2, p.p.x, p.p.z, Vec3fa(0.8f, 0.2f, 0.2f));
381 }
382
383 // draw nearest neighbours
384 while (!result.knn.empty())
385 {
386 Point const& p = g_points[result.knn.top().primID];
387 splat_color(pixels, width, height, 2, p.p.x, p.p.z, Vec3fa(0.2f, 0.8f, 0.2f));
388 result.knn.pop();
389 }
390
391 // draw query point
392 splat_color(pixels, width, height, 2, g_query_point.x, g_query_point.z, Vec3fa(0.8f, 0.8f, 0.2f));
393 }
394 }
395
396 /* called by the C++ code for cleanup */
device_cleanup()397 extern "C" void device_cleanup ()
398 {
399 rtcReleaseScene (g_scene); g_scene = nullptr;
400 rtcReleaseDevice(g_device); g_device = nullptr;
401 alignedFree(g_points); g_points = nullptr;
402 alignedFree(g_points_tmp); g_points_tmp = nullptr;
403 alignedFree(g_colors); g_colors = nullptr;
404 }
405
406 } // namespace embree
407