From 849461fb6fe489c485b82694ccf4b87fbdffb27b Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Sat, 12 Nov 2011 15:52:51 +0000 Subject: [PATCH] tutorial: navigate in the fractal using the mouse. --- test/tutorial/tut03.cpp | 184 +++++++++++++++++++++++++--------------- 1 file changed, 117 insertions(+), 67 deletions(-) diff --git a/test/tutorial/tut03.cpp b/test/tutorial/tut03.cpp index 4d1aa58d..0de4eb34 100644 --- a/test/tutorial/tut03.cpp +++ b/test/tutorial/tut03.cpp @@ -38,78 +38,117 @@ public: m_size = size; m_pixels = new u8vec4[size.x * size.y]; m_frame = -1; - m_center = 0; - //m_target = f64cmplx(0.001643721971153, 0.822467633298876); - m_target = f64cmplx(-1.207205434596, 0.315432814901); - //m_target = f64cmplx(-0.79192956889854, -0.14632423080102); - //m_target = f64cmplx(0.3245046418497685, 0.04855101129280834); - //m_target = f64cmplx(0.28693186889504513, 0.014286693904085048); - m_angle = 0.0; - m_radius = 8.0; + m_dirty = 8; + m_center = -0.75; + //f64cmplx(0.001643721971153, 0.822467633298876); + //f64cmplx(-1.207205434596, 0.315432814901); + //f64cmplx(-0.79192956889854, -0.14632423080102); + //f64cmplx(0.3245046418497685, 0.04855101129280834); + //f64cmplx(0.28693186889504513, 0.014286693904085048); + m_radius = 1.5; + m_screenradius = 0.5 * (m_size.x < m_size.y ? m_size.x : m_size.y); m_ready = false; + + m_centertext = new Text(NULL, "gfx/font/ascii.png"); + m_centertext->SetPos(ivec3(5, m_size.y - 15, 1)); + Ticker::Ref(m_centertext); + + m_mousetext = new Text(NULL, "gfx/font/ascii.png"); + m_mousetext->SetPos(ivec3(5, m_size.y - 29, 1)); + Ticker::Ref(m_mousetext); + + position = ivec3(0, 0, 0); + bbox[0] = position; + bbox[1] = ivec3(size, 0); + Input::TrackMouse(this); } ~Fractal() { + Input::UntrackMouse(this); + Ticker::Unref(m_centertext); + Ticker::Unref(m_mousetext); delete m_pixels; } + inline f64cmplx ScreenToWorldOffset(ivec2 pixel) + { + f64cmplx tmp = f64cmplx(pixel.x - m_size.x / 2, + m_size.y / 2 - pixel.y); + return tmp * (m_radius / m_screenradius); + } + virtual void TickGame(float deltams) { WorldEntity::TickGame(deltams); m_frame = (m_frame + 1) % 4; - double zoom = pow(2.0, -deltams * 0.0005); - m_radius *= zoom; - m_center = (m_center - m_target) * zoom * zoom + m_target; + f64cmplx worldmouse = m_center + ScreenToWorldOffset(mousepos); - double step = m_radius / (m_size.x > m_size.y ? m_size.x : m_size.y); -// m_angle -= deltams * 0.00015; - f64cmplx transform = step * f64cmplx(cos(m_angle), sin(m_angle)); + ivec3 buttons = Input::GetMouseButtons(); + if ((buttons[0] || buttons[2]) && mousepos.x != -1) + { + double zoom = pow(2.0, (buttons[0] ? -deltams : deltams) * 0.0015); + if (m_radius * zoom > 1.5) + zoom = 1.0; + m_radius *= zoom; + m_center = (m_center - worldmouse) * zoom + worldmouse; + worldmouse = m_center + ScreenToWorldOffset(mousepos); + m_dirty = 8; + } + + char buf[128]; + sprintf(buf, "center: %+13.11f%+13.11fi", m_center.x, m_center.y); + m_centertext->SetText(buf); + sprintf(buf, " mouse: %+13.11f%+13.11fi", worldmouse.x, worldmouse.y); + m_mousetext->SetText(buf); u8vec4 *m_pixelstart = m_pixels + m_size.x * m_size.y / 4 * m_frame; - for (int j = ((m_frame + 1) % 4) / 2; j < m_size.y; j += 2) - for (int i = m_frame % 2; i < m_size.x; i += 2) + if (m_dirty) { - double const maxlen = 32; - int const maxiter = 170; - - f64cmplx delta(i - m_size.x / 2, j - m_size.y / 2); - - f64cmplx z0 = m_center + transform * delta; - f64cmplx r0 = z0; - //f64cmplx r0(0.28693186889504513, 0.014286693904085048); - //f64cmplx r0(0.001643721971153, 0.822467633298876); - f64cmplx z; - int iter = maxiter; - for (z = z0; iter && z.sqlen() < maxlen * maxlen; z = z * z + r0) - --iter; - - double f = iter; - double n = z.sqlen(); - - double k = log(n) * 0.5f / log(maxlen); - /* Approximate log2(k) in [1,2]. */ - f += (- 0.344847817623168308695977510213252644185 * k - + 2.024664188044341212602376988171727038739) * k - - 1.674876738008591047163498125918330313237; - - if (iter) - { - double r = 0.5 * sin(f * 0.27 - 2.0) + 0.5; - double g = 0.5 * sin(f * 0.13 + 1.0) + 0.5; - double b = 0.5 * sin(f * 0.21) + 0.5; - - uint8_t red = r * 255.0f; - uint8_t green = g * 255.0f; - uint8_t blue = b * 255.0f; - *m_pixelstart++ = u8vec4(red, green, blue, 0); - } - else + m_dirty--; + + for (int j = ((m_frame + 1) % 4) / 2; j < m_size.y; j += 2) + for (int i = m_frame % 2; i < m_size.x; i += 2) { - *m_pixelstart++ = u8vec4(0, 0, 0, 0); + double const maxlen = 32; + int const maxiter = 170; + + f64cmplx z0 = m_center + ScreenToWorldOffset(ivec2(i, j)); + f64cmplx r0 = z0; + //f64cmplx r0(0.28693186889504513, 0.014286693904085048); + //f64cmplx r0(0.001643721971153, 0.822467633298876); + f64cmplx z; + int iter = maxiter; + for (z = z0; iter && z.sqlen() < maxlen * maxlen; z = z * z + r0) + --iter; + + double f = iter; + double n = z.sqlen(); + + double k = log(n) * 0.5f / log(maxlen); + /* Approximate log2(k) in [1,2]. */ + f += (- 0.344847817623168308695977510213252644185 * k + + 2.024664188044341212602376988171727038739) * k + - 1.674876738008591047163498125918330313237; + + if (iter) + { + double r = 0.5 * sin(f * 0.27 - 1.5) + 0.5; + double g = 0.5 * sin(f * 0.13 + 1.3) + 0.5; + double b = 0.5 * sin(f * 0.21 + 0.4) + 0.5; + + uint8_t red = r * 255.0f; + uint8_t green = g * 255.0f; + uint8_t blue = b * 255.0f; + *m_pixelstart++ = u8vec4(red, green, blue, 0); + } + else + { + *m_pixelstart++ = u8vec4(0, 0, 0, 0); + } } } } @@ -130,12 +169,12 @@ public: static float const texcoords[] = { - 1.0f, 0.0f, - 0.0f, 0.0f, - 0.0f, 1.0f, - 0.0f, 1.0f, 1.0f, 1.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, }; if (!m_ready) @@ -166,9 +205,11 @@ public: /* gl_FragCoord is centered inside the pixel, so we remove * 0.5 from gl_FragCoord.x. Also, (0,0) is at the bottom * left whereas our images have (0,0) at the top left, so we - * _add_ 0.5 to gl_FragCoord.y. */ + * _add_ 0.5 to gl_FragCoord.y. (XXX: this is no longer true + * but will be again when mouse coordinates are back to + * being top-left again). */ " float i = mod(gl_FragCoord.x - 0.5, 2.0);" - " float j = mod(gl_FragCoord.y + 0.5 + i, 2.0);" + " float j = mod(gl_FragCoord.y - 0.5 + i, 2.0);" " coord.y += i + j * 2;" " coord.y *= 0.25;" " vec4 p = texture2D(in_Texture, coord);" @@ -216,15 +257,21 @@ public: glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, m_texid); - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, m_frame * m_size.y / 2, m_size.x / 2, m_size.y / 2, + + if (m_dirty) + { + m_dirty--; + + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, m_frame * m_size.y / 2, + m_size.x / 2, m_size.y / 2, #if !defined __CELLOS_LV2__ - GL_RGBA, GL_UNSIGNED_BYTE, + GL_RGBA, GL_UNSIGNED_BYTE, #else - /* The PS3 is big-endian */ - GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, + /* The PS3 is big-endian */ + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, #endif - m_pixels + m_size.x * m_size.y / 4 * m_frame); + m_pixels + m_size.x * m_size.y / 4 * m_frame); + } m_shader->Bind(); #if !defined __CELLOS_LV2__ && !defined __ANDROID__ && !defined __APPLE__ @@ -272,11 +319,14 @@ private: GLuint m_tco; #endif int m_vertexattrib, m_texattrib; - int m_frame; + int m_frame, m_dirty; bool m_ready; - f64cmplx m_center, m_target; - double m_radius, m_angle; + f64cmplx m_center; + double m_radius, m_screenradius; + + /* Debug information */ + Text *m_centertext, *m_mousetext; }; int main()