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