You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw/apps/opencs/view/render/brushdraw.cpp

272 lines
11 KiB
C++

#include "brushdraw.hpp"
#include <limits>
#include <set>
#include <utility>
#include <osg/Array>
#include <osg/GL>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/Math>
#include <osg/PrimitiveSet>
#include <osg/StateAttribute>
#include <osg/StateSet>
#include <osg/Vec4f>
#include <osgUtil/IntersectionVisitor>
#include <osgUtil/LineSegmentIntersector>
#include <components/esm3/loadland.hpp>
#include "../../model/world/cellcoordinates.hpp"
#include "../widget/brushshapes.hpp"
#include "mask.hpp"
CSVRender::BrushDraw::BrushDraw(osg::ref_ptr<osg::Group> parentNode, bool textureMode)
: mParentNode(std::move(parentNode))
, mTextureMode(textureMode)
{
mBrushDrawNode = new osg::Group();
mGeometry = new osg::Geometry();
mBrushDrawNode->addChild(mGeometry);
mBrushDrawNode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
mBrushDrawNode->getOrCreateStateSet()->setRenderBinDetails(11, "RenderBin");
mParentNode->addChild(mBrushDrawNode);
if (mTextureMode)
mLandSizeFactor = static_cast<float>(ESM::Land::REAL_SIZE) / static_cast<float>(ESM::Land::LAND_TEXTURE_SIZE);
else
mLandSizeFactor = static_cast<float>(ESM::Land::REAL_SIZE) / static_cast<float>(ESM::Land::LAND_SIZE - 1);
}
CSVRender::BrushDraw::~BrushDraw()
{
mBrushDrawNode->removeChild(mGeometry);
mParentNode->removeChild(mBrushDrawNode);
}
float CSVRender::BrushDraw::getIntersectionHeight(const osg::Vec3d& point)
{
osg::Vec3d start = point;
osg::Vec3d end = point;
start.z() = std::numeric_limits<int>::max();
end.z() = std::numeric_limits<int>::lowest();
osg::Vec3d direction = end - start;
// Get intersection
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector(
new osgUtil::LineSegmentIntersector(osgUtil::Intersector::MODEL, start, end));
intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT);
osgUtil::IntersectionVisitor visitor(intersector);
visitor.setTraversalMask(Mask_Terrain);
mParentNode->accept(visitor);
for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin();
it != intersector->getIntersections().end(); ++it)
{
osgUtil::LineSegmentIntersector::Intersection intersection = *it;
// reject back-facing polygons
if (direction * intersection.getWorldIntersectNormal() > 0)
{
continue;
}
return intersection.getWorldIntersectPoint().z();
}
return 0.0f;
}
void CSVRender::BrushDraw::buildPointGeometry(const osg::Vec3d& point)
{
osg::ref_ptr<osg::Geometry> geom(new osg::Geometry());
osg::ref_ptr<osg::Vec3Array> vertices(new osg::Vec3Array());
osg::ref_ptr<osg::Vec4Array> colors(new osg::Vec4Array());
const float brushOutlineHeight(1.0f);
const float crossHeadSize(8.0f);
osg::Vec4f lineColor(1.0f, 1.0f, 1.0f, 0.6f);
vertices->push_back(osg::Vec3d(point.x() - crossHeadSize, point.y() - crossHeadSize,
getIntersectionHeight(osg::Vec3d(point.x() - crossHeadSize, point.y() - crossHeadSize, point.z()))
+ brushOutlineHeight));
colors->push_back(lineColor);
vertices->push_back(osg::Vec3d(point.x() + crossHeadSize, point.y() + crossHeadSize,
getIntersectionHeight(osg::Vec3d(point.x() + crossHeadSize, point.y() + crossHeadSize, point.z()))
+ brushOutlineHeight));
colors->push_back(lineColor);
vertices->push_back(osg::Vec3d(point.x() + crossHeadSize, point.y() - crossHeadSize,
getIntersectionHeight(osg::Vec3d(point.x() + crossHeadSize, point.y() - crossHeadSize, point.z()))
+ brushOutlineHeight));
colors->push_back(lineColor);
vertices->push_back(osg::Vec3d(point.x() - crossHeadSize, point.y() + crossHeadSize,
getIntersectionHeight(osg::Vec3d(point.x() - crossHeadSize, point.y() + crossHeadSize, point.z()))
+ brushOutlineHeight));
colors->push_back(lineColor);
geom->setVertexArray(vertices);
geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, 4));
mGeometry = geom;
}
void CSVRender::BrushDraw::buildSquareGeometry(const float& radius, const osg::Vec3d& point)
{
osg::ref_ptr<osg::Geometry> geom(new osg::Geometry());
osg::ref_ptr<osg::Vec3Array> vertices(new osg::Vec3Array());
osg::ref_ptr<osg::Vec4Array> colors(new osg::Vec4Array());
const float brushOutlineHeight(1.0f);
float diameter = radius * 2;
int resolution = static_cast<int>(2.f * diameter / mLandSizeFactor); // half a vertex resolution
float resAdjustedLandSizeFactor = mLandSizeFactor / 2; // 128
if (resolution > 128) // limit accuracy for performance
{
resolution = 128;
resAdjustedLandSizeFactor = diameter / resolution;
}
osg::Vec4f lineColor(1.0f, 1.0f, 1.0f, 0.6f);
for (int i = 0; i < resolution; i++)
{
int step = i * resAdjustedLandSizeFactor;
int step2 = (i + 1) * resAdjustedLandSizeFactor;
osg::Vec3d upHorizontalLinePoint1(point.x() - radius + step, point.y() - radius,
getIntersectionHeight(osg::Vec3d(point.x() - radius + step, point.y() - radius, point.z()))
+ brushOutlineHeight);
osg::Vec3d upHorizontalLinePoint2(point.x() - radius + step2, point.y() - radius,
getIntersectionHeight(osg::Vec3d(point.x() - radius + step2, point.y() - radius, point.z()))
+ brushOutlineHeight);
osg::Vec3d upVerticalLinePoint1(point.x() - radius, point.y() - radius + step,
getIntersectionHeight(osg::Vec3d(point.x() - radius, point.y() - radius + step, point.z()))
+ brushOutlineHeight);
osg::Vec3d upVerticalLinePoint2(point.x() - radius, point.y() - radius + step2,
getIntersectionHeight(osg::Vec3d(point.x() - radius, point.y() - radius + step2, point.z()))
+ brushOutlineHeight);
osg::Vec3d downHorizontalLinePoint1(point.x() + radius - step, point.y() + radius,
getIntersectionHeight(osg::Vec3d(point.x() + radius - step, point.y() + radius, point.z()))
+ brushOutlineHeight);
osg::Vec3d downHorizontalLinePoint2(point.x() + radius - step2, point.y() + radius,
getIntersectionHeight(osg::Vec3d(point.x() + radius - step2, point.y() + radius, point.z()))
+ brushOutlineHeight);
osg::Vec3d downVerticalLinePoint1(point.x() + radius, point.y() + radius - step,
getIntersectionHeight(osg::Vec3d(point.x() + radius, point.y() + radius - step, point.z()))
+ brushOutlineHeight);
osg::Vec3d downVerticalLinePoint2(point.x() + radius, point.y() + radius - step2,
getIntersectionHeight(osg::Vec3d(point.x() + radius, point.y() + radius - step2, point.z()))
+ brushOutlineHeight);
vertices->push_back(upHorizontalLinePoint1);
colors->push_back(lineColor);
vertices->push_back(upHorizontalLinePoint2);
colors->push_back(lineColor);
vertices->push_back(upVerticalLinePoint1);
colors->push_back(lineColor);
vertices->push_back(upVerticalLinePoint2);
colors->push_back(lineColor);
vertices->push_back(downHorizontalLinePoint1);
colors->push_back(lineColor);
vertices->push_back(downHorizontalLinePoint2);
colors->push_back(lineColor);
vertices->push_back(downVerticalLinePoint1);
colors->push_back(lineColor);
vertices->push_back(downVerticalLinePoint2);
colors->push_back(lineColor);
}
geom->setVertexArray(vertices);
geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, resolution * 8));
mGeometry = geom;
}
void CSVRender::BrushDraw::buildCircleGeometry(const float& radius, const osg::Vec3d& point)
{
osg::ref_ptr<osg::Geometry> geom(new osg::Geometry());
osg::ref_ptr<osg::Vec3Array> vertices(new osg::Vec3Array());
osg::ref_ptr<osg::Vec4Array> colors(new osg::Vec4Array());
const int amountOfPoints = 128;
const float step((osg::PI * 2.0f) / static_cast<float>(amountOfPoints));
const float brushOutlineHeight(1.0f);
osg::Vec4f lineColor(1.0f, 1.0f, 1.0f, 0.6f);
for (int i = 0; i < amountOfPoints + 2; i++)
{
float angle(i * step);
vertices->push_back(osg::Vec3d(point.x() + radius * cosf(angle), point.y() + radius * sinf(angle),
getIntersectionHeight(
osg::Vec3d(point.x() + radius * cosf(angle), point.y() + radius * sinf(angle), point.z()))
+ brushOutlineHeight));
colors->push_back(lineColor);
angle = static_cast<float>(i + 1) * step;
vertices->push_back(osg::Vec3d(point.x() + radius * cosf(angle), point.y() + radius * sinf(angle),
getIntersectionHeight(
osg::Vec3d(point.x() + radius * cosf(angle), point.y() + radius * sinf(angle), point.z()))
+ brushOutlineHeight));
colors->push_back(lineColor);
}
geom->setVertexArray(vertices);
geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, amountOfPoints * 2));
mGeometry = geom;
}
void CSVRender::BrushDraw::buildCustomGeometry(const float& radius, const osg::Vec3d& point)
{
// Not implemented
}
void CSVRender::BrushDraw::update(osg::Vec3d point, int brushSize, CSVWidget::BrushShape toolShape)
{
if (mBrushDrawNode->containsNode(mGeometry))
mBrushDrawNode->removeChild(mGeometry);
float radius = (mLandSizeFactor * brushSize) / 2;
osg::Vec3d snapToGridPoint = point;
if (mTextureMode)
{
std::pair<int, int> snapToGridXY = CSMWorld::CellCoordinates::toTextureCoords(point);
float offsetToMiddle = mLandSizeFactor * 0.5f;
snapToGridPoint = osg::Vec3d(
CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(snapToGridXY.first) + offsetToMiddle,
CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(snapToGridXY.second) + offsetToMiddle, point.z());
}
else
{
std::pair<int, int> snapToGridXY = CSMWorld::CellCoordinates::toVertexCoords(point);
snapToGridPoint = osg::Vec3d(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(snapToGridXY.first),
CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(snapToGridXY.second), point.z());
}
switch (toolShape)
{
case (CSVWidget::BrushShape_Point):
buildPointGeometry(snapToGridPoint);
break;
case (CSVWidget::BrushShape_Square):
buildSquareGeometry(radius, snapToGridPoint);
break;
case (CSVWidget::BrushShape_Circle):
buildCircleGeometry(radius, snapToGridPoint);
break;
case (CSVWidget::BrushShape_Custom):
buildSquareGeometry(1, snapToGridPoint);
// buildCustomGeometry
break;
}
mGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
mBrushDrawNode->addChild(mGeometry);
}
void CSVRender::BrushDraw::hide()
{
if (mBrushDrawNode->containsNode(mGeometry))
mBrushDrawNode->removeChild(mGeometry);
}