/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield * * 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 #include #include using namespace osg; // arbitrary minima for rows & segments const unsigned int MIN_NUM_ROWS = 3; const unsigned int MIN_NUM_SEGMENTS = 5; /////////////////////////////////////////////////////////////////////////////// // // draw shape // class DrawShapeVisitor : public ConstShapeVisitor { public: DrawShapeVisitor(State& state,const TessellationHints* hints): _state(state), _hints(hints) { #if 0 if (hints) { notify(NOTICE)<<"Warning: TessellationHints ignored in present osg::ShapeDrawable implementation."<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 glBegin(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(); glPushMatrix(); glTranslatef(box.getCenter().x(),box.getCenter().y(),box.getCenter().z()); if (!box.zeroRotation()) { Matrix rotation(box.computeRotationMatrix()); glMultMatrix(rotation.ptr()); } glBegin(GL_QUADS); if (createBody) { // -ve y plane glNormal3f(0.0f,-1.0f,0.0f); glTexCoord2f(0.0f,1.0f); glVertex3f(-dx,-dy,dz); glTexCoord2f(0.0f,0.0f); glVertex3f(-dx,-dy,-dz); glTexCoord2f(1.0f,0.0f); glVertex3f(dx,-dy,-dz); glTexCoord2f(1.0f,1.0f); glVertex3f(dx,-dy,dz); // +ve y plane glNormal3f(0.0f,1.0f,0.0f); glTexCoord2f(0.0f,1.0f); glVertex3f(dx,dy,dz); glTexCoord2f(0.0f,0.0f); glVertex3f(dx,dy,-dz); glTexCoord2f(1.0f,0.0f); glVertex3f(-dx,dy,-dz); glTexCoord2f(1.0f,1.0f); glVertex3f(-dx,dy,dz); // +ve x plane glNormal3f(1.0f,0.0f,0.0f); glTexCoord2f(0.0f,1.0f); glVertex3f(dx,-dy,dz); glTexCoord2f(0.0f,0.0f); glVertex3f(dx,-dy,-dz); glTexCoord2f(1.0f,0.0f); glVertex3f(dx,dy,-dz); glTexCoord2f(1.0f,1.0f); glVertex3f(dx,dy,dz); // -ve x plane glNormal3f(-1.0f,0.0f,0.0f); glTexCoord2f(0.0f,1.0f); glVertex3f(-dx,dy,dz); glTexCoord2f(0.0f,0.0f); glVertex3f(-dx,dy,-dz); glTexCoord2f(1.0f,0.0f); glVertex3f(-dx,-dy,-dz); glTexCoord2f(1.0f,1.0f); glVertex3f(-dx,-dy,dz); } if (createTop) { // +ve z plane glNormal3f(0.0f,0.0f,1.0f); glTexCoord2f(0.0f,1.0f); glVertex3f(-dx,dy,dz); glTexCoord2f(0.0f,0.0f); glVertex3f(-dx,-dy,dz); glTexCoord2f(1.0f,0.0f); glVertex3f(dx,-dy,dz); glTexCoord2f(1.0f,1.0f); glVertex3f(dx,dy,dz); } if (createBottom) { // -ve z plane glNormal3f(0.0f,0.0f,-1.0f); glTexCoord2f(0.0f,1.0f); glVertex3f(dx,dy,-dz); glTexCoord2f(0.0f,0.0f); glVertex3f(dx,-dy,-dz); glTexCoord2f(1.0f,0.0f); glVertex3f(-dx,-dy,-dz); glTexCoord2f(1.0f,1.0f); glVertex3f(-dx,dy,-dz); } glEnd(); glPopMatrix(); } void DrawShapeVisitor::apply(const Cone& cone) { glPushMatrix(); glTranslatef(cone.getCenter().x(),cone.getCenter().y(),cone.getCenter().z()); if (!cone.zeroRotation()) { Matrix rotation(cone.computeRotationMatrix()); glMultMatrix(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) { glBegin(GL_TRIANGLE_FAN); glNormal3f(0.0f,0.0f,1.0f); glTexCoord2f(0.5f,0.5f); glVertex3f(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; } // 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); glPopMatrix(); } void DrawShapeVisitor::apply(const InfinitePlane&) { notify(NOTICE)<<"Warning: DrawShapeVisitor::apply(const InfinitePlane& plane) not yet implementated. "<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)]; Vec3 normal = (v2-v1)^(v3-v2); normal.normalize(); glNormal3fv(normal.ptr()); glVertex3fv(v1.ptr()); glVertex3fv(v2.ptr()); glVertex3fv(v3.ptr()); } glEnd(); } } void DrawShapeVisitor::apply(const ConvexHull& hull) { apply((const TriangleMesh&)hull); } void DrawShapeVisitor::apply(const HeightField& field) { if (field.getNumColumns()==0 || field.getNumRows()==0) return; glPushMatrix(); glTranslatef(field.getOrigin().x(),field.getOrigin().y(),field.getOrigin().z()); if (!field.zeroRotation()) { Matrix rotation(field.computeRotationMatrix()); glMultMatrix(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; Vec3 vertTop; Vec3 normTop; Vec3 vertBase; Vec3 normBase; if (field.getSkirtHeight()!=0.0f) { glBegin(GL_QUAD_STRIP); float u = 0.0f; // draw bottom skirt unsigned int col; vertTop.y() = 0.0f; for(col=0;colaccept(*this); } } /////////////////////////////////////////////////////////////////////////////// // // Compute bounding of shape // class ComputeBoundShapeVisitor : public ConstShapeVisitor { public: ComputeBoundShapeVisitor(BoundingBox& bb):_bb(bb) {} virtual void apply(const Sphere&); virtual void apply(const Box&); virtual void apply(const Cone&); virtual void apply(const Cylinder&); virtual void apply(const Capsule&); virtual void apply(const InfinitePlane&); virtual void apply(const TriangleMesh&); virtual void apply(const ConvexHull&); virtual void apply(const HeightField&); virtual void apply(const CompositeShape&); BoundingBox& _bb; }; void ComputeBoundShapeVisitor::apply(const Sphere& sphere) { Vec3 halfLengths(sphere.getRadius(),sphere.getRadius(),sphere.getRadius()); _bb.expandBy(sphere.getCenter()-halfLengths); _bb.expandBy(sphere.getCenter()+halfLengths); } void ComputeBoundShapeVisitor::apply(const Box& box) { if (box.zeroRotation()) { _bb.expandBy(box.getCenter()-box.getHalfLengths()); _bb.expandBy(box.getCenter()+box.getHalfLengths()); } else { float x = box.getHalfLengths().x(); float y = box.getHalfLengths().y(); float z = box.getHalfLengths().z(); Vec3 base_1(Vec3(-x,-y,-z)); Vec3 base_2(Vec3(x,-y,-z)); Vec3 base_3(Vec3(x,y,-z)); Vec3 base_4(Vec3(-x,y,-z)); Vec3 top_1(Vec3(-x,-y,z)); Vec3 top_2(Vec3(x,-y,z)); Vec3 top_3(Vec3(x,y,z)); Vec3 top_4(Vec3(-x,y,z)); Matrix matrix = box.computeRotationMatrix(); _bb.expandBy(box.getCenter()+base_1*matrix); _bb.expandBy(box.getCenter()+base_2*matrix); _bb.expandBy(box.getCenter()+base_3*matrix); _bb.expandBy(box.getCenter()+base_4*matrix); _bb.expandBy(box.getCenter()+top_1*matrix); _bb.expandBy(box.getCenter()+top_2*matrix); _bb.expandBy(box.getCenter()+top_3*matrix); _bb.expandBy(box.getCenter()+top_4*matrix); } } void ComputeBoundShapeVisitor::apply(const Cone& cone) { if (cone.zeroRotation()) { _bb.expandBy(cone.getCenter()+Vec3(-cone.getRadius(),-cone.getRadius(),cone.getBaseOffset())); _bb.expandBy(cone.getCenter()+Vec3(cone.getRadius(),cone.getRadius(),cone.getHeight()+cone.getBaseOffset())); } else { Vec3 top(Vec3(cone.getRadius(),cone.getRadius(),cone.getHeight()+cone.getBaseOffset())); Vec3 base_1(Vec3(-cone.getRadius(),-cone.getRadius(),cone.getBaseOffset())); Vec3 base_2(Vec3(cone.getRadius(),-cone.getRadius(),cone.getBaseOffset())); Vec3 base_3(Vec3(cone.getRadius(),cone.getRadius(),cone.getBaseOffset())); Vec3 base_4(Vec3(-cone.getRadius(),cone.getRadius(),cone.getBaseOffset())); Matrix matrix = cone.computeRotationMatrix(); _bb.expandBy(cone.getCenter()+base_1*matrix); _bb.expandBy(cone.getCenter()+base_2*matrix); _bb.expandBy(cone.getCenter()+base_3*matrix); _bb.expandBy(cone.getCenter()+base_4*matrix); _bb.expandBy(cone.getCenter()+top*matrix); } } void ComputeBoundShapeVisitor::apply(const Cylinder& cylinder) { if (cylinder.zeroRotation()) { Vec3 halfLengths(cylinder.getRadius(),cylinder.getRadius(),cylinder.getHeight()*0.5f); _bb.expandBy(cylinder.getCenter()-halfLengths); _bb.expandBy(cylinder.getCenter()+halfLengths); } else { float r = cylinder.getRadius(); float z = cylinder.getHeight()*0.5f; Vec3 base_1(Vec3(-r,-r,-z)); Vec3 base_2(Vec3(r,-r,-z)); Vec3 base_3(Vec3(r,r,-z)); Vec3 base_4(Vec3(-r,r,-z)); Vec3 top_1(Vec3(-r,-r,z)); Vec3 top_2(Vec3(r,-r,z)); Vec3 top_3(Vec3(r,r,z)); Vec3 top_4(Vec3(-r,r,z)); Matrix matrix = cylinder.computeRotationMatrix(); _bb.expandBy(cylinder.getCenter()+base_1*matrix); _bb.expandBy(cylinder.getCenter()+base_2*matrix); _bb.expandBy(cylinder.getCenter()+base_3*matrix); _bb.expandBy(cylinder.getCenter()+base_4*matrix); _bb.expandBy(cylinder.getCenter()+top_1*matrix); _bb.expandBy(cylinder.getCenter()+top_2*matrix); _bb.expandBy(cylinder.getCenter()+top_3*matrix); _bb.expandBy(cylinder.getCenter()+top_4*matrix); } } void ComputeBoundShapeVisitor::apply(const Capsule& capsule) { if (capsule.zeroRotation()) { Vec3 halfLengths(capsule.getRadius(),capsule.getRadius(),capsule.getHeight()*0.5f + capsule.getRadius()); _bb.expandBy(capsule.getCenter()-halfLengths); _bb.expandBy(capsule.getCenter()+halfLengths); } else { float r = capsule.getRadius(); float z = capsule.getHeight()*0.5f + capsule.getRadius(); Vec3 base_1(Vec3(-r,-r,-z)); Vec3 base_2(Vec3(r,-r,-z)); Vec3 base_3(Vec3(r,r,-z)); Vec3 base_4(Vec3(-r,r,-z)); Vec3 top_1(Vec3(-r,-r,z)); Vec3 top_2(Vec3(r,-r,z)); Vec3 top_3(Vec3(r,r,z)); Vec3 top_4(Vec3(-r,r,z)); Matrix matrix = capsule.computeRotationMatrix(); _bb.expandBy(capsule.getCenter()+base_1*matrix); _bb.expandBy(capsule.getCenter()+base_2*matrix); _bb.expandBy(capsule.getCenter()+base_3*matrix); _bb.expandBy(capsule.getCenter()+base_4*matrix); _bb.expandBy(capsule.getCenter()+top_1*matrix); _bb.expandBy(capsule.getCenter()+top_2*matrix); _bb.expandBy(capsule.getCenter()+top_3*matrix); _bb.expandBy(capsule.getCenter()+top_4*matrix); } } void ComputeBoundShapeVisitor::apply(const InfinitePlane&) { // can't compute the bounding box of an infinite plane!!! :-) } void ComputeBoundShapeVisitor::apply(const TriangleMesh& mesh) { const Vec3Array* vertices = mesh.getVertices(); const IndexArray* indices = mesh.getIndices(); if (vertices && indices) { for(unsigned int i=0;igetNumElements();++i) { const osg::Vec3& v=(*vertices)[indices->index(i)]; _bb.expandBy(v); } } } void ComputeBoundShapeVisitor::apply(const ConvexHull& hull) { apply((const TriangleMesh&)hull); } void ComputeBoundShapeVisitor::apply(const HeightField& field) { float zMin=FLT_MAX; float zMax=-FLT_MAX; for(unsigned int row=0;rowzMax) zMax = z; } } if (zMin>zMax) { // no valid entries so don't reset the bounding box return; } if (field.zeroRotation()) { _bb.expandBy(field.getOrigin()+osg::Vec3(0.0f,0.0f,zMin)); _bb.expandBy(field.getOrigin()+osg::Vec3(field.getXInterval()*(field.getNumColumns()-1),field.getYInterval()*(field.getNumRows()-1),zMax)); } else { float x = field.getXInterval()*(field.getNumColumns()-1); float y = field.getYInterval()*(field.getNumRows()-1); Vec3 base_1(Vec3(0,0,zMin)); Vec3 base_2(Vec3(x,0,zMin)); Vec3 base_3(Vec3(x,y,zMin)); Vec3 base_4(Vec3(0,y,zMin)); Vec3 top_1(Vec3(0,0,zMax)); Vec3 top_2(Vec3(x,0,zMax)); Vec3 top_3(Vec3(x,y,zMax)); Vec3 top_4(Vec3(0,y,zMax)); Matrix matrix = field.computeRotationMatrix(); _bb.expandBy(field.getOrigin()+base_1*matrix); _bb.expandBy(field.getOrigin()+base_2*matrix); _bb.expandBy(field.getOrigin()+base_3*matrix); _bb.expandBy(field.getOrigin()+base_4*matrix); _bb.expandBy(field.getOrigin()+top_1*matrix); _bb.expandBy(field.getOrigin()+top_2*matrix); _bb.expandBy(field.getOrigin()+top_3*matrix); _bb.expandBy(field.getOrigin()+top_4*matrix); } } void ComputeBoundShapeVisitor::apply(const CompositeShape& group) { for(unsigned int i=0;iaccept(*this); } } /////////////////////////////////////////////////////////////////////////////// // // Accept a primtive functor for each of the shapes. // class PrimitiveShapeVisitor : public ConstShapeVisitor { public: PrimitiveShapeVisitor(PrimitiveFunctor& functor,const TessellationHints* hints): _functor(functor), _hints(hints) {} virtual void apply(const Sphere&); virtual void apply(const Box&); virtual void apply(const Cone&); virtual void apply(const Cylinder&); virtual void apply(const Capsule&); virtual void apply(const InfinitePlane&); virtual void apply(const TriangleMesh&); virtual void apply(const ConvexHull&); virtual void apply(const HeightField&); virtual void apply(const CompositeShape&); PrimitiveFunctor& _functor; const TessellationHints* _hints; }; void PrimitiveShapeVisitor::apply(const Sphere& sphere) { float tx = sphere.getCenter().x(); float ty = sphere.getCenter().y(); float tz = sphere.getCenter().z(); unsigned int numSegments = 40; unsigned int numRows = 20; 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; float lBase=-osg::PI*0.5f; float rBase=0.0f; float zBase=-sphere.getRadius(); float vBase=0.0f; for(unsigned int rowi=0; rowigetNumElements();i+=3) { _functor.vertex((*vertices)[indices->index(i)]); _functor.vertex((*vertices)[indices->index(i+1)]); _functor.vertex((*vertices)[indices->index(i+2)]); } _functor.end(); } } void PrimitiveShapeVisitor::apply(const ConvexHull& hull) { apply((const TriangleMesh&)hull); } void PrimitiveShapeVisitor::apply(const HeightField& field) { if (field.getNumColumns()==0 || field.getNumRows()==0) return; Matrix matrix = field.computeRotationMatrix(); matrix.setTrans(field.getOrigin()); float dx = field.getXInterval(); float dy = field.getYInterval(); for(unsigned int row=0;rowaccept(*this); } } /////////////////////////////////////////////////////////////////////////////// // // ShapeDrawable itself.. // ShapeDrawable::ShapeDrawable(): _color(1.0f,1.0f,1.0f,1.0f) { } ShapeDrawable::ShapeDrawable(Shape* shape,TessellationHints* hints): _color(1.0f,1.0f,1.0f,1.0f), _tessellationHints(hints) { setShape(shape); } ShapeDrawable::ShapeDrawable(const ShapeDrawable& pg,const CopyOp& copyop): Drawable(pg,copyop), _color(pg._color), _tessellationHints(pg._tessellationHints) { } ShapeDrawable::~ShapeDrawable() { } void ShapeDrawable::drawImplementation(State& state) const { if (_shape.valid()) { glColor4fv(_color.ptr()); DrawShapeVisitor dsv(state,_tessellationHints.get()); _shape->accept(dsv); } } void ShapeDrawable::accept(ConstAttributeFunctor&) const { } void ShapeDrawable::accept(PrimitiveFunctor& pf) const { if (_shape.valid()) { PrimitiveShapeVisitor psv(pf,_tessellationHints.get()); _shape->accept(psv); } } bool ShapeDrawable::computeBound() const { _bbox.init(); if (_shape.valid()) { ComputeBoundShapeVisitor cbsv(_bbox); _shape->accept(cbsv); _bbox_computed = true; return true; } return false; }