Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

OSGExtrusionGeometry.cpp

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------------*\
00002  *                                OpenSG                                     *
00003  *                                                                           *
00004  *                                                                           *
00005  *           Copyright (C) 2000,2001,2002 by the OpenSG Forum                *
00006  *                                                                           *
00007  *                            www.opensg.org                                 *
00008  *                                                                           *
00009  *   contact: dirk@opensg.org, gerrit.voss@vossg.org, jbehr@zgdv.de          *
00010  *                                                                           *
00011 \*---------------------------------------------------------------------------*/
00012 /*---------------------------------------------------------------------------*\
00013  *                                License                                    *
00014  *                                                                           *
00015  * This library is free software; you can redistribute it and/or modify it   *
00016  * under the terms of the GNU Library General Public License as published    *
00017  * by the Free Software Foundation, version 2.                               *
00018  *                                                                           *
00019  * This library is distributed in the hope that it will be useful, but       *
00020  * WITHOUT ANY WARRANTY; without even the implied warranty of                *
00021  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU         *
00022  * Library General Public License for more details.                          *
00023  *                                                                           *
00024  * You should have received a copy of the GNU Library General Public         *
00025  * License along with this library; if not, write to the Free Software       *
00026  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                 *
00027  *                                                                           *
00028 \*---------------------------------------------------------------------------*/
00029 /*---------------------------------------------------------------------------*\
00030  *                                Changes                                    *
00031  *                                                                           *
00032  *                                                                           *
00033  *                                                                           *
00034  *                                                                           *
00035  *                                                                           *
00036  *                                                                           *
00037 \*---------------------------------------------------------------------------*/
00038 
00039 //-----------------------------------------------------------------------------
00040 //                                 Includes                                    
00041 //-----------------------------------------------------------------------------
00042 
00043 
00044 #include <memory>
00045 #include <assert.h>
00046 
00047 #include "OSGConfig.h"
00048 #include <OSGLog.h>
00049 
00050 #include "OSGExtrusionGeometry.h"
00051 
00052 // #define DEBUG_VERTEX_NORMALS
00053 // #define DEBUG_FACE_NORMALS
00054 
00055 
00056 OSG_USING_NAMESPACE
00057 
00058 
00059 /*---------------------------------------------------------------------*/
00105 //-----------------------------------------------------------------------------
00106 //                           Forward Declarations                              
00107 //-----------------------------------------------------------------------------
00108 template <class VectorTypeT>
00109 void subdivide(const typename std::vector<VectorTypeT> &dataIn,
00110                      typename std::vector<VectorTypeT> *dataOut,
00111                      bool closed);
00112 
00113 template <class VectorTypeT>
00114 void subdivide(const typename std::vector<VectorTypeT> &dataIn,
00115                      typename std::vector<VectorTypeT> *dataOut,
00116                      UInt32 nTimes,
00117                      bool close);
00118 
00119 
00120 //-----------------------------------------------------------------------------
00121 // Constructor
00122 //
00123 // Author: afischle
00124 //-----------------------------------------------------------------------------
00125 ExtrusionSurface::ExtrusionSurface(const std::vector<Pnt2f> &crossSection,
00126                                    const std::vector<Quaternion> &orientation,
00127                                    const std::vector<Vec2f> &scale,
00128                                    const std::vector<Pnt3f> &spine,
00129                                    Real32 creaseAngle,
00130                                    bool beginCap,
00131                                    bool endCap,
00132                                    bool ccw,
00133                                    bool convex,
00134                                    bool buildNormal,
00135                                    bool buildTexCoord) :
00136 
00137     _creaseAngle(creaseAngle),
00138     _beginCap(beginCap),
00139     _endCap(endCap),
00140     _ccw(ccw),
00141     _convex(convex),
00142     _createNormals(buildNormal),
00143     _createTexCoords(buildTexCoord),
00144     _primitiveCount(0),
00145     _vertexCount(0),
00146     _totalVertexCount(0)
00147 {
00148     // initialize cross section
00149     if(!crossSection.empty()) 
00150     {
00151         // copy argument
00152         _crossSection.assign(crossSection.begin(), crossSection.end());
00153     }
00154     else
00155     {
00156         // copy default
00157         FDEBUG(("OSGExtrusion: Empty cross section. Using default.\n"));
00158         for(UInt32 i = 0; i < DEF_N_CROSS_SECTION_POINTS; i++)
00159             _crossSection.push_back(Pnt2f(DEF_CROSS_SECTION[i]));
00160     }
00161 
00162     // initialize orientations
00163     if(!orientation.empty())
00164     {
00165         // copy argument
00166         _orientation.assign(orientation.begin(), orientation.end());
00167     }
00168     else
00169     {
00170         // copy default
00171         FDEBUG(("OSGExtrusion: Empty orientation. Using default.\n"));
00172         for(UInt32 i = 0; i < DEF_N_ORIENTATION_PARAMS; i++)
00173             _orientation.push_back(Quaternion(Vec3f(DEF_ORIENTATION[i]),
00174                                               DEF_ORIENTATION[i][3]));
00175     }
00176     
00177     // initialize scale parameters
00178     if(!scale.empty())
00179     {
00180         // copy argument
00181         _scale.assign(scale.begin(), scale.end());
00182     }
00183     else
00184     {
00185         // copy default
00186         FDEBUG(("OSGExtrusion: Empty scale parameter. Using default.\n"));
00187         for(UInt32 i = 0; i < DEF_N_SCALE_PARAMS; i++)
00188             _scale.push_back(Vec2f(DEF_SCALE[i]));
00189     }
00190 
00191 
00192     // initialize spine
00193     if(!spine.empty())
00194     {
00195         // copy argument
00196         _spine.assign(spine.begin(), spine.end());
00197     }
00198     else
00199     {
00200         // copy default
00201         FDEBUG(("OSGExtrusion: Empty spine. Using default.\n"));
00202         for(UInt32 i = 0; i < DEF_N_SPINE_POINTS; i++)
00203             _spine.push_back(Pnt3f(DEF_SPINE[i]));
00204     }
00205 }
00206 
00207 
00208 //-----------------------------------------------------------------------------
00209 // Returns true if the member fields describe a valid extrusion surface and
00210 // false otherwise.
00211 // 
00212 // Author: afischle
00213 //-----------------------------------------------------------------------------
00214 bool ExtrusionSurface::verifyInput(void)
00215 {
00216     if(_crossSection.size() < 2)
00217     {
00218         FWARNING(("OSGExtrusion:checkInput: Cross section is degenerate\n"));
00219         return false;
00220     }
00221 
00222     if(_spine.size() < 2)
00223     {
00224         FWARNING(("OSGExtrusion:checkInput: spine is degenerate\n"));
00225         return false;
00226     }
00227 
00228     return true;
00229 }
00230 
00231 
00232 //-----------------------------------------------------------------------------
00233 // Determines the topology of the sweep surface and sets the topology member
00234 // variables accordingly.
00235 //
00236 // Author: afischle
00237 //-----------------------------------------------------------------------------
00238 void ExtrusionSurface::determineTopology(void)
00239 {
00240     // compute vector differences between start and end points
00241     Vec3f sDiff = _spine.back() - _spine.front();
00242     Vec2f cDiff = _crossSection.back() - _crossSection.front();
00243 
00244     // determine whether the curves are closed
00245     _spineClosed = (sDiff.length() < Eps) ? true : false;
00246     _crossSectionClosed = (cDiff.length() < Eps) ? true : false;
00247     
00248     FDEBUG(("OSGExtrusion:checkInput: Topology: (Spine: %s / Cross-section: %s)\n", 
00249             (_spineClosed ? "Closed" : "Open"),
00250             (_crossSectionClosed ? "Closed" : "Open")));
00251 }
00252 
00253 
00254 //-----------------------------------------------------------------------------
00255 // Refines the cross-section n times using a simple subdivision scheme.
00256 // 
00257 // Author: afischle
00258 //-----------------------------------------------------------------------------
00259 void ExtrusionSurface::refineCrossSection(UInt32 nTimes)
00260 {
00261     FDEBUG(("OSGExtrusion:refineCrossSection: Refinining %d times\n",nTimes));
00262     std::vector<Vec2f> output;
00263 
00264     // cast <Pnt2f> to <Vec2f> because we need the vector interface
00265     std::vector<Vec2f> & csVec2f ((std::vector<Vec2f>&)(_crossSection));
00266     subdivide(csVec2f, &output, nTimes, _crossSectionClosed);
00267 
00268     // swap output data into _crossSection
00269     output.swap(csVec2f);
00270 }
00271 
00272 //-----------------------------------------------------------------------------
00273 // Refines the spine n times using a simple subdivision scheme.
00274 // 
00275 // Author: afischle
00276 //-----------------------------------------------------------------------------
00277 void ExtrusionSurface::refineSpine(UInt32 nTimes)
00278 {
00279     FDEBUG(("OSGExtrusion:refineSpine: Refinining %d times\n",nTimes));
00280 
00281     std::vector<Vec3f> output;
00282 
00283     // cast <Pnt3f> to <Vec3f> because we need the vector interface    
00284     std::vector<Vec3f> & spVec3f ((std::vector<Vec3f>&)(_spine));
00285     subdivide<Vec3f>(spVec3f, &output, nTimes, _spineClosed);
00286 
00287     // swap output data into _spine
00288     output.swap(spVec3f);
00289 }
00290 
00291 
00292 //-----------------------------------------------------------------------------
00293 // Refines the scale parameters n times using a simple subdivision scheme.
00294 // 
00295 // Author: afischle
00296 //-----------------------------------------------------------------------------
00297 void ExtrusionSurface::refineScale(UInt32 nTimes)
00298 {
00299     FDEBUG(("OSGExtrusion:refineScale: Refinining %d times\n",nTimes));
00300 
00301     std::vector<Vec2f> output;
00302     subdivide<Vec2f>(_scale, &output, nTimes, _spineClosed);
00303 
00304     // swap output data into _scale
00305     output.swap(_scale);
00306 }
00307 
00308 
00309 //-----------------------------------------------------------------------------
00310 // Refines the scale parameters n times using a simple subdivision scheme.
00311 //
00312 // Todo: Not implemented yet
00313 //
00314 // Author: afischle
00315 //-----------------------------------------------------------------------------
00316 void ExtrusionSurface::refineOrientation(UInt32 nTimes)
00317 {
00318     FFATAL(("OSGExtrusion:refineOrientation: Not Implemented\n"));
00319 }
00320 
00321 
00322 //-----------------------------------------------------------------------------
00323 // Calculates the y-axes of the base transform from the cross section plane
00324 // to the spine-aligned cross section plane (SCP). The y- and z-axes are
00325 // expected to be already stored in the _transform vector.
00326 //
00327 // The computed x-axes are stored in the _transform vector.
00328 // 
00329 // Author: afischle
00330 //-----------------------------------------------------------------------------
00331 void ExtrusionSurface::calcXAxes(void)
00332 {
00333     std::vector<Matrix>::iterator tIt = _transform.begin();
00334 
00335     // Either of the two cases below implies the cross product of
00336     // the y- and the z-axis vanish (and so do all x-axes)
00337     if(!_spineCollinear && !_revolutionSurface)
00338     {
00339         for(; tIt != _transform.end(); ++tIt)
00340         {
00341             Vec3f xAxis = calcXAxis(tIt);
00342             (*tIt)[0].setValue(xAxis);
00343         }
00344     }
00345     else
00346     {
00347         (*tIt)[0].setValue(Vec3f(0.f, 0.f, 0.f));
00348     }
00349 }
00350 
00351 
00352 //-----------------------------------------------------------------------------
00353 // Calculates the y-axes of the base transform from the cross-section plane
00354 // to the spine-aligned cross section plane (SCP). If no y-axis could be
00355 // defined it sets the _revolutionSurface flag to true (and false otherwise.)
00356 //
00357 // The computed y-axes are stored in the _transform vector.
00358 //
00359 // Author: afischle
00360 //-----------------------------------------------------------------------------
00361 void ExtrusionSurface::calcYAxes(void)
00362 {
00363     bool yAxisDefined = false;
00364 
00365     std::vector<Pnt3f>::const_iterator  spineIt = _spine.begin();
00366     std::vector<Matrix>::iterator tIt = _transform.begin();
00367     for(; spineIt != _spine.end(); ++spineIt, ++tIt)
00368     {
00369         Vec3f yAxis = calcNonUnitYAxis(spineIt);
00370         if(yAxis.length() > Eps) // we have found a valid tangent
00371         {
00372             yAxis.normalize();
00373             if(!yAxisDefined)
00374             {
00375                 FDEBUG(("OSGExtrusion:calcYAxes: yAxis is defined\n"));
00376                 yAxisDefined = true;
00377 
00378                 // assign found y-axis to all transforms
00379                 // up to and including the current one
00380                 std::vector<Matrix>::iterator yIt;
00381                 for(yIt = _transform.begin(); yIt != (tIt + 1); ++yIt)
00382                     (*yIt)[1].setValue(yAxis);
00383             }
00384             else 
00385             {
00386                 // assign found y-axis to the current transform only
00387                 (*tIt)[1].setValue(yAxis);
00388             }
00389         }
00390         else if(yAxisDefined)
00391         {
00392             // reuse y-axis determined during last step
00393             (*tIt)[1] = (*(tIt - 1))[1];
00394         }
00395     }
00396 
00397     // all spine points coincided => surface of revolution
00398     _revolutionSurface = !yAxisDefined;
00399 }
00400 
00401 //-----------------------------------------------------------------------------
00402 // Calculates the z-axes of the base transform from the cross-section plane (CP)
00403 // to the spine-aligned cross section plane (SCP). If no z-axis could be
00404 // defined it sets the _spineCollinear flag to true (and false otherwise.)
00405 //
00406 // The computed z-axes are stored in the _transform vector.
00407 //
00408 // Author: afischle
00409 //-----------------------------------------------------------------------------
00410 void ExtrusionSurface::calcZAxes(void)
00411 {
00412     bool zAxisDefined = false;
00413     
00414     std::vector<Pnt3f>::const_iterator  spineIt = _spine.begin();
00415     std::vector<Matrix>::iterator tIt = _transform.begin();
00416     for(; spineIt != _spine.end(); ++spineIt, ++tIt)
00417     {
00418         Vec3f zAxis = calcNonUnitZAxis(spineIt);
00419         if(zAxis.length() > Eps)
00420         {
00421             zAxis.normalize();
00422             if(!zAxisDefined)
00423             {
00424                 FDEBUG(("OSGExtrusion:calcZAxes: zAxis is defined\n"));
00425                 zAxisDefined = true;
00426                 
00427                 // set the current z-Axis for the current transform
00428                 (*tIt)[2].setValue(zAxis);
00429 
00430                 // assign found z-axis to all previous transforms
00431                 std::vector<Matrix>::iterator zIt;
00432                 for(zIt = _transform.begin(); zIt != tIt; ++zIt)
00433                 {
00434                     (*zIt)[2].setValue(zAxis);
00435                 }
00436             }
00437             else // assign found z-axis to this basis _only_
00438             {
00439                 (*tIt)[2].setValue(zAxis);
00440 
00441                 // as the zAxis is defined tIt - 1 contains a valid value 
00442                 Vec3f prevZAxis((*(tIt - 1))[2][0],
00443                                 (*(tIt - 1))[2][1],
00444                                 (*(tIt - 1))[2][2]);
00445 
00446                 // check whether the z-axis needs to be flipped
00447                 if(prevZAxis.dot(zAxis) + Eps < 0.f)
00448                 {
00449                     FDEBUG(("OSGExtrusion:calcZAxes: Flipping z-axis\n"));
00450                     zAxis.negate();
00451                 }
00452 
00453                 // assign zAxis to current transform
00454                 (*tIt)[2].setValue(zAxis);
00455             }
00456         }
00457         else if(zAxisDefined)
00458         {
00459             // reuse previous value
00460             (*tIt)[2] = (*(tIt - 1))[2];
00461         }
00462     } // end for
00463 
00464     // All cross products vanished => spine has no non-collinear part
00465     // and is thus composed of collinear parts and revolutions only
00466     _spineCollinear = !zAxisDefined;
00467 }
00468 
00469 //-----------------------------------------------------------------------------
00470 // Calculates the affine basis transformations from the cross-section plane
00471 // (i.e. the xz-plane) to the spine-aligne cross-section plane (SCP) and
00472 // determines whether:
00473 //
00474 // 1. the surface has a collinear spine,
00475 // 2. the surface is a pure surface of revolution
00476 // 3. the surface is not of one of the above special types.
00477 //
00478 // Author: afischle
00479 //-----------------------------------------------------------------------------
00480 void ExtrusionSurface::calcTransforms(void)
00481 {
00482     _transform.clear();
00483 
00484     // initialize the transformations with Id
00485     _transform.resize(_spine.size());
00486 
00487     /* X3D Spec:
00488      * The SCP is computed by first computing its Y-axis and Z-axis, then 
00489      * taking the cross product of these to determine the X-axis. These three 
00490      * axes are then used to determine the rotation value needed to rotate the
00491      * Y=0 plane to the SCP. This results in a plane that is the approximate 
00492      * tangent of the spine at each point, as shown in Figure 13.5.
00493      */
00494     calcZAxes(); // sets bool _spineCollinear
00495     calcYAxes(); // sets bool _revolutionSurface
00496 
00497     // We need to make a difference between surfaces having at least
00498     // one non-trivial collinear part in their spine and surfaces created
00499     // by pure revolution of the crossection.
00500     if(_revolutionSurface)
00501     {
00502         _spineCollinear = false;
00503     }
00504     calcXAxes(); // uses _spineCollinear and _revolutionSurface
00505 
00506     /* X3D Spec:
00507      *
00508      * If the entire spine is collinear, the SCP is computed by finding the 
00509      * rotation of a vector along the positive Y-axis (v1) to the vector formed
00510      * by the spine points (v2). The Y=0 plane is then rotated by this value.
00511      *
00512      * If two points are coincident, they both have the same SCP. If each point
00513      * has a different orientation value, then the surface is constructed by 
00514      * connecting edges of the cross-sections as normal. This is useful in 
00515      * creating revolved surfaces.
00516      */
00517     Matrix rotation; // m = Id and thus ok for revolution case
00518     if(_spineCollinear && !_revolutionSurface)
00519     {
00520         // calculate the rotation that rotates the y unit vector to the
00521         // direction of the spine.
00522         Vec3f v1(0.f, 1.f, 0.f);
00523         Vec3f v2(_transform[0][1]);
00524             
00525         Quaternion q;
00526         q.setValue(v1, v2);
00527         q.getValue(rotation);
00528     }
00529 
00530     std::vector<Matrix>::iterator tIt             = _transform.begin();
00531     std::vector<Pnt3f>::const_iterator spineIt    = _spine.begin();
00532     std::vector<Vec2f>::const_iterator scaleIt    = _scale.begin(); 
00533     std::vector<Quaternion>::const_iterator rotIt = _orientation.begin();
00534 
00535     // calculate an affine transformation for the cross-section at
00536     // each spine point
00537     for(; spineIt != _spine.end(); ++spineIt, ++tIt)
00538     {
00539         // special cases
00540         if(_spineCollinear || _revolutionSurface)
00541         {
00542             // defines the x,y and z-axes in this case
00543             *tIt = rotation;
00544         }        
00545 
00546         // setup transform of the CP
00547         Matrix cSPlaneTransform;
00548 
00549         // Scale the x- and z-axes
00550         cSPlaneTransform[0] *= (*scaleIt)[0];
00551         cSPlaneTransform[2] *= (*scaleIt)[1];
00552 
00553         // apply orientation
00554         Matrix orientationMatrix;
00555         rotIt->getValue(orientationMatrix);
00556         cSPlaneTransform.multLeft(orientationMatrix);
00557 
00558         tIt->mult(cSPlaneTransform);
00559 
00560         // affine translation to the spine point
00561         tIt->setTranslate(*spineIt);
00562         
00563         // The affine transformation *tIt should now
00564         //
00565         //   1. scale the CP
00566         //   2. apply the orientation to the CP        
00567         //   2. translate the origin to spine[i]
00568         //
00569         // in exactly this order.
00570 
00571         /* X3D Spec quotation:
00572          *
00573          * If the number of scale or orientation values is greater than the
00574          * number of spine points, the excess values are ignored. If they 
00575          * contain one value, it is applied at all spine points. The 
00576          * results are undefined if the number of scale or orientation 
00577          * values is greater than one but less than the number of spine 
00578          * points. The scale values shall be positive.
00579          */
00580         
00581         // always use last available value
00582         if(rotIt + 1   != _orientation.end()) { ++rotIt;   }
00583         if(scaleIt + 1 != _scale.end())       { ++scaleIt; }
00584     }
00585 
00586     FDEBUG(("OSGExtrusion:calcTransforms: spine collinear: %s\n", 
00587             (_spineCollinear ? "true" : "false")));
00588 
00589     FDEBUG(("OSGExtrusion:calcTransforms: revolution surface: %s\n",
00590             (_revolutionSurface ? "true" : "false")));
00591 }
00592 
00593 
00594 //-----------------------------------------------------------------------------
00595 // Initializes the vertex grid with _spine.size() rows and _crossSection.size()
00596 // columns.
00597 //
00598 // Author: afischle
00599 //-----------------------------------------------------------------------------
00600 void ExtrusionSurface::initGrid(void)
00601 {
00602     FDEBUG(("OSGExtrusion:initGrid: Initializing vertex grid\n"));
00603     
00604     _grid.clear();
00605     _grid.resize(_spine.size());
00606 
00607     VertexGrid::iterator gridRowIt = _grid.begin();
00608     for(Int32 i = 0; i < _spine.size(); i++, ++gridRowIt)
00609     {
00610         gridRowIt->resize(_crossSection.size());
00611     }
00612 }
00613 
00614 
00615 //-----------------------------------------------------------------------------
00616 // Calculates the vertex positions of the sweep surface by transforming the 
00617 // cross-section using the _transforms member field.
00618 //
00619 // The calculated positions are stored in the position components of the
00620 // vertices in the _grid.
00621 //
00622 // Author: afischle
00623 //-----------------------------------------------------------------------------
00624 void ExtrusionSurface::calcSweepSurfacePositions(void)
00625 {
00626     FDEBUG(("OSGExtrusion:calcSweepSurfacePositions: Calculation vertex positions\n"));
00627 
00628     if(_grid.size() < _spine.size())
00629     {
00630         FFATAL(("OSGExtrusion:calcSweepSurfacePositions: Grid not initialized\n"));
00631         return;
00632     }
00633 
00634     _posMap.clear();
00635 
00636     // compute transformation matrices
00637     calcTransforms();
00638 
00639     if(_grid.size() < _spine.size()) // grid not yet or incorrectly initialized
00640     {
00641         initGrid();
00642     }
00643 
00644     std::vector<Matrix>::iterator transIt = _transform.begin();
00645     VertexGrid::iterator gridRowIt = _grid.begin();
00646 
00647     // iterate over the spine points
00648     for(Int32 i = 0; i < _spine.size(); i++, ++gridRowIt)
00649     {
00650         if(transIt->det3() < Eps) // degenerate transform
00651         {
00652             FWARNING(("OSGExtrusion:calcSurfacePositions: Degenerate transformation matrix\n"));
00653             SWARNING << "Degenerate transform[" << i << "]:\n "
00654                      << *transIt << std::endl;
00655         }
00656 
00657         std::vector<Vertex>::iterator gridColIt = gridRowIt->begin();
00658         std::vector<Pnt2f>::iterator crossIt;
00659 
00660         // iterate over the cross-section curve
00661         for(crossIt = _crossSection.begin(); crossIt != _crossSection.end();
00662             ++crossIt, ++gridColIt)
00663         {
00664             Pnt3f p = Pnt3f((*crossIt)[0], 0.f, (*crossIt)[1]);
00665 
00666             // apply "CP to SCP"-transform
00667             transIt->multMatrixPnt(p, gridColIt->position);
00668         }
00669 
00670         // always use last defined transformation matrix
00671         if(transIt + 1 != _transform.end()) { ++transIt; }
00672     }
00673 }
00674 
00675 
00676 //-----------------------------------------------------------------------------
00677 // Calculates the texture coordinates of the sweep surface. The texture coords
00678 // are obtained by measuring the lengths along the row- and column-curves
00679 // of the grid and renormalizing them to the usual [0, 1]-range. The grid
00680 // is expected to already hold the position data of the sweep surface.
00681 //
00682 // The calculated texture coordinates are stored in the _grid.
00683 //
00684 // Author: afischle
00685 //-----------------------------------------------------------------------------
00686 void ExtrusionSurface::calcSweepSurfaceTexCoords(void)
00687 {
00688     FDEBUG(("OSGExtrusion:calcSweepSurfaceTexCoords: Calculating texture coordinates\n"));
00689     if(_grid.size() < _spine.size())
00690     {
00691         FFATAL(("OSGExtrusion:calcSweepSurfaceTexCoords: Grid not initialized\n"));
00692         return;
00693     }
00694     _texCoordMap.clear();
00695     
00696     /* X3D Spec:
00697      *
00698      *  Textures are mapped so that the coordinates range in the U direction 
00699      *  from 0 to 1 along the crossSection curve (with 0 corresponding to the 
00700      *  first point in crossSection and 1 to the last) and in the V direction
00701      *  from 0 to 1 along the spine curve (with 0 corresponding to the first 
00702      *  listed spine point and 1 to the last) 
00703      */
00704 
00705     // accumulate distances along u and v directions
00706     for(UInt32 i = 0; i < _spine.size(); ++i)
00707     {
00708         for(UInt32 j = 0; j < _crossSection.size(); ++j)
00709         {
00710             Real32 u,v;
00711             UInt32 iPrev = (i == 0) ? 0 : i - 1;
00712             UInt32 jPrev = (j == 0) ? 0 : j - 1;
00713 
00714             // contains length of i-th column curve up to the (j-1)-th point
00715             u = _grid[i][jPrev].texCoord[0];
00716             // add distance to j-th point
00717             u += _grid[i][j].position.dist(_grid[i][jPrev].position);
00718 
00719             // contains length of j-th row curve up to the (i-1)-th point
00720             v = _grid[iPrev][j].texCoord[1];
00721             // add distance to i-th point
00722             v += _grid[i][j].position.dist(_grid[iPrev][j].position);
00723 
00724             _grid[i][j].texCoord[0] = u;
00725             _grid[i][j].texCoord[1] = v;
00726         }
00727     }
00728 
00729     // renormalize distances to [0,1]x[0,1]
00730     for(UInt32 i = 0; i < _spine.size(); ++i)
00731     {
00732         for(UInt32 j = 0; j < _crossSection.size(); ++j)
00733         {
00734             // length is maximal in the row/column curve end points
00735             Real32 uMax = _grid[i][_crossSection.size() - 1].texCoord[0];
00736             Real32 vMax = _grid[_spine.size() - 1][j].texCoord[1];
00737 
00738             if(uMax > Eps)
00739                 _grid[i][j].texCoord[0] /= uMax;
00740 
00741             if(vMax > Eps)
00742                 _grid[i][j].texCoord[1] /= vMax;
00743         }
00744     }
00745 }
00746 
00747 
00748 void ExtrusionSurface::calcSweepSurfaceFaceNormals(void)
00749 {
00750     FDEBUG(("OSGExtrusion:calcSweepSurfaceFaceNormals: Calculating sweep surface face normals\n"));
00751 
00752     if(_grid.size() < _spine.size())
00753     {
00754         FFATAL(("OSGExtrusion:calcSweepSurfaceFaceNormals: Grid not initialized\n"));
00755         return;
00756     }
00757     
00758     // calculate face normals
00759     for(UInt32 i = 0; i < _spine.size(); ++i)
00760     {
00761         UInt32 iPrev, iNext;
00762 
00763         // determine adjacent row indices
00764         if(_spineClosed)
00765         {
00766             // first point == last point
00767             iPrev = (i != 0) ? i - 1 : _spine.size() - 2;
00768             iNext = (i != _spine.size() - 1) ? i + 1 : 1;
00769         }
00770         else
00771         {
00772             iPrev = (i != 0) ? i - 1 : 0;
00773             iNext = (i != _spine.size() - 1) ? i + 1 : i;
00774         }
00775 
00776         for(UInt32 j = 0; j < _crossSection.size(); ++j)
00777         {
00778             Vec3f n;
00779             UInt32 jPrev, jNext;
00780 
00781             // determine adjacent column indices
00782             if(_crossSectionClosed)
00783             {
00784                 // first point == last point
00785                 jPrev = (j != 0) ? j - 1 : _crossSection.size() - 2;
00786                 jNext = (j != _crossSection.size() - 1) ? j + 1 : 1;
00787             }
00788             else
00789             {
00790                 jPrev = (j != 0) ? j - 1 : 0;
00791                 jNext = (j != _crossSection.size() - 1) ? j + 1 : j;
00792             }
00793 
00794             // upper right face normal (1st quadrant)
00795             _grid[i][j].adjFaceNormals[0] =
00796                 calcQuadFaceNormal(_grid[i][j].position,
00797                                    _grid[i][jNext].position,
00798                                    _grid[iNext][jNext].position,
00799                                    _grid[iNext][j].position);
00800 
00801 
00802             // upper left face normal (2nd quadrant)
00803             _grid[i][j].adjFaceNormals[1] = 
00804                 calcQuadFaceNormal(_grid[i][j].position,
00805                                    _grid[iNext][j].position,
00806                                    _grid[iNext][jPrev].position,
00807                                    _grid[i][jPrev].position);
00808 
00809 
00810             // lower left face normal (3nd quadrant)
00811             _grid[i][j].adjFaceNormals[2] = 
00812                 calcQuadFaceNormal(_grid[i][j].position,
00813                                    _grid[i][jPrev].position,
00814                                    _grid[iPrev][jPrev].position,
00815                                    _grid[iPrev][j].position);
00816 
00817 
00818             // lower right face normal (4th quadrant)
00819             _grid[i][j].adjFaceNormals[3] = 
00820                 calcQuadFaceNormal(_grid[i][j].position,
00821                                    _grid[iPrev][j].position,
00822                                    _grid[iPrev][jNext].position,
00823                                    _grid[i][jNext].position);
00824         }
00825     }
00826 }
00827 
00828 
00829 
00830 //-----------------------------------------------------------------------------
00831 // 
00832 // Author: afischle
00833 //-----------------------------------------------------------------------------
00834 void ExtrusionSurface::storeSweepSurfaceWithoutNormals(GeoIndicesUI32Ptr  indicesPtr,
00835                                                        GeoPLengthsUI32Ptr lensPtr,
00836                                                        GeoPTypesUI8Ptr    typesPtr)
00837 {
00838     assert(indicesPtr != NullFC);
00839     assert(lensPtr    != NullFC);
00840     assert(typesPtr   != NullFC);
00841 
00842     for(UInt32 i = 0; i < _spine.size() - 1; i++)
00843     {
00844         for(UInt32 j = 0; j < _crossSection.size(); j++)
00845         {
00846             Vertex v;
00847             // begin new triangle strip
00848             v = _grid[i+1][j];
00849 
00850             // store position (and texcoord if available)
00851             storeVertex(v, indicesPtr);
00852 
00853             v = _grid[i][j];
00854             // store position (and texcoord if available)
00855             storeVertex(v, indicesPtr);
00856         }
00857         // store row strip
00858         storePrimitive(GL_TRIANGLE_STRIP, lensPtr, typesPtr);
00859     }
00860 }
00861 
00862 void ExtrusionSurface::storeSweepSurfaceWithNormals(GeoIndicesUI32Ptr  indicesPtr,
00863                                                     GeoPLengthsUI32Ptr lensPtr,
00864                                                     GeoPTypesUI8Ptr    typesPtr)
00865 {
00866     assert(indicesPtr != NullFC);
00867     assert(lensPtr    != NullFC);
00868     assert(typesPtr   != NullFC);
00869 
00870     // we need to calculate the face normals first
00871     calcSweepSurfaceFaceNormals();
00872  
00873     if(_ccw)
00874     {
00875         // The current face is the face in quadrant 1 of the vertex
00876         // grid[i,j]. Stripes are generated in positive j direction
00877         // along the cross-section.
00878         UInt32 spineEnd = _spine.size() - 1;
00879         UInt32 cSEnd = _crossSection.size() - 1;
00880         for(UInt32 i = 0; i < spineEnd; i++)
00881         {
00882             // upper left vertex of current quad face
00883             // current face is located in 4th quadrant of vertex
00884             calcVertexNormal(&_grid[i+1][0], 3);
00885             storeVertex(_grid[i+1][0], indicesPtr);
00886 
00887             // lower left vertex of current quad face
00888             // current face is located in 1st quadrant of vertex            
00889             calcVertexNormal(&_grid[i][0], 0);
00890             storeVertex(_grid[i][0], indicesPtr);
00891 
00892             Vec3f fN = _grid[i][0].adjFaceNormals[0];
00893             for(UInt32 j = 0; j < cSEnd; j++)
00894             {
00895                 // The next face is the face in quadrant 1 of the vertex
00896                 // grid[i,j+1]                
00897                 Vec3f fNNext = _grid[i][j+1].adjFaceNormals[0];
00898 
00899                 // if the creaseAngle is too small the strip has to be split,
00900                 // because the normals are not shared along the common edge
00901                 // of the current and the next face.
00902                 // if j + 1 == csEnd the current quad is the last one
00903                 if((fN.enclosedAngle(fNNext) + Eps > _creaseAngle - Eps)
00904                    || (j + 1 == cSEnd))
00905                 {
00906                     // upper right vertex of current quad face
00907                     // current face is located in 3rd quadrant of vertex
00908                     calcVertexNormal(&_grid[i+1][j+1], 2);
00909                     storeVertex(_grid[i+1][j+1], indicesPtr);
00910 
00911                     // upper right vertex of current quad face
00912                     // current face is located in 2nd quadrant of vertex
00913                     calcVertexNormal(&_grid[i][j+1], 1);
00914                     storeVertex(_grid[i][j+1], indicesPtr);
00915                     
00916                     // terminate strip
00917                     storePrimitive(GL_TRIANGLE_STRIP, lensPtr, typesPtr);
00918                 }
00919 
00920                 // Either the strip was split above or not. In both cases
00921                 // continue/begin new strip if there's still a quad left
00922                 if(j + 1 < cSEnd)
00923                 {
00924                     
00925                     // upper left vertex of _next_ quad face
00926                     // next face is located in 4th quadrant of vertex
00927                     calcVertexNormal(&_grid[i+1][j+1], 3); 
00928                     storeVertex(_grid[i+1][j+1], indicesPtr);
00929                     
00930                     // lower left vertex of _next_ quad face
00931                     // next face is located in 1st quadrant of vertex
00932                     calcVertexNormal(&_grid[i][j+1], 0);
00933                     storeVertex(_grid[i][j+1], indicesPtr);
00934                 }
00935 
00936                 // update face normal
00937                 fN = fNNext;                
00938             }
00939         }
00940     }
00941     else // cw case
00942     {
00943         // The current face is the face in quadrant 1 of the vertex
00944         // grid[i,j-1]. Stripes are generated in negative j direction
00945         // along the cross-section.
00946         for(UInt32 i = 0; i < _spine.size() - 1; i++)
00947         {
00948             UInt32 cSEnd = _crossSection.size() - 1;
00949 
00950             // begin new triangle strip
00951             calcVertexNormal(&_grid[i+1][cSEnd], 2);
00952             storeVertex(_grid[i+1][cSEnd], indicesPtr);
00953 
00954             calcVertexNormal(&_grid[i][cSEnd], 1);
00955             storeVertex(_grid[i][cSEnd], indicesPtr);
00956 
00957             Vec3f fN = _grid[i][cSEnd].adjFaceNormals[0];            
00958             for(UInt32 j = cSEnd; j > 0; j--)
00959             {
00960                 Vec3f fNNext = _grid[i][j - 1].adjFaceNormals[0];
00961                 if((fN.enclosedAngle(fNNext) + Eps > _creaseAngle - Eps)
00962                    || (j == 1))
00963                 {
00964                     calcVertexNormal(&_grid[i + 1][j - 1], 3);
00965                     storeVertex(_grid[i + 1][j - 1], indicesPtr);
00966                     
00967                     calcVertexNormal(&_grid[i][j - 1], 0);
00968                     storeVertex(_grid[i][j - 1], indicesPtr);
00969                     
00970                     // terminate strip
00971                     storePrimitive(GL_TRIANGLE_STRIP, lensPtr, typesPtr);
00972                 }
00973 
00974                 if(j > 1) // still one quad to go
00975                 {
00976                     calcVertexNormal(&_grid[i + 1][j - 1], 2);
00977                     storeVertex(_grid[i + 1][j - 1], indicesPtr);
00978                 
00979                     calcVertexNormal(&_grid[i][j - 1], 1);
00980                     storeVertex(_grid[i][j - 1], indicesPtr);
00981                 }
00982                 // update face normal
00983                 fN = fNNext;                
00984             }
00985         }
00986     }
00987 }
00988 
00989 
00990 //----------------------------------------------------------------------
00991 // Returns true if the origin lies in the left half plane of the
00992 // oriented line AB.
00993 //
00994 // Author: afischle
00995 //----------------------------------------------------------------------
00996 inline bool ExtrusionSurface::isLeft(const Pnt2f &a, const Pnt2f &b)
00997 {
00998     Vec2f d = b - a;
00999     d.normalize();
01000     // compute the normal pointing into the _right_ half plane of the oriented
01001     // line
01002     Vec2f n = Vec2f(d.y(), -d.x());
01003     // the dot product is >0 when two vectors point into the same direction
01004     return n.dot(a) > 0.f;
01005 }
01006 
01007 
01008 //-----------------------------------------------------------------------------
01009 // Computes the minimum of the y-components of all points of the contour.
01010 //
01011 // Author: afischle
01012 //-----------------------------------------------------------------------------
01013 Real32 ExtrusionSurface::computeMinYAbs(const std::vector<Pnt2f> &contour,
01014                                         Real32 alpha)
01015 {
01016     const Real32 sinAlpha = osgsin(alpha);
01017     const Real32 cosAlpha = osgcos(alpha);
01018 
01019     Real32 minYAbs = 1.f;
01020     for(std::vector<Pnt2f>::const_iterator it = contour.begin();
01021         it != contour.end(); ++it)
01022     {
01023         Real32 y = sinAlpha * it->x() + cosAlpha * it->y();
01024         if(osgabs(y) < minYAbs)
01025             minYAbs = y;
01026     }
01027 
01028     return minYAbs;
01029 }
01030 
01031 //-----------------------------------------------------------------------------
01032 // Computes a rotation of the curve such that the minimum of the y-components
01033 // of all of its points is the maximal for alpha = n * (2 * Pi / nAngles),
01034 // where 0 <= n <= nAngles.
01035 //
01036 // Author: afischle
01037 //-----------------------------------------------------------------------------
01038 Real32 ExtrusionSurface::calcBetterRotationAngle(const std::vector<Pnt2f> &contour,
01039                                                  UInt32 nAngles)
01040 {
01041     Real32 maxMinY = 0.f;
01042     Real32 bestAlpha = 0.f;
01043 
01044     Real32 alphaInc = 2 * Pi / nAngles;
01045     for(Real32 alpha = 0.f; alpha < 2 * Pi; alpha += alphaInc)
01046     {
01047         Real32 minYAlpha = computeMinYAbs(contour, alpha);
01048         if(minYAlpha > maxMinY)
01049         {
01050             maxMinY = minYAlpha;
01051             bestAlpha = alpha;
01052         }
01053     }
01054 
01055     return bestAlpha;
01056 }
01057 
01058 
01059 //-----------------------------------------------------------------------------
01060 // Translates the given curve such that the specified reference point is
01061 // translated to the origin and rescales it to the unit square.
01062 //
01063 // Author: afischle
01064 //-----------------------------------------------------------------------------
01065 bool ExtrusionSurface::calcOptimizedContour(const std::vector<Pnt2f> &contour,
01066                                             const Pnt2f &point,
01067                                             std::vector<Pnt2f> *optimizedContour)
01068 {
01069     // copy the input
01070     std::vector<Pnt2f> coords(contour);
01071 
01072     // translate the reference point to the origin
01073     // and compute a scale factor
01074     Real32 maxLen = std::numeric_limits<Real32>::min();
01075     Real32 minLen = std::numeric_limits<Real32>::max();
01076 
01077     for(std::vector<Pnt2f>::iterator it = coords.begin();
01078             it != coords.end(); ++it)
01079     {
01080         // translate the problem to the origin
01081         *it -= Vec2f(point);
01082         Real32 len = Vec2f(*it).length();
01083 
01084         if(len > maxLen)  maxLen = len;
01085         if(len < minLen)  minLen = len;
01086     }
01087 
01088     if(maxLen < Eps)
01089     {
01090         FWARNING(("OSGExtrusion:calcOptimizedContour: Contour completely degenerate\n"));
01091         return false;
01092     }
01093 
01094     if(minLen < Eps)
01095     {
01096         FWARNING(("OSGExtrusion:calcOptimizedContour: Reference point on curve\n"));
01097         return false;
01098     }
01099 
01100     // rescale the contour to the unit disk and determine the minimal
01101     // absolute y coordinate
01102     Real32 minAbsY = 1.f;
01103     for(std::vector<Pnt2f>::iterator it = coords.begin();
01104             it != coords.end(); ++it)
01105     {
01106         *it /= maxLen;
01107         // osgabs(it->y()) <= 1.f after rescale
01108         if(osgabs(it->y()) < minAbsY)
01109             minAbsY = osgabs(it->y());
01110     }
01111 
01112     optimizedContour->clear();
01113     optimizedContour->reserve(coords.size());
01114     
01115     if(minAbsY > Eps) // we can probably determine all crossings
01116     {
01117         optimizedContour->assign(coords.begin(), coords.end());
01118     }
01119     else // try to rotate the contour
01120     {
01121         // we need to rotate the contour to get a reasonable result
01122         FDEBUG(("OSGExtrusion:calcOptimizedContour: Contour needs rotation\n"));
01123         // compute the best rotation angle for 64 equal partitions 
01124         // of the circle
01125         Real32 alpha = calcBetterRotationAngle(contour, 64);
01126         FDEBUG(("OSGExtrusion:calcOptimizedContour: Rotation angle is: %f\n",
01127                 alpha));
01128 
01129         Real32 sinAlpha = osgsin(alpha);
01130         Real32 cosAlpha = osgcos(alpha);
01131 
01132         // rotate the contour and copy it into the output vector
01133         for(std::vector<Pnt2f>::iterator it = coords.begin();
01134             it != coords.end(); ++it)
01135         {
01136             Pnt2f p;
01137             p[0] = cosAlpha * it->x() - sinAlpha * it->y();
01138             p[1] = sinAlpha * it->x() + cosAlpha * it->y();
01139 
01140             optimizedContour->push_back(p);
01141         }
01142     }
01143 
01144     // optimization successful
01145     return true;
01146 }
01147 
01148 
01149 //----------------------------------------------------------------------
01150 // Returns the winding number of the given contour with respect to the
01151 // specified point by counting how often the infinite line f(x) = point.y
01152 // was crossed by the curve.
01153 // 
01154 // Author: afischle
01155 //----------------------------------------------------------------------
01156 Int32 ExtrusionSurface::calcWindingNumber(const std::vector<Pnt2f> &contour, 
01157                                           const Pnt2f &point)
01158 {
01159     Int32 windingNumber = 0;
01160     std::vector<Pnt2f> coords;
01161 
01162     // translate contour to origin and rotate it to a better position
01163     calcOptimizedContour(contour, point, &coords);
01164 
01165     const Pnt2f *prevPoint = &(coords[coords.size() - 1]);
01166     for(UInt32 i = 0; i < coords.size(); ++i)
01167     {
01168         const Pnt2f *curPoint = &(coords[i]);
01169         if(prevPoint->y() < 0.f)
01170         {
01171             // upward crossing
01172             if(curPoint->y() > 0.f)
01173             {
01174                 if(isLeft(*prevPoint, *curPoint) == true)
01175                     windingNumber++;
01176             }
01177         }
01178         else // prevPoint->y() > 0.f
01179         {
01180             // downward crossing
01181             if(curPoint->y() < 0.f)
01182             {
01183                 if(isLeft(*prevPoint, *curPoint) == false)
01184                     windingNumber--;
01185             }
01186         }
01187         prevPoint = curPoint;
01188     }
01189 
01190     return windingNumber;
01191 }
01192 
01193 
01194 //-----------------------------------------------------------------------------
01195 // Calculates a convex cap surface bounded by the row-curve with index
01196 // spineIndex in the grid. If it is a begin cap its normal is reversed.
01197 //
01198 // The positions, normals and texture coordinates of the generated primitives
01199 // are inserted into maps which map the respective properties to an integer
01200 // index. The indices so obtained are stored in the indices field container.
01201 // The lengths and types of the generated primitives are directly stored in
01202 // the respective field containers.
01203 //
01204 // Author: afischle
01205 //-----------------------------------------------------------------------------
01206 void ExtrusionSurface::storeConvexCap(UInt32 spineIndex, 
01207                                       bool isBeginCap, 
01208                                       GeoIndicesUI32Ptr  indicesPtr,
01209                                       GeoPLengthsUI32Ptr lensPtr,
01210                                       GeoPTypesUI8Ptr    typesPtr)
01211 {
01212     assert(indicesPtr != NullFC);
01213     assert(lensPtr    != NullFC);
01214     assert(typesPtr   != NullFC);
01215 
01216     if(!_convex)
01217     {