diff --git a/test/tutorial/tut03.cpp b/test/tutorial/tut03.cpp index 175bc021..228489c1 100644 --- a/test/tutorial/tut03.cpp +++ b/test/tutorial/tut03.cpp @@ -35,9 +35,14 @@ class Fractal : public WorldEntity public: Fractal(ivec2 const &size) { + /* Ensure size has even X and Y values */ m_size = size; - m_pixels = new u8vec4[size.x * size.y]; - m_tmppixels = new u8vec4[size.x / 2 * size.y / 2]; + m_size.x = (m_size.x + 1) & ~1; + m_size.y = (m_size.y + 1) & ~1; + + m_window_size = Video::GetSize(); + m_pixels = new u8vec4[m_size.x * m_size.y]; + m_tmppixels = new u8vec4[m_size.x / 2 * m_size.y / 2]; m_frame = -1; for (int i = 0; i < 4; i++) { @@ -47,11 +52,13 @@ public: } m_center = -0.75; m_radius = 1.5; - m_screenradius = 0.5 * (m_size.x < m_size.y ? m_size.x : m_size.y); + m_texture_radius = 0.5 * (m_size.x < m_size.y ? m_size.x : m_size.y); + m_window_radius = 0.5 * (m_window_size.x < m_window_size.y ? m_window_size.x : m_window_size.y); + m_pixel_delta = vec4(vec2(1.0, 1.0) / (vec2)m_size, m_size); m_ready = false; - m_palette = new u8vec4[MAX_ITERATIONS * PALETTE_STEP]; - for (int i = 0; i < MAX_ITERATIONS * PALETTE_STEP; i++) + m_palette = new u8vec4[(MAX_ITERATIONS + 1) * PALETTE_STEP]; + for (int i = 0; i < (MAX_ITERATIONS + 1) * PALETTE_STEP; i++) { float f = i / (double)PALETTE_STEP; @@ -66,20 +73,20 @@ public: } m_centertext = new Text(NULL, "gfx/font/ascii.png"); - m_centertext->SetPos(ivec3(5, m_size.y - 15, 1)); + m_centertext->SetPos(ivec3(5, m_window_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)); + m_mousetext->SetPos(ivec3(5, m_window_size.y - 29, 1)); Ticker::Ref(m_mousetext); m_zoomtext = new Text(NULL, "gfx/font/ascii.png"); - m_zoomtext->SetPos(ivec3(5, m_size.y - 43, 1)); + m_zoomtext->SetPos(ivec3(5, m_window_size.y - 43, 1)); Ticker::Ref(m_zoomtext); position = ivec3(0, 0, 0); bbox[0] = position; - bbox[1] = ivec3(size, 0); + bbox[1] = ivec3(m_window_size, 0); Input::TrackMouse(this); } @@ -94,11 +101,20 @@ public: delete m_palette; } + inline f64cmplx TexelToWorldOffset(ivec2 texel) + { + f64cmplx tmp = f64cmplx(0.5 + texel.x - m_size.x / 2, + 0.5 + m_size.y / 2 - texel.y); + return tmp * (m_radius / m_texture_radius); + } + inline f64cmplx ScreenToWorldOffset(ivec2 pixel) { - f64cmplx tmp = f64cmplx(0.5 + pixel.x - m_size.x / 2, - 0.5 + m_size.y / 2 - pixel.y); - return tmp * (m_radius / m_screenradius); + /* No 0.5 offset here, because we want to be able to position the + * mouse at (0,0) exactly. */ + f64cmplx tmp = f64cmplx(pixel.x - m_window_size.x / 2, + m_window_size.y / 2 - pixel.y); + return tmp * (m_radius / m_window_radius); } virtual void TickGame(float deltams) @@ -150,9 +166,9 @@ public: m_dirty[0] = m_dirty[1] = m_dirty[2] = m_dirty[3] = 2; char buf[128]; - sprintf(buf, "center: %+13.11f%+13.11fi", m_center.x, m_center.y); + sprintf(buf, "center: %+16.14f%+16.14fi", m_center.x, m_center.y); m_centertext->SetText(buf); - sprintf(buf, " mouse: %+13.11f%+13.11fi", worldmouse.x, worldmouse.y); + sprintf(buf, " mouse: %+16.14f%+16.14fi", worldmouse.x, worldmouse.y); m_mousetext->SetText(buf); sprintf(buf, " zoom: %g", 1.0 / m_radius); m_zoomtext->SetText(buf); @@ -166,9 +182,9 @@ public: 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) { - double const maxsqlen = 32; + double const maxsqlen = 1024; - f64cmplx z0 = m_center + ScreenToWorldOffset(ivec2(i, j)); + f64cmplx z0 = m_center + TexelToWorldOffset(ivec2(i, j)); f64cmplx r0 = z0; //f64cmplx r0(0.28693186889504513, 0.014286693904085048); //f64cmplx r0(0.001643721971153, 0.822467633298876); @@ -184,6 +200,8 @@ public: { double f = iter; double n = z.sqlen(); + if (n > maxsqlen * maxsqlen) + n = maxsqlen * maxsqlen; /* Approximate log(sqrt(n))/log(sqrt(maxsqlen)) */ union { double n; uint64_t x; } u = { n }; @@ -251,19 +269,20 @@ public: "}", "#version 120\n" + "uniform vec4 in_PixelDelta;\n" "uniform sampler2D in_Texture;\n" "void main(void) {" " vec2 coord = gl_TexCoord[0].xy;" - /* 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. (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);" + /* i is 0 or 1, depending on the current X coordinate within + * the current 2×2 texel. j is 0 or 1, depending on the Y + * coordinate _and_ the value of i, in order to stay on the + * Bayer dithering pattern. */ + " int i = int(mod(coord.x, 2.0 * in_PixelDelta.x) * in_PixelDelta.z);" + " int j = int(mod(coord.y + i * in_PixelDelta.y, 2.0 * in_PixelDelta.y) * in_PixelDelta.w);" + /* Choose the best slice depending on the value of i and j. */ " coord.y += i + j * 2;" " coord.y *= 0.25;" + /* Get a pixel from the best slice */ " vec4 p = texture2D(in_Texture, coord);" " gl_FragColor = p;" "}" @@ -279,6 +298,7 @@ public: "void main(float4 in_FragCoord : WPOS," " float2 in_TexCoord : TEXCOORD0," + " uniform float4 in_PixelDelta," " uniform sampler2D in_Texture," " out float4 out_FragColor : COLOR)" "{" @@ -294,6 +314,7 @@ public: ); m_vertexattrib = m_shader->GetAttribLocation("in_Vertex"); m_texattrib = m_shader->GetAttribLocation("in_TexCoord"); + m_pixeluni = m_shader->GetUniformLocation("in_PixelDelta"); m_ready = true; #if !defined __CELLOS_LV2__ && !defined __ANDROID__ && !defined __APPLE__ @@ -340,6 +361,7 @@ if (0) for (int i = 0; i < 4; i++) } m_shader->Bind(); + m_shader->SetUniform(m_pixeluni, m_pixel_delta); #if !defined __CELLOS_LV2__ && !defined __ANDROID__ && !defined __APPLE__ glBindBuffer(GL_ARRAY_BUFFER, m_vbo); glEnableVertexAttribArray(m_vertexattrib); @@ -379,7 +401,7 @@ private: static int const MAX_ITERATIONS = 170; static int const PALETTE_STEP = 32; - ivec2 m_size; + ivec2 m_size, m_window_size; u8vec4 *m_pixels, *m_tmppixels, *m_palette; Shader *m_shader; GLuint m_texid; @@ -387,12 +409,13 @@ private: GLuint m_vbo, m_tbo; GLuint m_tco; #endif - int m_vertexattrib, m_texattrib; + int m_vertexattrib, m_texattrib, m_pixeluni; int m_frame, m_dirty[4]; bool m_ready; f64cmplx m_center; - double m_radius, m_screenradius; + double m_radius, m_texture_radius, m_window_radius; + vec4 m_pixel_delta; f64cmplx m_deltashift[4]; double m_deltascale[4];