source: trunk/tutorial/11_fractal.cpp @ 1557

Last change on this file since 1557 was 1557, checked in by sam, 8 years ago

data: fix ascii.png search path.

  • Property svn:keywords set to Id
File size: 20.8 KB
Line 
1//
2// Lol Engine - Fractal tutorial
3//
4// Copyright: (c) 2011-2012 Sam Hocevar <sam@hocevar.net>
5//   This program is free software; you can redistribute it and/or
6//   modify it under the terms of the Do What The Fuck You Want To
7//   Public License, Version 2, as published by Sam Hocevar. See
8//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
9//
10
11#if defined HAVE_CONFIG_H
12#   include "config.h"
13#endif
14
15#include <cstring>
16
17#include "core.h"
18#include "lolgl.h"
19#include "loldebug.h"
20
21using namespace lol;
22
23#if defined _WIN32 && defined USE_D3D9
24#   define FAR
25#   define NEAR
26#   include <d3d9.h>
27#endif
28
29#if USE_SDL && defined __APPLE__
30#   include <SDL_main.h>
31#endif
32
33#if defined _WIN32
34#   undef main /* FIXME: still needed? */
35#   include <direct.h>
36#endif
37
38extern char const *lolfx_11_fractal;
39
40#if defined USE_D3D9
41extern IDirect3DDevice9 *g_d3ddevice;
42#elif defined _XBOX
43extern D3DDevice *g_d3ddevice;
44#elif __CELLOS_LV2__
45static GLint const INTERNAL_FORMAT = GL_ARGB_SCE;
46static GLenum const TEXTURE_FORMAT = GL_BGRA;
47static GLenum const TEXTURE_TYPE = GL_UNSIGNED_INT_8_8_8_8_REV;
48#elif defined __native_client__
49static GLint const INTERNAL_FORMAT = GL_RGBA;
50static GLenum const TEXTURE_FORMAT = GL_RGBA;
51static GLenum const TEXTURE_TYPE = GL_UNSIGNED_BYTE;
52#else
53/* Seems efficient for little endian textures */
54static GLint const INTERNAL_FORMAT = GL_RGBA;
55static GLenum const TEXTURE_FORMAT = GL_BGRA;
56static GLenum const TEXTURE_TYPE = GL_UNSIGNED_INT_8_8_8_8_REV;
57#endif
58
59class Fractal : public WorldEntity
60{
61public:
62    Fractal(ivec2 const &size)
63    {
64        /* Ensure texture size is a multiple of 16 for better aligned
65         * data access. Store the dimensions of a texel for our shader,
66         * as well as the half-size of the screen. */
67        m_size = size;
68        m_size.x = (m_size.x + 15) & ~15;
69        m_size.y = (m_size.y + 15) & ~15;
70        m_texel_settings = vec4(1.0, 1.0, 2.0, 2.0) / m_size.xyxy;
71        m_screen_settings = vec4(1.0, 1.0, 0.5, 0.5) * m_size.xyxy;
72
73        /* Window size decides the world aspect ratio. For instance, 640×480
74         * will be mapped to (-0.66,-0.5) - (0.66,0.5). */
75#if !defined __native_client__
76        m_window_size = Video::GetSize();
77#else
78        /* FIXME: it's illegal to call this on the game thread! */
79        m_window_size = ivec2(640, 480);
80#endif
81        if (m_window_size.y < m_window_size.x)
82            m_window2world = 0.5 / m_window_size.y;
83        else
84            m_window2world = 0.5 / m_window_size.x;
85        m_texel2world = (vec2)m_window_size / m_size * m_window2world;
86
87        m_oldmouse = ivec2(0, 0);
88
89        m_pixels = new u8vec4[m_size.x * m_size.y];
90        m_tmppixels = new u8vec4[m_size.x / 2 * m_size.y / 2];
91        m_frame = -1;
92        m_slices = 4;
93        for (int i = 0; i < 4; i++)
94        {
95            m_deltashift[i] = 0.0;
96            m_deltascale[i] = 1.0;
97            m_dirty[i] = 2;
98        }
99#if defined __CELLOS_LV2__ || defined _XBOX
100        //m_center = dcmplx(-.22815528839841, -1.11514249704382);
101        //m_center = dcmplx(0.001643721971153, 0.822467633298876);
102        m_center = dcmplx(-0.65823419062254, 0.50221777363480);
103        m_zoom_speed = -0.025;
104#else
105        m_center = -0.75;
106        m_zoom_speed = 0.0;
107#endif
108        m_translate = 0;
109        m_radius = 5.0;
110        m_ready = false;
111        m_drag = false;
112
113        m_palette = new u8vec4[(MAX_ITERATIONS + 1) * PALETTE_STEP];
114        for (int i = 0; i < (MAX_ITERATIONS + 1) * PALETTE_STEP; i++)
115        {
116            double f = (double)i / PALETTE_STEP;
117
118            double r = 0.5 * lol::sin(f * 0.27 + 2.0) + 0.5;
119            double g = 0.5 * lol::sin(f * 0.17 - 1.8) + 0.5;
120            double b = 0.5 * lol::sin(f * 0.21 - 2.6) + 0.5;
121
122            if (f < 7.0)
123            {
124                f = f < 1.0 ? 0.0 : (f - 1.0) / 6.0;
125                r *= f;
126                g *= f;
127                b *= f;
128            }
129
130            uint8_t red = r * 255.99f;
131            uint8_t green = g * 255.99f;
132            uint8_t blue = b * 255.99f;
133#if defined __CELLOS_LV2__ || defined _XBOX
134            m_palette[i] = u8vec4(255, red, green, blue);
135#elif defined __native_client__
136            m_palette[i] = u8vec4(red, green, blue, 255);
137#else
138            m_palette[i] = u8vec4(blue, green, red, 255);
139#endif
140        }
141
142#if !defined __native_client__
143        m_centertext = new Text(NULL, "src/data/font/ascii.png");
144        m_centertext->SetPos(ivec3(5, m_window_size.y - 15, 1));
145        Ticker::Ref(m_centertext);
146
147        m_mousetext = new Text(NULL, "src/data/font/ascii.png");
148        m_mousetext->SetPos(ivec3(5, m_window_size.y - 29, 1));
149        Ticker::Ref(m_mousetext);
150
151        m_zoomtext = new Text(NULL, "src/data/font/ascii.png");
152        m_zoomtext->SetPos(ivec3(5, m_window_size.y - 43, 1));
153        Ticker::Ref(m_zoomtext);
154#endif
155
156        m_position = ivec3(0, 0, 0);
157        m_bbox[0] = m_position;
158        m_bbox[1] = ivec3(m_window_size, 0);
159        Input::TrackMouse(this);
160
161        /* Spawn worker threads and wait for their readiness. */
162        for (int i = 0; i < MAX_THREADS; i++)
163            m_threads[i] = new Thread(DoWorkHelper, this);
164        for (int i = 0; i < MAX_THREADS; i++)
165            m_spawnqueue.Pop();
166    }
167
168    ~Fractal()
169    {
170        /* Signal worker threads for completion and wait for
171         * them to quit. */
172        for (int i = 0; i < MAX_THREADS; i++)
173            m_jobqueue.Push(-1);
174        for (int i = 0; i < MAX_THREADS; i++)
175            m_donequeue.Pop();
176
177        Input::UntrackMouse(this);
178#if !defined __native_client__
179        Ticker::Unref(m_centertext);
180        Ticker::Unref(m_mousetext);
181        Ticker::Unref(m_zoomtext);
182#endif
183        delete m_pixels;
184        delete m_tmppixels;
185        delete m_palette;
186    }
187
188    inline dcmplx TexelToWorldOffset(vec2 texel)
189    {
190        double dx = (0.5 + texel.x - m_size.x / 2) * m_texel2world.x;
191        double dy = (0.5 + m_size.y / 2 - texel.y) * m_texel2world.y;
192        return m_radius * dcmplx(dx, dy);
193    }
194
195    inline dcmplx ScreenToWorldOffset(vec2 pixel)
196    {
197        /* No 0.5 offset here, because we want to be able to position the
198         * mouse at (0,0) exactly. */
199        double dx = pixel.x - m_window_size.x / 2;
200        double dy = m_window_size.y / 2 - pixel.y;
201        return m_radius * m_window2world * dcmplx(dx, dy);
202    }
203
204    virtual void TickGame(float seconds)
205    {
206        WorldEntity::TickGame(seconds);
207
208        int prev_frame = m_frame;
209        m_frame = (m_frame + 1) % 4;
210
211        dcmplx worldmouse = m_center + ScreenToWorldOffset(m_mousepos);
212
213        ivec3 buttons = Input::GetMouseButtons();
214#if !defined __CELLOS_LV2__ && !defined _XBOX
215        if (buttons[1])
216        {
217            if (!m_drag)
218            {
219                m_oldmouse = m_mousepos;
220                m_drag = true;
221            }
222            m_translate = ScreenToWorldOffset(m_oldmouse)
223                        - ScreenToWorldOffset(m_mousepos);
224            /* XXX: the purpose of this hack is to avoid translating by
225             * an exact number of pixels. If this were to happen, the step()
226             * optimisation for i915 cards in our shader would behave
227             * incorrectly because a quarter of the pixels in the image
228             * would have tie rankings in the distance calculation. */
229            m_translate *= 1023.0 / 1024.0;
230            m_oldmouse = m_mousepos;
231        }
232        else
233        {
234            m_drag = false;
235            if (m_translate != 0.0)
236            {
237                m_translate *= std::pow(2.0, -seconds * 5.0);
238                if (m_translate.norm() / m_radius < 1e-4)
239                    m_translate = 0.0;
240            }
241        }
242
243        if ((buttons[0] || buttons[2]) && m_mousepos.x != -1)
244        {
245            double zoom = buttons[0] ? -0.5 : 0.5;
246            m_zoom_speed += seconds * zoom;
247            if (m_zoom_speed / zoom > 5e-3f)
248                m_zoom_speed = 5e-3f * zoom;
249        }
250        else if (m_zoom_speed)
251        {
252            m_zoom_speed *= std::pow(2.0, -seconds * 5.0);
253            if (abs(m_zoom_speed) < 1e-5 || m_drag)
254                m_zoom_speed = 0.0;
255        }
256#endif
257
258        if (m_zoom_speed || m_translate != 0.0)
259        {
260            dcmplx oldcenter = m_center;
261            double oldradius = m_radius;
262            double zoom = std::pow(2.0, seconds * 1e3f * m_zoom_speed);
263            if (m_radius * zoom > 8.0)
264            {
265                m_zoom_speed *= -1.0;
266                zoom = 8.0 / m_radius;
267            }
268            else if (m_radius * zoom < 1e-14)
269            {
270                m_zoom_speed *= -1.0;
271                zoom = 1e-14 / m_radius;
272            }
273            m_radius *= zoom;
274#if !defined __CELLOS_LV2__ && !defined _XBOX
275            m_center += m_translate;
276            m_center = (m_center - worldmouse) * zoom + worldmouse;
277            worldmouse = m_center + ScreenToWorldOffset(m_mousepos);
278#endif
279
280            /* Store the transformation properties to go from m_frame - 1
281             * to m_frame. */
282            m_deltashift[prev_frame] = (m_center - oldcenter) / oldradius;
283            m_deltashift[prev_frame].x /= m_size.x * m_texel2world.x;
284            m_deltashift[prev_frame].y /= m_size.y * m_texel2world.y;
285            m_deltascale[prev_frame] = m_radius / oldradius;
286            m_dirty[0] = m_dirty[1] = m_dirty[2] = m_dirty[3] = 2;
287        }
288        else
289        {
290            /* If settings didn't change, set transformation from previous
291             * frame to identity. */
292            m_deltashift[prev_frame] = 0.0;
293            m_deltascale[prev_frame] = 1.0;
294        }
295
296        /* Transformation from current frame to current frame is always
297         * identity. */
298        m_zoom_settings[m_frame][0] = 0.0f;
299        m_zoom_settings[m_frame][1] = 0.0f;
300        m_zoom_settings[m_frame][2] = 1.0f;
301
302        /* Compute transformation from other frames to current frame */
303        for (int i = 0; i < 3; i++)
304        {
305            int prev_index = (m_frame + 4 - i) % 4;
306            int cur_index = (m_frame + 3 - i) % 4;
307
308            m_zoom_settings[cur_index][0] = m_zoom_settings[prev_index][0] * m_deltascale[cur_index] + m_deltashift[cur_index].x;
309            m_zoom_settings[cur_index][1] = m_zoom_settings[prev_index][1] * m_deltascale[cur_index] + m_deltashift[cur_index].y;
310            m_zoom_settings[cur_index][2] = m_zoom_settings[prev_index][2] * m_deltascale[cur_index];
311        }
312
313        /* Precompute texture offset change instead of doing it in GLSL */
314        for (int i = 0; i < 4; i++)
315        {
316            m_zoom_settings[i][0] += 0.5 * (1.0 - m_zoom_settings[i][2]);
317            m_zoom_settings[i][1] -= 0.5 * (1.0 - m_zoom_settings[i][2]);
318        }
319
320#if !defined __native_client__
321        char buf[128];
322        sprintf(buf, "center: %+16.14f%+16.14fi", m_center.x, m_center.y);
323        m_centertext->SetText(buf);
324        sprintf(buf, " mouse: %+16.14f%+16.14fi", worldmouse.x, worldmouse.y);
325        m_mousetext->SetText(buf);
326        sprintf(buf, "  zoom: %g", 1.0 / m_radius);
327        m_zoomtext->SetText(buf);
328#endif
329
330        if (m_dirty[m_frame])
331        {
332            m_dirty[m_frame]--;
333
334            for (int i = 0; i < m_size.y; i += MAX_LINES * 2)
335                m_jobqueue.Push(i);
336        }
337    }
338
339    static void *DoWorkHelper(void *data)
340    {
341        Fractal *that = (Fractal *)data;
342        that->m_spawnqueue.Push(0);
343        for ( ; ; )
344        {
345            int line = that->m_jobqueue.Pop();
346            if (line == -1)
347                break;
348            that->DoWork(line);
349            that->m_donequeue.Push(0);
350        }
351        that->m_donequeue.Push(0);
352        return NULL;
353    };
354
355    void DoWork(int line)
356    {
357        double const maxsqlen = 1024;
358        double const k1 = 1.0 / (1 << 10) / (std::log(maxsqlen) / std::log(2.0));
359
360        int jmin = ((m_frame + 1) % 4) / 2 + line;
361        int jmax = jmin + MAX_LINES * 2;
362        if (jmax > m_size.y)
363            jmax = m_size.y;
364        u8vec4 *m_pixelstart = m_pixels
365                             + m_size.x * (m_size.y / 4 * m_frame + line / 4);
366
367        for (int j = jmin; j < jmax; j += 2)
368        for (int i = m_frame % 2; i < m_size.x; i += 2)
369        {
370            dcmplx z0 = m_center + TexelToWorldOffset(ivec2(i, j));
371            dcmplx z1, z2, z3, r0 = z0;
372            //dcmplx r0(0.28693186889504513, 0.014286693904085048);
373            //dcmplx r0(0.001643721971153, 0.822467633298876);
374            //dcmplx r0(-1.207205434596, 0.315432814901);
375            //dcmplx r0(-0.79192956889854, -0.14632423080102);
376            //dcmplx r0(0.3245046418497685, 0.04855101129280834);
377            int iter = MAX_ITERATIONS - 4;
378            for (;;)
379            {
380                /* Unroll the loop: tests are more expensive to do at each
381                 * iteration than the few extra multiplications. */
382                z1 = z0 * z0 + r0;
383                z2 = z1 * z1 + r0;
384                z3 = z2 * z2 + r0;
385                z0 = z3 * z3 + r0;
386                if (sqlength(z0) >= maxsqlen)
387                    break;
388                iter -= 4;
389                if (iter < 4)
390                    break;
391            }
392
393            if (iter)
394            {
395                double n = sqlength(z0);
396
397                if (sqlength(z1) >= maxsqlen) { iter += 3; n = sqlength(z1); }
398                else if (sqlength(z2) >= maxsqlen) { iter += 2; n = sqlength(z2); }
399                else if (sqlength(z3) >= maxsqlen) { iter += 1; n = sqlength(z3); }
400
401                if (n > maxsqlen * maxsqlen)
402                    n = maxsqlen * maxsqlen;
403
404                /* Approximate log(sqrt(n))/log(sqrt(maxsqlen)) */
405                double f = iter;
406                union { double n; uint64_t x; } u = { n };
407                double k = (u.x >> 42) - (((1 << 10) - 1) << 10);
408                k *= k1;
409
410                /* Approximate log2(k) in [1,2]. */
411                f += (- 0.344847817623168308695977510213252644185 * k
412                      + 2.024664188044341212602376988171727038739) * k
413                      - 1.674876738008591047163498125918330313237;
414
415                *m_pixelstart++ = m_palette[(int)(f * PALETTE_STEP)];
416            }
417            else
418            {
419#if defined __CELLOS_LV2__ || defined _XBOX
420                *m_pixelstart++ = u8vec4(255, 0, 0, 0);
421#else
422                *m_pixelstart++ = u8vec4(0, 0, 0, 255);
423#endif
424            }
425        }
426    }
427
428    virtual void TickDraw(float seconds)
429    {
430        WorldEntity::TickDraw(seconds);
431
432        static float const vertices[] =
433        {
434             1.0f,  1.0f,
435            -1.0f,  1.0f,
436            -1.0f, -1.0f,
437            -1.0f, -1.0f,
438             1.0f, -1.0f,
439             1.0f,  1.0f,
440        };
441
442        static float const texcoords[] =
443        {
444             1.0f,  1.0f,
445             0.0f,  1.0f,
446             0.0f,  0.0f,
447             0.0f,  0.0f,
448             1.0f,  0.0f,
449             1.0f,  1.0f,
450        };
451
452        if (!m_ready)
453        {
454#if !defined _XBOX && !defined USE_D3D9
455            /* Create a texture of half the width and twice the height
456             * so that we can upload four different subimages each frame. */
457            glGenTextures(1, &m_texid);
458            glBindTexture(GL_TEXTURE_2D, m_texid);
459            glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT,
460                         m_size.x / 2, m_size.y * 2, 0,
461                         TEXTURE_FORMAT, TEXTURE_TYPE, m_pixels);
462#   if defined __CELLOS_LV2__
463            /* We need this hint because by default the storage type is
464             * GL_TEXTURE_SWIZZLED_GPU_SCE. */
465            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_ALLOCATION_HINT_SCE,
466                            GL_TEXTURE_TILED_GPU_SCE);
467#   endif
468            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
469            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
470#elif defined _XBOX
471            /* By default the X360 will swizzle the texture. Ask for linear. */
472            g_d3ddevice->CreateTexture(m_size.x / 2, m_size.y * 2, 1,
473                                       D3DUSAGE_WRITEONLY, D3DFMT_LIN_A8R8G8B8,
474                                       D3DPOOL_DEFAULT, &m_tex, NULL);
475#else
476            g_d3ddevice->CreateTexture(m_size.x / 2, m_size.y * 2, 1,
477                                       D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8,
478                                       D3DPOOL_SYSTEMMEM, &m_tex, NULL);
479#endif
480
481            m_shader = Shader::Create(lolfx_11_fractal);
482
483            m_vertexattrib = m_shader->GetAttribLocation("a_Vertex", VertexUsage::Position, 0);
484            m_texattrib = m_shader->GetAttribLocation("a_TexCoord", VertexUsage::TexCoord, 0);
485            m_texeluni = m_shader->GetUniformLocation("u_TexelSize");
486            m_screenuni = m_shader->GetUniformLocation("u_ScreenSize");
487            m_zoomuni = m_shader->GetUniformLocation("u_ZoomSettings");
488
489            m_vdecl =
490              new VertexDeclaration(VertexStream<vec2>(VertexUsage::Position),
491                                    VertexStream<vec2>(VertexUsage::TexCoord));
492            m_vbo = new VertexBuffer(sizeof(vertices));
493            m_tbo = new VertexBuffer(sizeof(texcoords));
494
495            void *tmp = m_vbo->Lock(0, 0);
496            memcpy(tmp, vertices, sizeof(vertices));
497            m_vbo->Unlock();
498
499            tmp = m_tbo->Lock(0, 0);
500            memcpy(tmp, texcoords, sizeof(texcoords));
501            m_tbo->Unlock();
502
503            /* FIXME: this object never cleans up */
504            m_ready = true;
505        }
506
507#if defined _XBOX || defined USE_D3D9
508
509#else
510#   if !defined HAVE_GLES_2X
511        glEnable(GL_TEXTURE_2D);
512#   endif
513        glBindTexture(GL_TEXTURE_2D, m_texid);
514#endif
515
516        if (m_dirty[m_frame])
517        {
518            for (int i = 0; i < m_size.y; i += MAX_LINES * 2)
519                m_donequeue.Pop();
520
521            m_dirty[m_frame]--;
522
523#if defined _XBOX || defined USE_D3D9
524            D3DLOCKED_RECT rect;
525#   if defined _XBOX
526            m_tex->LockRect(0, &rect, NULL, D3DLOCK_NOOVERWRITE);
527#   else
528            m_tex->LockRect(0, &rect, NULL,
529                            D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE);
530#   endif
531            for (int j = 0; j < m_size.y * 2; j++)
532            {
533                u8vec4 *line = (u8vec4 *)rect.pBits + j * rect.Pitch / 4;
534                for (int i = 0; i < m_size.x / 2; i++)
535                    line[i] = m_pixels[m_size.x / 2 * j + i];
536            }
537            m_tex->UnlockRect(0);
538#elif defined __CELLOS_LV2__
539            /* glTexSubImage2D is extremely slow on the PS3, to the point
540             * that uploading the whole texture is 40 times faster. */
541            glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT,
542                         m_size.x / 2, m_size.y * 2, 0,
543                         TEXTURE_FORMAT, TEXTURE_TYPE, m_pixels);
544#else
545            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, m_frame * m_size.y / 2,
546                            m_size.x / 2, m_size.y / 2,
547                            TEXTURE_FORMAT, TEXTURE_TYPE,
548                            m_pixels + m_size.x * m_size.y / 4 * m_frame);
549#endif
550        }
551
552        m_shader->Bind();
553        m_shader->SetUniform(m_texeluni, m_texel_settings);
554        m_shader->SetUniform(m_screenuni, m_screen_settings);
555        m_shader->SetUniform(m_zoomuni, m_zoom_settings);
556        m_vdecl->Bind();
557        m_vdecl->SetStream(m_vbo, m_vertexattrib);
558        m_vdecl->SetStream(m_tbo, m_texattrib);
559#if defined _XBOX || defined USE_D3D9
560        g_d3ddevice->SetTexture(0, m_tex);
561        g_d3ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
562#elif !defined __CELLOS_LV2__ && !defined __ANDROID__
563#else
564#endif
565        m_vdecl->DrawElements(MeshPrimitive::Triangles, 0, 2);
566        m_vdecl->Unbind();
567    }
568
569private:
570    static int const MAX_ITERATIONS = 340;
571    static int const PALETTE_STEP = 32;
572    static int const MAX_THREADS = 8;
573    static int const MAX_LINES = 8;
574
575    ivec2 m_size, m_window_size, m_oldmouse;
576    double m_window2world;
577    dvec2 m_texel2world;
578    u8vec4 *m_pixels, *m_tmppixels, *m_palette;
579
580    Shader *m_shader;
581    ShaderAttrib m_vertexattrib, m_texattrib;
582    ShaderUniform m_texeluni, m_screenuni, m_zoomuni;
583
584    VertexDeclaration *m_vdecl;
585    VertexBuffer *m_vbo, *m_tbo;
586#if defined USE_D3D9
587    IDirect3DTexture9 *m_tex;
588#elif defined _XBOX
589    D3DTexture *m_tex;
590#else
591    GLuint m_texid;
592#endif
593    int m_frame, m_slices, m_dirty[4];
594    bool m_ready, m_drag;
595
596    dcmplx m_center, m_translate;
597    double m_zoom_speed, m_radius;
598    vec4 m_texel_settings, m_screen_settings;
599    mat4 m_zoom_settings;
600    dcmplx m_deltashift[4];
601    double m_deltascale[4];
602
603    /* Worker threads */
604    Thread *m_threads[MAX_THREADS];
605    Queue<int> m_spawnqueue, m_jobqueue, m_donequeue;
606
607    /* Debug information */
608#if !defined __native_client__
609    Text *m_centertext, *m_mousetext, *m_zoomtext;
610#endif
611};
612
613int main(int argc, char **argv)
614{
615    Application app("Tutorial 3: Fractal", ivec2(640, 480), 60.0f);
616
617#if defined _MSC_VER && !defined _XBOX
618    _chdir("..");
619#elif defined _WIN32 && !defined _XBOX
620    _chdir("../..");
621#endif
622
623    new DebugFps(5, 5);
624    new Fractal(ivec2(640, 480));
625    //new DebugRecord("fractalol.ogm", 60.0f);
626
627    app.Run();
628
629    return EXIT_SUCCESS;
630}
631
Note: See TracBrowser for help on using the repository browser.