1 #include "gui/renderers/MeshRenderer.h"
2 #include "gui/objects/Camera.h"
3 #include "gui/objects/Color.h"
4 #include "gui/objects/Colorizer.h"
5 #include "gui/objects/RenderContext.h"
6 #include "objects/finders/Order.h"
7 #include "quantities/Utility.h"
8 #include "system/Profiler.h"
9 #include "system/Statistics.h"
10 #include "thread/CheckFunction.h"
11
12 NAMESPACE_SPH_BEGIN
13
MeshRenderer(SharedPtr<IScheduler> scheduler,const GuiSettings & gui)14 MeshRenderer::MeshRenderer(SharedPtr<IScheduler> scheduler, const GuiSettings& gui)
15 : scheduler(scheduler) {
16 surfaceLevel = gui.get<Float>(GuiSettingsId::SURFACE_LEVEL);
17 surfaceResolution = gui.get<Float>(GuiSettingsId::SURFACE_RESOLUTION);
18 sunPosition = gui.get<Vector>(GuiSettingsId::SURFACE_SUN_POSITION);
19 sunIntensity = float(gui.get<Float>(GuiSettingsId::SURFACE_SUN_INTENSITY));
20 ambient = float(gui.get<Float>(GuiSettingsId::SURFACE_AMBIENT));
21
22 RunSettings settings;
23 settings.set(RunSettingsId::SPH_FINDER, FinderEnum::KD_TREE);
24 finder = Factory::getFinder(settings);
25 kernel = Factory::getKernel<3>(settings);
26 }
27
initialize(const Storage & storage,const IColorizer & colorizer,const ICamera & UNUSED (camera))28 void MeshRenderer::initialize(const Storage& storage,
29 const IColorizer& colorizer,
30 const ICamera& UNUSED(camera)) {
31 cached.colors.clear();
32
33 const Box boundingBox = getBoundingBox(storage);
34 const Float dim = maxElement(boundingBox.size());
35
36 McConfig config;
37 // clamp to avoid extreme resolution (which would most likely cause bad alloc)
38 config.gridResolution = clamp(surfaceResolution, 0.001_f * dim, 0.1_f * dim);
39 config.surfaceLevel = surfaceLevel;
40
41 // get the surface as triangles
42 cached.triangles = getSurfaceMesh(*scheduler, storage, config);
43
44 ArrayView<const Vector> r = storage.getValue<Vector>(QuantityId::POSITION);
45 finder->build(*scheduler, r);
46
47 this->setColorizer(colorizer);
48 }
49
isInitialized() const50 bool MeshRenderer::isInitialized() const {
51 return !cached.triangles.empty();
52 }
53
setColorizer(const IColorizer & colorizer)54 void MeshRenderer::setColorizer(const IColorizer& colorizer) {
55 Array<NeighborRecord> neighs;
56 cached.colors.clear();
57 for (Triangle& t : cached.triangles) {
58 const Vector pos = t.center();
59 finder->findAll(pos, 2._f * maxElement(t.getBBox().size()), neighs);
60
61 Rgba colorSum(0._f);
62 float weightSum = 0.f;
63 for (auto& n : neighs) {
64 const Size i = n.index;
65 const Rgba color = colorizer.evalColor(i);
66 /// \todo fix, the weight here should be consistent with MC
67 const float w = 1._f;
68 colorSum += color * w;
69 weightSum += w;
70 }
71
72 if (weightSum == 0._f) {
73 // we somehow didn't find any neighbors, indicate the error by red triangle
74 cached.colors.push(Rgba::red());
75 } else {
76 // supersimple diffuse shading
77 const float gray = ambient + sunIntensity * max(0.f, float(dot(sunPosition, t.normal())));
78 cached.colors.push(colorSum / weightSum * gray);
79 }
80 }
81 }
82
render(const RenderParams & params,Statistics & stats,IRenderOutput & output) const83 void MeshRenderer::render(const RenderParams& params, Statistics& stats, IRenderOutput& output) const {
84 const Pixel size = params.camera->getSize();
85 Bitmap<Rgba> bitmap(size);
86 PreviewRenderContext<OverPixelOp> context(bitmap);
87
88 // draw black background
89 context.fill(Rgba::transparent());
90
91 // sort the arrays by z-depth
92 Order triangleOrder(cached.triangles.size());
93 const Vector cameraDir = params.camera->getFrame().row(2);
94 triangleOrder.shuffle([this, &cameraDir](const Size i1, const Size i2) {
95 const Vector v1 = cached.triangles[i1].center();
96 const Vector v2 = cached.triangles[i2].center();
97 return dot(cameraDir, v1) > dot(cameraDir, v2);
98 });
99
100 // draw all triangles, starting from the ones with largest z-depth
101 for (Size i = 0; i < cached.triangles.size(); ++i) {
102 const Size idx = triangleOrder[i];
103 context.setColor(cached.colors[idx], ColorFlag::LINE | ColorFlag::FILL);
104
105 Optional<ProjectedPoint> p1 = params.camera->project(cached.triangles[idx][0]);
106 Optional<ProjectedPoint> p2 = params.camera->project(cached.triangles[idx][1]);
107 Optional<ProjectedPoint> p3 = params.camera->project(cached.triangles[idx][2]);
108 if (!p1 || !p2 || !p3) {
109 continue;
110 }
111 context.drawTriangle(p1->coords, p2->coords, p3->coords);
112 }
113
114 if (stats.has(StatisticsId::RUN_TIME)) {
115 const int64_t time = int64_t(stats.get<Float>(StatisticsId::RUN_TIME));
116 context.drawText(Coords(0, 0), TextAlign::RIGHT | TextAlign::BOTTOM, getFormattedTime(time * 1000));
117 }
118
119 output.update(bitmap, context.getLabels(), true);
120 }
121
122 NAMESPACE_SPH_END
123