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