Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/PlugIns/OctreeSceneManager/src/OgreTerrainRenderable.cpp @ 6

Last change on this file since 6 was 5, checked in by anonymous, 17 years ago

=hoffentlich gehts jetzt

File size: 50.6 KB
Line 
1/*
2-----------------------------------------------------------------------------
3This source file is part of OGRE
4(Object-oriented Graphics Rendering Engine)
5For the latest info, see http://www.ogre3d.org/
6
7Copyright (c) 2000-2006 Torus Knot Software Ltd
8Also see acknowledgements in Readme.html
9
10This program is free software; you can redistribute it and/or modify it under
11the terms of the GNU Lesser General Public License as published by the Free Software
12Foundation; either version 2 of the License, or (at your option) any later
13version.
14
15This program is distributed in the hope that it will be useful, but WITHOUT
16ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
18
19You should have received a copy of the GNU Lesser General Public License along with
20this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21Place - Suite 330, Boston, MA 02111-1307, USA, or go to
22http://www.gnu.org/copyleft/lesser.txt.
23
24You may alternatively use this source under the terms of a specific version of
25the OGRE Unrestricted License provided you have obtained such a license from
26Torus Knot Software Ltd.
27-----------------------------------------------------------------------------
28*/
29/***************************************************************************
30terrainrenderable.cpp  -  description
31-------------------
32begin                : Sat Oct 5 2002
33copyright            : (C) 2002 by Jon Anderson
34email                : janders@users.sf.net
35
36Enhancements 2003 - 2004 (C) The OGRE Team
37
38***************************************************************************/
39
40#include "OgreTerrainRenderable.h"
41#include "OgreSceneNode.h"
42#include "OgreRenderQueue.h"
43#include "OgreRenderOperation.h"
44#include "OgreCamera.h"
45#include "OgreRoot.h"
46#include "OgreTerrainSceneManager.h"
47#include "OgreLogManager.h"
48#include "OgreStringConverter.h"
49#include "OgreViewport.h"
50#include "OgreException.h"
51
52namespace Ogre
53{
54    //-----------------------------------------------------------------------
55    #define MAIN_BINDING 0
56    #define DELTA_BINDING 1
57    //-----------------------------------------------------------------------
58    //-----------------------------------------------------------------------
59    String TerrainRenderable::mType = "TerrainMipMap";
60    //-----------------------------------------------------------------------
61    //-----------------------------------------------------------------------
62
63    //-----------------------------------------------------------------------
64    TerrainRenderable::TerrainRenderable(const String& name, TerrainSceneManager* tsm)
65        : Renderable(), MovableObject(name), mSceneManager(tsm), mTerrain(0), mDeltaBuffers(0), mPositionBuffer(0)
66    {
67        mForcedRenderLevel = -1;
68        mLastNextLevel = -1;
69
70        mMinLevelDistSqr = 0;
71
72        mInit = false;
73        mLightListDirty = true;
74                MovableObject::mCastShadows = false;
75
76        for ( int i = 0; i < 4; i++ )
77        {
78            mNeighbors[ i ] = 0;
79        }
80
81                mOptions = &(mSceneManager->getOptions());
82
83
84    }
85    //-----------------------------------------------------------------------
86    TerrainRenderable::~TerrainRenderable()
87    {
88
89        deleteGeometry();
90    }
91        //-----------------------------------------------------------------------
92        uint32 TerrainRenderable::getTypeFlags(void) const
93        {
94                // return world flag
95                return SceneManager::WORLD_GEOMETRY_TYPE_MASK;
96        }
97    //-----------------------------------------------------------------------
98    void TerrainRenderable::deleteGeometry()
99    {
100        if(mTerrain)
101            delete mTerrain;
102
103        if (mPositionBuffer)
104            delete [] mPositionBuffer;
105
106        if (mDeltaBuffers)
107            delete [] mDeltaBuffers;
108
109        if ( mMinLevelDistSqr != 0 )
110            delete [] mMinLevelDistSqr;
111    }
112    //-----------------------------------------------------------------------
113    void TerrainRenderable::initialise(int startx, int startz, 
114        Real* pageHeightData)
115    {
116
117        if ( mOptions->maxGeoMipMapLevel != 0 )
118        {
119            int i = ( int ) 1 << ( mOptions->maxGeoMipMapLevel - 1 ) ;
120
121            if ( ( i + 1 ) > mOptions->tileSize )
122            {
123                printf( "Invalid maximum mipmap specifed, must be n, such that 2^(n-1)+1 < tileSize \n" );
124                return ;
125            }
126        }
127
128        deleteGeometry();
129
130        //calculate min and max heights;
131        Real min = 256000, max = 0;
132
133        mTerrain = new VertexData;
134        mTerrain->vertexStart = 0;
135        mTerrain->vertexCount = mOptions->tileSize * mOptions->tileSize;
136
137        VertexDeclaration* decl = mTerrain->vertexDeclaration;
138        VertexBufferBinding* bind = mTerrain->vertexBufferBinding;
139
140        // positions
141        size_t offset = 0;
142        decl->addElement(MAIN_BINDING, offset, VET_FLOAT3, VES_POSITION);
143        offset += VertexElement::getTypeSize(VET_FLOAT3);
144        if (mOptions->lit)
145        {
146            decl->addElement(MAIN_BINDING, offset, VET_FLOAT3, VES_NORMAL);
147            offset += VertexElement::getTypeSize(VET_FLOAT3);
148        }
149        // texture coord sets
150        decl->addElement(MAIN_BINDING, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0);
151        offset += VertexElement::getTypeSize(VET_FLOAT2);
152        decl->addElement(MAIN_BINDING, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 1);
153        offset += VertexElement::getTypeSize(VET_FLOAT2);
154        if (mOptions->coloured)
155        {
156            decl->addElement(MAIN_BINDING, offset, VET_COLOUR, VES_DIFFUSE);
157            offset += VertexElement::getTypeSize(VET_COLOUR);
158        }
159
160        // Create shared vertex buffer
161        mMainBuffer =
162            HardwareBufferManager::getSingleton().createVertexBuffer(
163            decl->getVertexSize(MAIN_BINDING),
164            mTerrain->vertexCount, 
165            HardwareBuffer::HBU_STATIC_WRITE_ONLY);
166        // Create system memory copy with just positions in it, for use in simple reads
167        mPositionBuffer = new float[mTerrain->vertexCount * 3];
168
169        bind->setBinding(MAIN_BINDING, mMainBuffer);
170
171        if (mOptions->lodMorph)
172        {
173            // Create additional element for delta
174            decl->addElement(DELTA_BINDING, 0, VET_FLOAT1, VES_BLEND_WEIGHTS);
175            // NB binding is not set here, it is set when deriving the LOD
176        }
177
178
179        mInit = true;
180
181        mRenderLevel = 1;
182
183        mMinLevelDistSqr = new Real[ mOptions->maxGeoMipMapLevel ];
184
185        int endx = startx + mOptions->tileSize;
186
187        int endz = startz + mOptions->tileSize;
188
189        Vector3 left, down, here;
190
191        const VertexElement* poselem = decl->findElementBySemantic(VES_POSITION);
192        const VertexElement* texelem0 = decl->findElementBySemantic(VES_TEXTURE_COORDINATES, 0);
193        const VertexElement* texelem1 = decl->findElementBySemantic(VES_TEXTURE_COORDINATES, 1);
194        float* pSysPos = mPositionBuffer;
195
196        unsigned char* pBase = static_cast<unsigned char*>(mMainBuffer->lock(HardwareBuffer::HBL_DISCARD));
197
198        for ( int j = startz; j < endz; j++ )
199        {
200            for ( int i = startx; i < endx; i++ )
201            {
202                float *pPos, *pTex0, *pTex1;
203                poselem->baseVertexPointerToElement(pBase, &pPos);
204                texelem0->baseVertexPointerToElement(pBase, &pTex0);
205                texelem1->baseVertexPointerToElement(pBase, &pTex1);
206   
207                Real height = pageHeightData[j * mOptions->pageSize + i];
208                height = height * mOptions->scale.y; // scale height
209
210                *pSysPos++ = *pPos++ = ( float ) i * mOptions->scale.x; //x
211                *pSysPos++ = *pPos++ = height; // y
212                *pSysPos++ = *pPos++ = ( float ) j * mOptions->scale.z; //z
213
214                *pTex0++ = ( float ) i / ( float ) ( mOptions->pageSize - 1 );
215                *pTex0++ = ( float ) j / ( float ) ( mOptions->pageSize - 1 );
216
217                *pTex1++ = ( ( float ) i / ( float ) ( mOptions->tileSize - 1 ) ) * mOptions->detailTile;
218                *pTex1++ = ( ( float ) j / ( float ) ( mOptions->tileSize - 1 ) ) * mOptions->detailTile;
219
220                if ( height < min )
221                    min = ( Real ) height;
222
223                if ( height > max )
224                    max = ( Real ) height;
225
226                pBase += mMainBuffer->getVertexSize();
227            }
228        }
229
230        mMainBuffer->unlock();
231
232        mBounds.setExtents( 
233            ( Real ) startx * mOptions->scale.x, 
234            min, 
235            ( Real ) startz * mOptions->scale.z,
236            ( Real ) ( endx - 1 ) * mOptions->scale.x, 
237            max, 
238            ( Real ) ( endz - 1 ) * mOptions->scale.z );
239
240
241        mCenter = Vector3( ( startx * mOptions->scale.x + (endx - 1) * mOptions->scale.x ) / 2,
242            ( min + max ) / 2,
243            ( startz * mOptions->scale.z + (endz - 1) * mOptions->scale.z ) / 2 );
244
245        mBoundingRadius = Math::Sqrt(
246            Math::Sqr(max - min) +
247            Math::Sqr((endx - 1 - startx) * mOptions->scale.x) +
248            Math::Sqr((endz - 1 - startz) * mOptions->scale.z)) / 2;
249
250        // Create delta buffer list if required to morph
251        if (mOptions->lodMorph)
252        {
253            // Create delta buffer for all except the lowest mip
254            mDeltaBuffers = new HardwareVertexBufferSharedPtr[mOptions->maxGeoMipMapLevel - 1];
255        }
256
257        Real C = _calculateCFactor();
258
259        _calculateMinLevelDist2( C );
260
261    }
262    //-----------------------------------------------------------------------
263    void TerrainRenderable::_getNormalAt( float x, float z, Vector3 * result )
264    {
265
266        assert(mOptions->lit && "No normals present");
267
268        Vector3 here, left, down;
269        here.x = x;
270        here.y = getHeightAt( x, z );
271        here.z = z;
272
273        left.x = x - 1;
274        left.y = getHeightAt( x - 1, z );
275        left.z = z;
276
277        down.x = x;
278        down.y = getHeightAt( x, z + 1 );
279        down.z = z + 1;
280
281        left = left - here;
282
283        down = down - here;
284
285        left.normalise();
286        down.normalise();
287
288        *result = left.crossProduct( down );
289        result -> normalise();
290
291        // result->x = - result->x;
292        // result->y = - result->y;
293        // result->z = - result->z;
294    }
295    //-----------------------------------------------------------------------
296    void TerrainRenderable::_calculateNormals()
297    {
298
299        Vector3 norm;
300
301        assert (mOptions->lit && "No normals present");
302
303        HardwareVertexBufferSharedPtr vbuf = 
304            mTerrain->vertexBufferBinding->getBuffer(MAIN_BINDING);
305        const VertexElement* elem = mTerrain->vertexDeclaration->findElementBySemantic(VES_NORMAL);
306        unsigned char* pBase = static_cast<unsigned char*>( vbuf->lock(HardwareBuffer::HBL_DISCARD) );
307        float* pNorm;
308
309        for ( size_t j = 0; j < mOptions->tileSize; j++ )
310        {
311            for ( size_t i = 0; i < mOptions->tileSize; i++ )
312            {
313
314                _getNormalAt( _vertex( i, j, 0 ), _vertex( i, j, 2 ), &norm );
315
316                //  printf( "Normal = %5f,%5f,%5f\n", norm.x, norm.y, norm.z );
317                elem->baseVertexPointerToElement(pBase, &pNorm);
318                *pNorm++ = norm.x;
319                *pNorm++ = norm.y;
320                *pNorm++ = norm.z;
321                pBase += vbuf->getVertexSize();
322            }
323
324        }
325        vbuf->unlock();
326    }
327    //-----------------------------------------------------------------------
328    void TerrainRenderable::_notifyCurrentCamera( Camera* cam )
329    {
330                MovableObject::_notifyCurrentCamera(cam);
331
332        if ( mForcedRenderLevel >= 0 )
333        {
334            mRenderLevel = mForcedRenderLevel;
335            return ;
336        }
337
338
339        Vector3 cpos = cam -> getDerivedPosition();
340        const AxisAlignedBox& aabb = getWorldBoundingBox(true);
341        Vector3 diff(0, 0, 0);
342        diff.makeFloor(cpos - aabb.getMinimum());
343        diff.makeCeil(cpos - aabb.getMaximum());
344
345        Real L = diff.squaredLength();
346
347        mRenderLevel = -1;
348
349        for ( int i = 0; i < mOptions->maxGeoMipMapLevel; i++ )
350        {
351            if ( mMinLevelDistSqr[ i ] > L )
352            {
353                mRenderLevel = i - 1;
354                break;
355            }
356        }
357
358        if ( mRenderLevel < 0 )
359            mRenderLevel = mOptions->maxGeoMipMapLevel - 1;
360
361        if (mOptions->lodMorph)
362        {
363            // Get the next LOD level down
364            int nextLevel = mNextLevelDown[mRenderLevel];
365            if (nextLevel == 0)
366            {
367                // No next level, so never morph
368                mLODMorphFactor = 0;
369            }
370            else
371            {
372                // Set the morph such that the morph happens in the last 0.25 of
373                // the distance range
374                Real range = mMinLevelDistSqr[nextLevel] - mMinLevelDistSqr[mRenderLevel];
375                if (range)
376                {
377                    Real percent = (L - mMinLevelDistSqr[mRenderLevel]) / range;
378                    // scale result so that msLODMorphStart == 0, 1 == 1, clamp to 0 below that
379                    Real rescale = 1.0f / (1.0f - mOptions->lodMorphStart);
380                    mLODMorphFactor = std::max((percent - mOptions->lodMorphStart) * rescale, 
381                                                static_cast<Real>(0.0));
382                }
383                else
384                {
385                    // Identical ranges
386                    mLODMorphFactor = 0.0f;
387                }
388
389                assert(mLODMorphFactor >= 0 && mLODMorphFactor <= 1);
390            }
391
392            // Bind the correct delta buffer if it has changed
393            // nextLevel - 1 since the first entry is for LOD 1 (since LOD 0 never needs it)
394            if (mLastNextLevel != nextLevel)
395            {
396                if (nextLevel > 0)
397                {
398                    mTerrain->vertexBufferBinding->setBinding(DELTA_BINDING, 
399                        mDeltaBuffers[nextLevel - 1]);
400                }
401                else
402                {
403                    // bind dummy (incase bindings checked)
404                    mTerrain->vertexBufferBinding->setBinding(DELTA_BINDING, 
405                        mDeltaBuffers[0]);
406                }
407            }
408            mLastNextLevel = nextLevel;
409
410        }
411
412    }
413    //-----------------------------------------------------------------------
414    void TerrainRenderable::_updateRenderQueue( RenderQueue* queue )
415    {
416        // Notify need to calculate light list when our sending to render queue
417        mLightListDirty = true;
418
419        queue->addRenderable(this, mRenderQueueID);
420    }
421    //-----------------------------------------------------------------------
422    void TerrainRenderable::getRenderOperation( RenderOperation& op )
423    {
424        //setup indexes for vertices and uvs...
425
426        assert( mInit && "Uninitialized" );
427
428        op.useIndexes = true;
429        op.operationType = mOptions->useTriStrips ? 
430            RenderOperation::OT_TRIANGLE_STRIP : RenderOperation::OT_TRIANGLE_LIST;
431        op.vertexData = mTerrain;
432        op.indexData = getIndexData();
433
434
435    }
436    //-----------------------------------------------------------------------
437    void TerrainRenderable::getWorldTransforms( Matrix4* xform ) const
438    {
439        *xform = mParentNode->_getFullTransform();
440    }
441    //-----------------------------------------------------------------------
442    const Quaternion& TerrainRenderable::getWorldOrientation(void) const
443    {
444        return mParentNode->_getDerivedOrientation();
445    }
446    //-----------------------------------------------------------------------
447    const Vector3& TerrainRenderable::getWorldPosition(void) const
448    {
449        return mParentNode->_getDerivedPosition();
450    }
451    //-----------------------------------------------------------------------
452    bool TerrainRenderable::_checkSize( int n )
453    {
454        for ( int i = 0; i < 10; i++ )
455        {
456            if ( ( ( 1 << i ) + 1 ) == n )
457                return true;
458        }
459
460        return false;
461    }
462    //-----------------------------------------------------------------------
463    void TerrainRenderable::_calculateMinLevelDist2( Real C )
464    {
465        //level 0 has no delta.
466        mMinLevelDistSqr[ 0 ] = 0;
467
468        int i, j;
469
470        for ( int level = 1; level < mOptions->maxGeoMipMapLevel; level++ )
471        {
472            mMinLevelDistSqr[ level ] = 0;
473
474            int step = 1 << level;
475            // The step of the next higher LOD
476            int higherstep = step >> 1;
477
478            float* pDeltas = 0;
479            if (mOptions->lodMorph)
480            {
481                // Create a set of delta values (store at index - 1 since 0 has none)
482                mDeltaBuffers[level - 1]  = createDeltaBuffer();
483                // Lock, but don't discard (we want the pre-initialised zeros)
484                pDeltas = static_cast<float*>(
485                    mDeltaBuffers[level - 1]->lock(HardwareBuffer::HBL_NORMAL));
486            }
487
488            for ( j = 0; j < mOptions->tileSize - step; j += step )
489            {
490                for ( i = 0; i < mOptions->tileSize - step; i += step )
491                {
492                    /* Form planes relating to the lower detail tris to be produced
493                    For tri lists and even tri strip rows, they are this shape:
494                    x---x
495                    | / |
496                    x---x
497                    For odd tri strip rows, they are this shape:
498                    x---x
499                    | \ |
500                    x---x
501                    */
502
503                    Vector3 v1(_vertex( i, j, 0 ), _vertex( i, j, 1 ), _vertex( i, j, 2 ));
504                    Vector3 v2(_vertex( i + step, j, 0 ), _vertex( i + step, j, 1 ), _vertex( i + step, j, 2 ));
505                    Vector3 v3(_vertex( i, j + step, 0 ), _vertex( i, j + step, 1 ), _vertex( i, j + step, 2 ));
506                    Vector3 v4(_vertex( i + step, j + step, 0 ), _vertex( i + step, j + step, 1 ), _vertex( i + step, j + step, 2 ));
507
508                    Plane t1, t2;
509                    bool backwardTri = false;
510                    if (!mOptions->useTriStrips || j % 2 == 0)
511                    {
512                        t1.redefine(v1, v3, v2);
513                        t2.redefine(v2, v3, v4);
514                    }
515                    else
516                    {
517                        t1.redefine(v1, v3, v4);
518                        t2.redefine(v1, v4, v2);
519                        backwardTri = true;
520                    }
521
522                    // include the bottommost row of vertices if this is the last row
523                    int zubound = (j == (mOptions->tileSize - step)? step : step - 1);
524                    for ( int z = 0; z <= zubound; z++ )
525                    {
526                        // include the rightmost col of vertices if this is the last col
527                        int xubound = (i == (mOptions->tileSize - step)? step : step - 1);
528                        for ( int x = 0; x <= xubound; x++ )
529                        {
530                            int fulldetailx = i + x;
531                            int fulldetailz = j + z;
532                            if ( fulldetailx % step == 0 && 
533                                fulldetailz % step == 0 )
534                            {
535                                // Skip, this one is a vertex at this level
536                                continue;
537                            }
538
539                            Real zpct = (Real)z / (Real)step;
540                            Real xpct = (Real)x / (Real)step;
541
542                            //interpolated height
543                            Vector3 actualPos(
544                                _vertex( fulldetailx, fulldetailz, 0 ), 
545                                _vertex( fulldetailx, fulldetailz, 1 ), 
546                                _vertex( fulldetailx, fulldetailz, 2 ));
547                            Real interp_h;
548                            // Determine which tri we're on
549                            if ((xpct + zpct <= 1.0f && !backwardTri) ||
550                                (xpct + (1-zpct) <= 1.0f && backwardTri))
551                            {
552                                // Solve for x/z
553                                interp_h = 
554                                    (-(t1.normal.x * actualPos.x)
555                                    - t1.normal.z * actualPos.z
556                                    - t1.d) / t1.normal.y;
557                            }
558                            else
559                            {
560                                // Second tri
561                                interp_h = 
562                                    (-(t2.normal.x * actualPos.x)
563                                    - t2.normal.z * actualPos.z
564                                    - t2.d) / t2.normal.y;
565                            }
566
567                            Real actual_h = _vertex( fulldetailx, fulldetailz, 1 );
568                            Real delta = fabs( interp_h - actual_h );
569
570                            Real D2 = delta * delta * C * C;
571
572                            if ( mMinLevelDistSqr[ level ] < D2 )
573                                mMinLevelDistSqr[ level ] = D2;
574
575                            // Should be save height difference?
576                            // Don't morph along edges
577                            if (mOptions->lodMorph && 
578                                fulldetailx != 0  && fulldetailx != (mOptions->tileSize - 1) && 
579                                fulldetailz != 0  && fulldetailz != (mOptions->tileSize - 1) )
580                            {
581                                // Save height difference
582                                pDeltas[fulldetailx + (fulldetailz * mOptions->tileSize)] = 
583                                    interp_h - actual_h;
584                            }
585
586                        }
587
588                    }
589                }
590            }
591
592            // Unlock morph deltas if required
593            if (mOptions->lodMorph)
594            {
595                mDeltaBuffers[level - 1]->unlock();
596            }
597        }
598
599
600
601        // Post validate the whole set
602        for ( i = 1; i < mOptions->maxGeoMipMapLevel; i++ )
603        {
604
605            // Make sure no LOD transition within the tile
606            // This is especially a problem when using large tiles with flat areas
607            /* Hmm, this can look bad on some areas, disable for now
608            Vector3 delta(_vertex(0,0,0), mCenter.y, _vertex(0,0,2));
609            delta = delta - mCenter;
610            Real minDist = delta.squaredLength();
611            mMinLevelDistSqr[ i ] = std::max(mMinLevelDistSqr[ i ], minDist);
612            */
613
614            //make sure the levels are increasing...
615            if ( mMinLevelDistSqr[ i ] < mMinLevelDistSqr[ i - 1 ] )
616            {
617                mMinLevelDistSqr[ i ] = mMinLevelDistSqr[ i - 1 ];
618            }
619        }
620
621        // Now reverse traverse the list setting the 'next level down'
622        Real lastDist = -1;
623        int lastIndex = 0;
624        for (i = mOptions->maxGeoMipMapLevel - 1; i >= 0; --i)
625        {
626            if (i == mOptions->maxGeoMipMapLevel - 1)
627            {
628                // Last one is always 0
629                lastIndex = i;
630                lastDist = mMinLevelDistSqr[i];
631                mNextLevelDown[i] = 0;
632            }
633            else
634            {
635                mNextLevelDown[i] = lastIndex;
636                if (mMinLevelDistSqr[i] != lastDist)
637                {
638                    lastIndex = i;
639                    lastDist = mMinLevelDistSqr[i];
640                }
641            }
642
643        }
644
645
646    }
647    //-----------------------------------------------------------------------
648    void TerrainRenderable::_adjustRenderLevel( int i )
649    {
650
651        mRenderLevel = i;
652    }
653    //-----------------------------------------------------------------------
654    Real TerrainRenderable::_calculateCFactor()
655    {
656        Real A, T;
657
658        if (!mOptions->primaryCamera)
659        {
660            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, 
661                "You have not created a camera yet!", 
662                "TerrainRenderable::_calculateCFactor");
663        }
664
665        //A = 1 / Math::Tan(Math::AngleUnitsToRadians(opts.primaryCamera->getFOVy()));
666        // Turn off detail compression at higher FOVs
667        A = 1.0f;
668
669        int vertRes = mOptions->primaryCamera->getViewport()->getActualHeight();
670
671        T = 2 * ( Real ) mOptions->maxPixelError / ( Real ) vertRes;
672
673        return A / T;
674    }
675    //-----------------------------------------------------------------------
676    float TerrainRenderable::getHeightAt( float x, float z )
677    {
678        Vector3 start;
679        Vector3 end;
680
681        start.x = _vertex( 0, 0, 0 );
682        start.y = _vertex( 0, 0, 1 );
683        start.z = _vertex( 0, 0, 2 );
684
685        end.x = _vertex( mOptions->tileSize - 1, mOptions->tileSize - 1, 0 );
686        end.y = _vertex( mOptions->tileSize - 1, mOptions->tileSize - 1, 1 );
687        end.z = _vertex( mOptions->tileSize - 1, mOptions->tileSize - 1, 2 );
688
689        /* Safety catch, if the point asked for is outside
690        * of this tile, it will ask the appropriate tile
691        */
692
693        if ( x < start.x )
694        {
695            if ( mNeighbors[ WEST ] != 0 )
696                return mNeighbors[ WEST ] ->getHeightAt( x, z );
697            else
698                x = start.x;
699        }
700
701        if ( x > end.x )
702        {
703            if ( mNeighbors[ EAST ] != 0 )
704                return mNeighbors[ EAST ] ->getHeightAt( x, z );
705            else
706                x = end.x;
707        }
708
709        if ( z < start.z )
710        {
711            if ( mNeighbors[ NORTH ] != 0 )
712                return mNeighbors[ NORTH ] ->getHeightAt( x, z );
713            else
714                z = start.z;
715        }
716
717        if ( z > end.z )
718        {
719            if ( mNeighbors[ SOUTH ] != 0 )
720                return mNeighbors[ SOUTH ] ->getHeightAt( x, z );
721            else
722                z = end.z;
723        }
724
725
726
727        float x_pct = ( x - start.x ) / ( end.x - start.x );
728        float z_pct = ( z - start.z ) / ( end.z - start.z );
729
730        float x_pt = x_pct * ( float ) ( mOptions->tileSize - 1 );
731        float z_pt = z_pct * ( float ) ( mOptions->tileSize - 1 );
732
733        int x_index = ( int ) x_pt;
734        int z_index = ( int ) z_pt;
735
736        // If we got to the far right / bottom edge, move one back
737        if (x_index == mOptions->tileSize - 1)
738        {
739            --x_index;
740            x_pct = 1.0f;
741        }
742        else
743        {
744            // get remainder
745            x_pct = x_pt - x_index;
746        }
747        if (z_index == mOptions->tileSize - 1)
748        {
749            --z_index;
750            z_pct = 1.0f;
751        }
752        else
753        {
754            z_pct = z_pt - z_index;
755        }
756
757        //bilinear interpolate to find the height.
758
759        float t1 = _vertex( x_index, z_index, 1 );
760        float t2 = _vertex( x_index + 1, z_index, 1 );
761        float b1 = _vertex( x_index, z_index + 1, 1 );
762        float b2 = _vertex( x_index + 1, z_index + 1, 1 );
763
764        float midpoint = (b1 + t2) / 2.0;
765
766        if (x_pct + z_pct <= 1) {
767            b2 = midpoint + (midpoint - t1);
768        } else {
769            t1 = midpoint + (midpoint - b2);
770        }
771
772        float t = ( t1 * ( 1 - x_pct ) ) + ( t2 * ( x_pct ) );
773        float b = ( b1 * ( 1 - x_pct ) ) + ( b2 * ( x_pct ) );
774
775        float h = ( t * ( 1 - z_pct ) ) + ( b * ( z_pct ) );
776
777        return h;
778    }
779    //-----------------------------------------------------------------------
780    bool TerrainRenderable::intersectSegment( const Vector3 & start, const Vector3 & end, Vector3 * result )
781    {
782        Vector3 dir = end - start;
783        Vector3 ray = start;
784
785        //special case...
786        if ( dir.x == 0 && dir.z == 0 )
787        {
788            if ( ray.y <= getHeightAt( ray.x, ray.z ) )
789            {
790                if ( result != 0 )
791                    * result = start;
792
793                return true;
794            }
795        }
796
797        dir.normalise();
798
799        //dir.x *= mScale.x;
800        //dir.y *= mScale.y;
801        //dir.z *= mScale.z;
802
803        const AxisAlignedBox& box = getBoundingBox();
804        //start with the next one...
805        ray += dir;
806
807
808        while ( ! ( ( ray.x < box.getMinimum().x ) ||
809            ( ray.x > box.getMaximum().x ) ||
810            ( ray.z < box.getMinimum().z ) ||
811            ( ray.z > box.getMaximum().z ) ) )
812        {
813
814
815            float h = getHeightAt( ray.x, ray.z );
816
817            if ( ray.y <= h )
818            {
819                if ( result != 0 )
820                    * result = ray;
821
822                return true;
823            }
824
825            else
826            {
827                ray += dir;
828            }
829
830        }
831
832        if ( ray.x < box.getMinimum().x && mNeighbors[ WEST ] != 0 )
833            return mNeighbors[ WEST ] ->intersectSegment( ray, end, result );
834        else if ( ray.z < box.getMinimum().z && mNeighbors[ NORTH ] != 0 )
835            return mNeighbors[ NORTH ] ->intersectSegment( ray, end, result );
836        else if ( ray.x > box.getMaximum().x && mNeighbors[ EAST ] != 0 )
837            return mNeighbors[ EAST ] ->intersectSegment( ray, end, result );
838        else if ( ray.z > box.getMaximum().z && mNeighbors[ SOUTH ] != 0 )
839            return mNeighbors[ SOUTH ] ->intersectSegment( ray, end, result );
840        else
841        {
842            if ( result != 0 )
843                * result = Vector3( -1, -1, -1 );
844
845            return false;
846        }
847    }
848    //-----------------------------------------------------------------------
849    void TerrainRenderable::_generateVertexLighting( const Vector3 &sunlight, ColourValue ambient )
850    {
851
852        Vector3 pt;
853        Vector3 normal;
854        Vector3 light;
855
856        HardwareVertexBufferSharedPtr vbuf = 
857            mTerrain->vertexBufferBinding->getBuffer(MAIN_BINDING);
858        const VertexElement* elem = mTerrain->vertexDeclaration->findElementBySemantic(VES_DIFFUSE);
859        //for each point in the terrain, see if it's in the line of sight for the sun.
860        for ( size_t i = 0; i < mOptions->tileSize; i++ )
861        {
862            for ( size_t j = 0; j < mOptions->tileSize; j++ )
863            {
864                //  printf( "Checking %f,%f,%f ", pt.x, pt.y, pt.z );
865                pt.x = _vertex( i, j, 0 );
866                pt.y = _vertex( i, j, 1 );
867                pt.z = _vertex( i, j, 2 );
868
869                light = sunlight - pt;
870
871                light.normalise();
872
873                if ( ! intersectSegment( pt, sunlight, 0 ) )
874                {
875                    //
876                    _getNormalAt( _vertex( i, j, 0 ), _vertex( i, j, 2 ), &normal );
877
878                    float l = light.dotProduct( normal );
879
880                    ColourValue v;
881                    v.r = ambient.r + l;
882                    v.g = ambient.g + l;
883                    v.b = ambient.b + l;
884
885                    if ( v.r > 1 ) v.r = 1;
886
887                    if ( v.g > 1 ) v.g = 1;
888
889                    if ( v.b > 1 ) v.b = 1;
890
891                    if ( v.r < 0 ) v.r = 0;
892
893                    if ( v.g < 0 ) v.g = 0;
894
895                    if ( v.b < 0 ) v.b = 0;
896
897                    RGBA colour;
898                    Root::getSingleton().convertColourValue( v, &colour );
899                    vbuf->writeData(
900                        (_index( i, j ) * vbuf->getVertexSize()) + elem->getOffset(),
901                        sizeof(RGBA), &colour);
902                }
903
904                else
905                {
906                    RGBA colour;
907                    Root::getSingleton().convertColourValue( ambient, &colour );
908
909                    vbuf->writeData(
910                        (_index( i, j ) * vbuf->getVertexSize()) + elem->getOffset(), 
911                        sizeof(RGBA), &colour);
912                }
913
914            }
915
916        }
917
918        printf( "." );
919    }
920    //-----------------------------------------------------------------------
921    Real TerrainRenderable::getSquaredViewDepth(const Camera* cam) const
922    {
923        Vector3 diff = mCenter - cam->getDerivedPosition();
924        // Use squared length to avoid square root
925        return diff.squaredLength();
926    }
927
928    //-----------------------------------------------------------------------
929    const LightList& TerrainRenderable::getLights(void) const
930    {
931        if (mLightListDirty)
932        {
933            getParentSceneNode()->getCreator()->_populateLightList(
934                mCenter, this->getBoundingRadius(), mLightList);
935            mLightListDirty = false;
936        }
937        return mLightList;
938    }
939    //-----------------------------------------------------------------------
940    IndexData* TerrainRenderable::getIndexData(void)
941    {
942        unsigned int stitchFlags = 0;
943
944        if ( mNeighbors[ EAST ] != 0 && mNeighbors[ EAST ] -> mRenderLevel > mRenderLevel )
945        {
946            stitchFlags |= STITCH_EAST;
947            stitchFlags |= 
948                (mNeighbors[ EAST ] -> mRenderLevel - mRenderLevel) << STITCH_EAST_SHIFT;
949        }
950
951        if ( mNeighbors[ WEST ] != 0 && mNeighbors[ WEST ] -> mRenderLevel > mRenderLevel )
952        {
953            stitchFlags |= STITCH_WEST;
954            stitchFlags |= 
955                (mNeighbors[ WEST ] -> mRenderLevel - mRenderLevel) << STITCH_WEST_SHIFT;
956        }
957
958        if ( mNeighbors[ NORTH ] != 0 && mNeighbors[ NORTH ] -> mRenderLevel > mRenderLevel )
959        {
960            stitchFlags |= STITCH_NORTH;
961            stitchFlags |= 
962                (mNeighbors[ NORTH ] -> mRenderLevel - mRenderLevel) << STITCH_NORTH_SHIFT;
963        }
964
965        if ( mNeighbors[ SOUTH ] != 0 && mNeighbors[ SOUTH ] -> mRenderLevel > mRenderLevel )
966        {
967            stitchFlags |= STITCH_SOUTH;
968            stitchFlags |= 
969                (mNeighbors[ SOUTH ] -> mRenderLevel - mRenderLevel) << STITCH_SOUTH_SHIFT;
970        }
971
972        // Check preexisting
973                LevelArray& levelIndex = mSceneManager->_getLevelIndex();
974        IndexMap::iterator ii = levelIndex[ mRenderLevel ]->find( stitchFlags );
975        IndexData* indexData;
976        if ( ii == levelIndex[ mRenderLevel ]->end())
977        {
978            // Create
979            if (mOptions->useTriStrips)
980            {
981                indexData = generateTriStripIndexes(stitchFlags);
982            }
983            else
984            {
985                indexData = generateTriListIndexes(stitchFlags);
986            }
987            levelIndex[ mRenderLevel ]->insert(
988                IndexMap::value_type(stitchFlags, indexData));
989        }
990        else
991        {
992            indexData = ii->second;
993        }
994
995
996        return indexData;
997
998
999    }
1000    //-----------------------------------------------------------------------
1001    IndexData* TerrainRenderable::generateTriStripIndexes(unsigned int stitchFlags)
1002    {
1003        // The step used for the current level
1004        int step = 1 << mRenderLevel;
1005        // The step used for the lower level
1006        int lowstep = 1 << (mRenderLevel + 1);
1007
1008        int numIndexes = 0;
1009
1010        // Calculate the number of indexes required
1011        // This is the number of 'cells' at this detail level x 2
1012        // plus 3 degenerates to turn corners
1013        int numTrisAcross = (((mOptions->tileSize-1) / step) * 2) + 3;
1014        // Num indexes is number of tris + 2
1015        int new_length = numTrisAcross * ((mOptions->tileSize-1) / step) + 2;
1016        //this is the maximum for a level.  It wastes a little, but shouldn't be a problem.
1017
1018        IndexData* indexData = new IndexData;
1019        indexData->indexBuffer = 
1020            HardwareBufferManager::getSingleton().createIndexBuffer(
1021            HardwareIndexBuffer::IT_16BIT,
1022            new_length, HardwareBuffer::HBU_STATIC_WRITE_ONLY);//, false);
1023
1024        mSceneManager->_getIndexCache().mCache.push_back( indexData );
1025
1026        unsigned short* pIdx = static_cast<unsigned short*>(
1027            indexData->indexBuffer->lock(0, 
1028            indexData->indexBuffer->getSizeInBytes(), 
1029            HardwareBuffer::HBL_DISCARD));
1030
1031        // Stripified mesh
1032        for ( int j = 0; j < mOptions->tileSize - 1; j += step )
1033        {
1034            int i;
1035            // Forward strip
1036            // We just do the |/ here, final | done after
1037            for ( i = 0; i < mOptions->tileSize - 1; i += step )
1038            {
1039                int x[4], y[4];
1040                x[0] = x[1] = i;
1041                x[2] = x[3] = i + step;
1042                y[0] = y[2] = j;
1043                y[1] = y[3] = j + step;
1044
1045                if (j == 0  && (stitchFlags & STITCH_NORTH))
1046                {
1047                    // North reduction means rounding x[0] and x[2]
1048                    if (x[0] % lowstep != 0)
1049                    {
1050                        // Since we know we only drop down one level of LOD,
1051                        // removing 1 step of higher LOD should return to lower
1052                        x[0] -= step;
1053                    }
1054                    if (x[2] % lowstep != 0)
1055                    {
1056                        x[2] -= step;
1057                    }
1058                }
1059
1060                // Never get a south tiling on a forward strip (always finish on
1061                // a backward strip)
1062
1063                if (i == 0  && (stitchFlags & STITCH_WEST))
1064                {
1065                    // West reduction means rounding y[0] / y[1]
1066                    if (y[0] % lowstep != 0)
1067                    {
1068                        y[0] -= step;
1069                    }
1070                    if (y[1] % lowstep != 0)
1071                    {
1072                        y[1] -= step;
1073                    }
1074                }
1075                if (i == (mOptions->tileSize - 1 - step) && (stitchFlags & STITCH_EAST))
1076                {
1077                    // East tiling means rounding y[2] & y[3]
1078                    if (y[2] % lowstep != 0)
1079                    {
1080                        y[2] -= step;
1081                    }
1082                    if (y[3] % lowstep != 0)
1083                    {
1084                        y[3] -= step;
1085                    }
1086                }
1087
1088                //triangles
1089                if (i == 0)
1090                {
1091                    // Starter
1092                    *pIdx++ = _index( x[0], y[0] ); numIndexes++;
1093                }
1094                *pIdx++ = _index( x[1], y[1] ); numIndexes++;
1095                *pIdx++ = _index( x[2], y[2] ); numIndexes++;
1096
1097                if (i == mOptions->tileSize - 1 - step)
1098                {
1099                    // Emit extra index to finish row
1100                    *pIdx++ = _index( x[3], y[3] ); numIndexes++;
1101                    if (j < mOptions->tileSize - 1 - step)
1102                    {
1103                        // Emit this index twice more (this is to turn around without
1104                        // artefacts)
1105                        // ** Hmm, looks like we can drop this and it's unnoticeable
1106                        //*pIdx++ = _index( x[3], y[3] ); numIndexes++;
1107                        //*pIdx++ = _index( x[3], y[3] ); numIndexes++;
1108                    }
1109                }
1110
1111            }
1112            // Increment row
1113            j += step;
1114            // Backward strip
1115            for ( i = mOptions->tileSize - 1; i > 0 ; i -= step )
1116            {
1117                int x[4], y[4];
1118                x[0] = x[1] = i;
1119                x[2] = x[3] = i - step;
1120                y[0] = y[2] = j;
1121                y[1] = y[3] = j + step;
1122
1123                // Never get a north tiling on a backward strip (always
1124                // start on a forward strip)
1125                if (j == (mOptions->tileSize - 1 - step) && (stitchFlags & STITCH_SOUTH))
1126                {
1127                    // South reduction means rounding x[1] / x[3]
1128                    if (x[1] % lowstep != 0)
1129                    {
1130                        x[1] -= step;
1131                    }
1132                    if (x[3] % lowstep != 0)
1133                    {
1134                        x[3] -= step;
1135                    }
1136                }
1137
1138                if (i == step  && (stitchFlags & STITCH_WEST))
1139                {
1140                    // West tiling on backward strip is rounding of y[2] / y[3]
1141                    if (y[2] % lowstep != 0)
1142                    {
1143                        y[2] -= step;
1144                    }
1145                    if (y[3] % lowstep != 0)
1146                    {
1147                        y[3] -= step;
1148                    }
1149                }
1150                if (i == mOptions->tileSize - 1 && (stitchFlags & STITCH_EAST))
1151                {
1152                    // East tiling means rounding y[0] and y[1] on backward strip
1153                    if (y[0] % lowstep != 0)
1154                    {
1155                        y[0] -= step;
1156                    }
1157                    if (y[1] % lowstep != 0)
1158                    {
1159                        y[1] -= step;
1160                    }
1161                }
1162
1163                //triangles
1164                if (i == mOptions->tileSize)
1165                {
1166                    // Starter
1167                    *pIdx++ = _index( x[0], y[0] ); numIndexes++;
1168                }
1169                *pIdx++ = _index( x[1], y[1] ); numIndexes++;
1170                *pIdx++ = _index( x[2], y[2] ); numIndexes++;
1171
1172                if (i == step)
1173                {
1174                    // Emit extra index to finish row
1175                    *pIdx++ = _index( x[3], y[3] ); numIndexes++;
1176                    if (j < mOptions->tileSize - 1 - step)
1177                    {
1178                        // Emit this index once more (this is to turn around)
1179                        *pIdx++ = _index( x[3], y[3] ); numIndexes++;
1180                    }
1181                }
1182            }
1183        }
1184
1185
1186        indexData->indexBuffer->unlock();
1187        indexData->indexCount = numIndexes;
1188        indexData->indexStart = 0;
1189
1190        return indexData;
1191
1192    }
1193    //-----------------------------------------------------------------------
1194    IndexData* TerrainRenderable::generateTriListIndexes(unsigned int stitchFlags)
1195    {
1196
1197        int numIndexes = 0;
1198        int step = 1 << mRenderLevel;
1199
1200        IndexData* indexData = 0;
1201
1202        int north = stitchFlags & STITCH_NORTH ? step : 0;
1203        int south = stitchFlags & STITCH_SOUTH ? step : 0;
1204        int east = stitchFlags & STITCH_EAST ? step : 0;
1205        int west = stitchFlags & STITCH_WEST ? step : 0;
1206
1207        int new_length = ( mOptions->tileSize / step ) * ( mOptions->tileSize / step ) * 2 * 2 * 2 ;
1208        //this is the maximum for a level.  It wastes a little, but shouldn't be a problem.
1209
1210        indexData = new IndexData;
1211        indexData->indexBuffer = 
1212            HardwareBufferManager::getSingleton().createIndexBuffer(
1213            HardwareIndexBuffer::IT_16BIT,
1214            new_length, HardwareBuffer::HBU_STATIC_WRITE_ONLY);//, false);
1215
1216        mSceneManager->_getIndexCache().mCache.push_back( indexData );
1217
1218        unsigned short* pIdx = static_cast<unsigned short*>(
1219            indexData->indexBuffer->lock(0, 
1220            indexData->indexBuffer->getSizeInBytes(), 
1221            HardwareBuffer::HBL_DISCARD));
1222
1223        // Do the core vertices, minus stitches
1224        for ( int j = north; j < mOptions->tileSize - 1 - south; j += step )
1225        {
1226            for ( int i = west; i < mOptions->tileSize - 1 - east; i += step )
1227            {
1228                //triangles
1229                *pIdx++ = _index( i, j ); numIndexes++;
1230                *pIdx++ = _index( i, j + step ); numIndexes++;
1231                *pIdx++ = _index( i + step, j ); numIndexes++;
1232
1233                *pIdx++ = _index( i, j + step ); numIndexes++;
1234                *pIdx++ = _index( i + step, j + step ); numIndexes++;
1235                *pIdx++ = _index( i + step, j ); numIndexes++;
1236            }
1237        }
1238
1239        // North stitching
1240        if ( north > 0 )
1241        {
1242            numIndexes += stitchEdge(NORTH, mRenderLevel, mNeighbors[NORTH]->mRenderLevel,
1243                west > 0, east > 0, &pIdx);
1244        }
1245        // East stitching
1246        if ( east > 0 )
1247        {
1248            numIndexes += stitchEdge(EAST, mRenderLevel, mNeighbors[EAST]->mRenderLevel,
1249                north > 0, south > 0, &pIdx);
1250        }
1251        // South stitching
1252        if ( south > 0 )
1253        {
1254            numIndexes += stitchEdge(SOUTH, mRenderLevel, mNeighbors[SOUTH]->mRenderLevel,
1255                east > 0, west > 0, &pIdx);
1256        }
1257        // West stitching
1258        if ( west > 0 )
1259        {
1260            numIndexes += stitchEdge(WEST, mRenderLevel, mNeighbors[WEST]->mRenderLevel,
1261                south > 0, north > 0, &pIdx);
1262        }
1263
1264
1265        indexData->indexBuffer->unlock();
1266        indexData->indexCount = numIndexes;
1267        indexData->indexStart = 0;
1268
1269        return indexData;
1270    }
1271    //-----------------------------------------------------------------------
1272    HardwareVertexBufferSharedPtr TerrainRenderable::createDeltaBuffer(void)
1273    {
1274        // Delta buffer is a 1D float buffer of height offsets
1275        HardwareVertexBufferSharedPtr buf = 
1276            HardwareBufferManager::getSingleton().createVertexBuffer(
1277            VertexElement::getTypeSize(VET_FLOAT1), 
1278            mOptions->tileSize * mOptions->tileSize,
1279            HardwareBuffer::HBU_STATIC_WRITE_ONLY);
1280        // Fill the buffer with zeros, we will only fill in delta
1281        void* pVoid = buf->lock(HardwareBuffer::HBL_DISCARD);
1282        memset(pVoid, 0, mOptions->tileSize * mOptions->tileSize * sizeof(float));
1283        buf->unlock();
1284
1285        return buf;
1286
1287    }
1288    //-----------------------------------------------------------------------
1289    void TerrainRenderable::_updateCustomGpuParameter(
1290        const GpuProgramParameters::AutoConstantEntry& constantEntry, 
1291        GpuProgramParameters* params) const
1292    {
1293        if (constantEntry.data == MORPH_CUSTOM_PARAM_ID)
1294        {
1295            // Update morph LOD factor
1296            params->_writeRawConstant(constantEntry.physicalIndex, mLODMorphFactor);
1297        }
1298        else
1299        {
1300            Renderable::_updateCustomGpuParameter(constantEntry, params);
1301        }
1302
1303    }
1304    //-----------------------------------------------------------------------
1305    int TerrainRenderable::stitchEdge(Neighbor neighbor, int hiLOD, int loLOD, 
1306        bool omitFirstTri, bool omitLastTri, unsigned short** ppIdx)
1307    {
1308        assert(loLOD > hiLOD);
1309        /*
1310        Now do the stitching; we can stitch from any level to any level.
1311        The stitch pattern is like this for each pair of vertices in the lower LOD
1312        (excuse the poor ascii art):
1313
1314        lower LOD
1315        *-----------*
1316        |\  \ 3 /  /|
1317        |1\2 \ / 4/5|
1318        *--*--*--*--*
1319        higher LOD
1320
1321        The algorithm is, for each pair of lower LOD vertices:
1322        1. Iterate over the higher LOD vertices, generating tris connected to the
1323        first lower LOD vertex, up to and including 1/2 the span of the lower LOD
1324        over the higher LOD (tris 1-2). Skip the first tri if it is on the edge
1325        of the tile and that edge is to be stitched itself.
1326        2. Generate a single tri for the middle using the 2 lower LOD vertices and
1327        the middle vertex of the higher LOD (tri 3).
1328        3. Iterate over the higher LOD vertices from 1/2 the span of the lower LOD
1329        to the end, generating tris connected to the second lower LOD vertex
1330        (tris 4-5). Skip the last tri if it is on the edge of a tile and that
1331        edge is to be stitched itself.
1332
1333        The same algorithm works for all edges of the patch; stitching is done
1334        clockwise so that the origin and steps used change, but the general
1335        approach does not.
1336        */
1337
1338        // Get pointer to be updated
1339        unsigned short* pIdx = *ppIdx;
1340
1341        // Work out the steps ie how to increment indexes
1342        // Step from one vertex to another in the high detail version
1343        int step = 1 << hiLOD;
1344        // Step from one vertex to another in the low detail version
1345        int superstep = 1 << loLOD;
1346        // Step half way between low detail steps
1347        int halfsuperstep = superstep >> 1;
1348
1349        // Work out the starting points and sign of increments
1350        // We always work the strip clockwise
1351        int startx, starty, endx, rowstep;
1352        bool horizontal;
1353        switch(neighbor)
1354        {
1355        case NORTH:
1356            startx = starty = 0;
1357            endx = mOptions->tileSize - 1;
1358            rowstep = step;
1359            horizontal = true;
1360            break;
1361        case SOUTH:
1362            // invert x AND y direction, helps to keep same winding
1363            startx = starty = mOptions->tileSize - 1;
1364            endx = 0;
1365            rowstep = -step;
1366            step = -step;
1367            superstep = -superstep;
1368            halfsuperstep = -halfsuperstep;
1369            horizontal = true;
1370            break;
1371        case EAST:
1372            startx = 0;
1373            endx = mOptions->tileSize - 1;
1374            starty = mOptions->tileSize - 1;
1375            rowstep = -step;
1376            horizontal = false;
1377            break;
1378        case WEST:
1379            startx = mOptions->tileSize - 1;
1380            endx = 0;
1381            starty = 0;
1382            rowstep = step;
1383            step = -step;
1384            superstep = -superstep;
1385            halfsuperstep = -halfsuperstep;
1386            horizontal = false;
1387            break;
1388        };
1389
1390        int numIndexes = 0;
1391
1392        for ( int j = startx; j != endx; j += superstep )
1393        {
1394            int k;
1395            for (k = 0; k != halfsuperstep; k += step)
1396            {
1397                int jk = j + k;
1398                //skip the first bit of the corner?
1399                if ( j != startx || k != 0 || !omitFirstTri )
1400                {
1401                    if (horizontal)
1402                    {
1403                        *pIdx++ = _index( j , starty ); numIndexes++;
1404                        *pIdx++ = _index( jk, starty + rowstep ); numIndexes++;
1405                        *pIdx++ = _index( jk + step, starty + rowstep ); numIndexes++;
1406                    }
1407                    else
1408                    {
1409                        *pIdx++ = _index( starty, j ); numIndexes++;
1410                        *pIdx++ = _index( starty + rowstep, jk ); numIndexes++;
1411                        *pIdx++ = _index( starty + rowstep, jk + step); numIndexes++;
1412                    }
1413                }
1414            }
1415
1416            // Middle tri
1417            if (horizontal)
1418            {
1419                *pIdx++ = _index( j, starty ); numIndexes++;
1420                *pIdx++ = _index( j + halfsuperstep, starty + rowstep); numIndexes++;
1421                *pIdx++ = _index( j + superstep, starty ); numIndexes++;
1422            }
1423            else
1424            {
1425                *pIdx++ = _index( starty, j ); numIndexes++;
1426                *pIdx++ = _index( starty + rowstep, j + halfsuperstep ); numIndexes++;
1427                *pIdx++ = _index( starty, j + superstep ); numIndexes++;
1428            }
1429
1430            for (k = halfsuperstep; k != superstep; k += step)
1431            {
1432                int jk = j + k;
1433                if ( j != endx - superstep || k != superstep - step || !omitLastTri )
1434                {
1435                    if (horizontal)
1436                    {
1437                        *pIdx++ = _index( j + superstep, starty ); numIndexes++;
1438                        *pIdx++ = _index( jk, starty + rowstep ); numIndexes++;
1439                        *pIdx++ = _index( jk + step, starty + rowstep ); numIndexes++;
1440                    }
1441                    else
1442                    {
1443                        *pIdx++ = _index( starty, j + superstep ); numIndexes++;
1444                        *pIdx++ = _index( starty + rowstep, jk ); numIndexes++;
1445                        *pIdx++ = _index( starty + rowstep, jk + step ); numIndexes++;
1446                    }
1447                }
1448            }
1449        }
1450
1451        *ppIdx = pIdx;
1452
1453        return numIndexes;
1454
1455    }
1456
1457
1458} //namespace
Note: See TracBrowser for help on using the repository browser.