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