root/cafu/trunk/CaTools/TerrainViewerOld.cpp

Revision 455, 34.4 KB (checked in by Carsten, 4 months ago)

Updated copyright banners (in C++ files).

Line 
1/*
2=================================================================================
3This file is part of Cafu, the open-source game engine and graphics engine
4for multiplayer, cross-platform, real-time 3D action.
5Copyright (C) 2002-2012 Carsten Fuchs Software.
6
7Cafu is free software: you can redistribute it and/or modify it under the terms
8of the GNU General Public License as published by the Free Software Foundation,
9either version 3 of the License, or (at your option) any later version.
10
11Cafu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13PURPOSE. See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with Cafu. If not, see <http://www.gnu.org/licenses/>.
17
18For support and more information about Cafu, visit us at <http://www.cafu.de>.
19=================================================================================
20*/
21
22#include <float.h>
23#include <math.h>
24#include <stdio.h>
25#include <string.h>
26
27#if defined _WIN32 && defined (_MSC_VER)
28#define WIN32_LEAN_AND_MEAN
29#include <windows.h>
30#if (_MSC_VER<1300)
31        #define for if (false) ; else for
32    #endif
33#endif
34
35#include <GL/gl.h>
36#include <GL/glu.h>
37#include "MaterialSystem/Common/OpenGLEx.hpp"   // for glext.h
38
39#include "ConsoleCommands/Console.hpp"
40#include "ConsoleCommands/ConsoleStdout.hpp"
41#include "FileSys/FileManImpl.hpp"
42#include "Templates/Array.hpp"
43#include "Math3D/Plane3.hpp"
44#include "Bitmap/Bitmap.hpp"
45#include "OpenGL/OpenGLWindow.hpp"
46#include "Util/Util.hpp"
47#include "Terrain/Terrain.hpp"
48
49
50static cf::ConsoleStdoutT ConsoleStdout;
51cf::ConsoleI* Console=&ConsoleStdout;
52
53static cf::FileSys::FileManImplT FileManImpl;
54cf::FileSys::FileManI* cf::FileSys::FileMan=&FileManImpl;
55
56
57// ******************** common.h ************************************************
58
59// Miscellaneous math macros.
60#define DEG2RAD(x)      ((3.1415927f / 180.0f) * (x))
61#define MAX(a, b)       ((a) > (b) ? (a) : (b))
62#define SQR(x)          ((x) * (x))
63
64// ******************** index.h ************************************************
65
66#define LINEAR_INDEX(i, j, m)   ((i) + (j) + ((j) << (m)))
67#define LINEAR_SPLIT(i, j, k)   (((j) + (k)) / 2)
68#define LINEAR_CHILD_L(i, j, k) LINEAR_SPLIT(i, j, k), j, i
69#define LINEAR_CHILD_R(i, j, k) LINEAR_SPLIT(i, j, k), i, k
70
71// Standard row-major (linear) matrix layout.
72#define I_SW(m)               LINEAR_INDEX(0 << (m), 0 << (m), m)
73#define I_SE(m)               LINEAR_INDEX(1 << (m), 0 << (m), m)
74#define I_NE(m)               LINEAR_INDEX(1 << (m), 1 << (m), m)
75#define I_NW(m)               LINEAR_INDEX(0 << (m), 1 << (m), m)
76#define I_C(m)                LINEAR_INDEX(1 << ((m) - 1), 1 << ((m) - 1), m)
77#define ROOT_S(m)             I_C(m), I_SW(m), I_SE(m)
78#define ROOT_E(m)             I_C(m), I_SE(m), I_NE(m)
79#define ROOT_N(m)             I_C(m), I_NE(m), I_NW(m)
80#define ROOT_W(m)             I_C(m), I_NW(m), I_SW(m)
81#define TRIANGLE(i, j, k)     i, j, k
82#define SPLIT(i, j, k)        LINEAR_SPLIT(i, j, k)
83#define CHILD_L(i, j, k)      LINEAR_CHILD_L(i, j, k)
84#define CHILD_R(i, j, k)      LINEAR_CHILD_R(i, j, k)
85
86
87// ******************** terrain.h ************************************************
88
89typedef unsigned long VERTEXid;
90
91// TODO: struct VERTEX : public VectorT   ???
92struct VERTEX
93{
94    VectorT p;     // vertex position
95    float   e;     // error
96    float   r;     // bounding sphere radius
97};
98
99
100// ******************** End Of Headers.h ************************************************
101
102
103struct TerrainOldT
104{
105    ArrayT<VERTEX> Vertices;
106    unsigned long  Size;        // Lateral dimensions of the terrain / heightmap, e.g. 257, 513, 1025, ...
107    unsigned long  Levels;      // Size == 2^(Levels/2)+1
108
109
110    unsigned long GetIdx(unsigned long i, unsigned long j)
111    {
112        return i+Size*j;
113    }
114
115
116    // i  - column index
117    // j  - row index
118    // di - non-negative col offset to bisected edge endpoint
119    // dj - row offset to bisected edge endpoint
120    // n  - one less array width/height (zero for leaves)
121    void ComputeVertexLoD(unsigned long i, unsigned long j, int di, int dj, unsigned long n)
122    {
123        VERTEX& Vertex=Vertices[GetIdx(i, j)];
124
125        // Compute actual error and initialize radius to zero. The error is simply the vertical difference between the vertex and the bisected edge,
126        // i.e. the error between two consecutive levels of refinement. This object-space error can be replaced with any measure of error,
127        // as long as the nesting step below is performed.
128        Vertex.e=float(fabs(Vertex.p.z-0.5*(Vertices[GetIdx(i-di, j-dj)].p.z+Vertices[GetIdx(i+di, j+dj)].p.z)));
129        Vertex.r=0.0f;
130
131        // If the vertex is not a leaf node, ensure that the error and radius are nested using information from its four children.
132        // Note that the offsets (+di, +dj) and (-di, -dj) from (i, j) initially get us to the two vertices of the bisected edge.
133        // By "rotating" (di, dj) 45 degrees (in a topological sense), we arrive at one of the children of (i, j) (assuming we're not on a boundary).
134        // Successive 90-degree rotations then allow us to visit all four children.
135        if (n==0) return;
136
137        dj=(di+dj)/2;   // dj' = (di+dj)/2
138        di-=dj;         // di' = (di-dj)/2
139
140        for (unsigned long k=0; k<4; k++)
141        {
142            // Test whether child vertex exists.
143            if ((i!=0 || di>=0) && (i!=Size-1 || di<=0) &&
144                (j!=0 || dj>=0) && (j!=Size-1 || dj<=0))
145            {
146                // Inflate error and radius as needed.
147                VERTEX& Child=Vertices[GetIdx(i+di, j+dj)];
148
149                Vertex.e=MAX(Vertex.e, Child.e);
150                Vertex.r=MAX(Vertex.r, float(length(Vertex.p-Child.p))+Child.r);
151            }
152
153            // di' = -dj
154            // dj' = +di
155            dj += di;
156            di -= dj;
157            dj += di;
158        }
159    }
160
161
162    TerrainOldT(const char* FileNameHeightMap) /*throw (BitmapT::LoadErrorT)*/
163    {
164        BitmapT HeightMap(FileNameHeightMap);
165
166        // Compute Levels and Size, and thereby perform some sanity checks.
167        if (HeightMap.SizeX!=HeightMap.SizeY) throw BitmapT::LoadErrorT();
168        if (HeightMap.SizeX<3) throw BitmapT::LoadErrorT();
169        if (HeightMap.SizeX>(1UL << 30/2)+1) throw BitmapT::LoadErrorT();
170
171        Levels=1;
172        while ((1UL << Levels)+1<HeightMap.SizeX) Levels++;
173        Levels*=2;
174
175        Size=(1UL << (Levels/2))+1;
176
177        if (Size!=HeightMap.SizeX) throw BitmapT::LoadErrorT();
178
179
180        // Copy the vertex coordinates from HeightMap into the p components of the Vertices.
181        // Note that we pick the red channel of the RBG HeightMap.Data as the relevant channel.
182        // Moreover, note that the y-axis of the HeightMap.Data points down in screen-space and thus towards us in world space.
183        // Our world-space y-axis points opposite (away from us), though, and therefore we access the HeightMap.Data at (i, Size-j-1).
184        Vertices.PushBackEmpty(Size*Size);
185
186        for (unsigned long j=0; j<Size; j++)
187            for (unsigned long i=0; i<Size; i++)
188            {
189                const float Resolution[3]={ 160.0, 160.0, 25.5*255.0 };
190
191                Vertices[GetIdx(i, j)].p.x=Resolution[0]*(i-0.5*(Size-1));
192                Vertices[GetIdx(i, j)].p.y=Resolution[1]*(j-0.5*(Size-1));
193                Vertices[GetIdx(i, j)].p.z=Resolution[2]*((HeightMap.Data[GetIdx(i, Size-j-1)] & 0xFF)/255.0);
194
195                // Note! The extra-brackets around  ( (HeightMap.Data[GetIdx(i, Size-j-1)] & 0xFF)/255.0 )  in the above line
196                // are needed to be 100% (bitwise) rounding error compatible to the new terrain lib!
197            }
198
199
200        // Compute error and radius bottom-up, level-by-level.
201        // Unfortunately, the paper explains this only rather briefly in section V.B. on page 15.
202        const unsigned long n=Size-1;
203        unsigned long       s;
204        int                 a, b, c;
205
206        for (a=c=1, b=2, s=0; (unsigned long)a!=n; a=c=b, b*=2, s=n)
207        {
208            unsigned long j;
209
210            // Process level in black quadtree.
211            for (j=a; j<n; j+=b)
212                for (unsigned long i=0; i<=n; i+=b)
213                {
214                    ComputeVertexLoD(i, j, 0, a, s);
215                    ComputeVertexLoD(j, i, a, 0, s);
216                }
217
218            // Process level in white quadtree.
219            for (j=a; j<n; c=-c, j+=b)
220                for (unsigned long i=a; i<n; c=-c, i+=b)
221                    ComputeVertexLoD(i, j, a, c, n);
222        }
223
224        // Lock center and corner vertices.
225        Vertices[GetIdx(0, 0)].e=Vertices[GetIdx(n, 0)].e=Vertices[GetIdx(0, n)].e=Vertices[GetIdx(n, n)].e=Vertices[GetIdx(n/2, n/2)].e=float(HUGE_VAL);
226        Vertices[GetIdx(0, 0)].r=Vertices[GetIdx(n, 0)].r=Vertices[GetIdx(0, n)].r=Vertices[GetIdx(n, n)].r=Vertices[GetIdx(n/2, n/2)].r=float(HUGE_VAL);
227    }
228
229
230    // ComputeIndexStrip() const;
231    // ComputeVectorStrip() const;
232};
233
234
235struct VDinfo
236{
237    bool    cull;               // perform view culling when set
238    bool    morph;              // perform geomorphing when set
239    float   nu;                 // inverse of error tolerance in radians
240    float   nu_min;             // lower morph parameter
241    float   nu_max;             // upper morph parameter
242    VectorT viewpoint;          // viewpoint
243    Plane3T<double>  viewplanes[5];      // view frustum planes (without the "far" plane)
244};
245
246
247const unsigned long SPHERE_MASK_UNDECIDED=0x20;     // 0010 0000   initial visibility mask
248const unsigned long SPHERE_MASK_VISIBLE  =0x3F;     // 0011 1111   guaranteed visible
249
250
251// vp   - vertex
252// vdp  - view-dependent parameters
253// mask - visibility mask
254static unsigned long sphere_visible(const VERTEX& vp, const VDinfo& vdp, unsigned long mask)
255{
256    // Compare the radius of the vertex's bounding sphere against the signed distance to each plane of the view volume.
257    // If the sphere is completely on the exterior side of a plane, it is invisible.
258    // Otherwise, if it is entirely on the interior side, then a flag is set for this plane, and no further
259    // tests are made between this plane and the vertex's descendants since the bounding spheres are nested.
260    for (unsigned long i=0; i<5; i++)
261        if (!(mask & (1u << i)))
262        {
263            const float d=float(vdp.viewplanes[i].GetDistance(vp.p));
264
265            if (d> vp.r) return 0;          // completely outside view volume
266            if (d<-vp.r) mask|=1u << i;     // completely on interior side of view plane
267        }
268
269    return mask;
270}
271
272
273// ******************************** BEGIN MORPH **************************************************
274//@p-r-i-v-a-t-e---t-y-p-e-s-------------------------------------------------
275
276ArrayT<VectorT> VectorStrip;
277VERTEXid        VectorStripHead;    // ID of most recent vertex.
278VERTEXid        VectorStripTail;    // ID of second most recent vertex.
279bool            VectorStripParity;  // Parity of most recent vertex.
280
281
282//@p-r-i-v-a-t-e---f-u-n-c-t-i-o-n-s-----------------------------------------
283
284// vv - array of vertices
285// v  - index of vertex to append
286// p  - parity of vertex
287// z  - elevation of morphed vertex
288static void tstrip_vector_append(const VERTEX* vv, VERTEXid v, bool Parity, float z)
289{
290    if (v!=VectorStripTail && v!=VectorStripHead)
291    {
292        if (Parity==VectorStripParity)
293        {
294            VectorStrip.PushBack(VectorStrip[VectorStrip.Size()-2]);    // turn corner; duplicate vertex
295        }
296        else
297        {
298            VectorStripParity=Parity;
299            VectorStripTail  =VectorStripHead;
300        }
301
302        VectorStripHead=v;
303
304        // append new vertex
305        VectorStrip.PushBack(VectorT(vv[v].p.x, vv[v].p.y, z));
306
307        // TriangleCount++;                                         // Count triangles for statistics.
308    }
309}
310
311
312// zmp - pointer to morphed elevation
313// vp  - pointer to vertex
314// vdp - view-dependent parameters
315// zl  - elevation of left endpoint of split edge
316// zr  - elevation of right endpoint of split edge
317static bool vertex_morph(float* zmp, const VERTEX* vp, const VDinfo& vdp, float zl, float zr)
318{
319    // Compute the elevation of the morphed vertex.  The return value indicates whether the vertex is active or not.
320    const VectorT View=vp->p-vdp.viewpoint;
321    const float   d   =float(dot(View, View));    // d=length(vp->p-vdp.viewpoint)^2
322    const float   dmax=SQR(vdp.nu_max * vp->e + vp->r);
323
324    if (dmax>d)
325    {
326        const float dmin=SQR(vdp.nu_min * vp->e + vp->r);
327
328        *zmp=dmin>d ? float(vp->p.z) : ((dmax-d) * float(vp->p.z) + (d-dmin) * 0.5f * (zl+zr)) / (dmax-dmin);
329        return true;
330    }
331    else return false;
332}
333
334
335// vv  - vertex array
336// vdp - view-dependent parameters
337// l   - refinement level
338// i   - triangle apex
339// j   - supplemental vertex #1
340// k   - supplemental vertex #2
341// za  - elevation of apex
342// zl  - elevation of left corner
343// zr  - elevation of right corner
344static void submesh_morph(const VERTEX* vv, const VDinfo& vdp, unsigned long l, TRIANGLE(VERTEXid i, VERTEXid j, VERTEXid k), float za, float zl, float zr)
345{
346    float      zm=0.0f;
347    const bool refine=(l!=0) && vertex_morph(&zm, vv + SPLIT(i, j, k), vdp, zl, zr);
348
349    // Recursively refine and morph the mesh.
350    if (refine) submesh_morph(vv, vdp, l-1, CHILD_L(i, j, k), zm, zl, za); tstrip_vector_append(vv, i, l & 1, za);
351    if (refine) submesh_morph(vv, vdp, l-1, CHILD_R(i, j, k), zm, za, zr);
352}
353
354
355// vv  - vertex array
356// vdp - view-dependent parameters
357// l   - refinement level
358// i   - triangle apex
359// j   - supplemental vertex #1
360// k   - supplemental vertex #2
361// za  - elevation of apex
362// zl  - elevation of left corner
363// zr  - elevation of right corner
364// m   - visibility mask
365static void submesh_morph_visible(const VERTEX* vv, const VDinfo& vdp, unsigned long l, TRIANGLE(VERTEXid i, VERTEXid j, VERTEXid k), float za, float zl, float zr, unsigned long m)
366{
367    // Recursively refine, morph, and cull the mesh.
368    if (m==SPHERE_MASK_VISIBLE)
369    {
370        submesh_morph(vv, vdp, l, TRIANGLE(i, j, k), za, zl, zr);
371        return;
372    }
373
374    m=sphere_visible(vv[SPLIT(i, j, k)], vdp, m);
375
376    float      zm=0.0f;
377    const bool refine=(l!=0) && vertex_morph(&zm, vv+SPLIT(i, j, k), vdp, zl, zr) && m;
378
379    if (refine) submesh_morph_visible(vv, vdp, l-1, CHILD_L(i, j, k), zm, zl, za, m); tstrip_vector_append(vv, i, l & 1, za);
380    if (refine) submesh_morph_visible(vv, vdp, l-1, CHILD_R(i, j, k), zm, za, zr, m);
381}
382
383
384// vdp - view-dependent parameters
385// m   - initial visibility mask
386ArrayT<VectorT>& mesh_morph(const TerrainOldT& Terrain, const VDinfo& vdp, unsigned long m)
387{
388    const unsigned long n  =Terrain.Levels;
389    const VERTEX*       vv =&Terrain.Vertices[0];
390    const float         zc =float(vv[I_C (n/2)].p.z);
391    const float         zsw=float(vv[I_SW(n/2)].p.z);
392    const float         zse=float(vv[I_SE(n/2)].p.z);
393    const float         zne=float(vv[I_NE(n/2)].p.z);
394    const float         znw=float(vv[I_NW(n/2)].p.z);
395
396    // Initialize vector strip.
397    VectorStrip.Overwrite();
398    VectorStrip.PushBack(vv[I_SW(n/2)].p); VectorStripTail=I_SW(n/2);
399    VectorStrip.PushBack(vv[I_SW(n/2)].p); VectorStripHead=I_SW(n/2);
400    VectorStripParity=1;
401
402    // Top-level function for constructing a morphed mesh.
403    submesh_morph_visible(vv, vdp, n-1, ROOT_S(n/2), zc, zsw, zse, m); tstrip_vector_append(vv, I_SE(n/2), 0, zse);
404    submesh_morph_visible(vv, vdp, n-1, ROOT_E(n/2), zc, zse, zne, m); tstrip_vector_append(vv, I_NE(n/2), 0, zne);
405    submesh_morph_visible(vv, vdp, n-1, ROOT_N(n/2), zc, zne, znw, m); tstrip_vector_append(vv, I_NW(n/2), 0, znw);
406    submesh_morph_visible(vv, vdp, n-1, ROOT_W(n/2), zc, znw, zsw, m); VectorStrip.PushBack(vv[I_SW(n/2)].p);
407
408    return VectorStrip;
409}
410
411// ******************************** END MORPH ****************************************************
412
413
414//@p-r-i-v-a-t-e---f-u-n-c-t-i-o-n-s-----------------------------------------
415
416ArrayT<VERTEXid> IndexStrip;
417bool             IndexStripParity;
418
419
420// vp  - pointer to vertex
421// vdp - view-dependent parameters
422static bool vertex_active(const VERTEX& vp, const VDinfo& vdp)
423{
424    const float d =float(length(vp.p-vdp.viewpoint));
425    const float d2=d*d;
426
427    return SQR(vdp.nu*vp.e+vp.r) > d2;
428}
429
430
431// v   - index of vertex to append
432// p   - parity of vertex
433void tstrip_index_append(VERTEXid v, bool Parity)
434{
435    const VERTEXid tail=IndexStrip[IndexStrip.Size()-2];
436    const VERTEXid head=IndexStrip[IndexStrip.Size()-1];
437
438    // Add vertex to end of triangle strip vertex buffer.
439    if (v!=tail && v!=head)
440    {
441        if (Parity==IndexStripParity) IndexStrip.PushBack(tail);    // turn corner; duplicate vertex
442        IndexStrip.PushBack(v);                                     // append new vertex
443        IndexStripParity=Parity;                                    // store parity here for easy access
444
445        // TriangleCount++;                                         // Count triangles for statistics.
446    }
447}
448
449
450// vv  - vertex array
451// vdp - view-dependent parameters
452// l   - refinement level
453// i   - triangle apex
454// j   - supplemental vertex #1
455// k   - supplemental vertex #2
456void submesh_refine(const VERTEX* vv, const VDinfo& vdp, unsigned long l, TRIANGLE(VERTEXid i, VERTEXid j, VERTEXid k))
457{
458    const bool refine=(l!=0) && vertex_active(vv[SPLIT(i, j, k)], vdp);
459
460    // Recursively refine the mesh.  Since the refinement condition is the
461    // same for both branches and can be somewhat expensive to evaluate,
462    // it is evaluated and tested *before* making the recursive calls.
463    if (refine) submesh_refine(vv, vdp, l-1, CHILD_L(i, j, k)); tstrip_index_append(i, l & 1);
464    if (refine) submesh_refine(vv, vdp, l-1, CHILD_R(i, j, k));
465}
466
467
468// vv  - vertex array
469// vdp - view-dependent parameters
470// l   - refinement level
471// i   - triangle apex
472// j   - supplemental vertex #1
473// k   - supplemental vertex #2
474// m   - visibility mask
475void submesh_refine_visible(const VERTEX* vv, const VDinfo& vdp, unsigned long l, TRIANGLE(VERTEXid i, VERTEXid j, VERTEXid k), unsigned long m)
476{
477    // If the sphere is contained inside the view volume, then transition to
478    // submesh_refine() and make no further view culling tests.  Otherwise, continue culling.
479    if (m==SPHERE_MASK_VISIBLE)
480    {
481        submesh_refine(vv, vdp, l, TRIANGLE(i, j, k));
482        return;
483    }
484
485    m=sphere_visible(vv[SPLIT(i, j, k)], vdp, m);
486    const bool refine=(l!=0) && vertex_active(vv[SPLIT(i, j, k)], vdp) && m;
487
488    if (refine) submesh_refine_visible(vv, vdp, l-1, CHILD_L(i, j, k), m); tstrip_index_append(i, l & 1);
489    if (refine) submesh_refine_visible(vv, vdp, l-1, CHILD_R(i, j, k), m);
490}
491
492
493// vdp    - view-dependent parameters
494// m      - initial visibility mask
495ArrayT<VERTEXid>& mesh_refine(const TerrainOldT& Terrain, const VDinfo& vdp, unsigned long m)
496{
497    const unsigned long n =Terrain.Levels;
498    const VERTEX*       vv=&Terrain.Vertices[0];
499
500    // Initialize index strip.
501    IndexStrip.Overwrite();
502    IndexStrip.PushBack(I_SW(n/2));
503    IndexStrip.PushBack(I_SW(n/2));
504    IndexStripParity=1;
505
506    // Top-level function for constructing an indexed mesh.
507    submesh_refine_visible(vv, vdp, n-1, ROOT_S(n/2), m); tstrip_index_append(I_SE(n/2), 0);
508    submesh_refine_visible(vv, vdp, n-1, ROOT_E(n/2), m); tstrip_index_append(I_NE(n/2), 0);
509    submesh_refine_visible(vv, vdp, n-1, ROOT_N(n/2), m); tstrip_index_append(I_NW(n/2), 0);
510    submesh_refine_visible(vv, vdp, n-1, ROOT_W(n/2), m); IndexStrip.PushBack(I_SW(n/2));
511
512    return IndexStrip;
513}
514
515
516//@p-u-b-l-i-c---f-u-n-c-t-i-o-n-s-------------------------------------------
517
518void Usage()
519{
520    printf("\nUSAGE: TerrainViewer TerrainName\n");
521    printf("\n");
522
523    exit(1);
524}
525
526
527int main(int ArgC, char* ArgV[])
528{
529    const char* TerrainName=NULL;
530    const char* TextureName=NULL;
531
532    // Initialize the FileMan by mounting the default file system.
533    // Note that specifying "./" (instead of "") as the file system description effectively prevents the use of
534    // absolute paths like "D:\abc\someDir\someFile.xy" or "/usr/bin/xy". This however should be fine for this application.
535    cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_LOCAL_PATH, "./", "");
536    cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, "Games/DeathMatch/Textures/TechDemo.zip", "Games/DeathMatch/Textures/TechDemo/", "Ca3DE");
537    cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, "Games/DeathMatch/Textures/SkyDomes.zip", "Games/DeathMatch/Textures/SkyDomes/", "Ca3DE");
538
539    for (int ArgNr=1; ArgNr<ArgC; ArgNr++)
540    {
541             if (ArgV[ArgNr][0]=='-') Usage();
542        else if (TerrainName==NULL) TerrainName=ArgV[ArgNr];
543        else if (TextureName==NULL) TextureName=ArgV[ArgNr];
544        else Usage();
545    }
546
547    if (!TerrainName)
548    {
549        printf("\nHmm. Specifying a terrain name wouldn't hurt...\n");
550        Usage();
551    }
552
553
554    try
555    {
556        TerrainOldT TerrainOld(TerrainName);
557        TerrainT    TerrainNew(TerrainName, Vector3fT(160.0f, 160.0f, 25.5f*255.0f));
558
559
560        // Compare the old and new terrains.
561        const TerrainT::VertexT* NewVertices=TerrainNew.GetVertices();
562
563        printf("Comparing terrains...\n");
564        for (unsigned long VNr=0; VNr<TerrainOld.Vertices.Size(); VNr++)
565        {
566            if (TerrainOld.Vertices[VNr].p.x!=NewVertices[VNr].x) { printf("COMPARE 1 FAILED! %lu, %f != %f\n", VNr, TerrainOld.Vertices[VNr].p.x, NewVertices[VNr].x); return 1; };
567            if (TerrainOld.Vertices[VNr].p.y!=NewVertices[VNr].y) { printf("COMPARE 2 FAILED! %lu, %f != %f\n", VNr, TerrainOld.Vertices[VNr].p.y, NewVertices[VNr].y); return 1; };
568            if (TerrainOld.Vertices[VNr].p.z!=NewVertices[VNr].z) { printf("COMPARE 3 FAILED! %lu, %f != %f\n", VNr, TerrainOld.Vertices[VNr].p.z, NewVertices[VNr].z); return 1; };
569            if (TerrainOld.Vertices[VNr].!=NewVertices[VNr].e) { printf("COMPARE 4 FAILED! %lu, %f != %f\n", VNr, TerrainOld.Vertices[VNr].e,   NewVertices[VNr].e); return 1; };
570            if (TerrainOld.Vertices[VNr].!=NewVertices[VNr].r) { printf("COMPARE 5 FAILED! %lu, %f != %f\n", VNr, TerrainOld.Vertices[VNr].r,   NewVertices[VNr].r); return 1; };
571        }
572        printf("OK!\n");
573
574        // Open OpenGL-Window.
575        const char* ErrorMsg=SingleOpenGLWindow->Open("Cafu Terrain Viewer 1.0", 800, 600, 32, false);
576
577        if (ErrorMsg)
578        {
579            printf("\nUnable to open OpenGL window: %s\n", ErrorMsg);
580            return 0;
581        }
582
583        glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
584
585        if (TextureName)
586        {
587            try
588            {
589                const float res0=160.0;
590                const float res1=160.0;
591
592                // BBmin and BBmax are BBs for the xy-plane, z is not needed here.
593                // Also note that the y-components have been multiplied by -1, or otherwise the texture gets flipped.
594                const float BBmin[2]={ -res0*0.5f*(TerrainOld.Size-1),  res1*0.5f*(TerrainOld.Size-1) };
595                const float BBmax[2]={  res0*0.5f*(TerrainOld.Size-1), -res1*0.5f*(TerrainOld.Size-1) };
596
597                const float Plane1[4]={ 1.0f/(BBmax[0]-BBmin[0]), 0.0, 0.0, -BBmin[0]/(BBmax[0]-BBmin[0]) };
598                const float Plane2[4]={ 0.0, 1.0f/(BBmax[1]-BBmin[1]), 0.0, -BBmin[1]/(BBmax[1]-BBmin[1]) };
599
600                BitmapT Texture(TextureName);
601
602                glEnable(GL_TEXTURE_GEN_S);
603                glEnable(GL_TEXTURE_GEN_T);
604
605                glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
606
607                gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA8, Texture.SizeX, Texture.SizeY, GL_RGBA, GL_UNSIGNED_BYTE, &Texture.Data[0]);
608                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
609                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
610                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
611                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
612
613                glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
614                glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
615
616                glTexGenfv(GL_S, GL_OBJECT_PLANE, Plane1);
617                glTexGenfv(GL_T, GL_OBJECT_PLANE, Plane2);
618
619                glEnable(GL_TEXTURE_2D);
620                glColor3f(1.0, 1.0, 1.0);
621            }
622            catch (const BitmapT::LoadErrorT& /*E*/) {}
623        }
624
625
626        VDinfo VDI;
627
628        VDI.cull =false;            // perform view culling when set
629        VDI.morph=true;             // perform geomorphing when set
630
631        // Master Game Loop
632        TimerT  Timer;
633        VectorT ViewerPos(0.0, -500.0, 50.0);
634        float   Heading=0.0;
635        float   Pitch  =0.0;
636
637        while (true)
638        {
639            // Rufe die Nachrichten der Windows-Nachrichtenschlange ab.
640            if (SingleOpenGLWindow->HandleWindowMessages()) break;
641
642            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
643
644            glLoadIdentity();
645            glRotatef(-90.0, 1.0, 0.0, 0.0);
646            glRotatef(Pitch  , 1.0, 0.0, 0.0);
647            glRotatef(Heading, 0.0, 0.0, 1.0);
648            glTranslatef(float(-ViewerPos.x), float(-ViewerPos.y), float(-ViewerPos.z));
649
650            if (TextureName==NULL) glColor3f(0.6f, 1.0f, 0.5f);
651            if (TextureName==NULL) glDisable(GL_POLYGON_OFFSET_FILL);
652            if (TextureName==NULL) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
653
654
655            const float tau    =4.0;    // Error tolerance in pixel.
656         // const float tau_min=tau;
657         // const float tau_max=VDI.morph ? (3.0/2.0)*tau_min : tau_min;
658            // The basic formula for fov_x is from soar/main.c, reshape_callback function, rearranged for fov_x.
659            // The 67.5 is a fixed value from OpenGLWindow.cpp.
660            const float fov_x  =float(2.0*atan(float(SingleOpenGLWindow->GetWidth())/float(SingleOpenGLWindow->GetHeight())*tan(DEG2RAD(67.5)/2.0)));
661            const float kappa  =tau/SingleOpenGLWindow->GetWidth() * fov_x;
662
663            VDI.nu    =kappa>0.0 ? 1.0f/kappa : FLT_MAX;    // inverse of error tolerance in radians
664            VDI.nu_min=2.0f/3.0f*VDI.nu;                    // lower morph parameter
665            VDI.nu_max=          VDI.nu;                    // upper morph parameter
666
667            VDI.viewpoint=ViewerPos;
668
669            // Set up VDI.viewplanes (clip planes) for view frustum culling.
670            GLdouble mp[16]; glGetDoublev(GL_PROJECTION_MATRIX, mp);
671            GLdouble mv[16]; glGetDoublev(GL_MODELVIEW_MATRIX, mv);
672            double   mpv[4][4];
673
674            // Multiply modelview and projection matrices.
675            for (unsigned long i=0; i<4; i++)
676                for (unsigned long j=0; j<4; j++)
677                {
678                    mpv[i][j]=0.0;
679
680                    for (unsigned long k=0; k<4; k++)
681                        mpv[i][j]+=mp[4*k+i] * mv[4*j+k];
682                }
683
684            // Compute view frustum planes.
685            for (unsigned long i=0; i<5; i++)
686            {
687                // m can be used to easily minify / shrink the view frustum.
688                // The values should be between 0 and 8: 0 is the default (no minification), 8 is the reasonable maximum.
689                const char    m=0;
690                const double  d=(i<4 && m>0) ? 1.0-0.75*m/8.0 : 1.0;
691                double        plane[4];
692
693                for (unsigned long j=0; j<4; j++)
694                    plane[j]=((i & 1) ? mpv[i/2][j] : -mpv[i/2][j]) - d*mpv[3][j];
695
696                const double l=sqrt(SQR(plane[0])+SQR(plane[1])+SQR(plane[2]));
697
698                VDI.viewplanes[i]=Plane3T<double>(VectorT(plane[0]/l, plane[1]/l, plane[2]/l), -plane[3]/l);
699            }
700
701
702            TerrainT::ViewInfoT VI;
703            VI.cull     =VDI.cull;
704            VI.nu       =VDI.nu;
705            VI.nu_min   =VDI.nu_min;
706            VI.nu_max   =VDI.nu_max;
707            VI.viewpoint=VDI.viewpoint.AsVectorOfFloat();
708            for (unsigned long i=0; i<5; i++)
709                VI.viewplanes[i]=Plane3fT(VDI.viewplanes[i].Normal.AsVectorOfFloat(), float(VDI.viewplanes[i].Dist));
710
711            glBegin(GL_TRIANGLE_STRIP);
712            if (VDI.morph)
713            {
714                ArrayT<Vector3fT>& VectorStripNew=TerrainNew.ComputeVectorStripByMorphing(VI);
715
716                // Compare the old and new vector strips!
717                {
718                    ArrayT<VectorT>& VectorStrip=mesh_morph(TerrainOld, VDI, VDI.cull ? SPHERE_MASK_UNDECIDED : SPHERE_MASK_VISIBLE);
719
720                    if (VectorStrip.Size()!=VectorStripNew.Size())
721                    {
722                        printf("Compare of vstrips sizes failed!  %lu %lu\n", VectorStrip.Size(), VectorStripNew.Size());
723                    }
724                    else
725                    {
726                        for (unsigned long v=0; v<VectorStrip.Size(); v++)
727                        {
728                            if (VectorStrip[v].x!=VectorStripNew[v].x) { printf("Compare of vstrips 1 failed! %lu %f %f\n", v, VectorStrip[v].x, VectorStripNew[v].x); }
729                            if (VectorStrip[v].y!=VectorStripNew[v].y) { printf("Compare of vstrips 2 failed! %lu %f %f\n", v, VectorStrip[v].y, VectorStripNew[v].y); }
730                            if (fabs(VectorStrip[v].z-VectorStripNew[v].z)>0.001) { printf("Compare of vstrips 3 failed!  diff==%.10f\n", VectorStrip[v].z-VectorStripNew[v].z); }
731                        }
732                    }
733                }
734
735                // Note that the first VectorT at VectorStrip[0] must be skipped!
736                for (unsigned long VNr=1; VNr<VectorStripNew.Size(); VNr++) glVertex3f(float(VectorStripNew[VNr].x), float(VectorStripNew[VNr].y), float(VectorStripNew[VNr].z));
737            }
738            else
739            {
740                ArrayT<VERTEXid>& IdxStripNew=TerrainNew.ComputeIndexStripByRefinement(VI);
741
742                // Compare the old and new index strips!
743                {
744                    ArrayT<VERTEXid>& IdxStrip=mesh_refine(TerrainOld, VDI, VDI.cull ? SPHERE_MASK_UNDECIDED : SPHERE_MASK_VISIBLE);
745
746                    if (IdxStrip.Size()!=IdxStripNew.Size()) { printf("Compare of istrips sizes failed!\n"); return 1; }
747                    for (unsigned long v=0; v<IdxStrip.Size(); v++)
748                        if (IdxStrip[v]!=IdxStripNew[v]) { printf("Compare of istrips failed!\n"); return 1; }
749                }
750
751                // Note that the first index at IdxStrip[0] must be skipped!
752                const TerrainT::VertexT* Vertices=TerrainNew.GetVertices();
753
754                for (unsigned long IdxNr=1; IdxNr<IdxStripNew.Size(); IdxNr++) glVertex3f(float(Vertices[IdxStripNew[IdxNr]].x), float(Vertices[IdxStripNew[IdxNr]].y), float(Vertices[IdxStripNew[IdxNr]].z));
755            }
756            glEnd();
757
758            if (TextureName==NULL) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
759            if (TextureName==NULL) glEnable(GL_POLYGON_OFFSET_FILL);
760
761
762            double DeltaTime=Timer.GetSecondsSinceLastCall();
763
764            float   MoveSpeed=float(1000.0*DeltaTime);
765            float   RotSpeed =float(  90.0*DeltaTime);
766
767
768            SingleOpenGLWindow->SwapBuffers();
769
770            CaKeyboardEventT KE;
771            bool QuitProgram=false;
772
773            while (SingleOpenGLWindow->GetNextKeyboardEvent(KE)>0)
774            {
775                if (KE.Type!=CaKeyboardEventT::CKE_KEYDOWN) continue;
776                if (KE.Key==CaKeyboardEventT::CK_ESCAPE) QuitProgram=true;
777                if (KE.Key==CaKeyboardEventT::CK_C     ) { VDI.cull =!VDI.cull;  printf("View frustum culling is %s.\n", VDI.cull ? "ON" : "OFF"); }
778                if (KE.Key==CaKeyboardEventT::CK_M     ) { VDI.morph=!VDI.morph; printf("Geo-morphing is %s\n", VDI.morph ? "ON" : "OFF"); }
779            }
780
781            if (QuitProgram) break;
782
783
784            const float vx=float(MoveSpeed*sin(Heading/180.0*3.1415926));
785            const float vy=float(MoveSpeed*cos(Heading/180.0*3.1415926));
786
787            if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_UP    ] || SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_W]) ViewerPos=ViewerPos+VectorT( vx,  vy, 0);
788            if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_DOWN  ] || SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_S]) ViewerPos=ViewerPos+VectorT(-vx, -vy, 0);
789            if (                                                                       SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_A]) ViewerPos=ViewerPos+VectorT(-vy,  vx, 0);
790            if (                                                                       SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_D]) ViewerPos=ViewerPos+VectorT( vy, -vx, 0);
791            if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_INSERT] || SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_R]) ViewerPos.z+=MoveSpeed;
792            if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_DELETE] || SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_F]) ViewerPos.z-=MoveSpeed;
793            if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_LEFT  ]                                                                  ) Heading-=RotSpeed;
794            if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_RIGHT ]                                                                  ) Heading+=RotSpeed;
795            if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_PGUP  ]                                                                  ) Pitch-=RotSpeed;
796            if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_PGDN  ]                                                                  ) Pitch+=RotSpeed;
797            if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_END   ]                                                                  ) Pitch=0.0;
798        }
799
800        SingleOpenGLWindow->Close();
801    }
802    catch (const BitmapT::LoadErrorT& /*E*/)
803    {
804        printf("\nEither \"%s\" could not be found, not be read,\n", TerrainName);
805        printf("is not square, is smaller than 3 pixels, or not of size 2^n+1.  Sorry.\n");
806    }
807
808    return 0;
809}
Note: See TracBrowser for help on using the browser.