| @@ -38,78 +38,117 @@ public: | |||||
| m_size = size; | m_size = size; | ||||
| m_pixels = new u8vec4[size.x * size.y]; | m_pixels = new u8vec4[size.x * size.y]; | ||||
| m_frame = -1; | 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_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() | ~Fractal() | ||||
| { | { | ||||
| Input::UntrackMouse(this); | |||||
| Ticker::Unref(m_centertext); | |||||
| Ticker::Unref(m_mousetext); | |||||
| delete m_pixels; | 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) | virtual void TickGame(float deltams) | ||||
| { | { | ||||
| WorldEntity::TickGame(deltams); | WorldEntity::TickGame(deltams); | ||||
| m_frame = (m_frame + 1) % 4; | 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; | 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[] = | static float const texcoords[] = | ||||
| { | { | ||||
| 1.0f, 0.0f, | |||||
| 0.0f, 0.0f, | |||||
| 0.0f, 1.0f, | |||||
| 0.0f, 1.0f, | |||||
| 1.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, 0.0f, | ||||
| 1.0f, 1.0f, | |||||
| }; | }; | ||||
| if (!m_ready) | if (!m_ready) | ||||
| @@ -166,9 +205,11 @@ public: | |||||
| /* gl_FragCoord is centered inside the pixel, so we remove | /* gl_FragCoord is centered inside the pixel, so we remove | ||||
| * 0.5 from gl_FragCoord.x. Also, (0,0) is at the bottom | * 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 | * 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 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 += i + j * 2;" | ||||
| " coord.y *= 0.25;" | " coord.y *= 0.25;" | ||||
| " vec4 p = texture2D(in_Texture, coord);" | " vec4 p = texture2D(in_Texture, coord);" | ||||
| @@ -216,15 +257,21 @@ public: | |||||
| glEnable(GL_TEXTURE_2D); | glEnable(GL_TEXTURE_2D); | ||||
| glBindTexture(GL_TEXTURE_2D, m_texid); | 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__ | #if !defined __CELLOS_LV2__ | ||||
| GL_RGBA, GL_UNSIGNED_BYTE, | |||||
| GL_RGBA, GL_UNSIGNED_BYTE, | |||||
| #else | #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 | #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(); | m_shader->Bind(); | ||||
| #if !defined __CELLOS_LV2__ && !defined __ANDROID__ && !defined __APPLE__ | #if !defined __CELLOS_LV2__ && !defined __ANDROID__ && !defined __APPLE__ | ||||
| @@ -272,11 +319,14 @@ private: | |||||
| GLuint m_tco; | GLuint m_tco; | ||||
| #endif | #endif | ||||
| int m_vertexattrib, m_texattrib; | int m_vertexattrib, m_texattrib; | ||||
| int m_frame; | |||||
| int m_frame, m_dirty; | |||||
| bool m_ready; | 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() | int main() | ||||