Commit 2b581529 authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Merge branch 'particle-visualization' into 'development'

Particle visualization

See merge request !95
parents fedbc913 f5cda4fc
...@@ -1322,6 +1322,16 @@ void Shader::setNormalizedAttribute(GLint index, const Vector4<GLuint>& v) { ...@@ -1322,6 +1322,16 @@ void Shader::setNormalizedAttribute(GLint index, const Vector4<GLuint>& v) {
glVertexAttrib4Nuiv(index, v.elem); glVertexAttrib4Nuiv(index, v.elem);
} }
bool Shader::selectSubroutine(ShaderObject::ShaderType type, const std::string& subroutineName) {
GLuint index = glGetSubroutineIndex(id_, type, subroutineName.c_str());
if (index == GL_INVALID_INDEX) {
LWARNING("Failed to locate subroutine Location: " << subroutineName);
return false;
}
glUniformSubroutinesuiv(type, 1, &index);
return true;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
const string ShaderManager::loggerCat_("tgt.Shader.Manager"); const string ShaderManager::loggerCat_("tgt.Shader.Manager");
......
...@@ -258,6 +258,10 @@ public: ...@@ -258,6 +258,10 @@ public:
std::string getLinkerLog() const; std::string getLinkerLog() const;
// Subroutines
bool selectSubroutine(ShaderObject::ShaderType type, const std::string& subroutineName);
// //
// Uniform stuff // Uniform stuff
// //
......
...@@ -80,4 +80,8 @@ namespace tgt { ...@@ -80,4 +80,8 @@ namespace tgt {
tgtAssert(false, "Could not find Vertex Attribute location for this BufferObject. You have to add it first using setVertexAttributePointer()!"); tgtAssert(false, "Could not find Vertex Attribute location for this BufferObject. You have to add it first using setVertexAttributePointer()!");
} }
GLuint VertexArrayObject::getId() const {
return _id;
}
} }
\ No newline at end of file
...@@ -30,6 +30,12 @@ namespace tgt { ...@@ -30,6 +30,12 @@ namespace tgt {
*/ */
~VertexArrayObject(); ~VertexArrayObject();
/**
* Returns the OpneGL object ID
* \return _id
*/
GLuint getId() const;
/** /**
* Binds this VertexArrayObject to the current OpenGL context. * Binds this VertexArrayObject to the current OpenGL context.
*/ */
......
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitaet Muenchen
// Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
//
// ================================================================================================
#include "tools/shading.frag"
#include "tools/transferfunction.frag"
in vec3 ex_Velocity;
in float ex_Transparency;
out vec4 out_Color; ///< outgoing fragment color
uniform sampler1D _transferFunction;
uniform TFParameters1D _transferFunctionParams;
uniform int _coloringMode;
uniform float _scale;
uniform vec2 _threshold;
void main() {
if (length(gl_PointCoord - vec2(0.5)) > 0.5) {
discard;
}
switch (_coloringMode) {
case 0: // age
out_Color = lookupTF(_transferFunction, _transferFunctionParams, ex_Transparency);
break;
case 1: // velocity
float f = length(ex_Velocity);
f -= _threshold.x;
f /= (_threshold.y - _threshold.x);
f *= _scale;
out_Color = lookupTF(_transferFunction, _transferFunctionParams, f);
break;
case 2: // direction
out_Color = vec4(normalize(abs(ex_Velocity)), 1.0);
break;
default:
discard;
}
#ifdef ENABLE_SHADING___
// compute gradient (needed for shading and normals)
vec3 gradient = ex_Normal;
out_Color.rgb = calculatePhongShading(ex_Position, _lightSource, _cameraPosition, gradient, _color.rgb, _color.rgb, vec3(1.0, 1.0, 1.0));
#endif
}
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitaet Muenchen
// Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
//
// ================================================================================================
#include "tools/texture3d.frag"
subroutine void RenderPassType();
subroutine uniform RenderPassType RenderPass;
layout(location = 0) in vec3 in_Position;
layout(location = 1) in vec3 in_Velocity;
layout(location = 2) in float in_StartTime;
layout(location = 3) in vec3 in_InitialPosition;
out vec3 ex_Position;
out vec3 ex_Velocity;
out float ex_StartTime;
out float ex_Transparency;
/// Matrix defining model-to-world transformation
uniform mat4 _modelMatrix = mat4(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
/// Matrix defining view transformation
uniform mat4 _viewMatrix = mat4(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
/// Matrix defining projection transformation
uniform mat4 _projectionMatrix = mat4(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
uniform float _time;
uniform float _frameLength;
uniform float _lifetime;
uniform float _scale;
uniform vec2 _threshold;
uniform sampler3D _volume;
uniform TextureParameters3D _volumeTextureParams;
subroutine (RenderPassType)
void update() {
if (_time > in_StartTime) {
float age = _time - in_StartTime;
if (age > _lifetime || length(in_Velocity) < _threshold.x || length(in_Velocity) > _threshold.y) {
// particle expired, recycle
ex_Position = in_InitialPosition;
ex_Velocity = texture(_volume, (ex_Position / _volumeTextureParams._size).xyz).xyz;
ex_StartTime = _time;
}
else {
// particle alive, advance
ex_Position = in_Position + (in_Velocity * _frameLength * 0.05 * _scale);
// compute new velocity by mixture of old velocity and flow at current location, model some inertia
vec3 v = texture(_volume, (ex_Position / _volumeTextureParams._size).xyz).xyz;
ex_Velocity = mix(in_Velocity, v, 0.5);//smoothstep(0.5, 2.0, v/in_Velocity));
ex_StartTime = in_StartTime;
}
}
else {
ex_Position = in_Position;
ex_Velocity = in_Velocity;
ex_StartTime = in_StartTime;
}
}
subroutine (RenderPassType)
void render() {
float age = _time - in_StartTime;
ex_Transparency = (age >= 0.0) ? 1.0 - (age / _lifetime) : 0.0;
gl_Position = _projectionMatrix * (_viewMatrix * (_modelMatrix * vec4(in_Position, 1.0)));
//ex_Position = in_Position;
mat4 normalMatrix = transpose(inverse(_modelMatrix));
//vec4 normalTmp = (normalMatrix * vec4(in_Normal, 0.0));
// ex_Normal = normalize((normalTmp).xyz);
}
void main() {
// call the current subroutine
RenderPass();
}
...@@ -28,24 +28,35 @@ ...@@ -28,24 +28,35 @@
#include "core/classification/geometry1dtransferfunction.h" #include "core/classification/geometry1dtransferfunction.h"
#include "core/classification/tfgeometry1d.h" #include "core/classification/tfgeometry1d.h"
#include "core/datastructures/imagedata.h"
namespace campvis { namespace campvis {
static const GenericOption<std::string> viewSelectionOptions[2] = {
GenericOption<std::string>("arrows", "Arrows"),
GenericOption<std::string>("particles", "Particle Simulation"),
};
VectorFieldDemo::VectorFieldDemo(DataContainer* dc) VectorFieldDemo::VectorFieldDemo(DataContainer* dc)
: AutoEvaluationPipeline(dc) : AutoEvaluationPipeline(dc)
, _lsp() , _lsp()
, _imageReader() , _imageReader()
, _vectorFieldReader() , _vectorFieldReader()
, _pfr(&_canvasSize)
, _vectorFieldRenderer(&_canvasSize) , _vectorFieldRenderer(&_canvasSize)
, _sliceRenderer(&_canvasSize) , _sliceRenderer(&_canvasSize)
, _rtc(&_canvasSize) , _rtc(&_canvasSize)
, p_camera("Camera", "Camera", tgt::Camera()) , p_camera("Camera", "Camera", tgt::Camera())
, p_sliceNumber("SliceNuber", "Slice Number", 0, 0, 1024) , p_sliceNumber("SliceNuber", "Slice Number", 0, 0, 1024)
, p_viewSelection("ViewSelection", "Select 3D View", viewSelectionOptions, 2)
, p_time("Time", "Time", 0, 0, 100)
, _trackballEH(0) , _trackballEH(0)
{ {
addProperty(p_camera); addProperty(p_camera);
addProperty(p_sliceNumber); addProperty(p_sliceNumber);
addProperty(p_viewSelection);
addProperty(p_time);
_trackballEH = new TrackballNavigationEventListener(&p_camera, &_canvasSize); _trackballEH = new TrackballNavigationEventListener(&p_camera, &_canvasSize);
addEventListenerToBack(_trackballEH); addEventListenerToBack(_trackballEH);
...@@ -53,6 +64,7 @@ namespace campvis { ...@@ -53,6 +64,7 @@ namespace campvis {
addProcessor(&_lsp); addProcessor(&_lsp);
addProcessor(&_imageReader); addProcessor(&_imageReader);
addProcessor(&_vectorFieldReader); addProcessor(&_vectorFieldReader);
addProcessor(&_pfr);
addProcessor(&_vectorFieldRenderer); addProcessor(&_vectorFieldRenderer);
addProcessor(&_sliceRenderer); addProcessor(&_sliceRenderer);
addProcessor(&_rtc); addProcessor(&_rtc);
...@@ -65,6 +77,7 @@ namespace campvis { ...@@ -65,6 +77,7 @@ namespace campvis {
AutoEvaluationPipeline::init(); AutoEvaluationPipeline::init();
p_camera.addSharedProperty(&_vectorFieldRenderer.p_camera); p_camera.addSharedProperty(&_vectorFieldRenderer.p_camera);
p_camera.addSharedProperty(&_pfr.p_camera);
p_camera.addSharedProperty(&_sliceRenderer.p_camera); p_camera.addSharedProperty(&_sliceRenderer.p_camera);
p_sliceNumber.addSharedProperty(&_vectorFieldRenderer.p_sliceNumber); p_sliceNumber.addSharedProperty(&_vectorFieldRenderer.p_sliceNumber);
...@@ -78,9 +91,10 @@ namespace campvis { ...@@ -78,9 +91,10 @@ namespace campvis {
_vectorFieldReader.p_url.setValue(CAMPVIS_SOURCE_DIR "/modules/vectorfield/sampledata/result_vec.mhd"); _vectorFieldReader.p_url.setValue(CAMPVIS_SOURCE_DIR "/modules/vectorfield/sampledata/result_vec.mhd");
_vectorFieldReader.p_targetImageID.setValue("vectors"); _vectorFieldReader.p_targetImageID.setValue("vectors");
_vectorFieldReader.p_targetImageID.addSharedProperty(&_pfr.p_inputVectors);
_vectorFieldReader.p_targetImageID.addSharedProperty(&_vectorFieldRenderer.p_inputVectors); _vectorFieldReader.p_targetImageID.addSharedProperty(&_vectorFieldRenderer.p_inputVectors);
_vectorFieldRenderer.p_renderOutput.addSharedProperty(&_rtc.p_firstImageId); _vectorFieldRenderer.p_renderOutput.setValue("arrows");
_vectorFieldRenderer.p_arrowSize.setValue(0.03f); _vectorFieldRenderer.p_arrowSize.setValue(0.03f);
_vectorFieldRenderer.p_lenThresholdMin.setValue(100.f); _vectorFieldRenderer.p_lenThresholdMin.setValue(100.f);
_vectorFieldRenderer.p_flowProfile1.setValue(0.4716088614374652f); _vectorFieldRenderer.p_flowProfile1.setValue(0.4716088614374652f);
...@@ -90,24 +104,53 @@ namespace campvis { ...@@ -90,24 +104,53 @@ namespace campvis {
_vectorFieldRenderer.p_lenThresholdMax.setValue(400.f); _vectorFieldRenderer.p_lenThresholdMax.setValue(400.f);
_vectorFieldRenderer.p_sliceOrientation.setValue(3); _vectorFieldRenderer.p_sliceOrientation.setValue(3);
_pfr.p_lenThresholdMin.setValue(100.f);
_pfr.p_flowProfile1.setValue(0.4716088614374652f);
_pfr.p_flowProfile2.setValue(0.0638348311845516f);
_pfr.p_flowProfile3.setValue(0.1713471562960614f);
_pfr.p_flowProfile4.setValue(0.1019371804834016f);
_pfr.p_lenThresholdMax.setValue(400.f);
_pfr.p_renderOutput.setValue("particles");
_pfr.setEnabled(false);
Geometry1DTransferFunction* tf = new Geometry1DTransferFunction(128, tgt::vec2(0.f, 1.f)); Geometry1DTransferFunction* tf = new Geometry1DTransferFunction(128, tgt::vec2(0.f, 1.f));
tf->addGeometry(TFGeometry1D::createQuad(tgt::vec2(0.f, 1.f), tgt::col4(0, 0, 0, 255), tgt::col4(255, 255, 255, 255))); tf->addGeometry(TFGeometry1D::createQuad(tgt::vec2(0.f, 1.f), tgt::col4(0, 0, 0, 255), tgt::col4(255, 255, 255, 255)));
_sliceRenderer.p_transferFunction.replaceTF(tf); _sliceRenderer.p_transferFunction.replaceTF(tf);
_sliceRenderer.p_targetImageID.setValue("slice"); _sliceRenderer.p_targetImageID.setValue("slice");
_sliceRenderer.p_targetImageID.addSharedProperty(&_rtc.p_secondImageId);
_rtc.p_firstImageId.setValue("arrows");
_rtc.p_secondImageId.setValue("slice");
_rtc.p_compositingMethod.selectById("depth"); _rtc.p_compositingMethod.selectById("depth");
_rtc.p_targetImageId.setValue("composed"); _rtc.p_targetImageId.setValue("composed");
_renderTargetID.setValue("composed"); _renderTargetID.setValue("composed");
p_time.addSharedProperty(&_vectorFieldRenderer.p_Time);
p_time.addSharedProperty(&_pfr.p_Time);
} }
void VectorFieldDemo::onProcessorValidated(AbstractProcessor* processor) { void VectorFieldDemo::onProcessorValidated(AbstractProcessor* processor) {
if (processor == &_imageReader) { if (processor == &_imageReader) {
// update camera // update camera
ScopedTypedData<IHasWorldBounds> img(*_data, _sliceRenderer.p_sourceImageID.getValue()); ScopedTypedData<ImageData> img(*_data, _sliceRenderer.p_sourceImageID.getValue());
if (img) { if (img) {
_trackballEH->reinitializeCamera(img); _trackballEH->reinitializeCamera(img);
p_sliceNumber.setMaxValue(static_cast<int>(img->getSize().z));
}
}
}
void VectorFieldDemo::onPropertyChanged(const AbstractProperty* prop) {
if (prop == &p_viewSelection) {
if (p_viewSelection.getOptionValue() == "arrows") {
_rtc.p_firstImageId.setValue("arrows");
_vectorFieldRenderer.setEnabled(true);
_pfr.setEnabled(false);
}
else if (p_viewSelection.getOptionValue() == "particles") {
_rtc.p_firstImageId.setValue("particles");
_vectorFieldRenderer.setEnabled(false);
_pfr.setEnabled(true);
} }
} }
} }
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "modules/base/processors/lightsourceprovider.h" #include "modules/base/processors/lightsourceprovider.h"
#include "modules/io/processors/mhdimagereader.h" #include "modules/io/processors/mhdimagereader.h"
#include "modules/vectorfield/processors/particleflowrenderer.h"
#include "modules/vectorfield/processors/vectorfieldrenderer.h" #include "modules/vectorfield/processors/vectorfieldrenderer.h"
#include "modules/vis/processors/slicerenderer3d.h" #include "modules/vis/processors/slicerenderer3d.h"
#include "modules/vis/processors/rendertargetcompositor.h" #include "modules/vis/processors/rendertargetcompositor.h"
...@@ -65,15 +66,22 @@ namespace campvis { ...@@ -65,15 +66,22 @@ namespace campvis {
*/ */
virtual void onProcessorValidated(AbstractProcessor* processor); virtual void onProcessorValidated(AbstractProcessor* processor);
/// \see HasPropertyCollection::onPropertyChanged
virtual void onPropertyChanged(const AbstractProperty* prop);
LightSourceProvider _lsp; LightSourceProvider _lsp;
MhdImageReader _imageReader; MhdImageReader _imageReader;
MhdImageReader _vectorFieldReader; MhdImageReader _vectorFieldReader;
ParticleFlowRenderer _pfr;
VectorFieldRenderer _vectorFieldRenderer; VectorFieldRenderer _vectorFieldRenderer;
SliceRenderer3D _sliceRenderer; SliceRenderer3D _sliceRenderer;
RenderTargetCompositor _rtc; RenderTargetCompositor _rtc;
CameraProperty p_camera; CameraProperty p_camera;
IntProperty p_sliceNumber; IntProperty p_sliceNumber;
GenericOptionProperty<std::string> p_viewSelection;
IntProperty p_time;
TrackballNavigationEventListener* _trackballEH; TrackballNavigationEventListener* _trackballEH;
}; };
......
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitaet Muenchen
// Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
//
// ================================================================================================
#include "particleflowrenderer.h"
#include "tgt/buffer.h"
#include "tgt/tgt_math.h"
#include "tgt/logmanager.h"
#include "tgt/shadermanager.h"
#include "tgt/textureunit.h"
#include "tgt/vertexarrayobject.h"
#include "core/classification/geometry1dtransferfunction.h"
#include "core/classification/tfgeometry1d.h"
#include "core/datastructures/imagedata.h"
#include "core/datastructures/imagerepresentationgl.h"
#include "core/datastructures/lightsourcedata.h"
#include "core/datastructures/geometrydatafactory.h"
#include "core/datastructures/renderdata.h"
namespace campvis {
static const GenericOption<ParticleFlowRenderer::ColoringMode> coloringModeOptions[3] = {
GenericOption<ParticleFlowRenderer::ColoringMode>("age", "Coloring by Age", ParticleFlowRenderer::COLORING_AGE),
GenericOption<ParticleFlowRenderer::ColoringMode>("velocity", "Coloring by Velocity", ParticleFlowRenderer::COLORING_VELOCITY),
GenericOption<ParticleFlowRenderer::ColoringMode>("direction", "Coloring by Direction", ParticleFlowRenderer::COLORING_DIRECTION)
};
const std::string ParticleFlowRenderer::loggerCat_ = "CAMPVis.modules.classification.ParticleFlowRenderer";
ParticleFlowRenderer::ParticleFlowRenderer(IVec2Property* viewportSizeProp)
: VisualizationProcessor(viewportSizeProp)
, p_resetButton("ResetButton", "Reset")
, p_inputVectors("InputImage", "Input Image Vectors", "vectors", DataNameProperty::READ)
, p_renderOutput("RenderOutput", "Output Image", "ParticleFlowRenderer.output", DataNameProperty::WRITE)
, p_lenThresholdMin("LenThresholdMin", "Length Threshold Min", .001f, 0.f, 1000.f, 0.005f)
, p_lenThresholdMax("LenThresholdMax", "Length Threshold Max", 10.f, 0.f, 10000.f, 10.f)
, p_numParticles("NumParticles", "Number of Particles", 2048, 32, 65536)
, p_lifetime("Lifetime", "Particle Lifetime", 10.f, 1.f, 100.f, 1.f, 1)
, p_flowProfile1("FlowSpline1", "Flow Profile - Spline 1", 1.f, .0f, 2.f)
, p_flowProfile2("FlowSpline2", "Flow Profile - Spline 2", 1.f, .0f, 2.f)
, p_flowProfile3("FlowSpline3", "Flow Profile - Spline 3", 1.f, .0f, 2.f)
, p_flowProfile4("FlowSpline4", "Flow Profile - Spline 4", 1.f, .0f, 2.f)
, p_Time("time", "Time", 0, 0, 100)
, p_pointSize("PointSize", "Point Size", 4, 1, 16)
, p_coloring("Coloring", "Color Scheme", coloringModeOptions, 3)
, p_transferFunction("TransferFunction", "Coloring Transfer Function", new Geometry1DTransferFunction(256))
, p_enableShading("EnableShading", "Enable Shading", true)
, p_lightId("LightId", "Input Light Source", "lightsource", DataNameProperty::READ)
, p_camera("Camera", "Camera", tgt::Camera())
, _shader(nullptr)
, _positionBufferA(nullptr)
, _positionBufferB(nullptr)
, _velocityBufferA(nullptr)
, _velocityBufferB(nullptr)
, _startTimeBufferA(nullptr)
, _startTimeBufferB(nullptr)
, _initialPositionBuffer(nullptr)
, _vaoA(nullptr)
, _vaoB(nullptr)
{
addProperty(p_resetButton, INVALID_PROPERTIES);
addProperty(p_Time, INVALID_RESULT | FIRST_FREE_TO_USE_INVALIDATION_LEVEL);
addProperty(p_inputVectors, INVALID_RESULT | INVALID_PROPERTIES);
addProperty(p_renderOutput);
addProperty(p_lenThresholdMin);
addProperty(p_lenThresholdMax);
addProperty(p_numParticles);
addProperty(p_lifetime);
addProperty(p_camera);
addProperty(p_flowProfile1);
addProperty(p_flowProfile2);
addProperty(p_flowProfile3);
addProperty(p_flowProfile4);
addProperty(p_pointSize);
addProperty(p_coloring);
addProperty(p_transferFunction);
addProperty(p_enableShading, INVALID_RESULT | INVALID_PROPERTIES | INVALID_SHADER);
addProperty(p_lightId);
static_cast<Geometry1DTransferFunction*>(p_transferFunction.getTF())->addGeometry(TFGeometry1D::createHeatedBodyColorMap());
}
ParticleFlowRenderer::~ParticleFlowRenderer() {
}
void ParticleFlowRenderer::init() {
VisualizationProcessor::init();