/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2014 Robert Osfield * Copyright (C) 2014 Pawel Ksiezopolski * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. * */ #include "ShapeToGeometry.h" #include FakeGLBeginEndAdapter::FakeGLBeginEndAdapter() : osg::GLBeginEndAdapter(NULL) { geometry = new osg::Geometry; } void FakeGLBeginEndAdapter::PushMatrix() { if (_matrixStack.empty()) _matrixStack.push_back(osg::Matrixd()); else _matrixStack.push_back(_matrixStack.back()); } void FakeGLBeginEndAdapter::MultMatrixd(const GLdouble* m) { if (_matrixStack.empty()) _matrixStack.push_back(osg::Matrixd()); _matrixStack.back().preMult(osg::Matrixd(m)); } void FakeGLBeginEndAdapter::Translated(GLdouble x, GLdouble y, GLdouble z) { if (_matrixStack.empty()) _matrixStack.push_back(osg::Matrixd()); _matrixStack.back().preMultTranslate(osg::Vec3d(x,y,z)); } void FakeGLBeginEndAdapter::Scaled(GLdouble x, GLdouble y, GLdouble z) { if (_matrixStack.empty()) { _matrixStack.push_back(osg::Matrixd()); } _matrixStack.back().preMultScale(osg::Vec3d(x,y,z)); } void FakeGLBeginEndAdapter::Rotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z) { if (_matrixStack.empty()) _matrixStack.push_back(osg::Matrixd()); _matrixStack.back().preMultRotate(osg::Quat(osg::DegreesToRadians(angle), osg::Vec3d(x,y,z))); } void FakeGLBeginEndAdapter::End() { if (!_vertices || _vertices->empty()) return; if (!_matrixStack.empty()) { const osg::Matrixd& matrix = _matrixStack.back(); osg::Matrixd inverse; inverse.invert(matrix); for(osg::Vec3Array::iterator itr = _vertices->begin(); itr != _vertices->end(); ++itr) { *itr = *itr * matrix; } if (_normalAssigned && _normals.valid()) { for(osg::Vec3Array::iterator itr = _normals->begin(); itr != _normals->end(); ++itr) { *itr = osg::Matrixd::transform3x3(inverse, *itr); (*itr).normalize(); } } else { _overallNormal = osg::Matrixd::transform3x3(inverse, _overallNormal); _overallNormal.normalize(); } } if (_colorAssigned) { if(geometry->getColorArray() == NULL ) geometry->setColorArray( new osg::Vec4Array, osg::Array::BIND_PER_VERTEX ); osg::Vec4Array* gColors = dynamic_cast(geometry->getColorArray()); gColors->insert( gColors->end(), _colors->begin(), _colors->end() ); } else if (_overallColorAssigned) { if(geometry->getColorArray() == NULL ) geometry->setColorArray( new osg::Vec4Array, osg::Array::BIND_PER_VERTEX ); osg::Vec4Array* gColors=dynamic_cast(geometry->getColorArray()); gColors->insert( gColors->end(), _vertices->size(), _overallColor ); } if (_normalAssigned) { if(geometry->getNormalArray() == NULL ) geometry->setNormalArray( new osg::Vec3Array, osg::Array::BIND_PER_VERTEX ); osg::Vec3Array* gNormals = dynamic_cast(geometry->getNormalArray()); gNormals->insert( gNormals->end(), _normals->begin(), _normals->end() ); } else if (_overallNormalAssigned) { if(geometry->getNormalArray() == NULL ) geometry->setNormalArray( new osg::Vec3Array, osg::Array::BIND_PER_VERTEX ); osg::Vec3Array* gNormals = dynamic_cast(geometry->getNormalArray()); gNormals->insert( gNormals->end(), _vertices->size(), _overallNormal ); } for(unsigned int unit=0; unit<_texCoordAssignedList.size(); ++unit) { if (_texCoordAssignedList[unit] && _texCoordsList[unit].valid()) { if(geometry->getTexCoordArray(unit) == NULL ) geometry->setTexCoordArray( unit, new osg::Vec4Array, osg::Array::BIND_PER_VERTEX ); osg::Vec4Array* gTexCoords = dynamic_cast(geometry->getTexCoordArray(unit)); gTexCoords->insert( gTexCoords->end(), _texCoordsList[unit]->begin(), _texCoordsList[unit]->end() ); } } for(unsigned int unit=0; unit<_vertexAttribAssignedList.size(); ++unit) { if (_vertexAttribAssignedList[unit] && _vertexAttribsList[unit].valid()) { if(geometry->getVertexAttribArray(unit) == NULL ) geometry->setVertexAttribArray( unit, new osg::Vec4Array, osg::Array::BIND_PER_VERTEX ); osg::Vec4Array* gVertexAttribs = dynamic_cast(geometry->getVertexAttribArray(unit)); gVertexAttribs->insert( gVertexAttribs->end(), _vertexAttribsList[unit]->begin(), _vertexAttribsList[unit]->end() ); } } if(geometry->getVertexArray() == NULL ) geometry->setVertexArray( new osg::Vec3Array ); osg::Vec3Array* gVertices = dynamic_cast(geometry->getVertexArray()); unsigned int vOffset = gVertices->size(); unsigned int vSize = _vertices->size(); gVertices->insert( gVertices->end(), _vertices->begin(), _vertices->end() ); if (_primitiveMode==GL_QUAD_STRIP) // will the winding be wrong? Do we need to swap it? geometry->addPrimitiveSet( new osg::DrawArrays( GL_TRIANGLE_STRIP, vOffset, vSize ) ); else if (_primitiveMode==GL_POLYGON) geometry->addPrimitiveSet( new osg::DrawArrays( GL_TRIANGLE_FAN, vOffset, vSize ) ); else geometry->addPrimitiveSet( new osg::DrawArrays( _primitiveMode, vOffset, vSize ) ); } void ShapeToGeometryVisitor::drawCylinderBody(unsigned int numSegments, float radius, float height) { const float angleDelta = 2.0f*osg::PI/(float)numSegments; const float texCoordDelta = 1.0f/(float)numSegments; const float r = radius; const float h = height; float basez = -h*0.5f; float topz = h*0.5f; float angle = 0.0f; float texCoord = 0.0f; bool drawFrontFace = _hints ? _hints->getCreateFrontFace() : true; bool drawBackFace = _hints ? _hints->getCreateBackFace() : false; // The only difference between the font & back face loops is that the // normals are inverted and the order of the vertex pairs is reversed. // The code is mostly duplicated in order to hoist the back/front face // test out of the loop for efficiency gl.Begin(GL_QUAD_STRIP); if (drawFrontFace) { for(unsigned int bodyi=0; bodyigetCreateFrontFace() : true; bool drawBackFace = _hints ? _hints->getCreateBackFace() : false; float angleDelta = osg::PI*2.0f/(float)numSegments; float texCoordHorzDelta = 1.0f/(float)numSegments; float lBase=-osg::PI*0.5f + (top?(lDelta*(numRows/2)):0.0f); float rBase=(top?(cosf(lBase)*radius):0.0f); float zBase=(top?(sinf(lBase)*radius):-radius); float vBase=(top?(vDelta*(numRows/2)):0.0f); float nzBase=(top?(sinf(lBase)):-1.0f); float nRatioBase=(top?(cosf(lBase)):0.0f); unsigned int rowbegin = top?numRows/2:0; unsigned int rowend = top?numRows:numRows/2; for(unsigned int rowi=rowbegin; rowigetCreateFrontFace() : true; bool drawBackFace = _hints ? _hints->getCreateBackFace() : false; unsigned int numSegments = 40; unsigned int numRows = 20; float ratio = (_hints ? _hints->getDetailRatio() : 1.0f); if (ratio > 0.0f && ratio != 1.0f) { numRows = (unsigned int) (numRows * ratio); if (numRows < MIN_NUM_ROWS) numRows = MIN_NUM_ROWS; numSegments = (unsigned int) (numSegments * ratio); if (numSegments < MIN_NUM_SEGMENTS) numSegments = MIN_NUM_SEGMENTS; } float lDelta = osg::PI/(float)numRows; float vDelta = 1.0f/(float)numRows; float angleDelta = osg::PI*2.0f/(float)numSegments; float texCoordHorzDelta = 1.0f/(float)numSegments; if (drawBackFace) { float lBase=-osg::PI*0.5f; float rBase=0.0f; float zBase=-sphere.getRadius(); float vBase=0.0f; float nzBase=-1.0f; float nRatioBase=0.0f; for(unsigned int rowi=0; rowigetCreateBody() : true); bool createTop = (_hints ? _hints->getCreateTop() : true); bool createBottom = (_hints ? _hints->getCreateBottom() : true); float dx = box.getHalfLengths().x(); float dy = box.getHalfLengths().y(); float dz = box.getHalfLengths().z(); gl.PushMatrix(); gl.Translated(box.getCenter().x(),box.getCenter().y(),box.getCenter().z()); if (!box.zeroRotation()) { osg::Matrixd rotation(box.computeRotationMatrix()); gl.MultMatrixd(rotation.ptr()); } gl.Begin(GL_QUADS); if (createBody) { // -ve y plane gl.Normal3f(0.0f,-1.0f,0.0f); gl.TexCoord2f(0.0f,1.0f); gl.Vertex3f(-dx,-dy,dz); gl.TexCoord2f(0.0f,0.0f); gl.Vertex3f(-dx,-dy,-dz); gl.TexCoord2f(1.0f,0.0f); gl.Vertex3f(dx,-dy,-dz); gl.TexCoord2f(1.0f,1.0f); gl.Vertex3f(dx,-dy,dz); // +ve y plane gl.Normal3f(0.0f,1.0f,0.0f); gl.TexCoord2f(0.0f,1.0f); gl.Vertex3f(dx,dy,dz); gl.TexCoord2f(0.0f,0.0f); gl.Vertex3f(dx,dy,-dz); gl.TexCoord2f(1.0f,0.0f); gl.Vertex3f(-dx,dy,-dz); gl.TexCoord2f(1.0f,1.0f); gl.Vertex3f(-dx,dy,dz); // +ve x plane gl.Normal3f(1.0f,0.0f,0.0f); gl.TexCoord2f(0.0f,1.0f); gl.Vertex3f(dx,-dy,dz); gl.TexCoord2f(0.0f,0.0f); gl.Vertex3f(dx,-dy,-dz); gl.TexCoord2f(1.0f,0.0f); gl.Vertex3f(dx,dy,-dz); gl.TexCoord2f(1.0f,1.0f); gl.Vertex3f(dx,dy,dz); // -ve x plane gl.Normal3f(-1.0f,0.0f,0.0f); gl.TexCoord2f(0.0f,1.0f); gl.Vertex3f(-dx,dy,dz); gl.TexCoord2f(0.0f,0.0f); gl.Vertex3f(-dx,dy,-dz); gl.TexCoord2f(1.0f,0.0f); gl.Vertex3f(-dx,-dy,-dz); gl.TexCoord2f(1.0f,1.0f); gl.Vertex3f(-dx,-dy,dz); } if (createTop) { // +ve z plane gl.Normal3f(0.0f,0.0f,1.0f); gl.TexCoord2f(0.0f,1.0f); gl.Vertex3f(-dx,dy,dz); gl.TexCoord2f(0.0f,0.0f); gl.Vertex3f(-dx,-dy,dz); gl.TexCoord2f(1.0f,0.0f); gl.Vertex3f(dx,-dy,dz); gl.TexCoord2f(1.0f,1.0f); gl.Vertex3f(dx,dy,dz); } if (createBottom) { // -ve z plane gl.Normal3f(0.0f,0.0f,-1.0f); gl.TexCoord2f(0.0f,1.0f); gl.Vertex3f(dx,dy,-dz); gl.TexCoord2f(0.0f,0.0f); gl.Vertex3f(dx,-dy,-dz); gl.TexCoord2f(1.0f,0.0f); gl.Vertex3f(-dx,-dy,-dz); gl.TexCoord2f(1.0f,1.0f); gl.Vertex3f(-dx,dy,-dz); } gl.End(); gl.PopMatrix(); } void ShapeToGeometryVisitor::apply(const osg::Cone& cone) { gl.PushMatrix(); gl.Translated(cone.getCenter().x(),cone.getCenter().y(),cone.getCenter().z()); if (!cone.zeroRotation()) { osg::Matrixd rotation(cone.computeRotationMatrix()); gl.MultMatrixd(rotation.ptr()); } // evaluate hints bool createBody = (_hints ? _hints->getCreateBody() : true); bool createBottom = (_hints ? _hints->getCreateBottom() : true); unsigned int numSegments = 40; unsigned int numRows = 10; float ratio = (_hints ? _hints->getDetailRatio() : 1.0f); if (ratio > 0.0f && ratio != 1.0f) { numRows = (unsigned int) (numRows * ratio); if (numRows < MIN_NUM_ROWS) numRows = MIN_NUM_ROWS; numSegments = (unsigned int) (numSegments * ratio); if (numSegments < MIN_NUM_SEGMENTS) numSegments = MIN_NUM_SEGMENTS; } float r = cone.getRadius(); float h = cone.getHeight(); float normalz = r/(sqrtf(r*r+h*h)); float normalRatio = 1.0f/(sqrtf(1.0f+normalz*normalz)); normalz *= normalRatio; float angleDelta = 2.0f*osg::PI/(float)numSegments; float texCoordHorzDelta = 1.0/(float)numSegments; float texCoordRowDelta = 1.0/(float)numRows; float hDelta = cone.getHeight()/(float)numRows; float rDelta = cone.getRadius()/(float)numRows; float topz=cone.getHeight()+cone.getBaseOffset(); float topr=0.0f; float topv=1.0f; float basez=topz-hDelta; float baser=rDelta; float basev=topv-texCoordRowDelta; float angle; float texCoord; if (createBody) { for(unsigned int rowi=0; rowigetCreateBody() : true); bool createTop = (_hints ? _hints->getCreateTop() : true); bool createBottom = (_hints ? _hints->getCreateBottom() : true); unsigned int numSegments = 40; float ratio = (_hints ? _hints->getDetailRatio() : 1.0f); if (ratio > 0.0f && ratio != 1.0f) { numSegments = (unsigned int) (numSegments * ratio); if (numSegments < MIN_NUM_SEGMENTS) numSegments = MIN_NUM_SEGMENTS; } // cylinder body if (createBody) drawCylinderBody(numSegments, cylinder.getRadius(), cylinder.getHeight()); float angleDelta = 2.0f*osg::PI/(float)numSegments; float texCoordDelta = 1.0f/(float)numSegments; float r = cylinder.getRadius(); float h = cylinder.getHeight(); float basez = -h*0.5f; float topz = h*0.5f; float angle = 0.0f; float texCoord = 0.0f; // cylinder top if (createTop) { gl.Begin(GL_TRIANGLE_FAN); gl.Normal3f(0.0f,0.0f,1.0f); gl.TexCoord2f(0.5f,0.5f); gl.Vertex3f(0.0f,0.0f,topz); angle = 0.0f; texCoord = 0.0f; for(unsigned int topi=0; topigetCreateBody() : true); bool createTop = (_hints ? _hints->getCreateTop() : true); bool createBottom = (_hints ? _hints->getCreateBottom() : true); unsigned int numSegments = 40; unsigned int numRows = 20; float ratio = (_hints ? _hints->getDetailRatio() : 1.0f); if (ratio > 0.0f && ratio != 1.0f) { numSegments = (unsigned int) (numSegments * ratio); if (numSegments < MIN_NUM_SEGMENTS) numSegments = MIN_NUM_SEGMENTS; numRows = (unsigned int) (numRows * ratio); if (numRows < MIN_NUM_ROWS) numRows = MIN_NUM_ROWS; } // if numRows is odd the top and bottom halves of sphere won't match, so bump up to the next event numRows if ((numRows%2)!=0) ++numRows; // capsule cylindrical body if (createBody) drawCylinderBody(numSegments, capsule.getRadius(), capsule.getHeight()); // capsule top cap if (createTop) drawHalfSphere(numSegments, numRows, capsule.getRadius(), SphereTopHalf, capsule.getHeight()/2.0f); // capsule bottom cap if (createBottom) drawHalfSphere(numSegments, numRows, capsule.getRadius(), SphereBottomHalf, -capsule.getHeight()/2.0f); gl.PopMatrix(); } void ShapeToGeometryVisitor::apply(const osg::InfinitePlane&) { OSG_NOTICE<<"Warning: ShapeToGeometryVisitor::apply(const InfinitePlane& plane) not yet implemented. "<getNumElements();i+=3) { const osg::Vec3& v1=(*vertices)[indices->index(i)]; const osg::Vec3& v2=(*vertices)[indices->index(i+1)]; const osg::Vec3& v3=(*vertices)[indices->index(i+2)]; osg::Vec3 normal = (v2-v1)^(v3-v2); normal.normalize(); gl.Normal3fv(normal.ptr()); gl.Vertex3fv(v1.ptr()); gl.Vertex3fv(v2.ptr()); gl.Vertex3fv(v3.ptr()); } gl.End(); } } void ShapeToGeometryVisitor::apply(const osg::ConvexHull& hull) { apply((const osg::TriangleMesh&)hull); } void ShapeToGeometryVisitor::apply(const osg::HeightField& field) { if (field.getNumColumns()==0 || field.getNumRows()==0) return; gl.PushMatrix(); gl.Translated(field.getOrigin().x(),field.getOrigin().y(),field.getOrigin().z()); if (!field.zeroRotation()) { osg::Matrixd rotation(field.computeRotationMatrix()); gl.MultMatrixd(rotation.ptr()); } float dx = field.getXInterval(); float dy = field.getYInterval(); float du = 1.0f/((float)field.getNumColumns()-1.0f); float dv = 1.0f/((float)field.getNumRows()-1.0f); float vBase = 0.0f; osg::Vec3 vertTop; osg::Vec3 normTop; osg::Vec3 vertBase; osg::Vec3 normBase; if (field.getSkirtHeight()!=0.0f) { gl.Begin(GL_QUAD_STRIP); float u = 0.0f; // draw bottom skirt unsigned int col; vertTop.y() = 0.0f; for(col=0;colaccept(*this); } } osg::Geometry* convertShapeToGeometry(const osg::Shape& shape, const osg::TessellationHints* hints) { osg::ref_ptr geometry; { ShapeToGeometryVisitor gfsVisitor(hints); shape.accept( gfsVisitor ); geometry = gfsVisitor.getGeometry(); } return geometry.release(); } osg::Geometry* convertShapeToGeometry(const osg::Shape& shape, const osg::TessellationHints* hints, const osg::Vec4& color) { osg::ref_ptr geometry = convertShapeToGeometry(shape,hints); osg::Vec4Array* colorArray = new osg::Vec4Array; colorArray->insert( colorArray->end(), geometry->getVertexArray()->getNumElements(), color ); geometry->setColorArray( colorArray, osg::Array::BIND_PER_VERTEX ); return geometry.release(); } osg::Geode* convertShapeToGeode(const osg::Shape& shape, const osg::TessellationHints* hints) { osg::Geode *geode = new osg::Geode; geode->addDrawable( convertShapeToGeometry(shape,hints) ); return geode; } osg::Geode* convertShapeToGeode(const osg::Shape& shape, const osg::TessellationHints* hints, const osg::Vec4& color) { osg::Geode *geode = new osg::Geode; geode->addDrawable( convertShapeToGeometry(shape,hints,color) ); return geode; } void ShapesToGeometriesVisitor::apply( osg::Geode& geode ) { for(unsigned int i=0; i( geode.getDrawable(i) ); if(drawable==NULL) continue; osg::Geometry* newGeom = convertShapeToGeometry(*(drawable->getShape()), _hints); newGeom->setStateSet( drawable->getStateSet() ); geode.setDrawable( i, newGeom ); } traverse(geode); }