Ignore:
Timestamp:
01/01/06 06:25:55 (19 years ago)
Author:
mattausch
Message:

fixed bug in raycasting
added valid view point regions, get view point only from valid regions

Location:
trunk/VUT/GtpVisibilityPreprocessor
Files:
19 edited

Legend:

Unmodified
Added
Removed
  • trunk/VUT/GtpVisibilityPreprocessor/scripts/Preprocessor.vcproj

    r469 r487  
    410410                        </File> 
    411411                </Filter> 
    412                 <File 
    413                         RelativePath=".\VTune\Preprocessor.vpj"> 
    414                 </File> 
    415412        </Files> 
    416413        <Globals> 
  • trunk/VUT/GtpVisibilityPreprocessor/scripts/default.env

    r486 r487  
    143143} 
    144144 
     145 
    145146ViewCells { 
    146147        loadFromFile false 
     
    154155        height 5.0 
    155156        maxViewCells 100 
     157        maxPvs 70 
     158         
    156159         
    157160        PostProcess { 
     
    170173                #colorCode MergedTreeDiff 
    171174                colorCode Random 
    172                 exportRays false 
     175                exportRays true 
    173176                exportGeometry true 
    174177        } 
     
    216219        # maximal cost for merging a view cell 
    217220        PostProcess { 
     221<<<<<<< .mine 
    218222                maxCostRatio 0.005 
    219                 minViewCells 100 
     223                minViewCells 200 
     224 
    220225                maxPvsSize   50000 
    221226        } 
     
    283288        PostProcess { 
    284289                maxCostRatio 0.1 
    285                 minViewCells 100 
     290                minViewCells 200 
     291 
    286292                maxPvsSize   500 
    287293                useRaysForMerge false 
  • trunk/VUT/GtpVisibilityPreprocessor/src/AxisAlignedBox3.h

    r486 r487  
    175175 
    176176 
    177   Vector3 GetRandomPoint() { 
     177  Vector3 GetRandomPoint() const { 
    178178    Vector3 size = Size(); 
    179179    return mMin + Vector3(RandomValue(0, size.x), 
  • trunk/VUT/GtpVisibilityPreprocessor/src/Environment.cpp

    r486 r487  
    12071207                 "0"); 
    12081208 
     1209   
     1210  RegisterOption("ViewCells.maxPvs", 
     1211                 optInt, 
     1212                 "-view_cells_max_pvs", 
     1213                 "300"); 
     1214 
    12091215  RegisterOption("ViewCells.PostProcess.minPvsDif", 
    12101216                 optInt, 
  • trunk/VUT/GtpVisibilityPreprocessor/src/Plane3.h

    r485 r487  
    8181          const float dv = DotProd(v, mNormal); 
    8282     
    83           // does not intersect 
     83          // does not intersect or coincident 
    8484          if (signum(dv) == 0) 
    85                   return -1; 
     85                  return 0; 
    8686         
    8787          return - Distance(a) / dv; // TODO: could be done more efficiently 
  • trunk/VUT/GtpVisibilityPreprocessor/src/Ray.cpp

    r485 r487  
    8686    return; // has been already precomputed 
    8787 
    88   const float eps = 1e-6; 
    89   const float invEps = 1e6; 
     88  const float eps = 1e-6f; 
     89  const float invEps = 1e6f; 
    9090   
    9191  // it does change the ray direction very slightly, 
  • trunk/VUT/GtpVisibilityPreprocessor/src/RssPreprocessor.cpp

    r477 r487  
    430430         
    431431  if (useViewSpaceBox) 
     432  { 
    432433        mViewSpaceBox = box; 
     434        mViewCellsManager->SetViewSpaceBox(*box); 
     435  } 
    433436  else 
     437  { 
    434438        mViewSpaceBox = NULL; 
     439        mViewCellsManager->SetViewSpaceBox(mKdTree->GetBox()); 
     440  } 
    435441                 
    436442 
     
    448454        for (int k=0; k < s; k++) { 
    449455                         
    450           Vector3 viewpoint = GetViewpoint(mViewSpaceBox); 
     456          //Vector3 viewpoint = GetViewpoint(mViewSpaceBox); 
     457          Vector3 viewpoint;  
     458          mViewCellsManager->GetViewPoint(viewpoint); 
    451459          Vector3 direction = GetDirection(viewpoint, mViewSpaceBox); 
    452460                         
     
    497505  if (mUseViewcells) { 
    498506        // construct view cells 
    499         mViewCellsManager->Construct(mObjects, mVssRays, mViewSpaceBox); 
     507        mViewCellsManager->Construct(mObjects, mVssRays); 
    500508         
    501509        VssRayContainer selectedRays; 
     
    555563        if (!mUseImportanceSampling) { 
    556564          for (int j=0; j < num; j++) { 
    557                 Vector3 viewpoint = GetViewpoint(mViewSpaceBox); 
     565            //changed by matt 
     566                //Vector3 viewpoint = GetViewpoint(mViewSpaceBox);  
     567                Vector3 viewpoint;  
     568                mViewCellsManager->GetViewPoint(viewpoint); 
    558569                Vector3 direction = GetDirection(viewpoint, mViewSpaceBox); 
    559570                rays.push_back(SimpleRay(viewpoint, direction)); 
  • trunk/VUT/GtpVisibilityPreprocessor/src/RssPreprocessor.h

    r464 r487  
    99class RssTree; 
    1010class RssTreeLeaf; 
     11 
    1112 
    1213/** Sampling based visibility preprocessing. The implementation is based on heuristical 
     
    3233 
    3334  ofstream mStats; 
    34          
     35 
    3536  ObjectContainer mObjects; 
    3637 
  • trunk/VUT/GtpVisibilityPreprocessor/src/SamplingPreprocessor.cpp

    r477 r487  
    250250  mSceneGraph->CollectObjects(&objects); 
    251251 
     252  mViewCellsManager->SetViewSpaceBox(mKdTree->GetBox()); 
     253   
    252254  Vector3 point, normal, direction; 
    253255  //Ray ray; 
  • trunk/VUT/GtpVisibilityPreprocessor/src/ViewCellBsp.cpp

    r486 r487  
    7676} 
    7777 
     78 
    7879int BspNode::GetDepth() const 
    7980{ 
     
    8990        return depth; 
    9091} 
     92 
     93 
     94bool BspNode::TreeValid() const 
     95{ 
     96        return mTreeValid; 
     97} 
     98 
     99 
     100void BspNode::SetTreeValid(const bool v) 
     101{ 
     102        mTreeValid = v; 
     103} 
     104 
    91105 
    92106/****************************************************************/ 
     
    186200        return true;  
    187201} 
    188  
    189202 
    190203/****************************************************************/ 
     
    17621775} 
    17631776 
     1777 
    17641778AxisAlignedBox3 BspTree::GetBoundingBox() const 
    17651779{ 
    17661780        return mBox; 
    17671781} 
     1782 
    17681783 
    17691784BspNode *BspTree::GetRoot() const 
  • trunk/VUT/GtpVisibilityPreprocessor/src/ViewCellBsp.h

    r485 r487  
    211211        */ 
    212212        int GetDepth() const; 
    213         static int sMailId; 
    214         int mMailbox; 
    215    
     213 
     214        /** returns true if the whole subtree is valid 
     215        */ 
     216        bool TreeValid() const; 
     217 
     218        void SetTreeValid(const bool v); 
     219 
     220        //-- mailing options 
     221 
    216222        void Mail() { mMailbox = sMailId; } 
    217223        static void NewMail() { ++ sMailId; } 
    218224        bool Mailed() const { return mMailbox == sMailId; } 
    219225 
     226        static int sMailId; 
     227        int mMailbox; 
     228 
    220229protected: 
    221230 
     231        /// if this sub tree is a completely valid view space region 
     232        bool mTreeValid; 
    222233        /// parent of this node 
    223234        BspInterior *mParent; 
     
    294305        void SetViewCell(BspViewCell *viewCell); 
    295306 
     307        /// Rays piercing this leaf. 
    296308        VssRayContainer mVssRays; 
    297309         
  • trunk/VUT/GtpVisibilityPreprocessor/src/ViewCellsManager.cpp

    r486 r487  
    2121mTotalArea(0.0f) 
    2222{ 
     23        mSceneBox.Initialize(); 
    2324        ParseEnvironment(); 
    2425} 
     
    3031mVisualizationSamples(0) 
    3132{ 
     33        mSceneBox.Initialize(); 
    3234        ParseEnvironment(); 
    3335} 
     
    8183 
    8284 
     85bool ViewCellsManager::GetViewPoint(Vector3 &viewPoint) const 
     86{ 
     87        viewPoint = mSceneBox.GetRandomPoint(); 
     88 
     89        return true; 
     90} 
     91 
     92 
    8393void ViewCellsManager::ComputeSampleContributions(const VssRayContainer &rays) 
    8494{ 
     
    8898   
    8999        VssRayContainer::const_iterator it, it_end = rays.end(); 
    90         int i = 0;       
     100 
    91101        for (it = rays.begin(); it != it_end; ++ it)  
    92         {       Debug << "here " << i ++ << endl; 
     102        {        
    93103                ComputeSampleContributions(*(*it)); 
    94                 Debug << "**" << endl; 
    95104        } 
    96105} 
     
    104113 
    105114        for (it = mViewCells.begin(); it != it_end; ++ it) 
     115        { 
    106116                (*it)->UpdateViewCellsStats(mViewCellsStats); 
     117        } 
    107118} 
    108119 
     
    295306 
    296307 
     308void ViewCellsManager::SetViewSpaceBox(const AxisAlignedBox3 &box) 
     309{ 
     310        mSceneBox = box; 
     311} 
     312 
     313 
    297314void ViewCellsManager::ResetViewCells() 
    298315{ 
     
    432449 
    433450int BspViewCellsManager::Construct(const ObjectContainer &objects,  
    434                                                                    const VssRayContainer &rays, 
    435                                                                    AxisAlignedBox3 *sceneBbox) 
     451                                                                   const VssRayContainer &rays) 
    436452{ 
    437453        // if view cells were already constructed 
     
    10241040 
    10251041int KdViewCellsManager::Construct(const ObjectContainer &objects,  
    1026                                                                   const VssRayContainer &rays, 
    1027                                                                   AxisAlignedBox3 *sceneBbox) 
     1042                                                                  const VssRayContainer &rays) 
    10281043{ 
    10291044        // if view cells already constructed 
     
    13091324 
    13101325int VspKdViewCellsManager::Construct(const ObjectContainer &objects,  
    1311                                                                          const VssRayContainer &rays, 
    1312                                                                          AxisAlignedBox3 *sceneBbox) 
     1326                                                                         const VssRayContainer &rays) 
    13131327{ 
    13141328        // if view cells already constructed 
     
    13271341                  << (int)constructionRays.size() << " samples" << endl; 
    13281342 
    1329         mVspKdTree->Construct(constructionRays, sceneBbox); 
     1343        mVspKdTree->Construct(constructionRays, &mSceneBox); 
    13301344        Debug << mVspKdTree->GetStatistics() << endl; 
    13311345 
     
    16841698 
    16851699int VspBspViewCellsManager::Construct(const ObjectContainer &objects,  
    1686                                                                           const VssRayContainer &rays, 
    1687                                                                           AxisAlignedBox3 *sceneBbox) 
     1700                                                                          const VssRayContainer &rays) 
    16881701{ 
    16891702        // if view cells were already constructed 
     
    16971710 
    16981711        GetRaySets(rays, mConstructionSamples, constructionRays, &savedRays); 
    1699          
     1712 
    17001713        Debug << "construction rays: " << (int)savedRays.size() << endl; 
    17011714        Debug << "saved rays: " << (int)constructionRays.size() << endl; 
    17021715 
    1703         mVspBspTree->Construct(constructionRays, sceneBbox); 
     1716        mVspBspTree->Construct(constructionRays, &mSceneBox); 
     1717 
    17041718        Debug << mVspBspTree->GetStatistics() << endl; 
    1705  
    17061719        ResetViewCells(); 
    17071720 
     
    19321945         
    19331946 
     1947bool VspBspViewCellsManager::GetViewPoint(Vector3 &viewPoint) const 
     1948{ 
     1949        if (!ViewCellsConstructed()) 
     1950                return ViewCellsManager::GetViewPoint(viewPoint); 
     1951 
     1952        const int limit = 10; 
     1953 
     1954        for (int i = 0; i < limit; ++ i) 
     1955        { 
     1956                viewPoint = mSceneBox.GetRandomPoint(); 
     1957                if (mVspBspTree->ViewPointValid(viewPoint)) 
     1958                        return true; 
     1959        } 
     1960 
     1961        return false; 
     1962} 
     1963 
    19341964void VspBspViewCellsManager::ExportSplits(const ObjectContainer &objects, 
    19351965                                                                                  const VssRayContainer &rays) 
  • trunk/VUT/GtpVisibilityPreprocessor/src/ViewCellsManager.h

    r485 r487  
    5555        */ 
    5656        virtual int Construct(const ObjectContainer &objects,  
    57                                                   const VssRayContainer &rays, 
    58                                                   AxisAlignedBox3 *sceneBbox = NULL) = 0; 
     57                                                  const VssRayContainer &rays) = 0; 
    5958 
    6059        /** Computes sample contributions of the rays to the view cells PVS. 
     
    217216        void SetRenderer(Renderer *renderer); 
    218217 
    219          
     218        /** Computes a (random) view point in the valid view space. 
     219                @returns true if valid view point was found 
     220        */ 
     221        virtual bool GetViewPoint(Vector3 &viewPoint) const; 
     222 
     223        /** Sets a view space boundary. 
     224        */ 
     225        void SetViewSpaceBox(const AxisAlignedBox3 &box); 
     226 
     227 
    220228protected: 
    221229 
     
    251259 
    252260        /// the view cell corresponding to unbounded space 
    253         ViewCell *mUnbounded; 
     261        //ViewCell *mUnbounded; 
    254262 
    255263        /// Renders the view cells. 
     
    280288 
    281289        ViewCellsStatistics mViewCellsStats; 
     290        /// the scene bounding box 
     291        AxisAlignedBox3 mSceneBox; 
     292 
     293        //-- visualization options 
     294         
     295        /// color code for view cells 
     296        int mColorCode; 
     297        bool mExportGeometry; 
     298        bool mExportRays; 
    282299}; 
    283300 
     
    299316 
    300317        int Construct(const ObjectContainer &objects,  
    301                                   const VssRayContainer &rays, 
    302                                   AxisAlignedBox3 *sceneBbox); 
     318                                  const VssRayContainer &rays); 
    303319 
    304320 
     
    372388 
    373389        int Construct(const ObjectContainer &objects,  
    374                                   const VssRayContainer &rays, 
    375                                   AxisAlignedBox3 *sceneBbox); 
     390                                  const VssRayContainer &rays); 
    376391 
    377392        int CastLineSegment(const Vector3 &origin, 
     
    426441 
    427442        int Construct(const ObjectContainer &objects,  
    428                                   const VssRayContainer &rays, 
    429                                   AxisAlignedBox3 *sceneBbox); 
     443                                  const VssRayContainer &rays); 
    430444 
    431445 
     
    483497 
    484498        int Construct(const ObjectContainer &objects,  
    485                                   const VssRayContainer &rays, 
    486                                   AxisAlignedBox3 *sceneBbox); 
    487  
     499                                  const VssRayContainer &rays); 
    488500 
    489501        int PostProcess(const ObjectContainer &objects,  
     
    510522        AxisAlignedBox3 GetSceneBbox() const; 
    511523 
     524        bool GetViewPoint(Vector3 &viewPoint) const; 
     525 
    512526protected: 
    513527        /** DEPRECATED 
  • trunk/VUT/GtpVisibilityPreprocessor/src/VspBspTree.cpp

    r486 r487  
    8585        environment->GetBoolValue("VspBspTree.splitUseOnlyDrivingAxis", mOnlyDrivingAxis); 
    8686        environment->GetBoolValue("VspBspTree.PostProcess.useRaysForMerge", mUseRaysForMerge); 
     87        environment->GetIntValue("ViewCells.maxPvs", mMaxPvs); 
     88 
    8789 
    8890        //-- debug output 
     
    400402                BspLeaf *leaf = dynamic_cast<BspLeaf *>(newNode); 
    401403 
     404                if (!CheckValid(tData)) 
     405                { 
     406                        leaf->SetTreeValid(false); 
     407                        PropagateUpValidity(leaf); 
     408                } 
     409 
    402410                // create new view cell for this leaf 
    403411                BspViewCell *viewCell = new BspViewCell(); 
    404412                leaf->SetViewCell(viewCell); 
    405                  
     413         
     414                //-- update pvs 
     415                int conSamp = 0, sampCon = 0; 
     416                AddToPvs(leaf, *tData.mRays, conSamp, sampCon); 
     417 
     418                mStat.contributingSamples += conSamp; 
     419                mStat.sampleContributions += sampCon; 
     420 
     421                //-- store additional info 
    406422                if (mStoreRays) 
    407423                { 
     
    415431                leaf->mArea = tData.mArea; 
    416432 
    417                 //-- update pvs 
    418                 int conSamp = 0, sampCon = 0; 
    419                 AddToPvs(leaf, *tData.mRays, conSamp, sampCon); 
    420          
    421                 mStat.contributingSamples += conSamp; 
    422                 mStat.sampleContributions += sampCon; 
    423  
    424                 EvaluateLeafStats(tData); 
    425         } 
    426  
     433                EvaluateLeafStats(tData);                
     434        } 
    427435 
    428436        //-- cleanup 
     
    431439        return newNode; 
    432440} 
     441 
    433442 
    434443BspNode *VspBspTree::SubdivideNode(VspBspTraversalData &tData, 
     
    519528 
    520529        // replace a link from node's parent 
    521         if (!leaf->IsRoot()) 
     530        if (parent) 
    522531        { 
    523532                parent->ReplaceChildLink(leaf, interior); 
     
    18971906                                if (extSide < 0) 
    18981907                                        node = in->GetBack(); 
    1899                                 else if (extSide > 0) 
     1908                                else  
    19001909                                        node = in->GetFront(); 
    1901  
     1910                                                                 
    19021911                                continue; // no far child 
    19031912                        } 
     
    19081917                        // find intersection of ray segment with plane 
    19091918                        extp = splitPlane.FindIntersection(origin, extp, &t); 
    1910                         //cout << "x"; 
    19111919                }  
    19121920                else 
    19131921                { 
    1914                         //cout << "o"; 
    19151922                        // reached leaf => intersection with view cell 
    19161923                        BspLeaf *leaf = dynamic_cast<BspLeaf *>(node); 
     
    19371944                } 
    19381945        } 
    1939         //cout << "!!!!!!!!!!!" << endl; 
     1946 
    19401947        return hits; 
    19411948} 
     
    25062513} 
    25072514 
     2515bool VspBspTree::ViewPointValid(const Vector3 &viewPoint) const 
     2516{ 
     2517        BspNode *node = mRoot; 
     2518 
     2519        while (1) 
     2520        { 
     2521                // early exit 
     2522                if (node->TreeValid()) 
     2523                        return true; 
     2524 
     2525                if (node->IsLeaf()) 
     2526                        return false; 
     2527                         
     2528                BspInterior *in = dynamic_cast<BspInterior *>(node); 
     2529                Plane3 splitPlane = in->GetPlane(); 
     2530                         
     2531                if (splitPlane.Side(viewPoint) <= 0)  
     2532                { 
     2533                        node = in->GetBack(); 
     2534                }  
     2535                else 
     2536                { 
     2537                        node = in->GetFront(); 
     2538                } 
     2539        } 
     2540 
     2541        // should never come here 
     2542        return false; 
     2543} 
     2544 
     2545 
     2546bool VspBspTree::CheckValid(const VspBspTraversalData &data) const 
     2547{ 
     2548        return data.mPvs <= mMaxPvs; 
     2549} 
     2550 
     2551 
     2552void VspBspTree::PropagateUpValidity(BspNode *node) 
     2553{ 
     2554        while (!node->IsRoot() && node->TreeValid()) 
     2555        { 
     2556                node = node->GetParent(); 
     2557                node->SetTreeValid(false); 
     2558        } 
     2559} 
     2560 
    25082561 
    25092562/************************************************************************/ 
  • trunk/VUT/GtpVisibilityPreprocessor/src/VspBspTree.h

    r486 r487  
    268268        void ConstructBspRays(vector<BspRay *> &bspRays, 
    269269                                                  const VssRayContainer &rays); 
    270  
    271270         
    272271        /** Merge view cells of leaves l1 and l2. 
    273272        */ 
    274273        bool MergeViewCells(BspLeaf *l1, BspLeaf *l2) const; 
     274 
     275        /** Returns true if this view point is in a valid view space, 
     276                false otherwise. 
     277        */ 
     278        bool ViewPointValid(const Vector3 &viewPoint) const; 
     279 
    275280 
    276281protected: 
     
    535540                                         BspViewCell *vc1,  
    536541                                         BspViewCell *vc2) const; 
     542 
     543        /** 
     544                Checks if this traversal data corresponds to 
     545                a valid view space region. 
     546        */ 
     547        bool CheckValid(const VspBspTraversalData &data) const; 
     548 
     549        /** Propagates valid flag up the tree. 
     550        */ 
     551        void PropagateUpValidity(BspNode *node); 
    537552 
    538553        /// Pointer to the root of the tree 
     
    620635        vector<SortableEntry> *mSplitCandidates; 
    621636 
     637 
    622638        typedef priority_queue<BspMergeCandidate> MergeQueue; 
     639 
    623640        MergeQueue mMergeQueue; 
    624          
     641        /// if rays should be used to collect merge candidates 
    625642        bool mUseRaysForMerge; 
     643        /// maximal allowed pvs so that view cell is valid 
     644        int mMaxPvs; 
     645 
    626646 
    627647private: 
  • trunk/VUT/GtpVisibilityPreprocessor/src/VssPreprocessor.cpp

    r485 r487  
    378378 
    379379  if (useViewSpaceBox) 
     380  { 
    380381        mViewSpaceBox = box; 
     382        mViewCellsManager->SetViewSpaceBox(*box); 
     383  } 
    381384  else 
     385  { 
    382386        mViewSpaceBox = NULL; 
    383  
    384  
     387        mViewCellsManager->SetViewSpaceBox(mKdTree->GetBox()); 
     388  } 
    385389  VssTree *vssTree = NULL; 
    386390 
     
    395399        int s = Min(mSamplesPerPass, mInitialSamples); 
    396400        for (int k=0; k < s; k++) { 
    397  
    398           Vector3 viewpoint = GetViewpoint(mViewSpaceBox); 
     401      // changed by matt 
     402          //Vector3 viewpoint = GetViewpoint(mViewSpaceBox); 
     403          Vector3 viewpoint;  
     404          mViewCellsManager->GetViewPoint(viewpoint); 
    399405          Vector3 direction = GetDirection(viewpoint, mViewSpaceBox); 
    400406 
     
    453459 
    454460  // construct view cells 
    455   mViewCellsManager->Construct(mObjects, mVssRays, mViewSpaceBox); 
     461  mViewCellsManager->Construct(mObjects, mVssRays); 
    456462 
    457463  vssTree = new VssTree; 
     
    501507        if (!mUseImportanceSampling) { 
    502508          for (int j=0; j < num; j++) { 
    503                 Vector3 viewpoint = GetViewpoint(mViewSpaceBox); 
     509            // changed by matt 
     510                //Vector3 viewpoint = GetViewpoint(mViewSpaceBox); 
     511                Vector3 viewpoint;  
     512                mViewCellsManager->GetViewPoint(viewpoint); 
    504513                Vector3 direction = GetDirection(viewpoint, mViewSpaceBox); 
    505514                rays.push_back(SimpleRay(viewpoint, direction)); 
  • trunk/VUT/GtpVisibilityPreprocessor/src/VssPreprocessor.h

    r469 r487  
    99class VssTree; 
    1010class VssTreeLeaf; 
     11 
    1112 
    1213/** Sampling based visibility preprocessing. The implementation is based on heuristical 
  • trunk/VUT/GtpVisibilityPreprocessor/src/VssRay.cpp

    r467 r487  
    211211   
    212212  s<<"##### VSS RAY STAT ##########\n"; 
    213   s<<"#RAYS\n"<<size()<<endl; 
     213  s<<"#RAYS\n"<<(int)size()<<endl; 
    214214  s<<"#AVG_RAY_PVS_CONTRIBUTION\n"<<sumContributions/(float)size()<<endl; 
    215215  s<<"#AVG_RAY_RELATIVE_PVS_CONTRIBUTION\n"<<sumRelContributions/ 
  • trunk/VUT/GtpVisibilityPreprocessor/src/main.cpp

    r464 r487  
    5757        p->KdTreeStatistics(cout); 
    5858         
    59    
    6059        // parse view cells related options 
    6160        p->PrepareViewCells(); 
    62  
    6361 
    6462  //  p->mSceneGraph->Export("soda.x3d"); 
Note: See TracChangeset for help on using the changeset viewer.