Refactored RayTracedTechnique to allow the position of the rendered part of the volume to be decoupled from the image data.
This commit is contained in:
parent
2232bd5ee5
commit
a4dedc227b
@ -15,6 +15,7 @@
|
|||||||
#define OSGVOLUME_RAYTRACEDTECHNIQUE 1
|
#define OSGVOLUME_RAYTRACEDTECHNIQUE 1
|
||||||
|
|
||||||
#include <osgVolume/VolumeTechnique>
|
#include <osgVolume/VolumeTechnique>
|
||||||
|
#include <osg/MatrixTransform>
|
||||||
|
|
||||||
namespace osgVolume {
|
namespace osgVolume {
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ class OSGVOLUME_EXPORT RayTracedTechnique : public VolumeTechnique
|
|||||||
|
|
||||||
virtual ~RayTracedTechnique();
|
virtual ~RayTracedTechnique();
|
||||||
|
|
||||||
osg::ref_ptr<osg::Geode> _geode;
|
osg::ref_ptr<osg::MatrixTransform> _transform;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -57,34 +57,34 @@ bool DraggerTransformCallback::receive(const MotionCommand& command)
|
|||||||
switch (command.getStage())
|
switch (command.getStage())
|
||||||
{
|
{
|
||||||
case MotionCommand::START:
|
case MotionCommand::START:
|
||||||
{
|
{
|
||||||
// Save the current matrix
|
// Save the current matrix
|
||||||
_startMotionMatrix = _transform->getMatrix();
|
_startMotionMatrix = _transform->getMatrix();
|
||||||
|
|
||||||
// Get the LocalToWorld and WorldToLocal matrix for this node.
|
// Get the LocalToWorld and WorldToLocal matrix for this node.
|
||||||
osg::NodePath nodePathToRoot;
|
osg::NodePath nodePathToRoot;
|
||||||
computeNodePathToRoot(*_transform,nodePathToRoot);
|
computeNodePathToRoot(*_transform,nodePathToRoot);
|
||||||
_localToWorld = osg::computeLocalToWorld(nodePathToRoot);
|
_localToWorld = osg::computeLocalToWorld(nodePathToRoot);
|
||||||
_worldToLocal = osg::Matrix::inverse(_localToWorld);
|
_worldToLocal = osg::Matrix::inverse(_localToWorld);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case MotionCommand::MOVE:
|
case MotionCommand::MOVE:
|
||||||
{
|
{
|
||||||
// Transform the command's motion matrix into local motion matrix.
|
// Transform the command's motion matrix into local motion matrix.
|
||||||
osg::Matrix localMotionMatrix = _localToWorld * command.getWorldToLocal()
|
osg::Matrix localMotionMatrix = _localToWorld * command.getWorldToLocal()
|
||||||
* command.getMotionMatrix()
|
* command.getMotionMatrix()
|
||||||
* command.getLocalToWorld() * _worldToLocal;
|
* command.getLocalToWorld() * _worldToLocal;
|
||||||
|
|
||||||
// Transform by the localMotionMatrix
|
// Transform by the localMotionMatrix
|
||||||
_transform->setMatrix(localMotionMatrix * _startMotionMatrix);
|
_transform->setMatrix(localMotionMatrix * _startMotionMatrix);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case MotionCommand::FINISH:
|
case MotionCommand::FINISH:
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case MotionCommand::NONE:
|
case MotionCommand::NONE:
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -74,11 +74,16 @@ void RayTracedTechnique::init()
|
|||||||
ShadingModel shadingModel = Isosurface;
|
ShadingModel shadingModel = Isosurface;
|
||||||
float alphaFuncValue = 0.1;
|
float alphaFuncValue = 0.1;
|
||||||
|
|
||||||
_geode = new osg::Geode;
|
_transform = new osg::MatrixTransform;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
|
||||||
|
|
||||||
|
_transform->addChild(geode.get());
|
||||||
|
|
||||||
osg::Image* image_3d = 0;
|
osg::Image* image_3d = 0;
|
||||||
osg::TransferFunction1D* tf = 0;
|
osg::TransferFunction1D* tf = 0;
|
||||||
osgVolume::Locator* masterLocator = _volumeTile->getLocator();
|
Locator* masterLocator = _volumeTile->getLocator();
|
||||||
|
Locator* layerLocator = _volumeTile->getLayer()->getLocator();
|
||||||
|
|
||||||
image_3d = _volumeTile->getLayer()->getImage();
|
image_3d = _volumeTile->getLayer()->getImage();
|
||||||
|
|
||||||
@ -118,19 +123,25 @@ void RayTracedTechnique::init()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (_volumeTile->getLayer() && !masterLocator)
|
if (!masterLocator && layerLocator) masterLocator = _volumeTile->getLayer()->getLocator();
|
||||||
{
|
if (!layerLocator && masterLocator) layerLocator = layerLocator;
|
||||||
masterLocator = _volumeTile->getLayer()->getLocator();
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::Matrix matrix;
|
|
||||||
|
osg::Matrix geometryMatrix;
|
||||||
if (masterLocator)
|
if (masterLocator)
|
||||||
{
|
{
|
||||||
matrix = masterLocator->getTransform();
|
geometryMatrix = masterLocator->getTransform();
|
||||||
|
_transform->setMatrix(geometryMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::Matrix imageMatrix;
|
||||||
|
if (layerLocator)
|
||||||
|
{
|
||||||
|
imageMatrix = layerLocator->getTransform();
|
||||||
|
}
|
||||||
|
|
||||||
osg::notify(osg::INFO)<<"RayTracedTechnique::init() : matrix = "<<matrix<<std::endl;
|
osg::notify(osg::INFO)<<"RayTracedTechnique::init() : geometryMatrix = "<<geometryMatrix<<std::endl;
|
||||||
|
osg::notify(osg::INFO)<<"RayTracedTechnique::init() : imageMatrix = "<<imageMatrix<<std::endl;
|
||||||
|
|
||||||
osg::Texture::InternalFormatMode internalFormatMode = osg::Texture::USE_IMAGE_DATA_FORMAT;
|
osg::Texture::InternalFormatMode internalFormatMode = osg::Texture::USE_IMAGE_DATA_FORMAT;
|
||||||
|
|
||||||
@ -139,7 +150,7 @@ void RayTracedTechnique::init()
|
|||||||
osg::Texture::FilterMode minFilter = osg::Texture::LINEAR;
|
osg::Texture::FilterMode minFilter = osg::Texture::LINEAR;
|
||||||
osg::Texture::FilterMode magFilter = osg::Texture::LINEAR;
|
osg::Texture::FilterMode magFilter = osg::Texture::LINEAR;
|
||||||
|
|
||||||
osg::StateSet* stateset = _geode->getOrCreateStateSet();
|
osg::StateSet* stateset = geode->getOrCreateStateSet();
|
||||||
|
|
||||||
stateset->setMode(GL_ALPHA_TEST,osg::StateAttribute::ON);
|
stateset->setMode(GL_ALPHA_TEST,osg::StateAttribute::ON);
|
||||||
|
|
||||||
@ -168,9 +179,10 @@ void RayTracedTechnique::init()
|
|||||||
texture3D->setResizeNonPowerOfTwoHint(false);
|
texture3D->setResizeNonPowerOfTwoHint(false);
|
||||||
texture3D->setFilter(osg::Texture3D::MIN_FILTER,minFilter);
|
texture3D->setFilter(osg::Texture3D::MIN_FILTER,minFilter);
|
||||||
texture3D->setFilter(osg::Texture3D::MAG_FILTER, magFilter);
|
texture3D->setFilter(osg::Texture3D::MAG_FILTER, magFilter);
|
||||||
texture3D->setWrap(osg::Texture3D::WRAP_R,osg::Texture3D::CLAMP_TO_EDGE);
|
texture3D->setWrap(osg::Texture3D::WRAP_R,osg::Texture3D::CLAMP_TO_BORDER);
|
||||||
texture3D->setWrap(osg::Texture3D::WRAP_S,osg::Texture3D::CLAMP_TO_EDGE);
|
texture3D->setWrap(osg::Texture3D::WRAP_S,osg::Texture3D::CLAMP_TO_BORDER);
|
||||||
texture3D->setWrap(osg::Texture3D::WRAP_T,osg::Texture3D::CLAMP_TO_EDGE);
|
texture3D->setWrap(osg::Texture3D::WRAP_T,osg::Texture3D::CLAMP_TO_BORDER);
|
||||||
|
texture3D->setBorderColor(osg::Vec4(0.0,0.0,0.0,0.0));
|
||||||
if (image_3d->getPixelFormat()==GL_ALPHA ||
|
if (image_3d->getPixelFormat()==GL_ALPHA ||
|
||||||
image_3d->getPixelFormat()==GL_LUMINANCE)
|
image_3d->getPixelFormat()==GL_LUMINANCE)
|
||||||
{
|
{
|
||||||
@ -265,13 +277,13 @@ void RayTracedTechnique::init()
|
|||||||
osg::Shader* fragmentShader = osgDB::readShaderFile(osg::Shader::FRAGMENT, "shaders/volume_iso.frag");
|
osg::Shader* fragmentShader = osgDB::readShaderFile(osg::Shader::FRAGMENT, "shaders/volume_iso.frag");
|
||||||
if (fragmentShader)
|
if (fragmentShader)
|
||||||
{
|
{
|
||||||
osg::notify(osg::NOTICE)<<"Shader found"<<std::endl;
|
osg::notify(osg::INFO)<<"Shader found"<<std::endl;
|
||||||
|
|
||||||
program->addShader(fragmentShader);
|
program->addShader(fragmentShader);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
osg::notify(osg::NOTICE)<<"No Shader found"<<std::endl;
|
osg::notify(osg::INFO)<<"No Shader found"<<std::endl;
|
||||||
|
|
||||||
#include "Shaders/volume_iso_frag.cpp"
|
#include "Shaders/volume_iso_frag.cpp"
|
||||||
program->addShader(new osg::Shader(osg::Shader::FRAGMENT, volume_iso_frag));
|
program->addShader(new osg::Shader(osg::Shader::FRAGMENT, volume_iso_frag));
|
||||||
@ -398,7 +410,7 @@ void RayTracedTechnique::init()
|
|||||||
|
|
||||||
osg::TexGen* texgen = new osg::TexGen;
|
osg::TexGen* texgen = new osg::TexGen;
|
||||||
texgen->setMode(osg::TexGen::OBJECT_LINEAR);
|
texgen->setMode(osg::TexGen::OBJECT_LINEAR);
|
||||||
texgen->setPlanesFromMatrix(osg::Matrix::inverse(matrix));
|
texgen->setPlanesFromMatrix( geometryMatrix * osg::Matrix::inverse(imageMatrix));
|
||||||
|
|
||||||
stateset->setTextureAttributeAndModes(0, texgen, osg::StateAttribute::ON);
|
stateset->setTextureAttributeAndModes(0, texgen, osg::StateAttribute::ON);
|
||||||
|
|
||||||
@ -408,14 +420,14 @@ void RayTracedTechnique::init()
|
|||||||
osg::Geometry* geom = new osg::Geometry;
|
osg::Geometry* geom = new osg::Geometry;
|
||||||
|
|
||||||
osg::Vec3Array* coords = new osg::Vec3Array(8);
|
osg::Vec3Array* coords = new osg::Vec3Array(8);
|
||||||
(*coords)[0] = osg::Vec3d(0.0,0.0,0.0) * matrix;
|
(*coords)[0] = osg::Vec3d(0.0,0.0,0.0);
|
||||||
(*coords)[1] = osg::Vec3d(1.0,0.0,0.0) * matrix;
|
(*coords)[1] = osg::Vec3d(1.0,0.0,0.0);
|
||||||
(*coords)[2] = osg::Vec3d(1.0,1.0,0.0) * matrix;
|
(*coords)[2] = osg::Vec3d(1.0,1.0,0.0);
|
||||||
(*coords)[3] = osg::Vec3d(0.0,1.0,0.0) * matrix;
|
(*coords)[3] = osg::Vec3d(0.0,1.0,0.0);
|
||||||
(*coords)[4] = osg::Vec3d(0.0,0.0,1.0) * matrix;
|
(*coords)[4] = osg::Vec3d(0.0,0.0,1.0);
|
||||||
(*coords)[5] = osg::Vec3d(1.0,0.0,1.0) * matrix;
|
(*coords)[5] = osg::Vec3d(1.0,0.0,1.0);
|
||||||
(*coords)[6] = osg::Vec3d(1.0,1.0,1.0) * matrix;
|
(*coords)[6] = osg::Vec3d(1.0,1.0,1.0);
|
||||||
(*coords)[7] = osg::Vec3d(0.0,1.0,1.0) * matrix;
|
(*coords)[7] = osg::Vec3d(0.0,1.0,1.0);
|
||||||
geom->setVertexArray(coords);
|
geom->setVertexArray(coords);
|
||||||
|
|
||||||
osg::Vec4Array* colours = new osg::Vec4Array(1);
|
osg::Vec4Array* colours = new osg::Vec4Array(1);
|
||||||
@ -462,7 +474,7 @@ void RayTracedTechnique::init()
|
|||||||
|
|
||||||
geom->addPrimitiveSet(drawElements);
|
geom->addPrimitiveSet(drawElements);
|
||||||
|
|
||||||
_geode->addDrawable(geom);
|
geode->addDrawable(geom);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,9 +488,9 @@ void RayTracedTechnique::update(osgUtil::UpdateVisitor* uv)
|
|||||||
void RayTracedTechnique::cull(osgUtil::CullVisitor* cv)
|
void RayTracedTechnique::cull(osgUtil::CullVisitor* cv)
|
||||||
{
|
{
|
||||||
//osg::notify(osg::NOTICE)<<"RayTracedTechnique::cull(osgUtil::CullVisitor* nv)"<<std::endl;
|
//osg::notify(osg::NOTICE)<<"RayTracedTechnique::cull(osgUtil::CullVisitor* nv)"<<std::endl;
|
||||||
if (_geode.valid())
|
if (_transform.valid())
|
||||||
{
|
{
|
||||||
_geode->accept(*cv);
|
_transform->accept(*cv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ char volume_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
"\n"
|
"\n"
|
||||||
"void main(void)\n"
|
"void main(void)\n"
|
||||||
"{ \n"
|
"{ \n"
|
||||||
" vec3 t0 = (texgen * vertexPos).xyz;\n"
|
" vec4 t0 = vertexPos;\n"
|
||||||
" vec3 te = (texgen * cameraPos).xyz;\n"
|
" vec4 te = cameraPos;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" if (te.x>=0.0 && te.x<=1.0 &&\n"
|
" if (te.x>=0.0 && te.x<=1.0 &&\n"
|
||||||
" te.y>=0.0 && te.y<=1.0 &&\n"
|
" te.y>=0.0 && te.y<=1.0 &&\n"
|
||||||
@ -57,8 +57,11 @@ char volume_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" t0 = t0 * texgen;\n"
|
||||||
|
" te = te * texgen;\n"
|
||||||
|
"\n"
|
||||||
" const float max_iteratrions = 2048.0;\n"
|
" const float max_iteratrions = 2048.0;\n"
|
||||||
" float num_iterations = ceil(length(te-t0)/SampleDensityValue);\n"
|
" float num_iterations = ceil(length((te-t0).xyz)/SampleDensityValue);\n"
|
||||||
" if (num_iterations<2.0) num_iterations = 2.0;\n"
|
" if (num_iterations<2.0) num_iterations = 2.0;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" if (num_iterations>max_iteratrions) \n"
|
" if (num_iterations>max_iteratrions) \n"
|
||||||
@ -66,8 +69,8 @@ char volume_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" num_iterations = max_iteratrions;\n"
|
" num_iterations = max_iteratrions;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
" vec3 deltaTexCoord=(te-t0)/float(num_iterations-1.0);\n"
|
" vec3 deltaTexCoord=(te-t0).xyz/float(num_iterations-1.0);\n"
|
||||||
" vec3 texcoord = t0;\n"
|
" vec3 texcoord = t0.xyz;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" vec4 fragColor = vec4(0.0, 0.0, 0.0, 0.0); \n"
|
" vec4 fragColor = vec4(0.0, 0.0, 0.0, 0.0); \n"
|
||||||
" while(num_iterations>0.0)\n"
|
" while(num_iterations>0.0)\n"
|
||||||
@ -95,4 +98,5 @@ char volume_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" if (fragColor.w<AlphaFuncValue) discard;\n"
|
" if (fragColor.w<AlphaFuncValue) discard;\n"
|
||||||
" \n"
|
" \n"
|
||||||
" gl_FragColor = fragColor;\n"
|
" gl_FragColor = fragColor;\n"
|
||||||
"}\n";
|
"}\n"
|
||||||
|
"\n";
|
||||||
|
@ -9,11 +9,9 @@ char volume_iso_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
"\n"
|
"\n"
|
||||||
"void main(void)\n"
|
"void main(void)\n"
|
||||||
"{ \n"
|
"{ \n"
|
||||||
"\n"
|
" vec4 t0 = vertexPos;\n"
|
||||||
" vec3 t0 = (texgen * vertexPos).xyz;\n"
|
" vec4 te = cameraPos;\n"
|
||||||
" vec3 te = (texgen * cameraPos).xyz;\n"
|
" vec3 eyeDirection = normalize((te-t0).xyz);\n"
|
||||||
"\n"
|
|
||||||
" vec3 eyeDirection = normalize(te-t0);\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
" if (te.x>=0.0 && te.x<=1.0 &&\n"
|
" if (te.x>=0.0 && te.x<=1.0 &&\n"
|
||||||
" te.y>=0.0 && te.y<=1.0 &&\n"
|
" te.y>=0.0 && te.y<=1.0 &&\n"
|
||||||
@ -60,8 +58,11 @@ char volume_iso_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" t0 = t0 * texgen;\n"
|
||||||
|
" te = te * texgen;\n"
|
||||||
|
"\n"
|
||||||
" const float max_iteratrions = 2048.0;\n"
|
" const float max_iteratrions = 2048.0;\n"
|
||||||
" float num_iterations = ceil(length(te-t0)/SampleDensityValue);\n"
|
" float num_iterations = ceil(length((te-t0).xyz)/SampleDensityValue);\n"
|
||||||
" if (num_iterations<2.0) num_iterations = 2.0;\n"
|
" if (num_iterations<2.0) num_iterations = 2.0;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" if (num_iterations>max_iteratrions) \n"
|
" if (num_iterations>max_iteratrions) \n"
|
||||||
@ -69,8 +70,8 @@ char volume_iso_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" num_iterations = max_iteratrions;\n"
|
" num_iterations = max_iteratrions;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
" vec3 deltaTexCoord=(t0-te)/float(num_iterations-1.0);\n"
|
" vec3 deltaTexCoord=(t0-te).xyz/float(num_iterations-1.0);\n"
|
||||||
" vec3 texcoord = te;\n"
|
" vec3 texcoord = te.xyz;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" vec4 previousColor = texture3D( baseTexture, texcoord);\n"
|
" vec4 previousColor = texture3D( baseTexture, texcoord);\n"
|
||||||
" \n"
|
" \n"
|
||||||
|
@ -9,10 +9,8 @@ char volume_lit_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
"\n"
|
"\n"
|
||||||
"void main(void)\n"
|
"void main(void)\n"
|
||||||
"{ \n"
|
"{ \n"
|
||||||
" vec3 t0 = (texgen * vertexPos).xyz;\n"
|
" vec4 t0 = vertexPos;\n"
|
||||||
" vec3 te = (texgen * cameraPos).xyz;\n"
|
" vec4 te = cameraPos;\n"
|
||||||
" \n"
|
|
||||||
" vec3 eyeDirection = normalize(te-t0);\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
" if (te.x>=0.0 && te.x<=1.0 &&\n"
|
" if (te.x>=0.0 && te.x<=1.0 &&\n"
|
||||||
" te.y>=0.0 && te.y<=1.0 &&\n"
|
" te.y>=0.0 && te.y<=1.0 &&\n"
|
||||||
@ -59,8 +57,13 @@ char volume_lit_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" t0 = t0 * texgen;\n"
|
||||||
|
" te = te * texgen;\n"
|
||||||
|
"\n"
|
||||||
|
" vec3 eyeDirection = normalize((te-t0).xyz);\n"
|
||||||
|
"\n"
|
||||||
" const float max_iteratrions = 2048.0;\n"
|
" const float max_iteratrions = 2048.0;\n"
|
||||||
" float num_iterations = ceil(length(te-t0)/SampleDensityValue);\n"
|
" float num_iterations = ceil(length((te-t0).xyz)/SampleDensityValue);\n"
|
||||||
" if (num_iterations<2.0) num_iterations = 2.0;\n"
|
" if (num_iterations<2.0) num_iterations = 2.0;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" if (num_iterations>max_iteratrions) \n"
|
" if (num_iterations>max_iteratrions) \n"
|
||||||
@ -68,8 +71,8 @@ char volume_lit_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" num_iterations = max_iteratrions;\n"
|
" num_iterations = max_iteratrions;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
" vec3 deltaTexCoord=(te-t0)/float(num_iterations-1.0);\n"
|
" vec3 deltaTexCoord=(te-t0).xyz/float(num_iterations-1.0);\n"
|
||||||
" vec3 texcoord = t0;\n"
|
" vec3 texcoord = t0.xyz;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" float normalSampleDistance = 1.0/512.0;\n"
|
" float normalSampleDistance = 1.0/512.0;\n"
|
||||||
" vec3 deltaX = vec3(normalSampleDistance, 0.0, 0.0);\n"
|
" vec3 deltaX = vec3(normalSampleDistance, 0.0, 0.0);\n"
|
||||||
@ -123,4 +126,5 @@ char volume_lit_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" if (fragColor.w<AlphaFuncValue) discard;\n"
|
" if (fragColor.w<AlphaFuncValue) discard;\n"
|
||||||
" \n"
|
" \n"
|
||||||
" gl_FragColor = fragColor;\n"
|
" gl_FragColor = fragColor;\n"
|
||||||
"}\n";
|
"}\n"
|
||||||
|
"\n";
|
||||||
|
@ -10,10 +10,8 @@ char volume_lit_tf_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
"\n"
|
"\n"
|
||||||
"void main(void)\n"
|
"void main(void)\n"
|
||||||
"{ \n"
|
"{ \n"
|
||||||
" vec3 t0 = (texgen * vertexPos).xyz;\n"
|
" vec4 t0 = vertexPos;\n"
|
||||||
" vec3 te = (texgen * cameraPos).xyz;\n"
|
" vec4 te = cameraPos;\n"
|
||||||
" \n"
|
|
||||||
" vec3 eyeDirection = normalize(te-t0);\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
" if (te.x>=0.0 && te.x<=1.0 &&\n"
|
" if (te.x>=0.0 && te.x<=1.0 &&\n"
|
||||||
" te.y>=0.0 && te.y<=1.0 &&\n"
|
" te.y>=0.0 && te.y<=1.0 &&\n"
|
||||||
@ -60,8 +58,13 @@ char volume_lit_tf_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" t0 = t0 * texgen;\n"
|
||||||
|
" te = te * texgen;\n"
|
||||||
|
"\n"
|
||||||
|
" vec3 eyeDirection = normalize((te-t0).xyz);\n"
|
||||||
|
"\n"
|
||||||
" const float max_iteratrions = 2048.0;\n"
|
" const float max_iteratrions = 2048.0;\n"
|
||||||
" float num_iterations = ceil(length(te-t0)/SampleDensityValue);\n"
|
" float num_iterations = ceil(length((te-t0).xyz)/SampleDensityValue);\n"
|
||||||
" if (num_iterations<2.0) num_iterations = 2.0;\n"
|
" if (num_iterations<2.0) num_iterations = 2.0;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" if (num_iterations>max_iteratrions) \n"
|
" if (num_iterations>max_iteratrions) \n"
|
||||||
@ -69,14 +72,14 @@ char volume_lit_tf_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" num_iterations = max_iteratrions;\n"
|
" num_iterations = max_iteratrions;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
" vec3 deltaTexCoord=(te-t0)/float(num_iterations-1.0);\n"
|
" vec3 deltaTexCoord=(te-t0).xyz/float(num_iterations-1.0);\n"
|
||||||
" vec3 texcoord = t0;\n"
|
" vec3 texcoord = t0.xyz;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" float normalSampleDistance = 1.0/512.0;\n"
|
" float normalSampleDistance = 1.0/512.0;\n"
|
||||||
" vec3 deltaX = vec3(normalSampleDistance, 0.0, 0.0);\n"
|
" vec3 deltaX = vec3(normalSampleDistance, 0.0, 0.0);\n"
|
||||||
" vec3 deltaY = vec3(0.0, normalSampleDistance, 0.0);\n"
|
" vec3 deltaY = vec3(0.0, normalSampleDistance, 0.0);\n"
|
||||||
" vec3 deltaZ = vec3(0.0, 0.0, normalSampleDistance);\n"
|
" vec3 deltaZ = vec3(0.0, 0.0, normalSampleDistance);\n"
|
||||||
" \n"
|
"\n"
|
||||||
" vec4 fragColor = vec4(0.0, 0.0, 0.0, 0.0); \n"
|
" vec4 fragColor = vec4(0.0, 0.0, 0.0, 0.0); \n"
|
||||||
" while(num_iterations>0.0)\n"
|
" while(num_iterations>0.0)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
@ -126,4 +129,5 @@ char volume_lit_tf_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" if (fragColor.w<AlphaFuncValue) discard;\n"
|
" if (fragColor.w<AlphaFuncValue) discard;\n"
|
||||||
" \n"
|
" \n"
|
||||||
" gl_FragColor = fragColor;\n"
|
" gl_FragColor = fragColor;\n"
|
||||||
"}\n";
|
"}\n"
|
||||||
|
"\n";
|
||||||
|
@ -9,8 +9,8 @@ char volume_mip_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
"\n"
|
"\n"
|
||||||
"void main(void)\n"
|
"void main(void)\n"
|
||||||
"{ \n"
|
"{ \n"
|
||||||
" vec3 t0 = (texgen * vertexPos).xyz;\n"
|
" vec4 t0 = vertexPos;\n"
|
||||||
" vec3 te = (texgen * cameraPos).xyz;\n"
|
" vec4 te = cameraPos;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" if (te.x>=0.0 && te.x<=1.0 &&\n"
|
" if (te.x>=0.0 && te.x<=1.0 &&\n"
|
||||||
" te.y>=0.0 && te.y<=1.0 &&\n"
|
" te.y>=0.0 && te.y<=1.0 &&\n"
|
||||||
@ -57,8 +57,11 @@ char volume_mip_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" t0 = t0 * texgen;\n"
|
||||||
|
" te = te * texgen;\n"
|
||||||
|
"\n"
|
||||||
" const float max_iteratrions = 2048.0;\n"
|
" const float max_iteratrions = 2048.0;\n"
|
||||||
" float num_iterations = ceil(length(te-t0)/SampleDensityValue);\n"
|
" float num_iterations = ceil(length((te-t0).xyz)/SampleDensityValue);\n"
|
||||||
" if (num_iterations<2.0) num_iterations = 2.0;\n"
|
" if (num_iterations<2.0) num_iterations = 2.0;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" if (num_iterations>max_iteratrions) \n"
|
" if (num_iterations>max_iteratrions) \n"
|
||||||
@ -66,8 +69,8 @@ char volume_mip_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" num_iterations = max_iteratrions;\n"
|
" num_iterations = max_iteratrions;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
" vec3 deltaTexCoord=(te-t0)/float(num_iterations-1.0);\n"
|
" vec3 deltaTexCoord=(te-t0).xyz/float(num_iterations-1.0);\n"
|
||||||
" vec3 texcoord = t0;\n"
|
" vec3 texcoord = t0.xyz;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" vec4 fragColor = vec4(0.0, 0.0, 0.0, 0.0); \n"
|
" vec4 fragColor = vec4(0.0, 0.0, 0.0, 0.0); \n"
|
||||||
" while(num_iterations>0.0)\n"
|
" while(num_iterations>0.0)\n"
|
||||||
@ -88,4 +91,5 @@ char volume_mip_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" if (fragColor.w<AlphaFuncValue) discard;\n"
|
" if (fragColor.w<AlphaFuncValue) discard;\n"
|
||||||
" \n"
|
" \n"
|
||||||
" gl_FragColor = fragColor;\n"
|
" gl_FragColor = fragColor;\n"
|
||||||
"}\n";
|
"}\n"
|
||||||
|
"\n";
|
||||||
|
@ -10,8 +10,8 @@ char volume_tf_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
"\n"
|
"\n"
|
||||||
"void main(void)\n"
|
"void main(void)\n"
|
||||||
"{ \n"
|
"{ \n"
|
||||||
" vec3 t0 = (texgen * vertexPos).xyz;\n"
|
" vec4 t0 = vertexPos;\n"
|
||||||
" vec3 te = (texgen * cameraPos).xyz;\n"
|
" vec4 te = cameraPos;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" if (te.x>=0.0 && te.x<=1.0 &&\n"
|
" if (te.x>=0.0 && te.x<=1.0 &&\n"
|
||||||
" te.y>=0.0 && te.y<=1.0 &&\n"
|
" te.y>=0.0 && te.y<=1.0 &&\n"
|
||||||
@ -58,8 +58,11 @@ char volume_tf_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" t0 = t0 * texgen;\n"
|
||||||
|
" te = te * texgen;\n"
|
||||||
|
"\n"
|
||||||
" const float max_iteratrions = 2048.0;\n"
|
" const float max_iteratrions = 2048.0;\n"
|
||||||
" float num_iterations = ceil(length(te-t0)/SampleDensityValue);\n"
|
" float num_iterations = ceil(length((te-t0).xyz)/SampleDensityValue);\n"
|
||||||
" if (num_iterations<2.0) num_iterations = 2.0;\n"
|
" if (num_iterations<2.0) num_iterations = 2.0;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" if (num_iterations>max_iteratrions) \n"
|
" if (num_iterations>max_iteratrions) \n"
|
||||||
@ -67,8 +70,8 @@ char volume_tf_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" num_iterations = max_iteratrions;\n"
|
" num_iterations = max_iteratrions;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
" vec3 deltaTexCoord=(te-t0)/float(num_iterations-1.0);\n"
|
" vec3 deltaTexCoord=(te-t0).xyz/float(num_iterations-1.0);\n"
|
||||||
" vec3 texcoord = t0;\n"
|
" vec3 texcoord = t0.xyz;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" vec4 fragColor = vec4(0.0, 0.0, 0.0, 0.0); \n"
|
" vec4 fragColor = vec4(0.0, 0.0, 0.0, 0.0); \n"
|
||||||
" while(num_iterations>0.0)\n"
|
" while(num_iterations>0.0)\n"
|
||||||
@ -98,4 +101,5 @@ char volume_tf_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" if (fragColor.w<AlphaFuncValue) discard;\n"
|
" if (fragColor.w<AlphaFuncValue) discard;\n"
|
||||||
" \n"
|
" \n"
|
||||||
" gl_FragColor = fragColor;\n"
|
" gl_FragColor = fragColor;\n"
|
||||||
"}\n";
|
"}\n"
|
||||||
|
"\n";
|
||||||
|
@ -10,120 +10,123 @@ char volume_tf_iso_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
"\n"
|
"\n"
|
||||||
"void main(void)\n"
|
"void main(void)\n"
|
||||||
"{ \n"
|
"{ \n"
|
||||||
" vec3 t0 = (texgen * vertexPos).xyz;\n"
|
" vec4 t0 = vertexPos;\n"
|
||||||
" vec3 te = (texgen * cameraPos).xyz;\n"
|
" vec4 te = cameraPos;\n"
|
||||||
|
" vec3 eyeDirection = normalize((te-t0).xyz);\n"
|
||||||
"\n"
|
"\n"
|
||||||
" vec3 eyeDirection = normalize(te-t0);\n"
|
" if (te.x>=0.0 && te.x<=1.0 &&\n"
|
||||||
|
" te.y>=0.0 && te.y<=1.0 &&\n"
|
||||||
|
" te.z>=0.0 && te.z<=1.0)\n"
|
||||||
|
" {\n"
|
||||||
|
" // do nothing... te inside volume\n"
|
||||||
|
" }\n"
|
||||||
|
" else\n"
|
||||||
|
" {\n"
|
||||||
|
" if (te.x<0.0)\n"
|
||||||
|
" {\n"
|
||||||
|
" float r = -te.x / (t0.x-te.x);\n"
|
||||||
|
" te = te + (t0-te)*r;\n"
|
||||||
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
" if (te.x>=0.0 && te.x<=1.0 &&\n"
|
" if (te.x>1.0)\n"
|
||||||
" te.y>=0.0 && te.y<=1.0 &&\n"
|
" {\n"
|
||||||
" te.z>=0.0 && te.z<=1.0)\n"
|
" float r = (1.0-te.x) / (t0.x-te.x);\n"
|
||||||
" {\n"
|
" te = te + (t0-te)*r;\n"
|
||||||
" // do nothing... te inside volume\n"
|
" }\n"
|
||||||
" }\n"
|
|
||||||
" else\n"
|
|
||||||
" {\n"
|
|
||||||
" if (te.x<0.0)\n"
|
|
||||||
" {\n"
|
|
||||||
" float r = -te.x / (t0.x-te.x);\n"
|
|
||||||
" te = te + (t0-te)*r;\n"
|
|
||||||
" }\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
" if (te.x>1.0)\n"
|
" if (te.y<0.0)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" float r = (1.0-te.x) / (t0.x-te.x);\n"
|
" float r = -te.y / (t0.y-te.y);\n"
|
||||||
" te = te + (t0-te)*r;\n"
|
" te = te + (t0-te)*r;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
" if (te.y<0.0)\n"
|
" if (te.y>1.0)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" float r = -te.y / (t0.y-te.y);\n"
|
" float r = (1.0-te.y) / (t0.y-te.y);\n"
|
||||||
" te = te + (t0-te)*r;\n"
|
" te = te + (t0-te)*r;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
" if (te.y>1.0)\n"
|
" if (te.z<0.0)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" float r = (1.0-te.y) / (t0.y-te.y);\n"
|
" float r = -te.z / (t0.z-te.z);\n"
|
||||||
" te = te + (t0-te)*r;\n"
|
" te = te + (t0-te)*r;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
" if (te.z<0.0)\n"
|
" if (te.z>1.0)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" float r = -te.z / (t0.z-te.z);\n"
|
" float r = (1.0-te.z) / (t0.z-te.z);\n"
|
||||||
" te = te + (t0-te)*r;\n"
|
" te = te + (t0-te)*r;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
" if (te.z>1.0)\n"
|
" t0 = t0 * texgen;\n"
|
||||||
" {\n"
|
" te = te * texgen;\n"
|
||||||
" float r = (1.0-te.z) / (t0.z-te.z);\n"
|
|
||||||
" te = te + (t0-te)*r;\n"
|
|
||||||
" }\n"
|
|
||||||
" }\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
" const float max_iteratrions = 2048.0;\n"
|
" const float max_iteratrions = 2048.0;\n"
|
||||||
" float num_iterations = ceil(length(te-t0)/SampleDensityValue);\n"
|
" float num_iterations = ceil(length((te-t0).xyz)/SampleDensityValue);\n"
|
||||||
" if (num_iterations<2.0) num_iterations = 2.0;\n"
|
" if (num_iterations<2.0) num_iterations = 2.0;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" if (num_iterations>max_iteratrions) \n"
|
" if (num_iterations>max_iteratrions)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" num_iterations = max_iteratrions;\n"
|
" num_iterations = max_iteratrions;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
" vec3 deltaTexCoord=(t0-te)/float(num_iterations-1.0);\n"
|
" vec3 deltaTexCoord=(t0-te).xyz/float(num_iterations-1.0);\n"
|
||||||
" vec3 texcoord = te;\n"
|
" vec3 texcoord = te.xyz;\n"
|
||||||
" float previousV = texture3D( baseTexture, texcoord).a;\n"
|
" float previousV = texture3D( baseTexture, texcoord).a;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" float normalSampleDistance = 1.0/512.0;\n"
|
" float normalSampleDistance = 1.0/512.0;\n"
|
||||||
" vec3 deltaX = vec3(normalSampleDistance, 0.0, 0.0);\n"
|
" vec3 deltaX = vec3(normalSampleDistance, 0.0, 0.0);\n"
|
||||||
" vec3 deltaY = vec3(0.0, normalSampleDistance, 0.0);\n"
|
" vec3 deltaY = vec3(0.0, normalSampleDistance, 0.0);\n"
|
||||||
" vec3 deltaZ = vec3(0.0, 0.0, normalSampleDistance);\n"
|
" vec3 deltaZ = vec3(0.0, 0.0, normalSampleDistance);\n"
|
||||||
"\n"
|
"\n"
|
||||||
" while(num_iterations>0.0)\n"
|
" while(num_iterations>0.0)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
"\n"
|
"\n"
|
||||||
" float v = texture3D( baseTexture, texcoord).a;\n"
|
" float v = texture3D( baseTexture, texcoord).a;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" float m = (previousV-IsoSurfaceValue) * (v-IsoSurfaceValue);\n"
|
" float m = (previousV-IsoSurfaceValue) * (v-IsoSurfaceValue);\n"
|
||||||
" if (m <= 0.0)\n"
|
" if (m <= 0.0)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" float r = (IsoSurfaceValue-v)/(previousV-v);\n"
|
" float r = (IsoSurfaceValue-v)/(previousV-v);\n"
|
||||||
" texcoord = texcoord - r*deltaTexCoord;\n"
|
" texcoord = texcoord - r*deltaTexCoord;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" v = texture3D( baseTexture, texcoord).a;\n"
|
" v = texture3D( baseTexture, texcoord).a;\n"
|
||||||
" vec4 color = texture1D( tfTexture, v);\n"
|
" vec4 color = texture1D( tfTexture, v);\n"
|
||||||
"\n"
|
"\n"
|
||||||
" float px = texture3D( baseTexture, texcoord + deltaX).a;\n"
|
" float px = texture3D( baseTexture, texcoord + deltaX).a;\n"
|
||||||
" float py = texture3D( baseTexture, texcoord + deltaY).a;\n"
|
" float py = texture3D( baseTexture, texcoord + deltaY).a;\n"
|
||||||
" float pz = texture3D( baseTexture, texcoord + deltaZ).a;\n"
|
" float pz = texture3D( baseTexture, texcoord + deltaZ).a;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" float nx = texture3D( baseTexture, texcoord - deltaX).a;\n"
|
" float nx = texture3D( baseTexture, texcoord - deltaX).a;\n"
|
||||||
" float ny = texture3D( baseTexture, texcoord - deltaY).a;\n"
|
" float ny = texture3D( baseTexture, texcoord - deltaY).a;\n"
|
||||||
" float nz = texture3D( baseTexture, texcoord - deltaZ).a;\n"
|
" float nz = texture3D( baseTexture, texcoord - deltaZ).a;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" vec3 grad = vec3(px-nx, py-ny, pz-nz);\n"
|
" vec3 grad = vec3(px-nx, py-ny, pz-nz);\n"
|
||||||
" if (grad.x!=0.0 || grad.y!=0.0 || grad.z!=0.0)\n"
|
" if (grad.x!=0.0 || grad.y!=0.0 || grad.z!=0.0)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" vec3 normal = normalize(grad);\n"
|
" vec3 normal = normalize(grad);\n"
|
||||||
" float lightScale = 0.1 + abs(dot(normal.xyz, eyeDirection))*0.9;\n"
|
" float lightScale = 0.1 + abs(dot(normal.xyz, eyeDirection))*0.9;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" color.x *= lightScale;\n"
|
" color.x *= lightScale;\n"
|
||||||
" color.y *= lightScale;\n"
|
" color.y *= lightScale;\n"
|
||||||
" color.z *= lightScale;\n"
|
" color.z *= lightScale;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
" gl_FragColor = color;\n"
|
" gl_FragColor = color;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" return;\n"
|
" return;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
" previousV = v;\n"
|
" previousV = v;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" texcoord += deltaTexCoord; \n"
|
" texcoord += deltaTexCoord;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" --num_iterations;\n"
|
" --num_iterations;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\n"
|
"\n"
|
||||||
" // we didn't find an intersection so just discard fragment\n"
|
" // we didn't find an intersection so just discard fragment\n"
|
||||||
" discard;\n"
|
" discard;\n"
|
||||||
"\n"
|
"\n"
|
||||||
"}\n";
|
"}\n"
|
||||||
|
"\n";
|
||||||
|
@ -89,4 +89,5 @@ char volume_tf_mip_frag[] = "uniform sampler3D baseTexture;\n"
|
|||||||
" if (fragColor.w>1.0) fragColor.w = 1.0; \n"
|
" if (fragColor.w>1.0) fragColor.w = 1.0; \n"
|
||||||
" if (fragColor.w<AlphaFuncValue) discard;\n"
|
" if (fragColor.w<AlphaFuncValue) discard;\n"
|
||||||
" gl_FragColor = fragColor;\n"
|
" gl_FragColor = fragColor;\n"
|
||||||
"}\n";
|
"}\n"
|
||||||
|
"\n";
|
||||||
|
@ -14,4 +14,5 @@ char volume_vert[] = "#version 110\n"
|
|||||||
" gl_ObjectPlaneT[0],\n"
|
" gl_ObjectPlaneT[0],\n"
|
||||||
" gl_ObjectPlaneR[0],\n"
|
" gl_ObjectPlaneR[0],\n"
|
||||||
" gl_ObjectPlaneQ[0]);\n"
|
" gl_ObjectPlaneQ[0]);\n"
|
||||||
"}\n";
|
"}\n"
|
||||||
|
"\n";
|
||||||
|
Loading…
Reference in New Issue
Block a user