#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
const std::string DATA_PATH = R"(..\data\)";
const std::string SHADER_PATH = R"(..\shaders\)";
int sizeX = 128, sizeY = 128, sizeZ = 128;
struct VTKData {
std::vector<osg::Vec3> points;
std::vector<std::vector<unsigned int>> cells;
std::vector<int> cell_types;
std::vector<std::array<float, 6>> stresses;
std::vector<std::array<float, 6>> strains;
std::vector<osg::Vec3> displacements;
std::vector<float> pore_pressures;
std::vector<float> PEEQs;
std::vector<float> mises_stresses;
std::vector<osg::Vec3> fluid_velocities;
int type = 0;
void computeMinMax()
{
for (const auto& stress : this->stresses) {
this->minStressesXX = osg::minimum(this->minStressesXX, stress[0]);
this->maxStressesXX = osg::maximum(this->maxStressesXX, stress[0]);
this->minStressesYY = osg::minimum(this->minStressesYY, stress[1]);
this->maxStressesYY = osg::maximum(this->maxStressesYY, stress[1]);
this->minStressesZZ = osg::minimum(this->minStressesZZ, stress[2]);
this->maxStressesZZ = osg::maximum(this->maxStressesZZ, stress[2]);
this->minStressesXY = osg::minimum(this->minStressesXY, stress[3]);
this->maxStressesXY = osg::maximum(this->maxStressesXY, stress[3]);
this->minStressesYZ = osg::minimum(this->minStressesYZ, stress[4]);
this->maxStressesYZ = osg::maximum(this->maxStressesYZ, stress[4]);
this->minStressesXZ = osg::minimum(this->minStressesXZ, stress[5]);
this->maxStressesXZ = osg::maximum(this->maxStressesXZ, stress[5]);
}
for (const auto& strain : this->strains) {
this->minStrainsXX = osg::minimum(this->minStrainsXX, strain[0]);
this->maxStrainsXX = osg::maximum(this->maxStrainsXX, strain[0]);
this->minStrainsYY = osg::minimum(this->minStrainsYY, strain[1]);
this->maxStrainsYY = osg::maximum(this->maxStrainsYY, strain[1]);
this->minStrainsZZ = osg::minimum(this->minStrainsZZ, strain[2]);
this->maxStrainsZZ = osg::maximum(this->maxStrainsZZ, strain[2]);
this->minStrainsXY = osg::minimum(this->minStrainsXY, strain[3]);
this->maxStrainsXY = osg::maximum(this->maxStrainsXY, strain[3]);
this->minStrainsYZ = osg::minimum(this->minStrainsYZ, strain[4]);
this->maxStrainsYZ = osg::maximum(this->maxStrainsYZ, strain[4]);
this->minStrainsXZ = osg::minimum(this->minStrainsXZ, strain[5]);
this->maxStrainsXZ = osg::maximum(this->maxStrainsXZ, strain[5]);
}
for (const auto& disp : this->displacements) {
this->minDisplacementsDX = osg::minimum(this->minDisplacementsDX, disp.x());
this->maxDisplacementsDX = osg::maximum(this->maxDisplacementsDX, disp.x());
this->minDisplacementsDY = osg::minimum(this->minDisplacementsDY, disp.y());
this->maxDisplacementsDY = osg::maximum(this->maxDisplacementsDY, disp.y());
this->minDisplacementsDZ = osg::minimum(this->minDisplacementsDZ, disp.z());
this->maxDisplacementsDZ = osg::maximum(this->maxDisplacementsDZ, disp.z());
}
for (float p : this->pore_pressures) {
this->minPorePressures = osg::minimum(this->minPorePressures, p);
this->maxPorePressures = osg::maximum(this->maxPorePressures, p);
}
for (float peeq : this->PEEQs) {
this->minPEEQs = osg::minimum(this->minPEEQs, peeq);
this->maxPEEQs = osg::maximum(this->maxPEEQs, peeq);
}
for (float mises : this->mises_stresses) {
this->minMisesStresses = osg::minimum(this->minMisesStresses, mises);
this->maxMisesStresses = osg::maximum(this->maxMisesStresses, mises);
}
for (const auto& vel : this->fluid_velocities) {
this->minFluidVelocitiesVX = osg::minimum(this->minFluidVelocitiesVX, vel.x());
this->maxFluidVelocitiesVX = osg::maximum(this->maxFluidVelocitiesVX, vel.x());
this->minFluidVelocitiesVY = osg::minimum(this->minFluidVelocitiesVY, vel.y());
this->maxFluidVelocitiesVY = osg::maximum(this->maxFluidVelocitiesVY, vel.y());
this->minFluidVelocitiesVZ = osg::minimum(this->minFluidVelocitiesVZ, vel.z());
this->maxFluidVelocitiesVZ = osg::maximum(this->maxFluidVelocitiesVZ, vel.z());
}
minVal = minStressesXY;
maxVal = maxStressesXY;
}
float minVal, maxVal;
float minStressesXX = FLT_MAX, maxStressesXX = -FLT_MAX;
float minStressesYY = FLT_MAX, maxStressesYY = -FLT_MAX;
float minStressesZZ = FLT_MAX, maxStressesZZ = -FLT_MAX;
float minStressesXY = FLT_MAX, maxStressesXY = -FLT_MAX;
float minStressesYZ = FLT_MAX, maxStressesYZ = -FLT_MAX;
float minStressesXZ = FLT_MAX, maxStressesXZ = -FLT_MAX;
float minStrainsXX = FLT_MAX, maxStrainsXX = -FLT_MAX;
float minStrainsYY = FLT_MAX, maxStrainsYY = -FLT_MAX;
float minStrainsZZ = FLT_MAX, maxStrainsZZ = -FLT_MAX;
float minStrainsXY = FLT_MAX, maxStrainsXY = -FLT_MAX;
float minStrainsYZ = FLT_MAX, maxStrainsYZ = -FLT_MAX;
float minStrainsXZ = FLT_MAX, maxStrainsXZ = -FLT_MAX;
float minDisplacementsDX = FLT_MAX, maxDisplacementsDX = -FLT_MAX;
float minDisplacementsDY = FLT_MAX, maxDisplacementsDY = -FLT_MAX;
float minDisplacementsDZ = FLT_MAX, maxDisplacementsDZ = -FLT_MAX;
float minPorePressures = FLT_MAX, maxPorePressures = -FLT_MAX;
float minPEEQs = FLT_MAX, maxPEEQs = -FLT_MAX;
float minMisesStresses = FLT_MAX, maxMisesStresses = -FLT_MAX;
float minFluidVelocitiesVX = FLT_MAX, maxFluidVelocitiesVX = -FLT_MAX;
float minFluidVelocitiesVY = FLT_MAX, maxFluidVelocitiesVY = -FLT_MAX;
float minFluidVelocitiesVZ = FLT_MAX, maxFluidVelocitiesVZ = -FLT_MAX;
};
VTKData ParseVtkFile(const std::string& filename)
{
VTKData data;
osgDB::ifstream fin((DATA_PATH + filename).c_str(), osgDB::ifstream::binary);
if (!fin)
{
std::cout << "Failed to open VTK file." << std::endl;
return data;
}
std::string line;
bool readingPoints = false;
bool readingCells = false;
bool readingCellTypes = false;
bool readingStresses = false;
bool readingStrains = false;
bool readingDisplacements = false;
bool readingPorePressures = false;
bool readingPEEQs = false;
bool readingMisesStresses = false;
bool readingFluidVelocities = false;
while (std::getline(fin, line)) {
if (line.empty() || line[0] == '#') continue;
if (line.find("POINTS") != std::string::npos) {
readingPoints = true;
continue;
}
if (readingPoints) {
if (line.find("CELLS") != std::string::npos) {
readingPoints = false;
readingCells = true;
continue;
}
std::stringstream ss(line);
float x, y, z;
while (ss >> x >> y >> z) {
data.points.push_back(osg::Vec3(x, y, z));
}
}
if (readingCells) {
if (line.find("CELL_TYPES") != std::string::npos) {
readingCells = false;
readingCellTypes = true;
continue;
}
std::stringstream ss(line);
int numIndices;
ss >> numIndices;
if (data.type == 0)
data.type = numIndices;
std::vector<unsigned int> cell;
int index;
for (int i = 0; i < numIndices; ++i) {
ss >> index;
cell.push_back(index);
}
data.cells.push_back(cell);
}
if (readingCellTypes) {
if (line.find("POINT_DATA") != std::string::npos) {
readingCellTypes = false;
continue;
}
std::stringstream ss(line);
int cellType;
while (ss >> cellType) {
data.cell_types.push_back(cellType);
}
}
if (line.find("POINT_DATA") != std::string::npos) {
continue;
}
if (line.find("SCALARS stress float 6") != std::string::npos) {
readingStresses = true;
std::getline(fin, line);
continue;
}
if(readingStresses)
{
std::stringstream ss(line);
std::array<float, 6> stressArray;
bool validStress = true;
for (int i = 0; i < 6; ++i) {
if (!(ss >> stressArray[i])) {
validStress = false;
break;
}
}
if (validStress) {
data.stresses.push_back(stressArray);
}
}
if (line.find("SCALARS strain float 6") != std::string::npos)
{
readingStrains = true;
std::getline(fin, line);
continue;
}
if(readingStrains)
{
std::stringstream ss(line);
std::array<float, 6> strainArray;
bool validStrain = true;
if (validStrain)
{
data.strains.push_back(strainArray);
}
}
if (line.find("SCALARS displacement float 3") != std::string::npos) {
readingDisplacements = true;
std::getline(fin, line);
continue;
}
if (readingDisplacements) {
if (line.find("SCALARS") != std::string::npos) {
readingDisplacements = false;
}
else
{
std::stringstream ss(line);
float dx, dy, dz;
while (ss >> dx >> dy >> dz) {
data.displacements.push_back(osg::Vec3(dx, dy, dz));
}
}
}
if (line.find("SCALARS pore_pressure float 1") != std::string::npos) {
readingPorePressures = true;
std::getline(fin, line);
continue;
}
if (readingPorePressures) {
if (line.find("SCALARS") != std::string::npos) {
readingPorePressures = false;
}
else
{
std::stringstream ss(line);
float pressure;
while (ss >> pressure) {
data.pore_pressures.push_back(pressure);
}
}
}
if (line.find("SCALARS PEEQ float 1") != std::string::npos) {
readingPEEQs = true;
std::getline(fin, line);
continue;
}
if(readingPEEQs)
{
std::stringstream ss(line);
float PEEQ;
while (ss >> PEEQ) {
data.PEEQs.push_back(PEEQ);
}
}
if (line.find("SCALARS Mises_stress float 1") != std::string::npos) {
readingMisesStresses = true;
std::getline(fin, line);
continue;
}
if (readingMisesStresses) {
if (line.find("SCALARS") != std::string::npos) {
readingMisesStresses = false;
}
else
{
std::stringstream ss(line);
float stress;
while (ss >> stress) {
data.mises_stresses.push_back(stress);
}
}
}
if (line.find("SCALARS fluid_velocity float 3") != std::string::npos) {
readingFluidVelocities = true;
std::getline(fin, line);
continue;
}
if(readingFluidVelocities)
{
std::stringstream ss(line);
float vx, vy, vz;
while (ss >> vx >> vy >> vz) {
data.fluid_velocities.push_back(osg::Vec3(vx, vy, vz));
}
}
}
fin.close();
data.computeMinMax();
return data;
}
void CalculateTextureSize(const std::vector<osg::Vec3>& points, int& sizeX, int& sizeY, int& sizeZ) {
float minX = std::numeric_limits<float>::max();
float minY = std::numeric_limits<float>::max();
float minZ = std::numeric_limits<float>::max();
float maxX = std::numeric_limits<float>::lowest();
float maxY = std::numeric_limits<float>::lowest();
float maxZ = std::numeric_limits<float>::lowest();
for (const auto& point : points) {
if (point.x() < minX) minX = point.x();
if (point.y() < minY) minY = point.y();
if (point.z() < minZ) minZ = point.z();
if (point.x() > maxX) maxX = point.x();
if (point.y() > maxY) maxY = point.y();
if (point.z() > maxZ) maxZ = point.z();
}
float xRange = maxX - minX;
float yRange = maxY - minY;
float zRange = maxZ - minZ;
sizeX = static_cast<int>(xRange / 1.0f);
sizeY = static_cast<int>(yRange / 1.0f);
sizeZ = static_cast<int>(zRange / 1.0f);
sizeX = std::min(sizeX, 2048);
sizeY = std::min(sizeY, 2048);
sizeZ = std::min(sizeZ, 2048);
}
void Export3DTextureSlices(osg::Image* image, int sizeX, int sizeY, int sizeZ, char direction, const std::string& outputDir) {
if (!image) {
throw std::invalid_argument("Invalid image pointer");
}
int primarySize, secondarySize1, secondarySize2;
auto getIndices = [&](int p, int s1, int s2) -> const unsigned char* {
switch (direction) {
case 'x': return image->data(p, s1, s2);
case 'y': return image->data(s1, p, s2);
case 'z': return image->data(s1, s2, p);
default: throw std::invalid_argument("Invalid direction for slicing");
}
};
switch (direction) {
case 'x':
primarySize = sizeX;
secondarySize1 = sizeY;
secondarySize2 = sizeZ;
break;
case 'y':
primarySize = sizeY;
secondarySize1 = sizeX;
secondarySize2 = sizeZ;
break;
case 'z':
primarySize = sizeZ;
secondarySize1 = sizeX;
secondarySize2 = sizeY;
break;
default:
throw std::invalid_argument("Invalid direction for slicing");
}
for (int p = 0; p < primarySize; ++p) {
osg::ref_ptr<osg::Image> sliceImage = new osg::Image();
sliceImage->allocateImage(secondarySize1, secondarySize2, 1, GL_RGBA, GL_UNSIGNED_BYTE);
for (int s1 = 0; s1 < secondarySize1; ++s1) {
for (int s2 = 0; s2 < secondarySize2; ++s2) {
const unsigned char* src = getIndices(p, s1, s2);
unsigned char* dest = sliceImage->data(s1, s2);
for (int c = 0; c < 4; ++c) {
dest[c] = src[c];
}
}
}
std::ostringstream filename;
filename << outputDir << "/slice_" << direction << "_"
<< std::setfill('0') << std::setw(4) << p << ".png";
osgDB::writeImageFile(*sliceImage, filename.str());
}
}
void Interpolate3DTexture(osg::Image* image, int sizeX, int sizeY, int sizeZ, char direction) {
int primarySize, secondarySize1, secondarySize2;
auto getIndices = [&](int p, int s1, int s2) -> unsigned char* {
switch (direction) {
case 'x': return image->data(p, s1, s2);
case 'y': return image->data(s1, p, s2);
case 'z': return image->data(s1, s2, p);
default: throw std::invalid_argument("Invalid direction for interpolation");
}
};
switch (direction) {
case 'x':
primarySize = sizeX;
secondarySize1 = sizeY;
secondarySize2 = sizeZ;
break;
case 'y':
primarySize = sizeY;
secondarySize1 = sizeX;
secondarySize2 = sizeZ;
break;
case 'z':
primarySize = sizeZ;
secondarySize1 = sizeX;
secondarySize2 = sizeY;
break;
default:
throw std::invalid_argument("Invalid direction for interpolation");
}
for (int s1 = 0; s1 < secondarySize1; ++s1) {
for (int s2 = 0; s2 < secondarySize2; ++s2) {
std::vector<int> nonTransparentIndices;
for (int p = 0; p < primarySize; ++p) {
const unsigned char* dataPtr = getIndices(p, s1, s2);
if (*dataPtr != 0.0) {
nonTransparentIndices.push_back(p);
}
}
for (size_t i = 0; i + 1 < nonTransparentIndices.size(); ++i) {
int p1 = nonTransparentIndices[i];
int p2 = nonTransparentIndices[i + 1];
const unsigned char* dataPtr1 = getIndices(p1, s1, s2);
const unsigned char* dataPtr2 = getIndices(p2, s1, s2);
for (int p = p1 + 1; p < p2; ++p) {
unsigned char* dataPtr = getIndices(p, s1, s2);
float t = float(p - p1) / (p2 - p1);
t = t * t * (3.0f - 2.0f * t);
*dataPtr = static_cast<unsigned char>(
*dataPtr1 * (1.0f - t) + *dataPtr2 * t);
}
}
}
}
}
osg::Texture3D* Create3DTextureFromVtk(const VTKData& data, osg::BoundingBox& bbox)
{
CalculateTextureSize(data.points, sizeX, sizeY, sizeZ);
sizeX = osg::Image::computeNearestPowerOfTwo(sizeX);
sizeY = osg::Image::computeNearestPowerOfTwo(sizeY);
sizeZ = osg::Image::computeNearestPowerOfTwo(sizeZ);
osg::Texture3D* t3d = new osg::Texture3D;
osg::Image* image = new osg::Image;
image->allocateImage(sizeX, sizeY, sizeZ, GL_LUMINANCE, GL_UNSIGNED_BYTE);
unsigned char* imageData = image->data();
std::memset(imageData, 0.0, sizeX * sizeY * sizeZ * 1);
t3d->setImage(image);
float minX = FLT_MAX;
float minY = FLT_MAX;
float minZ = FLT_MAX;
float maxX = -FLT_MAX;
float maxY = -FLT_MAX;
float maxZ = -FLT_MAX;
for (const auto& point : data.points) {
if (point.x() < minX) minX = point.x();
if (point.y() < minY) minY = point.y();
if (point.z() < minZ) minZ = point.z();
if (point.x() > maxX) maxX = point.x();
if (point.y() > maxY) maxY = point.y();
if (point.z() > maxZ) maxZ = point.z();
}
float xRange = maxX - minX;
float yRange = maxY - minY;
float zRange = maxZ - minZ;
bbox.set(osg::Vec3(minX, minY, minZ), osg::Vec3(maxX, maxY, maxZ));
const float range = data.maxVal - data.minVal;
for(int i = 0; i < data.points.size(); ++i)
{
const osg::Vec3 point = data.points[i];
osg::Vec3 texCoord((point.x() - minX) / xRange, (point.y() - minY) / yRange, (point.z() - minZ) / zRange);
osg::Vec3ui xyz(texCoord.x() * (sizeX - 1), texCoord.y() * (sizeY - 1), texCoord.z() * (sizeZ - 1));
const float stressNormalized = (data.stresses[i][3] - data.minVal) / range;
const float mappedValue = stressNormalized * 254.0f + 1.0f;
unsigned char* dataPtr = image->data(xyz.x(), xyz.y(), xyz.z());
*dataPtr = static_cast<unsigned char>(mappedValue);
}
Interpolate3DTexture(image, sizeX, sizeY, sizeZ, 'z');
Interpolate3DTexture(image, sizeX, sizeY, sizeZ, 'x');
Interpolate3DTexture(image, sizeX, sizeY, sizeZ, 'y');
t3d->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
t3d->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
t3d->setWrap(osg::Texture::WRAP_R, osg::Texture3D::CLAMP_TO_BORDER);
t3d->setWrap(osg::Texture::WRAP_T, osg::Texture3D::CLAMP_TO_BORDER);
t3d->setWrap(osg::Texture::WRAP_S, osg::Texture3D::CLAMP_TO_BORDER);
return t3d;
}
osg::Texture1D* Create1DTextureFromVtk(const VTKData& data)
{
osg::Texture1D* t1d = new osg::Texture1D;
osg::Image* image = new osg::Image;
image->allocateImage(256, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE);
t1d->setImage(image);
unsigned char* imageData = image->data();
osg::ref_ptr<osg::TransferFunction1D> tf = new osg::TransferFunction1D;
osg::Vec4 blue(0.0f, 0.0f, 1.0f, 1.0);
osg::Vec4 green(0.0f, 1.0f, 0.0f, 1.0);
osg::Vec4 yellow(1.0f, 1.0f, 0.0f, 1.0);
osg::Vec4 red(1.0f, 0.0f, 0.0f, 1.0);
tf->setColor(0.f / 255.0f, blue);
tf->setColor(85.f / 255.0f, green);
tf->setColor(170.f / 255.0f, yellow);
tf->setColor(255.f / 255.0f, red);
for (int i = 0; i < 256; ++i)
{
unsigned char* dataPtr = image->data(i, 0, 0);
if (i != 0)
{
const osg::Vec4 color = tf->getColor(static_cast<float>(i) / 255.0f);
dataPtr[0] = color.r() * 255.0;
dataPtr[1] = color.g() * 255.0;
dataPtr[2] = color.b() * 255.0;
dataPtr[3] = color.a() * 255.0;
}
else
{
dataPtr[0] = 0.0;
dataPtr[1] = 0.0;
dataPtr[2] = 0.0;
dataPtr[3] = 0.0;
}
}
t1d->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
t1d->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
t1d->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
return t1d;
}
osg::Node* CreateVtkMesh(const VTKData& vtkData) {
osg::Geode* geode = new osg::Geode;
osg::Geometry* geometry = new osg::Geometry;
osg::Vec3Array* vertices = new osg::Vec3Array;
osg::Vec3 minPoint(FLT_MAX, FLT_MAX, FLT_MAX);
osg::Vec3 maxPoint(-FLT_MAX, -FLT_MAX, -FLT_MAX);
for (const auto& point : vtkData.points) {
vertices->push_back(point);
}
geometry->setVertexArray(vertices);
const int size = vtkData.cells.size();
osg::DrawElementsUInt* elements;
if (vtkData.type == 8)
{
elements = new osg::DrawElementsUInt(GL_TRIANGLES);
for (const auto& cell : vtkData.cells) {
elements->push_back(cell[0]);
elements->push_back(cell[1]);
elements->push_back(cell[2]);
elements->push_back(cell[0]);
elements->push_back(cell[2]);
elements->push_back(cell[3]);
elements->push_back(cell[4]);
elements->push_back(cell[5]);
elements->push_back(cell[6]);
elements->push_back(cell[4]);
elements->push_back(cell[6]);
elements->push_back(cell[7]);
elements->push_back(cell[0]);
elements->push_back(cell[1]);
elements->push_back(cell[5]);
elements->push_back(cell[0]);
elements->push_back(cell[5]);
elements->push_back(cell[4]);
elements->push_back(cell[1]);
elements->push_back(cell[2]);
elements->push_back(cell[6]);
elements->push_back(cell[1]);
elements->push_back(cell[6]);
elements->push_back(cell[5]);
elements->push_back(cell[2]);
elements->push_back(cell[3]);
elements->push_back(cell[7]);
elements->push_back(cell[2]);
elements->push_back(cell[7]);
elements->push_back(cell[6]);
elements->push_back(cell[3]);
elements->push_back(cell[0]);
elements->push_back(cell[4]);
elements->push_back(cell[3]);
elements->push_back(cell[4]);
elements->push_back(cell[7]);
}
}
else if (vtkData.type == 4)
{
elements = new osg::DrawElementsUInt(GL_QUADS);
for (const auto& cell : vtkData.cells) {
elements->push_back(cell[0]);
elements->push_back(cell[1]);
elements->push_back(cell[2]);
elements->push_back(cell[3]);
}
}
geometry->addPrimitiveSet(elements);
geode->addDrawable(geometry);
osg::StateSet* ss = geode->getOrCreateStateSet();
ss->setMode(GL_BLEND, osg::StateAttribute::ON);
ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
return geode;
}
osg::Node* CreateVtkBox(const VTKData& vtkData)
{
osg::Geode* gnode = new osg::Geode;
osg::Geometry* geom = new osg::Geometry;
gnode->addDrawable(geom);
osg::BoundingBox bbox;
osg::ref_ptr<osg::Texture3D> t3d = Create3DTextureFromVtk(vtkData, bbox);
osg::ref_ptr<osg::Texture1D> t1d = Create1DTextureFromVtk(vtkData);
const osg::Vec3 min = bbox._min;
const osg::Vec3 max = bbox._max;
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
vertices->push_back(osg::Vec3(min.x(), min.y(), min.z()));
vertices->push_back(osg::Vec3(max.x(), min.y(), min.z()));
vertices->push_back(osg::Vec3(max.x(), max.y(), min.z()));
vertices->push_back(osg::Vec3(min.x(), max.y(), min.z()));
vertices->push_back(osg::Vec3(min.x(), min.y(), max.z()));
vertices->push_back(osg::Vec3(max.x(), min.y(), max.z()));
vertices->push_back(osg::Vec3(max.x(), max.y(), max.z()));
vertices->push_back(osg::Vec3(min.x(), max.y(), max.z()));
osg::ref_ptr<osg::DrawElementsUShort> indices = new osg::DrawElementsUShort(GL_QUADS);
indices->push_back(0); indices->push_back(1); indices->push_back(2); indices->push_back(3);
indices->push_back(4); indices->push_back(5); indices->push_back(6); indices->push_back(7);
indices->push_back(0); indices->push_back(3); indices->push_back(7); indices->push_back(4);
indices->push_back(1); indices->push_back(2); indices->push_back(6); indices->push_back(5);
indices->push_back(0); indices->push_back(1); indices->push_back(5); indices->push_back(4);
indices->push_back(2); indices->push_back(3); indices->push_back(7); indices->push_back(6);
geom->setVertexArray(vertices);
geom->addPrimitiveSet(indices);
osg::StateSet* ss = gnode->getOrCreateStateSet();
osg::Program* program = new osg::Program;
ss->setMode(GL_BLEND, osg::StateAttribute::ON);
ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
ss->setTextureAttributeAndModes(0, t3d, osg::StateAttribute::ON);
ss->setTextureAttributeAndModes(1, t1d, osg::StateAttribute::ON);
ss->setAttribute(program);
program->addShader(osgDB::readRefShaderFile(osg::Shader::VERTEX, SHADER_PATH + R"(volume.vert)"));
program->addShader(osgDB::readRefShaderFile(osg::Shader::FRAGMENT, SHADER_PATH + R"(volume.frag)"));
ss->addUniform(new osg::Uniform("steps", 100));
ss->addUniform(new osg::Uniform("densityFactor", 10.0f));
ss->addUniform(new osg::Uniform("minBound", min));
ss->addUniform(new osg::Uniform("maxBound", max));
ss->addUniform(new osg::Uniform("baseTexture", int(0)));
ss->addUniform(new osg::Uniform("tfTexture", int(1)));
return gnode;
}
class KeyboardEventHandler : public osgGA::GUIEventHandler {
public:
KeyboardEventHandler(osg::Node* vtkBox, osg::Node* vtkMesh)
: _vtkBox(vtkBox), _vtkMesh(vtkMesh), _vtkBox1Visible(true), _vtkMeshVisible(true) {
}
bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) override {
if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN) {
switch (ea.getKey()) {
case 'c':
_vtkBox1Visible = !_vtkBox1Visible;
_vtkBox->setNodeMask(_vtkBox1Visible ? 0xFFFFFFFF : 0);
return true;
case 'v':
_vtkMeshVisible = !_vtkMeshVisible;
_vtkMesh->setNodeMask(_vtkMeshVisible ? 0xFFFFFFFF : 0);
return true;
default:
return false;
}
}
return false;
}
private:
osg::ref_ptr<osg::Node> _vtkBox;
osg::ref_ptr<osg::Node> _vtkMesh;
bool _vtkBox1Visible;
bool _vtkMeshVisible;
};
osg::Texture3D* Create3DNoiseTexture(int size, osg::BoundingBox& bbox)
{
osg::Texture3D* t3d = new osg::Texture3D;
osg::Image* image = new osg::Image;
image->allocateImage(size, size, size, GL_LUMINANCE, GL_UNSIGNED_BYTE);
t3d->setImage(image);
const double frequency = 4.0;
const int octaves = 4;
unsigned char* imageData = image->data();
std::memset(imageData, 0.0, size * size * size * 1);
for (int z = 0; z < size; ++z) {
for (int y = 0; y < size; ++y) {
for (int x = 0; x < size; ++x) {
unsigned char* dataPtr = image->data(x, y, z);
float heightNorm = (float)z / size;
if (heightNorm > 0.5f) {
break;
}
float transitionZone = 0.1f;
float waterSurfaceHeight = 0.5f;
float distanceToSurface = waterSurfaceHeight - heightNorm;
if (distanceToSurface < transitionZone) {
float factor = distanceToSurface / transitionZone;
*dataPtr = static_cast<unsigned char>(0.0);
continue;
}
float noiseVal = 0.0f;
float amplitude = 1.0f;
float totalAmplitude = 0.0f;
for (int o = 0; o < octaves; ++o) {
float scale = frequency * (1 << o);
float sampleX = (float)x / size * scale;
float sampleY = (float)y / size * scale;
float sampleZ = (float)z / size * scale;
noiseVal += simpleNoise(sampleX, sampleY, sampleZ) * amplitude;
totalAmplitude += amplitude;
amplitude *= 0.5f;
}
noiseVal = (noiseVal / totalAmplitude + 1.0f) * 0.5f;
float heightFactor = 1.0f - (heightNorm / 0.5f);
noiseVal *= heightFactor * heightFactor;
unsigned char value = static_cast<unsigned char>(noiseVal * 255.0f);
*dataPtr = value;
}
}
}
bbox.set(osg::Vec3(-1, -1, -1), osg::Vec3(1, 1, 1));
t3d->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
t3d->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
t3d->setWrap(osg::Texture::WRAP_R, osg::Texture3D::CLAMP_TO_EDGE);
t3d->setWrap(osg::Texture::WRAP_T, osg::Texture3D::CLAMP_TO_EDGE);
t3d->setWrap(osg::Texture::WRAP_S, osg::Texture3D::CLAMP_TO_EDGE);
return t3d;
}
osg::Texture1D* CreateWaterTransferFunction()
{
osg::Texture1D* t1d = new osg::Texture1D;
osg::Image* image = new osg::Image;
image->allocateImage(256, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE);
t1d->setImage(image);
osg::Vec4 waterColor1(0.0f, 0.2f, 0.4f, 0.1f);
osg::Vec4 waterColor2(0.0f, 0.4f, 0.8f, 0.5f);
osg::Vec4 waterColor3(0.1f, 0.6f, 0.9f, 0.8f);
for (int i = 0; i < 256; ++i)
{
unsigned char* dataPtr = image->data(i, 0, 0);
float t = static_cast<float>(i) / 255.0f;
if (i == 0) {
dataPtr[0] = 0;
dataPtr[1] = 0;
dataPtr[2] = 0;
dataPtr[3] = 0;
}
else {
osg::Vec4 color;
if (t < 0.5f) {
float s = t / 0.5f;
color = waterColor1 * (1.0f - s) + waterColor2 * s;
}
else {
float s = (t - 0.5f) / 0.5f;
color = waterColor2 * (1.0f - s) + waterColor3 * s;
}
dataPtr[0] = static_cast<unsigned char>(color.r() * 255.0f);
dataPtr[1] = static_cast<unsigned char>(color.g() * 255.0f);
dataPtr[2] = static_cast<unsigned char>(color.b() * 255.0f);
dataPtr[3] = static_cast<unsigned char>(color.a() * 255.0f);
}
}
t1d->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
t1d->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
t1d->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
return t1d;
}
int main()
{
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
osg::ref_ptr<osg::Group> group = new osg::Group;
VTKData data = ParseVtkFile(R"(xxx.vtk)");
osg::Node* vtkBox = CreateVtkBox(data);
group->addChild(vtkBox);
osg::Node* vtkMesh = CreateVtkMesh(data);
group->addChild(vtkMesh);
osg::ref_ptr<KeyboardEventHandler> keyboardHandler = new KeyboardEventHandler(vtkBox, vtkMesh);
viewer->addEventHandler(keyboardHandler);
viewer->setSceneData(group);
viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));
viewer->addEventHandler(new osgViewer::StatsHandler());
return viewer->run();
}