source: OGRE/trunk/ogrenew/PlugIns/OctreeSceneManager/src/OgreTerrainRenderable.cpp @ 692

Revision 692, 51.7 KB checked in by mattausch, 18 years ago (diff)

adding ogre 1.2 and dependencies

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