1 // Copyright 2020 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 
4 #include "Builder.h"
5 #include "ospray_testing.h"
6 // stl
7 #include <random>
8 #include <vector>
9 
10 // file IO
11 #include <errno.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <sstream>
15 
16 // rkcommon
17 #include "rkcommon/math/range.h"
18 #include "rkcommon/math/vec.h"
19 #include "rkcommon/tasking/parallel_for.h"
20 #include "rkcommon/utility/random.h"
21 
22 using namespace rkcommon;
23 using namespace rkcommon::math;
24 
25 namespace ospray {
26 namespace testing {
27 
28 using VoxelArray = std::vector<float>;
29 
30 struct ParticleVolume : public detail::Builder
31 {
32   ParticleVolume(int numParticles,
33       bool withVolume,
34       bool withIsosurface,
35       bool withClipping,
36       bool multipleIsosurfaces,
37       bool provideWeights,
38       float clampMaxCumulativeValue,
39       float radiusSupportFactor);
40 
41   ~ParticleVolume() override = default;
42 
43   void commit() override;
44 
45   cpp::Group buildGroup() const override;
46   cpp::World buildWorld() const override;
47 
48  private:
49   // Data //
50 
51   int numParticles{1000};
52   bool withVolume{true};
53   bool withIsosurface{false};
54   bool withClipping{false};
55   bool multipleIsosurfaces{false};
56   bool provideWeights{true};
57   float clampMaxCumulativeValue{0.f};
58   float radiusSupportFactor{4.f};
59 
60   float isovalue{0.5f};
61 
62   range1f valueRange{0, 1.5f};
63   box3f bounds{{0, 0, 0}, {10, 10, 10}};
64 };
65 
66 // Inlined definitions ////////////////////////////////////////////////////
67 
ParticleVolume(int numParticles,bool withVolume,bool withIsosurface,bool withClipping,bool multipleIsosurfaces,bool provideWeights,float clampMaxCumulativeValue,float radiusSupportFactor)68 ParticleVolume::ParticleVolume(int numParticles,
69     bool withVolume,
70     bool withIsosurface,
71     bool withClipping,
72     bool multipleIsosurfaces,
73     bool provideWeights,
74     float clampMaxCumulativeValue,
75     float radiusSupportFactor)
76     : numParticles(numParticles),
77       withVolume(withVolume),
78       withIsosurface(withIsosurface),
79       withClipping(withClipping),
80       multipleIsosurfaces(multipleIsosurfaces),
81       provideWeights(provideWeights),
82       clampMaxCumulativeValue(clampMaxCumulativeValue),
83       radiusSupportFactor(radiusSupportFactor)
84 {}
85 
commit()86 void ParticleVolume::commit()
87 {
88   Builder::commit();
89 
90   numParticles = getParam<int>("numParticles", 1000);
91   withVolume = getParam<bool>("withVolume", withVolume);
92   withIsosurface = getParam<bool>("withIsosurface", withIsosurface);
93   isovalue = getParam<float>("isovalue", 0.5f);
94   withClipping = getParam<bool>("withClipping", withClipping);
95   multipleIsosurfaces =
96       getParam<bool>("multipleIsosurfaces", multipleIsosurfaces);
97   clampMaxCumulativeValue =
98       getParam<float>("clampMaxCumulativeValue", clampMaxCumulativeValue);
99   radiusSupportFactor =
100       getParam<float>("radiusSuportFactor", radiusSupportFactor);
101 
102   addPlane = false;
103 }
104 
buildGroup() const105 cpp::Group ParticleVolume::buildGroup() const
106 {
107   // AMK: Very similar to ProceduralParticleVolume.h in OpenVKL. Any way to
108   // combine?
109 
110   std::vector<vec3f> particles(numParticles);
111   std::vector<float> radius(numParticles);
112   std::vector<float> weights(numParticles);
113 
114   int32_t randomSeed = 0;
115 
116   // create random number distributions for point center and weight
117   std::mt19937 gen(randomSeed);
118 
119   range1f weightRange(0.5f, 1.5f);
120   const float radiusScale = 1.f / powf(numParticles, 1.f / 3.f);
121 
122   utility::uniform_real_distribution<float> centerDistribution_x(
123       bounds.lower.x, bounds.upper.x);
124   utility::uniform_real_distribution<float> centerDistribution_y(
125       bounds.lower.y, bounds.upper.y);
126   utility::uniform_real_distribution<float> centerDistribution_z(
127       bounds.lower.z, bounds.upper.z);
128   utility::uniform_real_distribution<float> radiusDistribution(
129       radiusScale, 2.f * radiusScale);
130   utility::uniform_real_distribution<float> weightDistribution(
131       weightRange.lower, weightRange.upper);
132 
133   // populate the points
134   for (int i = 0; i < numParticles; i++) {
135     auto &p = particles[i];
136     p.x = centerDistribution_x(gen);
137     p.y = centerDistribution_y(gen);
138     p.z = centerDistribution_z(gen);
139     radius[i] = radiusDistribution(gen);
140     weights[i] = provideWeights ? weightDistribution(gen) : 1.f;
141   }
142 
143   cpp::Volume volume("particle");
144   volume.setParam("particle.position", cpp::CopiedData(particles));
145   volume.setParam("particle.radius", cpp::CopiedData(radius));
146   volume.setParam("particle.weight", cpp::CopiedData(weights));
147   volume.setParam("clampMaxCumulativeValue", clampMaxCumulativeValue);
148   volume.setParam("radiusSupportFactor", radiusSupportFactor);
149 
150   volume.commit();
151 
152   cpp::VolumetricModel model(volume);
153 
154   // AMK: VKL will set the correct valueRange, but we don't have a good way to
155   // get that to OSPRay yet. Use the weightRange instead.
156   model.setParam(
157       "transferFunction", makeTransferFunction(vec2f(0.f, weightRange.upper)));
158   model.commit();
159 
160   cpp::Group group;
161 
162   if (withVolume)
163     group.setParam("volume", cpp::CopiedData(model));
164 
165   if (withIsosurface) {
166     cpp::Geometry isoGeom("isosurface");
167     std::vector<float> isovalues = {isovalue};
168     if (multipleIsosurfaces) {
169       isovalues.push_back(isovalue + .25f);
170     }
171 
172     isoGeom.setParam("isovalue", cpp::CopiedData(isovalues));
173     isoGeom.setParam("volume", volume);
174     isoGeom.commit();
175 
176     cpp::GeometricModel isoModel(isoGeom);
177 
178     if (rendererType == "pathtracer" || rendererType == "scivis"
179         || rendererType == "ao") {
180       cpp::Material mat(rendererType, "obj");
181       mat.setParam("kd", vec3f(1.f));
182       mat.setParam("d", 0.5f);
183       if (rendererType == "pathtracer" || rendererType == "scivis")
184         mat.setParam("ks", vec3f(0.2f));
185       mat.commit();
186 
187       if (multipleIsosurfaces) {
188         std::vector<vec4f> colors = {
189             vec4f(0.2f, 0.2f, 0.8f, 1.f), vec4f(0.8f, 0.2f, 0.2f, 1.f)};
190         isoModel.setParam("color", cpp::CopiedData(colors));
191       }
192       isoModel.setParam("material", mat);
193     }
194 
195     isoModel.commit();
196 
197     group.setParam("geometry", cpp::CopiedData(isoModel));
198   }
199 
200   group.commit();
201 
202   return group;
203 }
204 
buildWorld() const205 cpp::World ParticleVolume::buildWorld() const
206 {
207   // Skip clipping if not enabled
208   std::vector<cpp::Instance> instances;
209   if (withClipping) {
210     // Create clipping plane
211     std::vector<cpp::GeometricModel> geometricModels;
212     {
213       cpp::Geometry planeGeometry("plane");
214       std::vector<vec4f> coefficients = {vec4f(1.f, -1.f, 1.f, 0.f)};
215       planeGeometry.setParam(
216           "plane.coefficients", cpp::CopiedData(coefficients));
217       planeGeometry.commit();
218 
219       cpp::GeometricModel model(planeGeometry);
220       model.commit();
221       geometricModels.emplace_back(model);
222     }
223 
224     // Create clipping sphere
225     {
226       cpp::Geometry sphereGeometry("sphere");
227       std::vector<vec3f> position = {vec3f(.2f, -.2f, .2f)};
228       sphereGeometry.setParam("sphere.position", cpp::CopiedData(position));
229       sphereGeometry.setParam("radius", .5f);
230       sphereGeometry.commit();
231 
232       cpp::GeometricModel model(sphereGeometry);
233       model.commit();
234       geometricModels.emplace_back(model);
235     }
236 
237     cpp::Group group;
238     group.setParam("clippingGeometry", cpp::CopiedData(geometricModels));
239     group.commit();
240 
241     cpp::Instance inst(group);
242     inst.commit();
243     instances.push_back(inst);
244   }
245 
246   auto world = Builder::buildWorld(instances);
247 
248   return world;
249 }
250 
251 OSP_REGISTER_TESTING_BUILDER(
252     ParticleVolume(1000, true, false, false, false, true, 0.f, 4.f),
253     particle_volume);
254 
255 OSP_REGISTER_TESTING_BUILDER(
256     ParticleVolume(1000, false, true, false, false, true, 0.f, 4.f),
257     particle_volume_isosurface);
258 
259 OSP_REGISTER_TESTING_BUILDER(
260     ParticleVolume(1000, true, false, true, false, true, 0.f, 4.f),
261     clip_particle_volume);
262 
263 } // namespace testing
264 } // namespace ospray
265