diff --git a/src/lol/sys/file.h b/src/lol/sys/file.h index 173f4ebf..1c18bac9 100644 --- a/src/lol/sys/file.h +++ b/src/lol/sys/file.h @@ -34,6 +34,22 @@ struct FileAccess inline operator Value() { return m_value; } }; +struct StreamType +{ + enum Value + { + StdIn = 0, + StdOut, + StdErr, + File + } + m_value; + + inline StreamType(Value v) : m_value(v) {} + inline operator Value() { return m_value; } + inline int Value() { return m_value; } +}; + class File { public: @@ -42,12 +58,15 @@ public: File &operator =(File const &that); ~File(); + void Open(StreamType stream); void Open(String const &file, FileAccess mode); bool IsValid() const; void Close(); int Read(uint8_t *buf, int count); String ReadString(); + int Write(uint8_t *buf, int count); + int WriteString(const String &buf); private: class FileData *m_data; diff --git a/src/sys/file.cpp b/src/sys/file.cpp index d0d6b7ae..3bc4e407 100644 --- a/src/sys/file.cpp +++ b/src/sys/file.cpp @@ -32,8 +32,30 @@ class FileData { friend class File; + FileData() : m_type(StreamType::File) { } + + void Open(StreamType stream) + { + if (m_type == StreamType::File) + return; + m_type = stream; + switch((int)stream) + { +#if __CELLOS_LV2__ + /* FIXME: no modes, no error checking, no nothing */ +#elif __ANDROID__ + /* FIXME: no modes, no error checking, no nothing */ +#elif HAVE_STDIO_H + case StreamType::StdIn: m_fd = stdin; break; + case StreamType::StdOut: m_fd = stdout; break; + case StreamType::StdErr: m_fd = stderr; break; +#endif + } + } + void Open(String const &file, FileAccess mode) { + m_type = StreamType::File; #if __CELLOS_LV2__ String realfile = String(SYS_APP_HOME) + '/' + file; CellFsErrno err = cellFsOpen(realfile.C(), CELL_FS_O_RDONLY, @@ -105,8 +127,42 @@ class FileData return ret; } + int Write(uint8_t *buf, int count) + { +#if __CELLOS_LV2__ +/* + uint64_t done; + CellFsErrno err = cellFsRead(m_fd, buf, count, &done); + + if (err != CELL_FS_SUCCEEDED) + return -1; + + return (int)done; +*/ + return 0; +#elif __ANDROID__ + //return AAsset_read(m_asset, buf, count); + return 0; +#elif HAVE_STDIO_H + size_t done = fwrite(buf, 1, count, m_fd); + if (done <= 0) + return -1; + + return (int)done; +#else + return 0; +#endif + } + + int WriteString(const String &buf) + { + return Write((uint8_t *)buf.C(), buf.Count()); + } + void Close() { + if (m_type != StreamType::File) + return; #if __CELLOS_LV2__ if (m_fd >= 0) cellFsClose(m_fd); @@ -130,6 +186,7 @@ class FileData FILE *m_fd; #endif Atomic m_refcount; + StreamType m_type; }; File::File() @@ -173,6 +230,11 @@ File::~File() } } +void File::Open(StreamType stream) +{ + return m_data->Open(stream); +} + void File::Open(String const &file, FileAccess mode) { return m_data->Open(file, mode); @@ -193,6 +255,16 @@ String File::ReadString() return m_data->ReadString(); } +int File::Write(uint8_t *buf, int count) +{ + return m_data->Write(buf, count); +} + +int File::WriteString(const String &buf) +{ + return m_data->WriteString(buf); +} + void File::Close() { m_data->Close(); diff --git a/tutorial/01_triangle.cpp b/tutorial/01_triangle.cpp new file mode 100644 index 00000000..3b481f81 --- /dev/null +++ b/tutorial/01_triangle.cpp @@ -0,0 +1,83 @@ +// +// Lol Engine - Triangle tutorial +// +// Copyright: (c) 2012-2013 Sam Hocevar +// This program is free software; you can redistribute it and/or +// modify it under the terms of the Do What The Fuck You Want To +// Public License, Version 2, as published by Sam Hocevar. See +// http://www.wtfpl.net/ for more details. +// + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include "core.h" +#include "loldebug.h" + +using namespace std; +using namespace lol; + +LOLFX_RESOURCE_DECLARE(01_triangle); + +class Triangle : public WorldEntity +{ +public: + Triangle() + { + m_vertices << vec2( 0.0, 0.8); + m_vertices << vec2(-0.8, -0.8); + m_vertices << vec2( 0.8, -0.8); + m_ready = false; + } + + virtual void TickDraw(float seconds) + { + WorldEntity::TickDraw(seconds); + + if (!m_ready) + { + m_shader = Shader::Create(LOLFX_RESOURCE_NAME(01_triangle)); + m_coord = m_shader->GetAttribLocation("in_Position", VertexUsage::Position, 0); + + m_vdecl = new VertexDeclaration(VertexStream(VertexUsage::Position)); + + m_vbo = new VertexBuffer(m_vertices.Bytes()); + void *vertices = m_vbo->Lock(0, 0); + memcpy(vertices, &m_vertices[0], m_vertices.Bytes()); + m_vbo->Unlock(); + + m_ready = true; + + /* FIXME: this object never cleans up */ + } + + m_shader->Bind(); + m_vdecl->SetStream(m_vbo, m_coord); + m_vdecl->Bind(); + m_vdecl->DrawElements(MeshPrimitive::Triangles, 0, 3); + m_vdecl->Unbind(); + } + +private: + Array m_vertices; + Shader *m_shader; + ShaderAttrib m_coord; + VertexDeclaration *m_vdecl; + VertexBuffer *m_vbo; + bool m_ready; +}; + +int main(int argc, char **argv) +{ + System::Init(argc, argv); + + Application app("Tutorial 1: Triangle", ivec2(640, 480), 60.0f); + + new DebugFps(5, 5); + new Triangle(); + + app.Run(); + return EXIT_SUCCESS; +} + diff --git a/tutorial/01_triangle.lolfx b/tutorial/01_triangle.lolfx new file mode 100644 index 00000000..5f59871b --- /dev/null +++ b/tutorial/01_triangle.lolfx @@ -0,0 +1,48 @@ +/* + * Awesome triangle shader + */ + +technique FlatColor +{ + pass DefaultPass + { + vertexshader = vert; + pixelshader = frag; + } +} + +[vert.glsl] +#version 120 + +attribute vec2 in_Position; + +void main(void) +{ + gl_Position = vec4(in_Position, 0.0, 1.0); +} + +[frag.glsl] +#version 120 + +#if defined GL_ES +precision highp float; +#endif + +void main(void) +{ + gl_FragColor = vec4(0.7, 0.2, 0.5, 1.0); +} + +[vert.hlsl] +void main(float2 in_Position : POSITION, + out float4 out_Position : POSITION) +{ + out_Position = float4(in_Position, 0.0, 1.0); +} + +[frag.hlsl] +void main(out float4 out_FragColor : COLOR) +{ + out_FragColor = float4(0.7, 0.2, 0.5, 1.0); +} + diff --git a/tutorial/01_triangle.vcxproj b/tutorial/01_triangle.vcxproj new file mode 100644 index 00000000..57e317e6 --- /dev/null +++ b/tutorial/01_triangle.vcxproj @@ -0,0 +1,79 @@ + + + + + Debug + ORBIS + + + Debug + PS3 + + + Debug + Win32 + + + Debug + x64 + + + Debug + Xbox 360 + + + Release + ORBIS + + + Release + PS3 + + + Release + Win32 + + + Release + x64 + + + Release + Xbox 360 + + + + + + + + + + + {9e62f2fe-3408-4eae-8238-fd84238ceeda} + + + {83d3b207-c601-4025-8f41-01dedc354661} + + + + {b92abadc-45be-4cc5-b724-9426053123a1} + Application + Win32Proj + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tutorial/02_cube.cpp b/tutorial/02_cube.cpp new file mode 100644 index 00000000..ebe746da --- /dev/null +++ b/tutorial/02_cube.cpp @@ -0,0 +1,156 @@ +// +// Lol Engine - Cube tutorial +// +// Copyright: (c) 2011-2013 Sam Hocevar +// This program is free software; you can redistribute it and/or +// modify it under the terms of the Do What The Fuck You Want To +// Public License, Version 2, as published by Sam Hocevar. See +// http://www.wtfpl.net/ for more details. +// + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include "core.h" +#include "loldebug.h" + +using namespace std; +using namespace lol; + +LOLFX_RESOURCE_DECLARE(02_cube); + +class Cube : public WorldEntity +{ +public: + Cube() + { + m_angle = 0; + + /* Front vertices/colors */ + m_mesh.Push(vec3(-1.0, -1.0, 1.0), vec3(1.0, 0.0, 1.0)); + m_mesh.Push(vec3( 1.0, -1.0, 1.0), vec3(0.0, 1.0, 0.0)); + m_mesh.Push(vec3( 1.0, 1.0, 1.0), vec3(1.0, 0.5, 0.0)); + m_mesh.Push(vec3(-1.0, 1.0, 1.0), vec3(1.0, 1.0, 0.0)); + /* Back */ + m_mesh.Push(vec3(-1.0, -1.0, -1.0), vec3(1.0, 0.0, 0.0)); + m_mesh.Push(vec3( 1.0, -1.0, -1.0), vec3(0.0, 0.5, 0.0)); + m_mesh.Push(vec3( 1.0, 1.0, -1.0), vec3(0.0, 0.5, 1.0)); + m_mesh.Push(vec3(-1.0, 1.0, -1.0), vec3(0.0, 0.0, 1.0)); + + m_faces_indices << 0 << 1 << 2 << 2 << 3 << 0; + m_faces_indices << 1 << 5 << 6 << 6 << 2 << 1; + m_faces_indices << 7 << 6 << 5 << 5 << 4 << 7; + m_faces_indices << 4 << 0 << 3 << 3 << 7 << 4; + m_faces_indices << 4 << 5 << 1 << 1 << 0 << 4; + m_faces_indices << 3 << 2 << 6 << 6 << 7 << 3; + + m_lines_indices << 0 << 1 << 1 << 2 << 2 << 3 << 3 << 0; + m_lines_indices << 4 << 5 << 5 << 6 << 6 << 7 << 7 << 4; + m_lines_indices << 0 << 4 << 1 << 5 << 2 << 6 << 3 << 7; + + m_ready = false; + } + + virtual void TickGame(float seconds) + { + WorldEntity::TickGame(seconds); + + m_angle += seconds * 45.0f; + + mat4 anim = mat4::rotate(m_angle, vec3(0, 1, 0)); + mat4 model = mat4::translate(vec3(0, 0, -4.5)); + mat4 view = mat4::lookat(vec3(0, 2, 0), vec3(0, 0, -4), vec3(0, 1, 0)); + mat4 proj = mat4::perspective(45.0f, 640.0f, 480.0f, 0.1f, 10.0f); + + m_matrix = proj * view * model * anim; + } + + virtual void TickDraw(float seconds) + { + WorldEntity::TickDraw(seconds); + + if (!m_ready) + { + m_shader = Shader::Create(LOLFX_RESOURCE_NAME(02_cube)); + + m_mvp = m_shader->GetUniformLocation("in_Matrix"); + m_coord = m_shader->GetAttribLocation("in_Vertex", + VertexUsage::Position, 0); + m_color = m_shader->GetAttribLocation("in_Color", + VertexUsage::Color, 0); + + m_vdecl = + new VertexDeclaration(VertexStream(VertexUsage::Position, + VertexUsage::Color)); + + m_vbo = new VertexBuffer(m_mesh.Bytes()); + void *mesh = m_vbo->Lock(0, 0); + memcpy(mesh, &m_mesh[0], m_mesh.Bytes()); + m_vbo->Unlock(); + + m_lines_ibo = new IndexBuffer(m_lines_indices.Bytes()); + void *indices = m_lines_ibo->Lock(0, 0); + memcpy(indices, &m_lines_indices[0], m_lines_indices.Bytes()); + m_lines_ibo->Unlock(); + + m_faces_ibo = new IndexBuffer(m_faces_indices.Bytes()); + indices = m_faces_ibo->Lock(0, 0); + memcpy(indices, &m_faces_indices[0], m_faces_indices.Bytes()); + m_faces_ibo->Unlock(); + + /* FIXME: this object never cleans up */ + m_ready = true; + } + + g_renderer->SetClearColor(vec4(0.0f, 0.0f, 0.0f, 1.0f)); + + m_shader->Bind(); + m_vdecl->SetStream(m_vbo, m_coord, m_color); + m_vdecl->Bind(); + + m_shader->SetUniform(m_mvp, m_matrix); + m_lines_ibo->Bind(); + m_vdecl->DrawIndexedElements(MeshPrimitive::Lines, 0, 0, + m_mesh.Count(), 0, m_lines_indices.Count()); + m_lines_ibo->Unbind(); + + m_shader->SetUniform(m_mvp, m_matrix * mat4::scale(0.5f)); + m_faces_ibo->Bind(); + m_vdecl->DrawIndexedElements(MeshPrimitive::Triangles, 0, 0, + m_mesh.Count(), 0, m_faces_indices.Count()); + m_faces_ibo->Unbind(); + + m_vdecl->Unbind(); + } + +private: + float m_angle; + mat4 m_matrix; + Array m_mesh; + Array m_lines_indices, m_faces_indices; + + Shader *m_shader; + ShaderAttrib m_coord, m_color; + ShaderUniform m_mvp; + VertexDeclaration *m_vdecl; + VertexBuffer *m_vbo; + IndexBuffer *m_lines_ibo, *m_faces_ibo; + + bool m_ready; +}; + +int main(int argc, char **argv) +{ + System::Init(argc, argv); + + Application app("Tutorial 2: Cube", ivec2(640, 480), 60.0f); + + new DebugFps(5, 5); + new Cube(); + + app.Run(); + + return EXIT_SUCCESS; +} + diff --git a/tutorial/02_cube.lolfx b/tutorial/02_cube.lolfx new file mode 100644 index 00000000..8e1ae04f --- /dev/null +++ b/tutorial/02_cube.lolfx @@ -0,0 +1,50 @@ +[vert.glsl] + +#version 120 + +attribute vec3 in_Vertex; +attribute vec3 in_Color; +uniform mat4 in_Matrix; +varying vec3 pass_Color; + +void main(void) +{ + gl_Position = in_Matrix * vec4(in_Vertex, 1.0); + pass_Color = in_Color; +} + +[frag.glsl] + +#version 120 + +#if defined GL_ES +precision highp float; +#endif + +varying vec3 pass_Color; + +void main(void) +{ + gl_FragColor = vec4(pass_Color, 1.0); +} + +[vert.hlsl] + +void main(float3 in_Vertex : POSITION, + float3 in_Color : COLOR, + uniform float4x4 in_Matrix, + out float4 out_Position : POSITION, + out float3 pass_Color : COLOR) +{ + pass_Color = in_Color; + out_Position = mul(in_Matrix, float4(in_Vertex, 1.0)); +} + +[frag.hlsl] + +void main(float3 pass_Color : COLOR, + out float4 out_FragColor : COLOR) +{ + out_FragColor = float4(pass_Color, 1.0); +} + diff --git a/tutorial/02_cube.vcxproj b/tutorial/02_cube.vcxproj new file mode 100644 index 00000000..f2b1d571 --- /dev/null +++ b/tutorial/02_cube.vcxproj @@ -0,0 +1,79 @@ + + + + + Debug + ORBIS + + + Debug + PS3 + + + Debug + Win32 + + + Debug + x64 + + + Debug + Xbox 360 + + + Release + ORBIS + + + Release + PS3 + + + Release + Win32 + + + Release + x64 + + + Release + Xbox 360 + + + + + + + + + + + {9e62f2fe-3408-4eae-8238-fd84238ceeda} + + + {83d3b207-c601-4025-8f41-01dedc354661} + + + + {7b083da2-fe08-4f6d-bfdd-195d5c2783eb} + Application + Win32Proj + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tutorial/03_noise.cpp b/tutorial/03_noise.cpp new file mode 100644 index 00000000..35531e19 --- /dev/null +++ b/tutorial/03_noise.cpp @@ -0,0 +1,93 @@ +// +// Lol Engine - Noise tutorial +// +// Copyright: (c) 2012-2013 Sam Hocevar +// This program is free software; you can redistribute it and/or +// modify it under the terms of the Do What The Fuck You Want To +// Public License, Version 2, as published by Sam Hocevar. See +// http://www.wtfpl.net/ for more details. +// + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include "core.h" +#include "loldebug.h" + +using namespace std; +using namespace lol; + +LOLFX_RESOURCE_DECLARE(03_noise); + +class NoiseDemo : public WorldEntity +{ +public: + NoiseDemo() : + m_time(0.0), + m_ready(false) + { + m_vertices << vec2(-1.0, 1.0); + m_vertices << vec2(-1.0, -1.0); + m_vertices << vec2( 1.0, -1.0); + m_vertices << vec2(-1.0, 1.0); + m_vertices << vec2( 1.0, -1.0); + m_vertices << vec2( 1.0, 1.0); + m_ready = false; + } + + virtual void TickDraw(float seconds) + { + WorldEntity::TickDraw(seconds); + + m_time += seconds; + + if (!m_ready) + { + m_shader = Shader::Create(LOLFX_RESOURCE_NAME(03_noise)); + m_coord = m_shader->GetAttribLocation("in_Position", VertexUsage::Position, 0); + m_time_uni = m_shader->GetUniformLocation("u_Time"); + + m_vdecl = new VertexDeclaration(VertexStream(VertexUsage::Position)); + + m_vbo = new VertexBuffer(m_vertices.Bytes()); + void *vertices = m_vbo->Lock(0, 0); + memcpy(vertices, &m_vertices[0], m_vertices.Bytes()); + m_vbo->Unlock(); + + m_ready = true; + + /* FIXME: this object never cleans up */ + } + + m_shader->Bind(); + m_shader->SetUniform(m_time_uni, m_time); + m_vdecl->SetStream(m_vbo, m_coord); + m_vdecl->Bind(); + m_vdecl->DrawElements(MeshPrimitive::Triangles, 0, 6); + m_vdecl->Unbind(); + } + +private: + Array m_vertices; + Shader *m_shader; + ShaderAttrib m_coord; + ShaderUniform m_time_uni; + VertexDeclaration *m_vdecl; + VertexBuffer *m_vbo; + float m_time; + bool m_ready; +}; + +int main(int argc, char **argv) +{ + System::Init(argc, argv); + + Application app("Tutorial 3: Noise", ivec2(1280, 720), 60.0f); + + new NoiseDemo(); + + app.Run(); + return EXIT_SUCCESS; +} + diff --git a/tutorial/03_noise.lolfx b/tutorial/03_noise.lolfx new file mode 100644 index 00000000..184252a7 --- /dev/null +++ b/tutorial/03_noise.lolfx @@ -0,0 +1,278 @@ +[vert.glsl] + +#version 120 + +uniform float u_Time; + +attribute vec2 in_Position; + +varying vec4 pass_Position; +varying vec3 water[4]; +varying vec3 fire[4]; + +float mod289(float x) +{ + return x - floor(x * (1.0 / 289.0)) * 289.0; +} + +void main(void) +{ + mat3 r = mat3(0.36, 0.48, -0.8, -0.8, 0.60, 0.0, 0.48, 0.64, 0.60); + vec3 p_pos = r * vec3(in_Position * vec2(16.0, 9.0), 0.0); + vec3 p_time = r * vec3(0.0, 0.0, u_Time * 2.0); + + /* Noise sampling points for water */ + water[0] = p_pos / 2.0 + p_time; + water[1] = p_pos / 4.0 + p_time; + water[2] = p_pos / 8.0 + p_time; + water[3] = p_pos / 16.0 + p_time; + + /* Noise sampling points for fire */ + p_pos = 16.0 * p_pos - r * vec3(0.0, mod289(u_Time) * 128.0, 0.0); + fire[0] = p_pos / 2.0 + p_time * 2.0; + fire[1] = p_pos / 4.0 + p_time * 1.5; + fire[2] = p_pos / 8.0 + p_time; + fire[3] = p_pos / 16.0 + p_time; + + /* Pass rotated screen coordinates */ + pass_Position.xy = in_Position; + mat2 rot = mat2(cos(u_Time), sin(u_Time), -sin(u_Time), cos(u_Time)); + pass_Position.zw = rot * in_Position; + + gl_Position = vec4(in_Position, 0.0, 1.0); +} + +[frag.glsl] + +#version 120 + +#if defined GL_ES +precision highp float; +#endif + +uniform float u_Time; + +varying vec4 pass_Position; +varying vec3 water[4]; +varying vec3 fire[4]; + +vec4 mod289(vec4 x) +{ + return x - floor(x * (1.0 / 289.0)) * 289.0; +} + +vec4 perm(vec4 x) +{ + return mod289(((x * 34.0) + 1.0) * x); +} + +float noise3d(vec3 p) +{ + vec3 a = floor(p); + vec3 d = p - a; + d = d * d * (3.0 - 2.0 * d); + + vec4 b = a.xxyy + vec4(0.0, 1.0, 0.0, 1.0); + vec4 k1 = perm(b.xyxy); + vec4 k2 = perm(k1.xyxy + b.zzww); + + vec4 c = k2 + a.zzzz; + vec4 k3 = perm(c); + vec4 k4 = perm(c + 1.0); + + vec4 o1 = fract(k3 * (1.0 / 41.0)); + vec4 o2 = fract(k4 * (1.0 / 41.0)); + + vec4 o3 = o2 * d.z + o1 * (1.0 - d.z); + vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x); + + return o4.y * d.y + o4.x * (1.0 - d.y); +} + +void main(void) +{ + /* Dither the transition between water and fire */ + float test = pass_Position.z * pass_Position.w + 1.5 * sin(u_Time); + vec2 d = vec2(16.0, 9.0) * pass_Position.xy; + test += 0.5 * (length(fract(d) - 0.5) - length(fract(d + 0.5) - 0.5)); + + /* Compute 4 octaves of noise */ +#if defined GL_ES + vec4 n = vec4(noise3d((test > 0.0) ? fire[0] : water[0]), + noise3d((test > 0.0) ? fire[1] : water[1]), + noise3d((test > 0.0) ? fire[2] : water[2]), + noise3d((test > 0.0) ? fire[3] : water[3])); +#else + vec3 points[4] = (test > 0.0) ? fire : water; + vec4 n = vec4(noise3d(points[0]), + noise3d(points[1]), + noise3d(points[2]), + noise3d(points[3])); +#endif + + vec4 color; + + if (test > 0.0) + { + /* Use noise results for fire */ + float p = dot(n, vec4(0.125, 0.125, 0.25, 0.5)); + + /* Fade to black on top of screen */ + p -= pass_Position.y * 0.8 + 0.25; + p = max(p, 0.0); + p = min(p, 1.0); + + float q = p * p * (3.0 - 2.0 * p); + float r = q * q * (3.0 - 2.0 * q); + color = vec4(min(q * 2.0, 1.0), + max(r * 1.5 - 0.5, 0.0), + max(q * 8.0 - 7.3, 0.0), + 1.0); + } + else + { + /* Use noise results for water */ + float p = dot(abs(2.0 * n - 1.0), + vec4(0.5, 0.25, 0.125, 0.125)); + float q = sqrt(p); + + color = vec4(1.0 - q, + 1.0 - 0.5 * q, + 1.0, + 1.0); + } + + gl_FragColor = color; +} + +[vert.hlsl] + +float mod289(float x) +{ + return x - floor(x * (1.0 / 289.0)) * 289.0; +} + +void main(float2 in_Position : POSITION, + uniform float u_Time, + out float4 out_Position : POSITION, + out float4 pass_Position : TEXCOORD0, + out float3 water[4] : TEXCOORD1, + out float3 fire[4] : TEXCOORD5) +{ + float3x3 r = float3x3(0.36, 0.48, -0.8, -0.8, 0.60, 0.0, 0.48, 0.64, 0.60); + float3 p_pos = mul(r, float3(in_Position * float2(16.0, 9.0), 0.0)); + float3 p_time = mul(r, float3(0.0, 0.0, u_Time * 2.0)); + + /* Noise sampling points for water */ + water[0] = p_pos / 2.0 + p_time; + water[1] = p_pos / 4.0 + p_time; + water[2] = p_pos / 8.0 + p_time; + water[3] = p_pos / 16.0 + p_time; + + /* Noise sampling points for fire */ + p_pos = 16.0 * p_pos - mul(r, float3(0.0, mod289(u_Time) * 128.0, 0.0)); + fire[0] = p_pos / 2.0 + p_time * 2.0; + fire[1] = p_pos / 4.0 + p_time * 1.5; + fire[2] = p_pos / 8.0 + p_time; + fire[3] = p_pos / 16.0 + p_time; + + /* Pass rotated screen coordinates */ + pass_Position.xy = in_Position; + float2x2 rot = float2x2(cos(u_Time), sin(u_Time), -sin(u_Time), cos(u_Time)); + pass_Position.zw = mul(rot, in_Position); + + out_Position = float4(in_Position, 0.0, 1.0); +} + +[frag.hlsl] + +float4 mod289(float4 x) +{ + return x - floor(x * (1.0 / 289.0)) * 289.0; +} + +float4 perm(float4 x) +{ + return mod289(((x * 34.0) + 1.0) * x); +} + +float noise3d(float3 p) +{ + float3 a = floor(p); + float3 d = p - a; + d = d * d * (3.0 - 2.0 * d); + + float4 b = a.xxyy + float4(0.0, 1.0, 0.0, 1.0); + float4 k1 = perm(b.xyxy); + float4 k2 = perm(k1.xyxy + b.zzww); + + float4 c = k2 + a.zzzz; + float4 k3 = perm(c); + float4 k4 = perm(c + 1.0); + + float4 o1 = frac(k3 * (1.0 / 41.0)); + float4 o2 = frac(k4 * (1.0 / 41.0)); + + float4 o3 = o2 * d.z + o1 * (1.0 - d.z); + float2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x); + + return o4.y * d.y + o4.x * (1.0 - d.y); +} + +void main(in float4 pass_Position : TEXCOORD0, + in float3 water[4] : TEXCOORD1, + in float3 fire[4] : TEXCOORD5, + uniform float u_Time, + out float4 out_FragColor : COLOR) +{ + /* Dither the transition between water and fire */ + float test = pass_Position.z * pass_Position.w + 1.5 * sin(u_Time); + float2 d = float2(16.0, 9.0) * pass_Position.xy; + test += 0.5 * (length(frac(d) - 0.5) - length(frac(d + 0.5) - 0.5)); + + float3 points[4]; + points[0] = (test > 0.0) ? fire[0] : water[0]; + points[1] = (test > 0.0) ? fire[1] : water[1]; + points[2] = (test > 0.0) ? fire[2] : water[2]; + points[3] = (test > 0.0) ? fire[3] : water[3]; + + /* Compute 4 octaves of noise */ + float4 n = float4(noise3d(points[0]), + noise3d(points[1]), + noise3d(points[2]), + noise3d(points[3])); + + float4 color; + + if (test > 0.0) + { + /* Use noise results for fire */ + float p = dot(n, float4(0.125, 0.125, 0.25, 0.5)); + + /* Fade to black on top of screen */ + p -= pass_Position.y * 0.8 + 0.25; + p = max(p, 0.0); + p = min(p, 1.0); + + float q = p * p * (3.0 - 2.0 * p); + float r = q * q * (3.0 - 2.0 * q); + color = float4(min(q * 2.0, 1.0), + max(r * 1.5 - 0.5, 0.0), + max(q * 8.0 - 7.3, 0.0), + 1.0); + } + else + { + /* Use noise results for water */ + float p = dot(abs(2.0 * n - 1.0), + float4(0.5, 0.25, 0.125, 0.125)); + float q = sqrt(p); + + color = float4(1.0 - q, + 1.0 - 0.5 * q, + 1.0, + 1.0); + } + + out_FragColor = color; +} diff --git a/tutorial/03_noise.vcxproj b/tutorial/03_noise.vcxproj new file mode 100644 index 00000000..c14bdbdb --- /dev/null +++ b/tutorial/03_noise.vcxproj @@ -0,0 +1,79 @@ + + + + + Debug + ORBIS + + + Debug + PS3 + + + Debug + Win32 + + + Debug + x64 + + + Debug + Xbox 360 + + + Release + ORBIS + + + Release + PS3 + + + Release + Win32 + + + Release + x64 + + + Release + Xbox 360 + + + + + + + + + + + {9e62f2fe-3408-4eae-8238-fd84238ceeda} + + + {83d3b207-c601-4025-8f41-01dedc354661} + + + + {b0a53d75-cbb4-4fdf-93ac-2d12a79ada0e} + Application + Win32Proj + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tutorial/04_texture.cpp b/tutorial/04_texture.cpp new file mode 100644 index 00000000..e0a1b0be --- /dev/null +++ b/tutorial/04_texture.cpp @@ -0,0 +1,127 @@ +// +// Lol Engine - Graphing tutorial +// +// Copyright: (c) 2012-2013 Sam Hocevar +// This program is free software; you can redistribute it and/or +// modify it under the terms of the Do What The Fuck You Want To +// Public License, Version 2, as published by Sam Hocevar. See +// http://www.wtfpl.net/ for more details. +// + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include "core.h" +#include "loldebug.h" + +using namespace std; +using namespace lol; + +static int const TEXTURE_WIDTH = 256; + +LOLFX_RESOURCE_DECLARE(04_texture); + +class TextureDemo : public WorldEntity +{ +public: + TextureDemo() : + m_frames(0), + m_ready(false) + { + m_vertices << vec2(-1.0, 1.0); + m_vertices << vec2(-1.0, -1.0); + m_vertices << vec2( 1.0, -1.0); + m_vertices << vec2(-1.0, 1.0); + m_vertices << vec2( 1.0, -1.0); + m_vertices << vec2( 1.0, 1.0); + + m_heightmap = new uint8_t[TEXTURE_WIDTH * 1]; + } + + virtual ~TextureDemo() + { + delete m_heightmap; + } + + virtual void TickGame(float seconds) + { + WorldEntity::TickGame(seconds); + + /* Generate a new heightmap at the beginning */ + if (m_frames == 0) + memset(m_heightmap, 255, TEXTURE_WIDTH); + + /* Scroll left */ + for (int i = 0; i < TEXTURE_WIDTH - 1; i++) + m_heightmap[i] = m_heightmap[i + 1]; + + int height = m_heightmap[TEXTURE_WIDTH - 1]; + height = (height + 127 + 40 * lol::sin(m_frames * 0.03) + rand() % 97 - 38) / 2; + height = std::max(15, std::min(height, 240)); + m_heightmap[TEXTURE_WIDTH - 1] = height; + + /* Update frame counter */ + ++m_frames; + } + + virtual void TickDraw(float seconds) + { + WorldEntity::TickDraw(seconds); + + /* Initialise GPU data */ + if (!m_ready) + { + m_texture = new Texture(ivec2(TEXTURE_WIDTH, 1), PixelFormat::Y_8); + + m_shader = Shader::Create(LOLFX_RESOURCE_NAME(04_texture)); + m_coord = m_shader->GetAttribLocation("in_Position", VertexUsage::Position, 0); + + m_vdecl = new VertexDeclaration(VertexStream(VertexUsage::Position)); + + m_vbo = new VertexBuffer(m_vertices.Bytes()); + void *vertices = m_vbo->Lock(0, 0); + memcpy(vertices, &m_vertices[0], m_vertices.Bytes()); + m_vbo->Unlock(); + + m_ready = true; + + /* FIXME: this object never cleans up */ + } + + /* Send new heightmap to GPU */ + m_texture->SetData(m_heightmap); + + m_shader->Bind(); + m_shader->SetUniform(m_texture_uni, m_texture->GetTexture(), 0); + m_vdecl->SetStream(m_vbo, m_coord); + m_vdecl->Bind(); + m_vdecl->DrawElements(MeshPrimitive::Triangles, 0, 6); + m_vdecl->Unbind(); + } + +private: + Array m_vertices; + Texture *m_texture; + Shader *m_shader; + ShaderAttrib m_coord; + ShaderUniform m_texture_uni; + VertexDeclaration *m_vdecl; + VertexBuffer *m_vbo; + uint8_t *m_heightmap; + int m_frames; + bool m_ready; +}; + +int main(int argc, char **argv) +{ + System::Init(argc, argv); + + Application app("Tutorial 4: Texture", ivec2(1280, 720), 60.0f); + + new TextureDemo(); + + app.Run(); + return EXIT_SUCCESS; +} + diff --git a/tutorial/04_texture.lolfx b/tutorial/04_texture.lolfx new file mode 100644 index 00000000..6ee4804d --- /dev/null +++ b/tutorial/04_texture.lolfx @@ -0,0 +1,148 @@ +[vert.glsl] + +#version 120 + +attribute vec2 in_Position; + +varying vec4 pass_Position; + +void main(void) +{ + pass_Position = vec4(0.5 * in_Position + 0.5, 0.0, 1.0); + gl_Position = vec4(in_Position, 0.5, 1.0); +} + +[frag.glsl] + +#version 120 + +#if defined GL_ES +precision highp float; +#endif + +uniform sampler2D u_Texture; + +varying vec4 pass_Position; + +float segdist(vec2 p1, vec2 p2, vec2 a) +{ + float d = max(1e-10, dot(p2 - p1, p2 - p1)); + float t = clamp(dot(a - p1, p2 - p1) / d, 0.0, 1.0); + return distance(a, mix(p1, p2, t)); +} + +void main(void) +{ + float width = 800.0; + float height = 600.0; + float texture_width = 256.0; + float line_width = 1.2; + float dot_size = 1.0; + vec4 delta = vec4(1.0 / texture_width, 0.0, + 2.0 / texture_width, 0.0); + + vec2 p = pass_Position.xy; + vec2 tc = vec2(floor(p.x * texture_width) / texture_width, p.y); + float t = p.x * texture_width - floor(p.x * texture_width); + vec4 c; + c[0] = texture2D(u_Texture, tc - delta.xy).x; + c[1] = texture2D(u_Texture, tc).x; + c[2] = texture2D(u_Texture, tc + delta.xy).x; + c[3] = texture2D(u_Texture, tc + delta.zw).x; + + /* Find the 4 closest points in screen space */ + vec2 p0 = vec2((tc.x - delta.x) * width, c[0] * height); + vec2 p1 = vec2((tc.x ) * width, c[1] * height); + vec2 p2 = vec2((tc.x + delta.x) * width, c[2] * height); + vec2 p3 = vec2((tc.x + delta.z) * width, c[3] * height); + vec2 a = vec2(p.x * width, p.y * height); + + /* Compute distance to segments */ + float d = segdist(p0, p1, a); + d = min(d, segdist(p1, p2, a)); + d = min(d, segdist(p2, p3, a)); + + /* Compute distance to dots */ + d = min(d, length(a - p0) - dot_size); + d = min(d, length(a - p1) - dot_size); + d = min(d, length(a - p2) - dot_size); + d = min(d, length(a - p3) - dot_size); + + /* Add line width */ + float lum = clamp(line_width - d, 0.0, 1.0); + + /* Compensate for sRGB */ + lum = pow(1.0 - lum, 1.0 / 2.4); + + /* Choose some funny colours */ + gl_FragColor = vec4(mix(p.x, 1.0, lum), lum, lum, 1.0); +} + +[vert.hlsl] + +void main(float2 in_Position : POSITION, + out float4 out_Position : POSITION, + out float4 pass_Position : TEXCOORD0) +{ + pass_Position = float4(0.5 * in_Position + 0.5, 0.0, 1.0); + out_Position = float4(in_Position, 0.5, 1.0); +} + +[frag.hlsl] + +float segdist(float2 p1, float2 p2, float2 a) +{ + float d = max(1e-10, dot(p2 - p1, p2 - p1)); + float t = clamp(dot(a - p1, p2 - p1) / d, 0.0, 1.0); + return distance(a, lerp(p1, p2, t)); +} + +void main(in float4 pass_Position : TEXCOORD0, + uniform sampler2D u_Texture, + out float4 out_FragColor : COLOR) +{ + float width = 800.0; + float height = 600.0; + float texture_width = 256.0; + float line_width = 1.2; + float dot_size = 1.0; + float4 delta = float4(1.0 / texture_width, 0.0, + 2.0 / texture_width, 0.0); + + float2 p = pass_Position.xy; + float2 tc = float2(floor(p.x * texture_width) / texture_width, p.y); + float t = p.x * texture_width - floor(p.x * texture_width); + float4 c; + c[0] = tex2D(u_Texture, tc - delta.xy).x; + c[1] = tex2D(u_Texture, tc).x; + c[2] = tex2D(u_Texture, tc + delta.xy).x; + c[3] = tex2D(u_Texture, tc + delta.zw).x; + + /* Find the 4 closest points in screen space */ + float2 p0 = float2((tc.x - delta.x) * width, c[0] * height); + float2 p1 = float2((tc.x ) * width, c[1] * height); + float2 p2 = float2((tc.x + delta.x) * width, c[2] * height); + float2 p3 = float2((tc.x + delta.z) * width, c[3] * height); + float2 a = float2(p.x * width, p.y * height); + + /* Compute distance to segments */ + float d = segdist(p0, p1, a); + d = min(d, segdist(p1, p2, a)); + d = min(d, segdist(p2, p3, a)); + + /* Compute distance to dots */ + d = min(d, length(a - p0) - dot_size); + d = min(d, length(a - p1) - dot_size); + d = min(d, length(a - p2) - dot_size); + d = min(d, length(a - p3) - dot_size); + + /* Add line width */ + float lum = clamp(line_width - d, 0.0, 1.0); + + /* Compensate for sRGB */ + lum = pow(1.0 - lum, 1.0 / 2.4); + + /* Choose some funny colours */ + out_FragColor = float4(lerp(p.x, 1.0, lum), lum, lum, 1.0); +} + diff --git a/tutorial/04_texture.vcxproj b/tutorial/04_texture.vcxproj new file mode 100644 index 00000000..4404cd30 --- /dev/null +++ b/tutorial/04_texture.vcxproj @@ -0,0 +1,79 @@ + + + + + Debug + ORBIS + + + Debug + PS3 + + + Debug + Win32 + + + Debug + x64 + + + Debug + Xbox 360 + + + Release + ORBIS + + + Release + PS3 + + + Release + Win32 + + + Release + x64 + + + Release + Xbox 360 + + + + + + + + + + + {9e62f2fe-3408-4eae-8238-fd84238ceeda} + + + {83d3b207-c601-4025-8f41-01dedc354661} + + + + {834852db-edb6-4fd0-bcf9-45cd01126962} + Application + Win32Proj + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tutorial/05_easymesh.cpp b/tutorial/05_easymesh.cpp new file mode 100644 index 00000000..8cf71187 --- /dev/null +++ b/tutorial/05_easymesh.cpp @@ -0,0 +1,145 @@ +// +// Lol Engine - EasyMesh tutorial +// +// Copyright: (c) 2011-2013 Sam Hocevar +// (c) 2012-2013 Benjamin "Touky" Huet +// This program is free software; you can redistribute it and/or +// modify it under the terms of the Do What The Fuck You Want To +// Public License, Version 2, as published by Sam Hocevar. See +// http://www.wtfpl.net/ for more details. +// + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include "core.h" + +using namespace std; +using namespace lol; + +class EasyMeshTutorial : public WorldEntity +{ +public: + EasyMeshTutorial() + { + m_gears.Push(EasyMesh(), mat4(1.0f), 0.0f); + m_gears.Push(EasyMesh(), mat4(1.0f), 0.0f); + m_gears.Push(EasyMesh(), mat4(1.0f), 180.0f / 18); + m_gears.Push(EasyMesh(), mat4(1.0f), 180.0f / 18); + m_gears.Push(EasyMesh(), mat4(1.0f), 180.0f / 18); + + m_gears[0].m1.Compile("[sc#00f ab 8 1 8 ty -.25]" + "[sc#f9f scb#f9f acg 12 10 5 5 20 20 5 5 0.1 0 s .1 .1 .1 ty -.1 csgu]" + "[sc#fff scb#000 acg 12 10 10 10 20 20 5 5 0.1 0 s .05 .05 .05 tx -1.5 ty .3 csgu]" + "[sc#00f ab 5 3 9 tx 2.5 csgs]" + "[[ sc#fff ab 3 1.4 2 tx -2 tz -2 " + "[sc#fff ab 2.1 .7 1.1 ty .5 tx -1.4 tz -1.4 csgs] mz] csgu]"); + //m_gears[0].m1.Compile("[sc#f9f scb#f9f acg 12 10 5 5 20 20 5 5 0.1 0 s .1 .1 .1 [sc#00f ab 3 1 2 ty .25 tx 1 csgs]]"); + m_gears[1].m1.Compile("sc#ff9 scb#ff9 acg 54 10 95 95 90 90 -5 -5 0.1 0 s .1 .1 .1"); + //m_gears[2].m1.Compile("sc#9ff scb#9ff acg 18 10 5 5 30 30 5 5 0.1 0 s .1 .1 .1 [sc#00f scb#00f ab 2 2 2 tx 1.5]"); + //m_gears[3].m1.Compile("sc#9ff scb#9ff acg 18 10 5 5 30 30 5 5 0.1 0 s .1 .1 .1 [sc#00f scb#00f ab 2 2 2 tx 1.5]"); + //m_gears[4].m1.Compile("sc#9ff scb#9ff acg 18 10 5 5 30 30 5 5 0.1 0 s .1 .1 .1 [sc#00f scb#00f ab 2 2 2 tx 1.5]"); + m_gears[2].m1.Compile("[sc#0f0 ab 2 2 2 t .8 .8 .8 rx 20 ry 20 [sc#00f ab 2 2 2 tx 0 csgu]]"); + m_gears[3].m1.Compile("[sc#0f0 ab 2 2 2 t .8 .8 .8 rx 20 ry 20 [sc#00f ab 2 2 2 tx 0 csgs]]"); + m_gears[4].m1.Compile("[sc#0f0 ab 2 2 2 t .8 .8 .8 rx 20 ry 20 [sc#00f ab 2 2 2 tx 0 csga]]"); + + m_angle = 0; + + m_camera = new Camera(); + m_camera->SetProjection(mat4::perspective(30.f, 960.f, 600.f, .1f, 1000.f)); + m_camera->SetView(mat4::lookat(vec3(-15.f, 5.f, 0.f), + vec3(0.f, -1.f, 0.f), + vec3(0.f, 1.f, 0.f))); + g_scene->PushCamera(m_camera); + + /* Add a white directional light */ + m_light1 = new Light(); + m_light1->SetPosition(vec4(0.2f, 0.2f, 0.f, 0.f)); + m_light1->SetColor(vec4(0.5f, 0.5f, 0.5f, 1.f)); + Ticker::Ref(m_light1); + + /* Add an orangeish point light */ + m_light2 = new Light(); + m_light2->SetPosition(vec4(-15.f, 15.f, 15.f, 1.f)); + m_light2->SetColor(vec4(0.4f, 0.3f, 0.2f, 1.f)); + Ticker::Ref(m_light2); + + m_ready = false; + } + + ~EasyMeshTutorial() + { + g_scene->PopCamera(m_camera); + Ticker::Unref(m_light1); + Ticker::Unref(m_light2); + } + + virtual void TickGame(float seconds) + { + WorldEntity::TickGame(seconds); + + m_angle += seconds * 70.0f; + m_mat = mat4::rotate(10.0f, vec3(0, 0, 1)) + * mat4::rotate(100, vec3(0, 1, 0)); + // * mat4::rotate(m_angle, vec3(0, 1, 0)); + + m_gears[0].m3 += seconds * 20.0f; + m_gears[1].m3 += seconds * 20.0f * -2 / 9; + m_gears[2].m3 += seconds * 20.0f * -2 / 3; + m_gears[3].m3 += seconds * 20.0f * -2 / 3; + m_gears[4].m3 += seconds * 20.0f * -2 / 3; + + m_gears[0].m2 = mat4::translate(vec3(0, -1, 0)) + * mat4::rotate(m_gears[0].m3 - 130.0f, vec3(0, 1, 0)) + * mat4::rotate(40.0f, vec3(0, 0, 1)); + m_gears[1].m2 = mat4::translate(vec3(0, 0, 0)) + * mat4::rotate(m_gears[1].m3, vec3(0, 1, 0)); + m_gears[2].m2 = mat4::translate(vec3(0, 0, 5.5f)) + * mat4::rotate(m_gears[2].m3 - 40.0f, vec3(0, 1, 0)) + * mat4::rotate(90.0f, vec3(0, 0, 1)); + m_gears[3].m2 = mat4::translate(vec3(5.5f * lol::sqrt(3.f) * 0.5f, 0, -5.5f * 0.5f)) + * mat4::rotate(m_gears[3].m3 - 140.0f, vec3(0, 1, 0)) + * mat4::rotate(-70.0f, vec3(0, 0, 1)); + m_gears[4].m2 = mat4::translate(vec3(-5.5f * lol::sqrt(3.f) * 0.5f, 0, -5.5f * 0.5f)) + * mat4::rotate(m_gears[4].m3 - 80.0f, vec3(0, 1, 0)); + } + + virtual void TickDraw(float seconds) + { + WorldEntity::TickDraw(seconds); + + if (!m_ready) + { + g_renderer->SetClearColor(vec4(0.0f, 0.0f, 0.0f, 1.0f)); + + for (int i = 0; i < m_gears.Count(); i++) + m_gears[i].m1.MeshConvert(); + m_ready = true; + } + + for (int i = 0; i < m_gears.Count(); i++) + m_gears[i].m1.Render(m_mat * m_gears[i].m2); + } + +private: + Array m_gears; + float m_angle; + mat4 m_mat; + Camera *m_camera; + Light *m_light1, *m_light2; + + bool m_ready; +}; + +int main(int argc, char **argv) +{ + System::Init(argc, argv); + + Application app("Tutorial 5: EasyMesh", ivec2(960, 600), 60.0f); + new EasyMeshTutorial(); + app.Run(); + + return EXIT_SUCCESS; +} + diff --git a/tutorial/05_easymesh.vcxproj b/tutorial/05_easymesh.vcxproj new file mode 100644 index 00000000..5cefd62f --- /dev/null +++ b/tutorial/05_easymesh.vcxproj @@ -0,0 +1,76 @@ + + + + + Debug + ORBIS + + + Debug + PS3 + + + Debug + Win32 + + + Debug + x64 + + + Debug + Xbox 360 + + + Release + ORBIS + + + Release + PS3 + + + Release + Win32 + + + Release + x64 + + + Release + Xbox 360 + + + + + + + + {9e62f2fe-3408-4eae-8238-fd84238ceeda} + + + {83d3b207-c601-4025-8f41-01dedc354661} + + + + {1c5b8702-290c-42da-aa9e-671348f5b747} + Application + Win32Proj + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tutorial/06_sprite.cpp b/tutorial/06_sprite.cpp new file mode 100644 index 00000000..fa56caba --- /dev/null +++ b/tutorial/06_sprite.cpp @@ -0,0 +1,106 @@ +// +// Lol Engine - Sprite tutorial +// +// Copyright: (c) 2011-2013 Sam Hocevar +// (c) 2012 Daniel Stephens (artwork) +// This program is free software; you can redistribute it and/or +// modify it under the terms of the Do What The Fuck You Want To +// Public License, Version 2, as published by Sam Hocevar. See +// http://www.wtfpl.net/ for more details. +// + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include "core.h" + +using namespace std; +using namespace lol; + +class SpriteTutorial : public WorldEntity +{ +public: + SpriteTutorial() + { + m_camera = new Camera(); + m_camera->SetView(mat4(1.f)); + m_camera->SetProjection(mat4::ortho(0.f, 640.f, 0.f, 480.f, -100.f, 100.f)); + g_scene->PushCamera(m_camera); + Ticker::Ref(m_camera); + + m_tileset = Tiler::Register("06_sprite.png"); + for (int i = 0; i < FRAME_COUNT; ++i) + m_tileset->AddTile(ibox2(i * 24, 376, 24 + i * 24, 24 + 376)); + + for (int i = 0; i < SPRITE_COUNT; ++i) + { + m_sprites.Push(ivec3(rand(-96, 640), rand(-96, 480), 0), + rand(0.f, 1.f)); + } + + m_ready = false; + } + + ~SpriteTutorial() + { + Tiler::Deregister(m_tileset); + + g_scene->PopCamera(m_camera); + Ticker::Unref(m_camera); + } + + virtual void TickGame(float seconds) + { + for (int i = 0; i < SPRITE_COUNT; ++i) + { + m_sprites[i].m1.y += 50.f * seconds; + m_sprites[i].m2 = lol::fmod(m_sprites[i].m2 + seconds, 1.f); + if (m_sprites[i].m1.y > 480 + 48) + m_sprites[i].m1.y = rand(-96, -48); + } + + WorldEntity::TickGame(seconds); + } + + virtual void TickDraw(float seconds) + { + WorldEntity::TickDraw(seconds); + + if (!m_ready) + { + g_renderer->SetClearColor(vec4(0.0f, 0.0f, 0.0f, 1.0f)); + m_ready = true; + } + + for (int i = 0; i < SPRITE_COUNT; ++i) + { + int frame = (int)(m_sprites[i].m2 * FRAME_COUNT); +// m_sprites[i].m1.z = frame; + g_scene->AddTile(m_tileset, frame, + (ivec3)m_sprites[i].m1, 0, vec2(2.f)); + } + } + +private: + Camera *m_camera; + TileSet *m_tileset; + + static int const SPRITE_COUNT = 192; + static int const FRAME_COUNT = 16; + Array m_sprites; + + bool m_ready; +}; + +int main(int argc, char **argv) +{ + System::Init(argc, argv); + + Application app("Tutorial 6: Sprite", ivec2(640, 480), 60.0f); + new SpriteTutorial(); + app.Run(); + + return EXIT_SUCCESS; +} + diff --git a/tutorial/06_sprite.png b/tutorial/06_sprite.png new file mode 100644 index 00000000..e0bdabbb Binary files /dev/null and b/tutorial/06_sprite.png differ diff --git a/tutorial/06_sprite.vcxproj b/tutorial/06_sprite.vcxproj new file mode 100644 index 00000000..d7221b91 --- /dev/null +++ b/tutorial/06_sprite.vcxproj @@ -0,0 +1,68 @@ + + + + + Debug + PS3 + + + Debug + Win32 + + + Debug + x64 + + + Debug + Xbox 360 + + + Release + PS3 + + + Release + Win32 + + + Release + x64 + + + Release + Xbox 360 + + + + + + + + {9e62f2fe-3408-4eae-8238-fd84238ceeda} + + + {83d3b207-c601-4025-8f41-01dedc354661} + + + + {1c5b8702-290c-42da-aa9e-671348f5b747} + Application + Win32Proj + + + + + + + + + + + + + + + + + diff --git a/tutorial/08_fbo.cpp b/tutorial/08_fbo.cpp new file mode 100644 index 00000000..291021c7 --- /dev/null +++ b/tutorial/08_fbo.cpp @@ -0,0 +1,154 @@ +// +// Lol Engine - Framebuffer Object tutorial +// +// Copyright: (c) 2012-2013 Sam Hocevar +// This program is free software; you can redistribute it and/or +// modify it under the terms of the Do What The Fuck You Want To +// Public License, Version 2, as published by Sam Hocevar. See +// http://www.wtfpl.net/ for more details. +// + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include "core.h" +#include "loldebug.h" + +using namespace std; +using namespace lol; + +LOLFX_RESOURCE_DECLARE(08_fbo); + +class FBO : public WorldEntity +{ +public: + FBO() + : m_time(0.f), + m_ready(false) + { + m_vertices << vec2( 1.0, 1.0); + m_vertices << vec2(-1.0, -1.0); + m_vertices << vec2( 1.0, -1.0); + m_vertices << vec2(-1.0, -1.0); + m_vertices << vec2( 1.0, 1.0); + m_vertices << vec2(-1.0, 1.0); + } + + virtual void TickGame(float seconds) + { + WorldEntity::TickGame(seconds); + + m_time += seconds; + m_hotspot = 0.4f * vec3(lol::sin(m_time * 4.f) + lol::cos(m_time * 5.3f), + lol::sin(m_time * 5.7f) + lol::cos(m_time * 4.4f), + lol::sin(m_time * 5.f)); + m_color = 0.25f * vec3(1.1f + lol::sin(m_time * 2.5f + 1.f), + 1.1f + lol::sin(m_time * 2.8f + 1.3f), + 1.1f + lol::sin(m_time * 2.7f)); + /* Saturate dot color */ + float x = std::max(m_color.x, std::max(m_color.y, m_color.z)); + m_color /= x; + } + + virtual void TickDraw(float seconds) + { + WorldEntity::TickDraw(seconds); + + if (!m_ready) + { + m_shader = Shader::Create(LOLFX_RESOURCE_NAME(08_fbo)); + m_coord = m_shader->GetAttribLocation("in_Position", VertexUsage::Position, 0); + m_uni_flag = m_shader->GetUniformLocation("in_Flag"); + m_uni_point = m_shader->GetUniformLocation("in_Point"); + m_uni_color = m_shader->GetUniformLocation("in_Color"); + m_uni_texture = m_shader->GetUniformLocation("in_Texture"); + + m_vdecl = new VertexDeclaration(VertexStream(VertexUsage::Position)); + + m_vbo = new VertexBuffer(m_vertices.Bytes()); + void *vertices = m_vbo->Lock(0, 0); + memcpy(vertices, &m_vertices[0], m_vertices.Bytes()); + m_vbo->Unlock(); + + m_fbo = new Framebuffer(Video::GetSize()); + m_fbo->Bind(); + + { + RenderContext rc; + rc.SetClearColor(vec4(0.f, 0.f, 0.f, 1.f)); + rc.SetClearDepth(1.f); + g_renderer->Clear(ClearMask::Color | ClearMask::Depth); + } + + m_fbo->Unbind(); + + m_ready = true; + + /* FIXME: this object never cleans up */ + } + + /* FIXME: we should just disable depth test in the shader */ + RenderContext rc; + rc.SetDepthFunc(DepthFunc::Disabled); + + m_fbo->Bind(); + m_shader->Bind(); + +#if _XBOX + /* FIXME: the Xbox enforces full EDRAM clears on each frame, so + * we cannot expect the render target contents to be preserved. + * This code snippet should be moved inside the Framebuffer class. */ + m_shader->SetUniform(m_uni_flag, 1.f); + m_shader->SetUniform(m_uni_texture, m_fbo->GetTexture(), 0); + m_vdecl->SetStream(m_vbo, m_coord); + m_vdecl->Bind(); + m_vdecl->DrawElements(MeshPrimitive::Triangles, 0, 6); + m_vdecl->Unbind(); +#endif + + m_shader->SetUniform(m_uni_flag, 0.f); + m_shader->SetUniform(m_uni_point, m_hotspot); + m_shader->SetUniform(m_uni_color, m_color); + m_vdecl->SetStream(m_vbo, m_coord); + m_vdecl->Bind(); + m_vdecl->DrawElements(MeshPrimitive::Triangles, 0, 6); + m_vdecl->Unbind(); + m_shader->Unbind(); + m_fbo->Unbind(); + + m_shader->Bind(); + m_shader->SetUniform(m_uni_flag, 1.f); + m_shader->SetUniform(m_uni_texture, m_fbo->GetTexture(), 0); + m_vdecl->SetStream(m_vbo, m_coord); + m_vdecl->Bind(); + m_vdecl->DrawElements(MeshPrimitive::Triangles, 0, 6); + m_vdecl->Unbind(); + m_shader->Unbind(); + } + +private: + Array m_vertices; + Shader *m_shader; + ShaderAttrib m_coord; + ShaderUniform m_uni_flag, m_uni_point, m_uni_color, m_uni_texture; + VertexDeclaration *m_vdecl; + VertexBuffer *m_vbo; + Framebuffer *m_fbo; + double m_time; + vec3 m_hotspot, m_color; + bool m_ready; +}; + +int main(int argc, char **argv) +{ + System::Init(argc, argv); + + Application app("Tutorial 08: Framebuffer Object", ivec2(512, 512), 60.0f); + + new FBO(); + + app.Run(); + return EXIT_SUCCESS; +} + diff --git a/tutorial/08_fbo.lolfx b/tutorial/08_fbo.lolfx new file mode 100644 index 00000000..d47db363 --- /dev/null +++ b/tutorial/08_fbo.lolfx @@ -0,0 +1,94 @@ +[vert.glsl] + +#version 120 + +attribute vec2 in_Position; + +varying vec2 pass_Position; + +void main() +{ + pass_Position = in_Position; + gl_Position = vec4(in_Position, 0.0, 1.0); +} + +[frag.glsl] + +#version 120 + +#if defined GL_ES +precision highp float; +#endif + +uniform sampler2D in_Texture; +uniform float in_Flag; +uniform vec3 in_Point; +uniform vec3 in_Color; + +varying vec2 pass_Position; + +void main(void) +{ + if (in_Flag == 0.0) + { + float tc = 0.0, ta = 0.0; + { + float s = 3.0 + 2.0 * in_Point.z; + vec2 p = pass_Position - in_Point.xy * 0.9; + float t = clamp(1.2 - dot(s * p, s * p), 0.0, 1.0); + float u = t * t * t * t; + tc += 3.0 * t * t - 2.0 * t * t * t; + ta += 3.0 * u * u - 2.0 * u * u * u; + } + + gl_FragColor = vec4(tc * in_Color, ta + 0.1); + } + else + { + vec2 texcoords = pass_Position * 0.5 + vec2(0.5, 0.5); + gl_FragColor = vec4(texture2D(in_Texture, texcoords).xyz, 1.0); + } +} + +[vert.hlsl] + +void main(float2 in_Position : POSITION, + out float2 pass_Position : TEXCOORD0, + out float4 out_Position : POSITION) +{ + pass_Position = in_Position; + out_Position = float4(in_Position, 0.0, 1.0); +} + +[frag.hlsl] + +void main(in float2 pass_Position : TEXCOORD0, + uniform sampler2D in_Texture, + uniform float in_Flag, + uniform float3 in_Point, + uniform float3 in_Color, + out float4 out_FragColor : COLOR) +{ + if (in_Flag == 0.0) + { + float tc = 0.0, ta = 0.0; + { + float s = 3.0 + 2.0 * in_Point.z; + float2 p = pass_Position - in_Point.xy * 0.9; + float t = clamp(1.2 - dot(s * p, s * p), 0.0, 1.0); + float u = t * t * t * t; + tc += 3.0 * t * t - 2.0 * t * t * t; + ta += 3.0 * u * u - 2.0 * u * u * u; + } + + out_FragColor = float4(tc * in_Color, ta + 0.1); + } + else + { + float2 texcoords = pass_Position * float2(0.5, -0.5) + float2(0.5, 0.5); + /* FIXME: this should be passed as a uniform or something */ + texcoords += float2(0.5 / 800.0, 0.5 / 600.0); + out_FragColor = float4(tex2D(in_Texture, texcoords).xyz, 1.0); + } +} + diff --git a/tutorial/08_fbo.vcxproj b/tutorial/08_fbo.vcxproj new file mode 100644 index 00000000..44e1500f --- /dev/null +++ b/tutorial/08_fbo.vcxproj @@ -0,0 +1,79 @@ + + + + + Debug + ORBIS + + + Debug + PS3 + + + Debug + Win32 + + + Debug + x64 + + + Debug + Xbox 360 + + + Release + ORBIS + + + Release + PS3 + + + Release + Win32 + + + Release + x64 + + + Release + Xbox 360 + + + + + + + + + + + {9e62f2fe-3408-4eae-8238-fd84238ceeda} + + + {83d3b207-c601-4025-8f41-01dedc354661} + + + + {BCEE0132-8E24-49BE-AFEB-96DAD14396BA} + Application + Win32Proj + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tutorial/11_fractal.cpp b/tutorial/11_fractal.cpp new file mode 100644 index 00000000..0380e39b --- /dev/null +++ b/tutorial/11_fractal.cpp @@ -0,0 +1,579 @@ +// +// Lol Engine - Fractal tutorial +// +// Copyright: (c) 2011-2013 Sam Hocevar +// This program is free software; you can redistribute it and/or +// modify it under the terms of the Do What The Fuck You Want To +// Public License, Version 2, as published by Sam Hocevar. See +// http://www.wtfpl.net/ for more details. +// + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "core.h" +#include "loldebug.h" + +using namespace lol; + +LOLFX_RESOURCE_DECLARE(11_fractal); + +class Fractal : public WorldEntity +{ +public: + Fractal(ivec2 const &size) + { + /* Ensure texture size is a multiple of 16 for better aligned + * data access. Store the dimensions of a texel for our shader, + * as well as the half-size of the screen. */ + m_size = size; + m_size.x = (m_size.x + 15) & ~15; + m_size.y = (m_size.y + 15) & ~15; + m_texel_settings = vec4(1.0, 1.0, 2.0, 2.0) / m_size.xyxy; + m_screen_settings = vec4(1.0, 1.0, 0.5, 0.5) * m_size.xyxy; + + /* Window size decides the world aspect ratio. For instance, 640×480 + * will be mapped to (-0.66,-0.5) - (0.66,0.5). */ +#if !defined __native_client__ + m_window_size = Video::GetSize(); +#else + /* FIXME: it's illegal to call this on the game thread! */ + m_window_size = ivec2(640, 480); +#endif + if (m_window_size.y < m_window_size.x) + m_window2world = 0.5 / m_window_size.y; + else + m_window2world = 0.5 / m_window_size.x; + m_texel2world = (dvec2)m_window_size / (dvec2)m_size * m_window2world; + + m_oldmouse = ivec2(0, 0); + + m_pixels.Resize(m_size.x * m_size.y); + m_frame = -1; + m_slices = 4; + for (int i = 0; i < 4; i++) + { + m_deltashift[i] = real("0"); + m_deltascale[i] = real("1"); + m_dirty[i] = 2; + } +#if defined __CELLOS_LV2__ || defined _XBOX + //m_center = rcmplx(-.22815528839841, -1.11514249704382); + //m_center = rcmplx(0.001643721971153, 0.822467633298876); + m_center = rcmplx("-0.65823419062254", "0.50221777363480"); + m_zoom_speed = -0.025; +#else + m_center = rcmplx(-0.75, 0.0); + m_zoom_speed = 0.0; +#endif + m_translate = rcmplx(0.0, 0.0); + m_radius = 5.0; + m_ready = false; + m_drag = false; + + for (int i = 0; i < (MAX_ITERATIONS + 1) * PALETTE_STEP; i++) + { + double f = (double)i / PALETTE_STEP; + + double r = 0.5 * lol::sin(f * 0.27 + 2.0) + 0.5; + double g = 0.5 * lol::sin(f * 0.17 - 1.8) + 0.5; + double b = 0.5 * lol::sin(f * 0.21 - 2.6) + 0.5; + + if (f < 7.0) + { + f = f < 1.0 ? 0.0 : (f - 1.0) / 6.0; + r *= f; + g *= f; + b *= f; + } + + uint8_t red = r * 255.99f; + uint8_t green = g * 255.99f; + uint8_t blue = b * 255.99f; +#if defined __CELLOS_LV2__ || defined _XBOX + m_palette.Push(u8vec4(255, red, green, blue)); +#elif defined __native_client__ + m_palette.Push(u8vec4(red, green, blue, 255)); +#else + m_palette.Push(u8vec4(blue, green, red, 255)); +#endif + } + +#if !defined __native_client__ + m_centertext = new Text(NULL, "data/font/ascii.png"); + m_centertext->SetPos(ivec3(5, m_window_size.y - 15, 1)); + Ticker::Ref(m_centertext); + + m_mousetext = new Text(NULL, "data/font/ascii.png"); + m_mousetext->SetPos(ivec3(5, m_window_size.y - 29, 1)); + Ticker::Ref(m_mousetext); + + m_zoomtext = new Text(NULL, "data/font/ascii.png"); + m_zoomtext->SetPos(ivec3(5, m_window_size.y - 43, 1)); + Ticker::Ref(m_zoomtext); +#endif + + m_position = ivec3(0, 0, 0); + m_bbox[0] = m_position; + m_bbox[1] = ivec3(m_window_size, 0); + Input::TrackMouse(this); + +#if LOL_FEATURE_THREADS + /* Spawn worker threads and wait for their readiness. */ + for (int i = 0; i < MAX_THREADS; i++) + m_threads[i] = new Thread(DoWorkHelper, this); + for (int i = 0; i < MAX_THREADS; i++) + m_spawnqueue.Pop(); +#endif + } + + ~Fractal() + { +#if LOL_FEATURE_THREADS + /* Signal worker threads for completion and wait for + * them to quit. */ + for (int i = 0; i < MAX_THREADS; i++) + m_jobqueue.Push(-1); + for (int i = 0; i < MAX_THREADS; i++) + m_donequeue.Pop(); +#endif + + Input::UntrackMouse(this); +#if !defined __native_client__ + Ticker::Unref(m_centertext); + Ticker::Unref(m_mousetext); + Ticker::Unref(m_zoomtext); +#endif + } + + inline dcmplx TexelToWorldOffset(vec2 texel) + { + double dx = (0.5 + texel.x - m_size.x / 2) * m_texel2world.x; + double dy = (0.5 + m_size.y / 2 - texel.y) * m_texel2world.y; + return m_radius * dcmplx(dx, dy); + } + + inline dcmplx ScreenToWorldOffset(vec2 pixel) + { + /* No 0.5 offset here, because we want to be able to position the + * mouse at (0,0) exactly. */ + double dx = pixel.x - m_window_size.x / 2; + double dy = m_window_size.y / 2 - pixel.y; + return m_radius * m_window2world * dcmplx(dx, dy); + } + + virtual void TickGame(float seconds) + { + WorldEntity::TickGame(seconds); + + int prev_frame = (m_frame + 4) % 4; + m_frame = (m_frame + 1) % 4; + + rcmplx worldmouse = m_center + rcmplx(ScreenToWorldOffset(m_mousepos)); + + uint32_t buttons = Input::GetMouseButtons(); +#if !defined __CELLOS_LV2__ && !defined _XBOX + if (buttons & 0x2) + { + if (!m_drag) + { + m_oldmouse = m_mousepos; + m_drag = true; + } + m_translate = ScreenToWorldOffset(m_oldmouse) + - ScreenToWorldOffset(m_mousepos); + /* XXX: the purpose of this hack is to avoid translating by + * an exact number of pixels. If this were to happen, the step() + * optimisation for i915 cards in our shader would behave + * incorrectly because a quarter of the pixels in the image + * would have tie rankings in the distance calculation. */ + m_translate *= real(1023.0 / 1024.0); + m_oldmouse = m_mousepos; + } + else + { + m_drag = false; + if (m_translate != rcmplx(0.0, 0.0)) + { + m_translate *= real(std::pow(2.0, -seconds * 5.0)); + if ((double)m_translate.norm() < m_radius * 1e-4) + m_translate = rcmplx(0.0, 0.0); + } + } + + if (buttons & 0x5 && m_mousepos.x != -1) + { + double zoom = (buttons & 0x1) ? -0.5 : 0.5; + m_zoom_speed += zoom * seconds; + if (m_zoom_speed / zoom > 5e-3f) + m_zoom_speed = zoom * 5e-3f; + } + else if (m_zoom_speed) + { + m_zoom_speed *= std::pow(2.0, -seconds * 5.0); + if (lol::abs(m_zoom_speed) < 1e-5 || m_drag) + m_zoom_speed = 0.0; + } +#endif + + if (m_zoom_speed || m_translate != rcmplx(0.0, 0.0)) + { + rcmplx oldcenter = m_center; + double oldradius = m_radius; + double zoom = std::pow(2.0, seconds * 1e3f * m_zoom_speed); + if (m_radius * zoom > 8.0) + { + m_zoom_speed *= -1.0; + zoom = 8.0 / m_radius; + } + else if (m_radius * zoom < 1e-14) + { + m_zoom_speed *= -1.0; + zoom = 1e-14 / m_radius; + } + m_radius *= zoom; +#if !defined __CELLOS_LV2__ && !defined _XBOX + m_center += m_translate; + m_center = (m_center - worldmouse) * real(zoom) + worldmouse; + worldmouse = m_center + rcmplx(ScreenToWorldOffset(m_mousepos)); +#endif + + /* Store the transformation properties to go from m_frame - 1 + * to m_frame. */ + m_deltashift[prev_frame] = (m_center - oldcenter) / real(oldradius); + m_deltashift[prev_frame].x /= m_size.x * m_texel2world.x; + m_deltashift[prev_frame].y /= m_size.y * m_texel2world.y; + m_deltascale[prev_frame] = m_radius / oldradius; + m_dirty[0] = m_dirty[1] = m_dirty[2] = m_dirty[3] = 2; + } + else + { + /* If settings didn't change, set transformation from previous + * frame to identity. */ + m_deltashift[prev_frame] = real::R_0(); + m_deltascale[prev_frame] = real::R_1(); + } + + /* Transformation from current frame to current frame is always + * identity. */ + m_zoom_settings[m_frame][0] = 0.0f; + m_zoom_settings[m_frame][1] = 0.0f; + m_zoom_settings[m_frame][2] = 1.0f; + + /* Compute transformation from other frames to current frame */ + for (int i = 0; i < 3; i++) + { + int prev_index = (m_frame + 4 - i) % 4; + int cur_index = (m_frame + 3 - i) % 4; + + m_zoom_settings[cur_index][0] = (real)m_zoom_settings[prev_index][0] * m_deltascale[cur_index] + m_deltashift[cur_index].x; + m_zoom_settings[cur_index][1] = (real)m_zoom_settings[prev_index][1] * m_deltascale[cur_index] + m_deltashift[cur_index].y; + m_zoom_settings[cur_index][2] = (real)m_zoom_settings[prev_index][2] * m_deltascale[cur_index]; + } + + /* Precompute texture offset change instead of doing it in GLSL */ + for (int i = 0; i < 4; i++) + { + m_zoom_settings[i][0] += 0.5 * (1.0 - m_zoom_settings[i][2]); + m_zoom_settings[i][1] -= 0.5 * (1.0 - m_zoom_settings[i][2]); + } + +#if !defined __native_client__ + char buf[256]; + std::sprintf(buf, "center: "); + m_center.x.sprintf(buf + strlen(buf), 30); + std::sprintf(buf + strlen(buf), " "); + m_center.y.sprintf(buf + strlen(buf), 30); + m_centertext->SetText(buf); + std::sprintf(buf, " mouse: "); + worldmouse.x.sprintf(buf + strlen(buf), 30); + std::sprintf(buf + strlen(buf), " "); + worldmouse.y.sprintf(buf + strlen(buf), 30); + m_mousetext->SetText(buf); + std::sprintf(buf, " zoom: %g", 1.0 / m_radius); + m_zoomtext->SetText(buf); +#endif + + if (m_dirty[m_frame]) + { + m_dirty[m_frame]--; + + for (int i = 0; i < m_size.y; i += MAX_LINES * 2) + { +#if LOL_FEATURE_THREADS + m_jobqueue.Push(i); +#else + DoWork(i); +#endif + } + } + } + +#if LOL_FEATURE_THREADS + static void *DoWorkHelper(void *data) + { + Fractal *that = (Fractal *)data; + that->m_spawnqueue.Push(0); + for ( ; ; ) + { + int line = that->m_jobqueue.Pop(); + if (line == -1) + break; + that->DoWork(line); + that->m_donequeue.Push(0); + } + that->m_donequeue.Push(0); + return NULL; + }; +#endif + + void DoWork(int line) + { + double const maxsqlen = 1024; + double const k1 = 1.0 / (1 << 10) / (std::log(maxsqlen) / std::log(2.0)); + + int jmin = ((m_frame + 1) % 4) / 2 + line; + int jmax = jmin + MAX_LINES * 2; + if (jmax > m_size.y) + jmax = m_size.y; + u8vec4 *m_pixelstart = &m_pixels[0] + + m_size.x * (m_size.y / 4 * m_frame + line / 4); + + dcmplx c = (dcmplx)m_center; + + for (int j = jmin; j < jmax; j += 2) + for (int i = m_frame % 2; i < m_size.x; i += 2) + { + double xr, yr, x0, y0, x1, y1, x2, y2, x3, y3; + dcmplx z0 = c + TexelToWorldOffset(ivec2(i, j)); + //dcmplx r0(0.28693186889504513, 0.014286693904085048); + //dcmplx r0(0.001643721971153, 0.822467633298876); + //dcmplx r0(-1.207205434596, 0.315432814901); + //dcmplx r0(-0.79192956889854, -0.14632423080102); + //dcmplx r0(0.3245046418497685, 0.04855101129280834); + dcmplx r0 = z0; + + x0 = z0.x; y0 = z0.y; + xr = r0.x; yr = r0.y; + + int iter = MAX_ITERATIONS - 4; + for (;;) + { + /* Unroll the loop: tests are more expensive to do at each + * iteration than the few extra multiplications. */ + x1 = x0 * x0 - y0 * y0 + xr; + y1 = x0 * y0 + x0 * y0 + yr; + x2 = x1 * x1 - y1 * y1 + xr; + y2 = x1 * y1 + x1 * y1 + yr; + x3 = x2 * x2 - y2 * y2 + xr; + y3 = x2 * y2 + x2 * y2 + yr; + x0 = x3 * x3 - y3 * y3 + xr; + y0 = x3 * y3 + x3 * y3 + yr; + + if (x0 * x0 + y0 * y0 >= maxsqlen) + break; + iter -= 4; + if (iter < 4) + break; + } + + if (iter) + { + double n = x0 * x0 + y0 * y0; + + if (x1 * x1 + y1 * y1 >= maxsqlen) + { + iter += 3; n = x1 * x1 + y1 * y1; + } + else if (x2 * x2 + y2 * y2 >= maxsqlen) + { + iter += 2; n = x2 * x2 + y2 * y2; + } + else if (x3 * x3 + y3 * y3 >= maxsqlen) + { + iter += 1; n = x3 * x3 + y3 * y3; + } + + if (n > maxsqlen * maxsqlen) + n = maxsqlen * maxsqlen; + + /* Approximate log(sqrt(n))/log(sqrt(maxsqlen)) */ + double f = iter; + union { double n; uint64_t x; } u = { n }; + double k = (u.x >> 42) - (((1 << 10) - 1) << 10); + k *= k1; + + /* Approximate log2(k) in [1,2]. */ + f += (- 0.344847817623168308695977510213252644185 * k + + 2.024664188044341212602376988171727038739) * k + - 1.674876738008591047163498125918330313237; + + *m_pixelstart++ = m_palette[(int)(f * PALETTE_STEP)]; + } + else + { +#if defined __CELLOS_LV2__ || defined _XBOX + *m_pixelstart++ = u8vec4(255, 0, 0, 0); +#else + *m_pixelstart++ = u8vec4(0, 0, 0, 255); +#endif + } + } + } + + virtual void TickDraw(float seconds) + { + WorldEntity::TickDraw(seconds); + + static float const vertices[] = + { + 1.0f, 1.0f, + -1.0f, 1.0f, + -1.0f, -1.0f, + -1.0f, -1.0f, + 1.0f, -1.0f, + 1.0f, 1.0f, + }; + + static float const texcoords[] = + { + 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) + { + /* Create a texture of half the width and twice the height + * so that we can upload four different subimages each frame. */ + m_texture = new Texture(ivec2(m_size.x / 2, m_size.y * 2), + PixelFormat::ABGR_8); + + /* Ensure the texture data is complete at least once, otherwise + * uploading subimages will not work. */ + m_texture->SetData(&m_pixels[0]); + + m_shader = Shader::Create(LOLFX_RESOURCE_NAME(11_fractal)); + + m_vertexattrib = m_shader->GetAttribLocation("a_Vertex", VertexUsage::Position, 0); + m_texattrib = m_shader->GetAttribLocation("a_TexCoord", VertexUsage::TexCoord, 0); + m_texeluni = m_shader->GetUniformLocation("u_TexelSize"); + m_screenuni = m_shader->GetUniformLocation("u_ScreenSize"); + m_zoomuni = m_shader->GetUniformLocation("u_ZoomSettings"); + + m_vdecl = + new VertexDeclaration(VertexStream(VertexUsage::Position), + VertexStream(VertexUsage::TexCoord)); + m_vbo = new VertexBuffer(sizeof(vertices)); + m_tbo = new VertexBuffer(sizeof(texcoords)); + + void *tmp = m_vbo->Lock(0, 0); + memcpy(tmp, vertices, sizeof(vertices)); + m_vbo->Unlock(); + + tmp = m_tbo->Lock(0, 0); + memcpy(tmp, texcoords, sizeof(texcoords)); + m_tbo->Unlock(); + + /* FIXME: this object never cleans up */ + m_ready = true; + } + + m_texture->Bind(); + + if (m_dirty[m_frame]) + { +#if LOL_FEATURE_THREADS + for (int i = 0; i < m_size.y; i += MAX_LINES * 2) + m_donequeue.Pop(); +#endif + + m_dirty[m_frame]--; + +#if defined __CELLOS_LV2__ + /* glTexSubImage2D is extremely slow on the PS3, to the point + * that uploading the whole texture is 40 times faster. */ + m_texture->SetData(&m_pixels[0]); +#else + m_texture->SetSubData(ivec2(0, m_frame * m_size.y / 2), + m_size / 2, + &m_pixels[m_size.x * m_size.y / 4 * m_frame]); +#endif + } + + m_shader->Bind(); + m_shader->SetUniform(m_texeluni, m_texel_settings); + m_shader->SetUniform(m_screenuni, m_screen_settings); + m_shader->SetUniform(m_zoomuni, m_zoom_settings); + m_vdecl->Bind(); + m_vdecl->SetStream(m_vbo, m_vertexattrib); + m_vdecl->SetStream(m_tbo, m_texattrib); + m_texture->Bind(); + m_vdecl->DrawElements(MeshPrimitive::Triangles, 0, 6); + m_vdecl->Unbind(); + } + +private: + static int const MAX_ITERATIONS = 340; + static int const PALETTE_STEP = 32; + static int const MAX_THREADS = 8; + static int const MAX_LINES = 8; + + ivec2 m_size, m_window_size, m_oldmouse; + double m_window2world; + dvec2 m_texel2world; + Array m_pixels, m_palette; + + Shader *m_shader; + ShaderAttrib m_vertexattrib, m_texattrib; + ShaderUniform m_texeluni, m_screenuni, m_zoomuni; + + VertexDeclaration *m_vdecl; + VertexBuffer *m_vbo, *m_tbo; + Texture *m_texture; + + int m_frame, m_slices, m_dirty[4]; + bool m_ready, m_drag; + + rcmplx m_deltashift[4], m_center, m_translate; + real m_deltascale[4]; + double m_zoom_speed, m_radius; + + vec4 m_texel_settings, m_screen_settings; + mat4 m_zoom_settings; + +#if LOL_FEATURE_THREADS + /* Worker threads */ + Thread *m_threads[MAX_THREADS]; + Queue m_spawnqueue, m_jobqueue, m_donequeue; +#endif + +#if !defined __native_client__ + /* Debug information */ + Text *m_centertext, *m_mousetext, *m_zoomtext; +#endif +}; + +int main(int argc, char **argv) +{ + ivec2 window_size(640, 480); + + System::Init(argc, argv); + Application app("Tutorial 3: Fractal", window_size, 60.0f); + + new DebugFps(5, 5); + new Fractal(window_size); + //new DebugRecord("fractalol.ogm", 60.0f); + + app.Run(); + + return EXIT_SUCCESS; +} + diff --git a/tutorial/11_fractal.lolfx b/tutorial/11_fractal.lolfx new file mode 100644 index 00000000..803e8021 --- /dev/null +++ b/tutorial/11_fractal.lolfx @@ -0,0 +1,192 @@ +[vert.glsl] + +#version 120 + +uniform mat4 u_ZoomSettings; +uniform vec4 u_TexelSize; +uniform vec4 u_ScreenSize; + +attribute vec2 a_TexCoord; +attribute vec2 a_Vertex; + +varying vec4 v_CenterX, v_CenterY, v_IndexX, v_IndexY; + +void main(void) +{ + gl_Position = vec4(a_Vertex, 0.0, 1.0); + /* Center point in [-.5,.5], apply zoom and translation + * transformation, and go back to texture coordinates + * in [0,1]. That's the ideal point we would like to + * compute the value for. Then add or remove half the + * size of a texel: the distance from this new point to + * the final point will be our error. */ + vec4 offsets = vec4(0.5, -0.5, 0.015625, -0.015625); + vec4 zoomscale = vec4(u_ZoomSettings[0][2], + u_ZoomSettings[1][2], + u_ZoomSettings[2][2], + u_ZoomSettings[3][2]); + vec4 zoomtx = vec4(u_ZoomSettings[0][0], + u_ZoomSettings[1][0], + u_ZoomSettings[2][0], + u_ZoomSettings[3][0]); + vec4 zoomty = vec4(u_ZoomSettings[0][1], + u_ZoomSettings[1][1], + u_ZoomSettings[2][1], + u_ZoomSettings[3][1]); + v_CenterX = zoomscale * a_TexCoord.x + zoomtx + + offsets.xyxy * u_TexelSize.x; + v_CenterY = zoomscale * a_TexCoord.y - zoomty + + offsets.xyyx * u_TexelSize.y; + /* Precompute the multiple of one texel where our ideal + * point lies. The fragment shader will call floor() on + * this value. We add or remove a slight offset to avoid + * rounding issues at the image's edges. */ + v_IndexX = v_CenterX * u_ScreenSize.z - offsets.zwzw; + v_IndexY = v_CenterY * u_ScreenSize.w - offsets.zwwz; +} + +[frag.glsl] + +#version 120 + +#if defined GL_ES +precision highp float; +#endif + +uniform vec4 u_TexelSize; +uniform sampler2D u_Texture; + +varying vec4 v_CenterX, v_CenterY, v_IndexX, v_IndexY; + +void main(void) +{ + vec4 v05 = vec4(0.5, 0.5, 0.5, 0.5); + vec4 rx, ry, t0, dx, dy, dd; + /* Get a pixel coordinate from each slice into rx & ry */ + rx = u_TexelSize.x + u_TexelSize.z * floor(v_IndexX); + ry = u_TexelSize.y + u_TexelSize.w * floor(v_IndexY); + /* Compute inverse distance to expected pixel in dd, + * and put zero if we fall outside the texture. */ + t0 = step(abs(rx - v05), v05) * step(abs(ry - v05), v05); + dx = rx - v_CenterX; + dy = ry - v_CenterY; +#if 0 + vec4 dd = t0 * (abs(dx) + abs(dy)); + vec4 dd = t0 / (0.001 + sqrt((dx * dx) + (dy * dy))); +#endif + dd = t0 / (0.000001 + (dx * dx) + (dy * dy)); + /* Modify Y coordinate to select proper quarter. */ + ry = ry * 0.25 + vec4(0.0, 0.25, 0.5, 0.75); + +#if 1 +# if 0 + /* XXX: disabled until we can autodetect i915 */ + /* t1.x <-- dd.x > dd.y */ + /* t1.y <-- dd.z > dd.w */ + vec2 t1 = step(dd.xz, dd.yw); + /* ret.x <-- max(rx.x, rx.y) wrt. t1.x */ + /* ret.y <-- max(rx.z, rx.w) wrt. t1.y */ + /* ret.z <-- max(ry.x, ry.y) wrt. t1.x */ + /* ret.w <-- max(ry.z, ry.w) wrt. t1.y */ + vec4 ret = mix(vec4(rx.xz, ry.xz), + vec4(rx.yw, ry.yw), t1.xyxy); + /* dd.x <-- max(dd.x, dd.y) */ + /* dd.z <-- max(dd.z, dd.w) */ + dd.xy = mix(dd.xz, dd.yw, t1); + /* t2 <-- dd.x > dd.z */ + float t2 = step(dd.x, dd.y); + /* ret.x <-- max(ret.x, ret.y); */ + /* ret.y <-- max(ret.z, ret.w); */ + ret.xy = mix(ret.xz, ret.yw, t2); +# else + /* Fallback for i915 cards -- the trick to reduce the + * number of operations is to compute both step(a,b) + * and step(b,a) and hope that their sum is 1. This is + * almost always the case, and when it isn't we can + * afford to have a few wrong pixels. However, a real + * problem is when panning the image, because half the + * screen is likely to flicker. To avoid this problem, + * we cheat a little (see m_translate comment above). */ + vec4 t1 = step(dd.xzyw, dd.ywxz); + vec4 ret = vec4(rx.xz, ry.xz) * t1.zwzw + + vec4(rx.yw, ry.yw) * t1.xyxy; + dd.xy = dd.xz * t1.zw + dd.yw * t1.xy; + vec2 t2 = step(dd.xy, dd.yx); + ret.xy = ret.xz * t2.yy + ret.yw * t2.xx; +# endif + /* Nearest neighbour */ + gl_FragColor = texture2D(u_Texture, ret.xy); +#else + /* Alternate version: some kind of linear interpolation */ + vec4 p0 = texture2D(u_Texture, vec2(rx.x, ry.x)); + vec4 p1 = texture2D(u_Texture, vec2(rx.y, ry.y)); + vec4 p2 = texture2D(u_Texture, vec2(rx.z, ry.z)); + vec4 p3 = texture2D(u_Texture, vec2(rx.w, ry.w)); + gl_FragColor = 1.0 / (dd.x + dd.y + dd.z + dd.w) + * (dd.x * p0 + dd.y * p1 + dd.z * p2 + dd.w * p3); +#endif +} + +[vert.hlsl] + +void main(float2 a_Vertex : POSITION, + float2 a_TexCoord : TEXCOORD0, + uniform float4x4 u_ZoomSettings, + uniform float4 u_TexelSize, + uniform float4 u_ScreenSize, + out float4 out_Position : POSITION0, + out float4 v_CenterX : TEXCOORD0, + out float4 v_CenterY : TEXCOORD1, + out float4 v_IndexX : TEXCOORD2, + out float4 v_IndexY : TEXCOORD3) +{ + out_Position = float4(a_Vertex, 0.0, 1.0); + float4 offsets = float4(0.5, -0.5, 0.015625, -0.015625); + float4 zoomscale = float4(u_ZoomSettings[2][0], + u_ZoomSettings[2][1], + u_ZoomSettings[2][2], + u_ZoomSettings[2][3]); + float4 zoomtx = float4(u_ZoomSettings[0][0], + u_ZoomSettings[0][1], + u_ZoomSettings[0][2], + u_ZoomSettings[0][3]); + float4 zoomty = float4(u_ZoomSettings[1][0], + u_ZoomSettings[1][1], + u_ZoomSettings[1][2], + u_ZoomSettings[1][3]); + v_CenterX = zoomscale * a_TexCoord.x + zoomtx + + offsets.xyxy * u_TexelSize.x; + v_CenterY = zoomscale * a_TexCoord.y - zoomty + + offsets.xyyx * u_TexelSize.y; + v_IndexX = v_CenterX * u_ScreenSize.z - offsets.zwzw; + v_IndexY = v_CenterY * u_ScreenSize.w - offsets.zwwz; +} + +[frag.hlsl] + +void main(in float4 v_CenterX : TEXCOORD0, + in float4 v_CenterY : TEXCOORD1, + in float4 v_IndexX : TEXCOORD2, + in float4 v_IndexY : TEXCOORD3, + uniform float4 u_TexelSize, + uniform sampler2D u_Texture, + out float4 out_FragColor : COLOR) +{ + float4 v05 = float4(0.5, 0.5, 0.5, 0.5); + float4 rx, ry, t0, dx, dy, dd; + rx = u_TexelSize.x + u_TexelSize.z * floor(v_IndexX); + ry = u_TexelSize.y + u_TexelSize.w * floor(v_IndexY); + t0 = step(abs(rx - v05), v05) * step(abs(ry - v05), v05); + dx = rx - v_CenterX; + dy = ry - v_CenterY; + dd = t0 / (0.000001 + (dx * dx) + (dy * dy)); + ry = ry * 0.25 + float4(0.0, 0.25, 0.5, 0.75); + float2 t1 = step(dd.xz, dd.yw); + float4 ret = lerp(float4(rx.xz, ry.xz), + float4(rx.yw, ry.yw), t1.xyxy); + dd.xy = lerp(dd.xz, dd.yw, t1); + float t2 = step(dd.x, dd.y); + ret.xy = lerp(ret.xz, ret.yw, t2); + out_FragColor = tex2D(u_Texture, ret.xy); +} + diff --git a/tutorial/11_fractal.vcxproj b/tutorial/11_fractal.vcxproj new file mode 100644 index 00000000..63370a0e --- /dev/null +++ b/tutorial/11_fractal.vcxproj @@ -0,0 +1,79 @@ + + + + + Debug + ORBIS + + + Debug + PS3 + + + Debug + Win32 + + + Debug + x64 + + + Debug + Xbox 360 + + + Release + ORBIS + + + Release + PS3 + + + Release + Win32 + + + Release + x64 + + + Release + Xbox 360 + + + + + + + + + + + {9e62f2fe-3408-4eae-8238-fd84238ceeda} + + + {83d3b207-c601-4025-8f41-01dedc354661} + + + + {6bf81b39-edc2-4227-9992-c2d8abea95af} + Application + Win32Proj + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tutorial/12_distance.lolfx b/tutorial/12_distance.lolfx new file mode 100644 index 00000000..d47db363 --- /dev/null +++ b/tutorial/12_distance.lolfx @@ -0,0 +1,94 @@ +[vert.glsl] + +#version 120 + +attribute vec2 in_Position; + +varying vec2 pass_Position; + +void main() +{ + pass_Position = in_Position; + gl_Position = vec4(in_Position, 0.0, 1.0); +} + +[frag.glsl] + +#version 120 + +#if defined GL_ES +precision highp float; +#endif + +uniform sampler2D in_Texture; +uniform float in_Flag; +uniform vec3 in_Point; +uniform vec3 in_Color; + +varying vec2 pass_Position; + +void main(void) +{ + if (in_Flag == 0.0) + { + float tc = 0.0, ta = 0.0; + { + float s = 3.0 + 2.0 * in_Point.z; + vec2 p = pass_Position - in_Point.xy * 0.9; + float t = clamp(1.2 - dot(s * p, s * p), 0.0, 1.0); + float u = t * t * t * t; + tc += 3.0 * t * t - 2.0 * t * t * t; + ta += 3.0 * u * u - 2.0 * u * u * u; + } + + gl_FragColor = vec4(tc * in_Color, ta + 0.1); + } + else + { + vec2 texcoords = pass_Position * 0.5 + vec2(0.5, 0.5); + gl_FragColor = vec4(texture2D(in_Texture, texcoords).xyz, 1.0); + } +} + +[vert.hlsl] + +void main(float2 in_Position : POSITION, + out float2 pass_Position : TEXCOORD0, + out float4 out_Position : POSITION) +{ + pass_Position = in_Position; + out_Position = float4(in_Position, 0.0, 1.0); +} + +[frag.hlsl] + +void main(in float2 pass_Position : TEXCOORD0, + uniform sampler2D in_Texture, + uniform float in_Flag, + uniform float3 in_Point, + uniform float3 in_Color, + out float4 out_FragColor : COLOR) +{ + if (in_Flag == 0.0) + { + float tc = 0.0, ta = 0.0; + { + float s = 3.0 + 2.0 * in_Point.z; + float2 p = pass_Position - in_Point.xy * 0.9; + float t = clamp(1.2 - dot(s * p, s * p), 0.0, 1.0); + float u = t * t * t * t; + tc += 3.0 * t * t - 2.0 * t * t * t; + ta += 3.0 * u * u - 2.0 * u * u * u; + } + + out_FragColor = float4(tc * in_Color, ta + 0.1); + } + else + { + float2 texcoords = pass_Position * float2(0.5, -0.5) + float2(0.5, 0.5); + /* FIXME: this should be passed as a uniform or something */ + texcoords += float2(0.5 / 800.0, 0.5 / 600.0); + out_FragColor = float4(tex2D(in_Texture, texcoords).xyz, 1.0); + } +} + diff --git a/tutorial/12_texture_to_screen.lolfx b/tutorial/12_texture_to_screen.lolfx new file mode 100644 index 00000000..78ca4d6d --- /dev/null +++ b/tutorial/12_texture_to_screen.lolfx @@ -0,0 +1,92 @@ +[vert.glsl] + +#version 120 + +attribute vec2 in_position; + +varying vec2 pass_position; + +void main() +{ + pass_position = in_position; + gl_Position = vec4(in_position, 0.0, 1.0); +} + +[frag.glsl] + +#version 120 + +#if defined GL_ES +precision highp float; +#endif + +uniform sampler2D in_texture; +varying vec2 pass_position; + +vec3 rand_color(float t) +{ + return vec3(0.5 + 0.5 * sin(t * 19.0 + 17.0), + 0.5 + 0.5 * sin(t * 24.0 + 23.0), + 0.5 + 0.5 * sin(t * 37.0 + 12.0)); +} + +void main(void) +{ + vec2 texcoords = pass_position * 0.5 + vec2(0.5, 0.5); + vec4 src_color = texture2D(in_texture, texcoords); + float newg = src_color.z; + float newb = 0.0; + if (newg > 0.0) + newb = 1.0; + gl_FragColor = vec4(rand_color(newg), 1.0); +} + +[vert.hlsl] + +void main(float2 in_Position : POSITION, + out float2 pass_Position : TEXCOORD0, + out float4 out_Position : POSITION) +{ + pass_Position = in_Position; + out_Position = float4(in_Position, 0.0, 1.0); +} + +[frag.hlsl] + +float3 rand_color(float t) +{ + return float3(0.5 + 0.5 * sin(t * 9.0 + 3.0), + 0.5 + 0.5 * sin(t * 4.0 + 1.0), + 0.5 + 0.5 * sin(t * 7.0 + 2.0)); +} + +void main(in float2 pass_Position : TEXCOORD0, + uniform sampler2D in_Texture, + uniform float in_Flag, + uniform float3 in_Point, + uniform float3 in_Color, + out float4 out_FragColor : COLOR) +{ + if (in_Flag == 0.0) + { + float tc = 0.0, ta = 0.0; + { + float s = 3.0 + 2.0 * in_Point.z; + float2 p = pass_Position - in_Point.xy * 0.9; + float t = clamp(1.2 - dot(s * p, s * p), 0.0, 1.0); + float u = t * t * t * t; + tc += 3.0 * t * t - 2.0 * t * t * t; + ta += 3.0 * u * u - 2.0 * u * u * u; + } + + out_FragColor = float4(tc * in_Color, ta + 0.1); + } + else + { + float2 texcoords = pass_Position * float2(0.5, -0.5) + float2(0.5, 0.5); + /* FIXME: this should be passed as a uniform or something */ + texcoords += float2(0.5 / 800.0, 0.5 / 600.0); + out_FragColor = float4(tex2D(in_Texture, texcoords).xyz, 1.0); + } +} + diff --git a/tutorial/12_voronoi.cpp b/tutorial/12_voronoi.cpp new file mode 100644 index 00000000..08548943 --- /dev/null +++ b/tutorial/12_voronoi.cpp @@ -0,0 +1,405 @@ +// +// Lol Engine - Framebuffer Object tutorial +// +// Copyright: (c) 2013-2013 Sam Hocevar +// (c) 2013-2013 Benjamin "Touky" Huet +// This program is free software; you can redistribute it and/or +// modify it under the terms of the Do What The Fuck You Want To +// Public License, Version 2, as published by Sam Hocevar. See +// http://www.wtfpl.net/ for more details. +// + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include "core.h" +#include "loldebug.h" + +using namespace std; +using namespace lol; + +LOLFX_RESOURCE_DECLARE(12_voronoi); +LOLFX_RESOURCE_DECLARE(12_voronoi_setup); +LOLFX_RESOURCE_DECLARE(12_voronoi_distance); +LOLFX_RESOURCE_DECLARE(12_distance); +LOLFX_RESOURCE_DECLARE(12_texture_to_screen); + +enum FboType +{ + SrcVoronoiFbo, + VoronoiFbo, + DistanceVoronoiFbo, + DistanceFbo, + + MaxFboType +}; + +class Voronoi : public WorldEntity +{ +public: + Voronoi() + { + m_vertices << vec2( 1.0, 1.0); + m_vertices << vec2(-1.0, -1.0); + m_vertices << vec2( 1.0, -1.0); + m_vertices << vec2(-1.0, -1.0); + m_vertices << vec2( 1.0, 1.0); + m_vertices << vec2(-1.0, 1.0); + m_ready = false; + m_cur_fbo = 0; + m_time = .0f; + m_timer = -1.0f; + mode = 0; + } + + virtual void TickGame(float seconds) + { + WorldEntity::TickGame(seconds); + + { + //Shutdown logic + if (Input::WasReleased(Key::Escape)) + Ticker::Shutdown(); + } + + m_time += seconds; + m_hotspot = 0.4f * vec3(lol::sin(m_time * 4.f) + lol::cos(m_time * 5.3f), + lol::sin(m_time * 5.7f) + lol::cos(m_time * 4.4f), + lol::sin(m_time * 5.f)); + m_color = 0.25f * vec3(1.1f + lol::sin(m_time * 2.5f + 1.f), + 1.1f + lol::sin(m_time * 2.8f + 1.3f), + 1.1f + lol::sin(m_time * 2.7f)); + /* Saturate dot color */ + float x = std::max(m_color.x, std::max(m_color.y, m_color.z)); + m_color /= x; + } + + virtual void TickDraw(float seconds) + { + WorldEntity::TickDraw(seconds); + + if (!m_ready) + { + m_vdecl = new VertexDeclaration(VertexStream(VertexUsage::Position)); + + m_vbo = new VertexBuffer(m_vertices.Bytes()); + void *vertices = m_vbo->Lock(0, 0); + memcpy(vertices, &m_vertices[0], m_vertices.Bytes()); + m_vbo->Unlock(); + + m_screen_shader = Shader::Create(LOLFX_RESOURCE_NAME(12_texture_to_screen)); + m_screen_coord = m_screen_shader->GetAttribLocation("in_position", VertexUsage::Position, 0); + m_screen_texture = m_screen_shader->GetUniformLocation("in_texture"); + + for (int i = 0; i < MaxFboType; ++i) + { + m_fbos.Push(new Framebuffer(Video::GetSize()), 0, Array(), Array() ); + + if (i == SrcVoronoiFbo) + { + m_fbos[i].m2 = Shader::Create(LOLFX_RESOURCE_NAME(12_voronoi_setup)); + m_fbos[i].m3 << m_fbos[i].m2->GetUniformLocation("in_texture"); + m_fbos[i].m3 << m_fbos[i].m2->GetUniformLocation("in_source_point"); + m_fbos[i].m3 << m_fbos[i].m2->GetUniformLocation("in_screen_res"); + m_fbos[i].m4 << m_fbos[i].m2->GetAttribLocation("in_position", VertexUsage::Position, 0); + } + else if (i == VoronoiFbo) + { + m_fbos[i].m2 = Shader::Create(LOLFX_RESOURCE_NAME(12_voronoi)); + m_fbos[i].m3 << m_fbos[i].m2->GetUniformLocation("in_texture"); + m_fbos[i].m3 << m_fbos[i].m2->GetUniformLocation("in_step"); + m_fbos[i].m3 << m_fbos[i].m2->GetUniformLocation("in_screen_res"); + m_fbos[i].m4 << m_fbos[i].m2->GetAttribLocation("in_position", VertexUsage::Position, 0); + } + else if (i == DistanceVoronoiFbo) + { + m_fbos[i].m2 = Shader::Create(LOLFX_RESOURCE_NAME(12_voronoi_distance)); + } + else if (i == DistanceFbo) + { + m_fbos[i].m2 = Shader::Create(LOLFX_RESOURCE_NAME(12_distance)); + } + + m_fbos.Last().m1->Bind(); + { + RenderContext rc; + rc.SetClearColor(vec4(0.f, 0.f, 0.f, 1.f)); + rc.SetClearDepth(1.f); + g_renderer->Clear(ClearMask::Color | ClearMask::Depth); + } + m_fbos.Last().m1->Unbind(); + } + + temp_buffer = new Framebuffer(Video::GetSize()); + temp_buffer->Bind(); + { + RenderContext rc; + rc.SetClearColor(vec4(0.f, 0.f, 0.f, 1.f)); + rc.SetClearDepth(1.f); + g_renderer->Clear(ClearMask::Color | ClearMask::Depth); + } + temp_buffer->Unbind(); + + m_ready = true; + /* FIXME: this object never cleans up */ + + //SRC SETUP + m_cur_fbo = VoronoiFbo; + } + + { + //Shutdown logic + if (Input::WasReleased(Key::O)) + voronoi_points.Pop(); + else if (Input::WasReleased(Key::P)) + voronoi_points.Push(vec3(rand(512.f), rand(512.f), .0f), + vec2(64.f + rand(64.f), 64.f + rand(64.f))); + else if (Input::WasReleased(Key::F1)) + m_cur_fbo = SrcVoronoiFbo; + else if (Input::WasReleased(Key::F2)) + m_cur_fbo = VoronoiFbo; + else if (Input::WasReleased(Key::F3)) + { + voronoi_points.Empty(); + if (mode == 0) + { + int i = 4; + while (i-- > 0) + voronoi_points.Push(vec3(rand(512.f), rand(512.f), .0f), + vec2(64.f + rand(64.f), 64.f + rand(64.f)) + //vec2(0.f) + ); + mode = 1; + } + else + { + mode = 0; + } + } + } + + if (mode == 0) + { + voronoi_points.Empty(); + int maxi = 6; + for (int i = 0; i < maxi; ++i) + { + voronoi_points.Push(vec3(256.f) + 196.f * vec3(lol::cos(m_time + i * 2.f * F_PI / maxi), lol::sin(m_time + i * 2.f * F_PI / maxi), .0f), vec2(.0f)); + voronoi_points.Push(vec3(256.f) + 128.f * vec3(lol::cos(-m_time + i * 2.f * F_PI / maxi), lol::sin(-m_time + i * 2.f * F_PI / maxi), .0f), vec2(.0f)); + voronoi_points.Push(vec3(256.f) + 64.f * vec3(lol::cos(m_time + i * 2.f * F_PI / maxi), lol::sin(m_time + i * 2.f * F_PI / maxi), .0f), vec2(.0f)); + voronoi_points.Push(vec3(256.f) + 32.f * vec3(lol::cos(-m_time + i * 2.f * F_PI / maxi), lol::sin(-m_time + i * 2.f * F_PI / maxi), .0f), vec2(.0f)); + } + voronoi_points.Push(vec3(256.f), vec2(.0f)); + } + + temp_buffer->Bind(); + { + RenderContext rc; + rc.SetClearColor(vec4(0.f, 0.f, 0.f, 1.f)); + rc.SetClearDepth(1.f); + g_renderer->Clear(ClearMask::Color | ClearMask::Depth); + } + temp_buffer->Unbind(); + + { + vec2 limit(1.f, 511.f); + //SRC SETUP + for (int j = 0; j < voronoi_points.Count(); ++j) + { + voronoi_points[j].m1 = vec3(voronoi_points[j].m1.xy + voronoi_points[j].m2 * seconds, voronoi_points[j].m1.z); + if (voronoi_points[j].m1.x >= limit.y || voronoi_points[j].m1.x <= limit.x) + { + voronoi_points[j].m2.x *= -1.f; + voronoi_points[j].m1.x = clamp(voronoi_points[j].m1.x, limit.x, limit.y); + } + if (voronoi_points[j].m1.y >= limit.y || voronoi_points[j].m1.y <= limit.x) + { + voronoi_points[j].m2.y *= -1.f; + voronoi_points[j].m1.y = clamp(voronoi_points[j].m1.y, limit.x, limit.y); + } + voronoi_points[j].m1.z = ((float)j + 1) / ((float)voronoi_points.Count()); + } + + int f = SrcVoronoiFbo; + + m_fbos[f].m1->Bind(); + { + RenderContext rc; + rc.SetClearColor(vec4(0.f, 0.f, 0.f, 1.f)); + rc.SetClearDepth(1.f); + g_renderer->Clear(ClearMask::Color | ClearMask::Depth); + } + m_fbos[f].m1->Unbind(); + + int buf = voronoi_points.Count() % 2; + for (int j = 0; j < voronoi_points.Count(); ++j) + { + Framebuffer *dst_buf; + Framebuffer *src_buf; + + if (buf) + { + dst_buf = m_fbos[f].m1; + src_buf = temp_buffer; + } + else + { + src_buf = m_fbos[f].m1; + dst_buf = temp_buffer; + } + + dst_buf->Bind(); + /* FIXME: we should just disable depth test in the shader */ + g_renderer->Clear(ClearMask::Depth); + m_fbos[f].m2->Bind(); + + int i = 0; + m_fbos[f].m2->SetUniform(m_fbos[f].m3[i++], src_buf->GetTexture(), 0); //"in_texture" + m_fbos[f].m2->SetUniform(m_fbos[f].m3[i++], voronoi_points[j].m1); //"in_source_point" + m_fbos[f].m2->SetUniform(m_fbos[f].m3[i++], vec2(512.f, 512.f)); //"in_screen_res" + + m_vdecl->SetStream(m_vbo, m_fbos[f].m4.Last()); + m_vdecl->Bind(); + m_vdecl->DrawElements(MeshPrimitive::Triangles, 0, 6); + m_vdecl->Unbind(); + m_fbos[f].m2->Unbind(); + dst_buf->Unbind(); + + buf = 1 - buf; + } + } + + g_renderer->Clear(ClearMask::Color | ClearMask::Depth); + + //FRAME BUFFER DRAW + m_timer -= seconds; + if (m_timer < .0f && m_cur_fbo != SrcVoronoiFbo) + { + //m_timer = 1.0f; + m_fbos[m_cur_fbo].m1->Bind(); + { + RenderContext rc; + rc.SetClearColor(vec4(0.f, 0.f, 0.f, 1.f)); + rc.SetClearDepth(1.f); + g_renderer->Clear(ClearMask::Color | ClearMask::Depth); + } + m_fbos[m_cur_fbo].m1->Unbind(); + + ivec2 curres = ivec2(512, 512) / 2; + int buf = 0; + while (1) + { + Framebuffer *dst_buf; + Framebuffer *src_buf; + Shader *shader; + + if (curres == ivec2(0)) + shader = m_screen_shader; + else + shader = m_fbos[m_cur_fbo].m2; + + if (curres.x == 256) + src_buf = m_fbos[SrcVoronoiFbo].m1; + else if (buf) + src_buf = m_fbos[m_cur_fbo].m1; + else + src_buf = temp_buffer; + + if (buf) + dst_buf = temp_buffer; + else + dst_buf = m_fbos[m_cur_fbo].m1; + + dst_buf->Bind(); + /* FIXME: we should just disable depth test in the shader */ + g_renderer->Clear(ClearMask::Depth); + shader->Bind(); + + //08_FBO ?? + #if _XBOX + /* FIXME: the Xbox enforces full EDRAM clears on each frame, so + * we cannot expect the render target contents to be preserved. + * This code snippet should be moved inside the Framebuffer class. */ + //m_fbos[m_cur_fbo].m2->SetUniform(m_uni_flag, 1.f); + //m_fbos[m_cur_fbo].m2->SetUniform(m_uni_texture, m_fbo->GetTexture(), 0); + //m_vdecl->SetStream(m_vbo, m_fbos[m_cur_fbo].m4.Last()); + //m_vdecl->Bind(); + //m_vdecl->DrawElements(MeshPrimitive::Triangles, 0, 6); + //m_vdecl->Unbind(); + #endif + + int i = 0; + if (curres == ivec2(0)) + m_screen_shader->SetUniform(m_screen_texture, src_buf->GetTexture(), 0); + else if (m_cur_fbo == VoronoiFbo) + { + shader->SetUniform(m_fbos[m_cur_fbo].m3[i++], src_buf->GetTexture(), 0); //"in_texture" + shader->SetUniform(m_fbos[m_cur_fbo].m3[i++], ((float)curres.x) / 512.f); //"in_step" + shader->SetUniform(m_fbos[m_cur_fbo].m3[i++], vec2(512.f, 512.f)); //"in_screen_res" + } + + m_vdecl->SetStream(m_vbo, m_fbos[m_cur_fbo].m4.Last()); + m_vdecl->Bind(); + m_vdecl->DrawElements(MeshPrimitive::Triangles, 0, 6); + m_vdecl->Unbind(); + m_fbos[m_cur_fbo].m2->Unbind(); + dst_buf->Unbind(); + + if (curres == ivec2(0)) + break; + if (curres == ivec2(1)) + { + if (buf == 1) + curres = ivec2(0); + else + break; + } + buf = 1 - buf; + curres /= 2; + } + } + + //SCREEN DRAW + m_screen_shader->Bind(); + m_screen_shader->SetUniform(m_screen_texture, m_fbos[m_cur_fbo].m1->GetTexture(), 0); + m_vdecl->SetStream(m_vbo, m_screen_coord); + m_vdecl->Bind(); + m_vdecl->DrawElements(MeshPrimitive::Triangles, 0, 6); + m_vdecl->Unbind(); + m_screen_shader->Unbind(); + } + +private: + Array voronoi_points; + Array m_vertices; + Shader *m_screen_shader; + ShaderAttrib m_screen_coord; + ShaderUniform m_screen_texture; + + VertexDeclaration *m_vdecl; + VertexBuffer *m_vbo; + + Array, Array > m_fbos; + Framebuffer *temp_buffer; + + int mode; + int m_cur_fbo; + double m_time; + vec3 m_hotspot, m_color; + bool m_ready; + float m_timer; +}; + +int main(int argc, char **argv) +{ + System::Init(argc, argv); + + Application app("Tutorial 12: Jump Flooding Algorithm & Voronoi", ivec2(512, 512), 60.0f); + + new Voronoi(); + + app.Run(); + return EXIT_SUCCESS; +} + diff --git a/tutorial/12_voronoi.lolfx b/tutorial/12_voronoi.lolfx new file mode 100644 index 00000000..8ae45a11 --- /dev/null +++ b/tutorial/12_voronoi.lolfx @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +//GLSL +//----------------------------------------------------------------------------- +[vert.glsl] + +#version 120 + +attribute vec2 in_position; + +uniform vec2 in_screen_res; + +uniform float in_step; +varying vec2 pass_pos; +varying vec2 pass_p[8]; + +void main() +{ + //JFA ALGO + pass_pos = ((vec2(1.0) + in_position) * 0.5); + + float k = in_step; + + vec2 p2 = pass_pos; + pass_p[0] = p2 + vec2(-k, -k); + pass_p[1] = p2 + vec2( 0, -k); + pass_p[2] = p2 + vec2( k, -k); + pass_p[3] = p2 + vec2(-k, 0); + pass_p[4] = p2 + vec2( k, 0); + pass_p[5] = p2 + vec2(-k, k); + pass_p[6] = p2 + vec2( 0, k); + pass_p[7] = p2 + vec2( k, k); + + gl_Position = vec4(in_position, 0.0, 1.0); +} + +[frag.glsl] + +#version 120 + +#if defined GL_ES +precision highp float; +#endif + +uniform sampler2D in_texture; + +varying vec2 pass_pos; +varying vec2 pass_p[8]; + +void main(void) +{ + vec4 src_color = texture2D(in_texture, pass_pos); + vec4 neigh_color; + + for (int i = 0; i < 8; ++i) + { + neigh_color = texture2D(in_texture, pass_p[i]); + + if (neigh_color.z > 0.0 && src_color.z == 0.0) + src_color = neigh_color; + else if (neigh_color.z > 0.0 && src_color.z > 0.0 && + length(neigh_color.xy - pass_pos) < length(src_color.xy - pass_pos)) + src_color = neigh_color; + + } + + gl_FragColor = src_color; +} + +//----------------------------------------------------------------------------- +//HLSL +//----------------------------------------------------------------------------- +[vert.hlsl] + +void main(float2 in_Position : POSITION, + out float2 pass_Position : TEXCOORD0, + out float4 out_Position : POSITION) +{ + pass_Position = in_Position; + out_Position = float4(in_Position, 0.0, 1.0); +} + +[frag.hlsl] + +void main(in float2 pass_Position : TEXCOORD0, + uniform sampler2D in_Texture, + uniform float in_Flag, + uniform float3 in_Point, + uniform float3 in_Color, + out float4 out_FragColor : COLOR) +{ + out_FragColor = float4(0.0); +} + diff --git a/tutorial/12_voronoi.vcxproj b/tutorial/12_voronoi.vcxproj new file mode 100644 index 00000000..19320898 --- /dev/null +++ b/tutorial/12_voronoi.vcxproj @@ -0,0 +1,83 @@ + + + + + Debug + ORBIS + + + Debug + PS3 + + + Debug + Win32 + + + Debug + x64 + + + Debug + Xbox 360 + + + Release + ORBIS + + + Release + PS3 + + + Release + Win32 + + + Release + x64 + + + Release + Xbox 360 + + + + + + + + + + + + + + + {9e62f2fe-3408-4eae-8238-fd84238ceeda} + + + {83d3b207-c601-4025-8f41-01dedc354661} + + + + {6bf81b39-edc2-4227-9982-c2d8abea95af} + Application + Win32Proj + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tutorial/12_voronoi_distance.lolfx b/tutorial/12_voronoi_distance.lolfx new file mode 100644 index 00000000..d47db363 --- /dev/null +++ b/tutorial/12_voronoi_distance.lolfx @@ -0,0 +1,94 @@ +[vert.glsl] + +#version 120 + +attribute vec2 in_Position; + +varying vec2 pass_Position; + +void main() +{ + pass_Position = in_Position; + gl_Position = vec4(in_Position, 0.0, 1.0); +} + +[frag.glsl] + +#version 120 + +#if defined GL_ES +precision highp float; +#endif + +uniform sampler2D in_Texture; +uniform float in_Flag; +uniform vec3 in_Point; +uniform vec3 in_Color; + +varying vec2 pass_Position; + +void main(void) +{ + if (in_Flag == 0.0) + { + float tc = 0.0, ta = 0.0; + { + float s = 3.0 + 2.0 * in_Point.z; + vec2 p = pass_Position - in_Point.xy * 0.9; + float t = clamp(1.2 - dot(s * p, s * p), 0.0, 1.0); + float u = t * t * t * t; + tc += 3.0 * t * t - 2.0 * t * t * t; + ta += 3.0 * u * u - 2.0 * u * u * u; + } + + gl_FragColor = vec4(tc * in_Color, ta + 0.1); + } + else + { + vec2 texcoords = pass_Position * 0.5 + vec2(0.5, 0.5); + gl_FragColor = vec4(texture2D(in_Texture, texcoords).xyz, 1.0); + } +} + +[vert.hlsl] + +void main(float2 in_Position : POSITION, + out float2 pass_Position : TEXCOORD0, + out float4 out_Position : POSITION) +{ + pass_Position = in_Position; + out_Position = float4(in_Position, 0.0, 1.0); +} + +[frag.hlsl] + +void main(in float2 pass_Position : TEXCOORD0, + uniform sampler2D in_Texture, + uniform float in_Flag, + uniform float3 in_Point, + uniform float3 in_Color, + out float4 out_FragColor : COLOR) +{ + if (in_Flag == 0.0) + { + float tc = 0.0, ta = 0.0; + { + float s = 3.0 + 2.0 * in_Point.z; + float2 p = pass_Position - in_Point.xy * 0.9; + float t = clamp(1.2 - dot(s * p, s * p), 0.0, 1.0); + float u = t * t * t * t; + tc += 3.0 * t * t - 2.0 * t * t * t; + ta += 3.0 * u * u - 2.0 * u * u * u; + } + + out_FragColor = float4(tc * in_Color, ta + 0.1); + } + else + { + float2 texcoords = pass_Position * float2(0.5, -0.5) + float2(0.5, 0.5); + /* FIXME: this should be passed as a uniform or something */ + texcoords += float2(0.5 / 800.0, 0.5 / 600.0); + out_FragColor = float4(tex2D(in_Texture, texcoords).xyz, 1.0); + } +} + diff --git a/tutorial/12_voronoi_setup.lolfx b/tutorial/12_voronoi_setup.lolfx new file mode 100644 index 00000000..3712ace8 --- /dev/null +++ b/tutorial/12_voronoi_setup.lolfx @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +//GLSL +//----------------------------------------------------------------------------- +[vert.glsl] + +#version 120 + +attribute vec2 in_position; + +uniform vec2 in_screen_res; + +varying vec2 pass_position; + +void main() +{ + pass_position = ((vec2(1.0) + in_position) * 0.5 * in_screen_res); + gl_Position = vec4(in_position, 0.0, 1.0); +} + +[frag.glsl] + +#version 120 + +#if defined GL_ES +precision highp float; +#endif + +uniform sampler2D in_texture; +uniform vec3 in_source_point; +uniform vec2 in_screen_res; + +varying vec2 pass_position; + +void main(void) +{ + if (floor(in_source_point.xy) == floor(pass_position)) + gl_FragColor = vec4(in_source_point.xy / in_screen_res, in_source_point.z, 1.0); + else + { + vec4 src_color = texture2D(in_texture, pass_position / in_screen_res); + gl_FragColor = src_color;//vec4(0.0, 0.0, 0.0, 1.0); + } + //vec4(pass_position / in_screen_res, 0.0, 1.0); +} + +//----------------------------------------------------------------------------- +//HLSL +//----------------------------------------------------------------------------- +[vert.hlsl] + +void main(float2 in_Position : POSITION, + out float2 pass_Position : TEXCOORD0, + out float4 out_Position : POSITION) +{ + pass_Position = in_Position; + out_Position = float4(in_Position, 0.0, 1.0); +} + +[frag.hlsl] + +void main(in float2 pass_Position : TEXCOORD0, + uniform sampler2D in_Texture, + uniform float in_Flag, + uniform float3 in_Point, + uniform float3 in_Color, + out float4 out_FragColor : COLOR) +{ + out_FragColor = float4(0.0); +} + diff --git a/tutorial/Makefile.am b/tutorial/Makefile.am new file mode 100644 index 00000000..dfb0a477 --- /dev/null +++ b/tutorial/Makefile.am @@ -0,0 +1,49 @@ + +include $(top_srcdir)/build/autotools/common.am + +noinst_PROGRAMS = 01_triangle 02_cube 03_noise 04_texture 05_easymesh \ + 06_sprite 08_fbo 11_fractal \ + 12_voronoi + +01_triangle_SOURCES = 01_triangle.cpp 01_triangle.lolfx +01_triangle_CPPFLAGS = $(AM_CPPFLAGS) +01_triangle_DEPENDENCIES = @LOL_DEPS@ + +02_cube_SOURCES = 02_cube.cpp 02_cube.lolfx +02_cube_CPPFLAGS = $(AM_CPPFLAGS) +02_cube_DEPENDENCIES = @LOL_DEPS@ + +03_noise_SOURCES = 03_noise.cpp 03_noise.lolfx +03_noise_CPPFLAGS = $(AM_CPPFLAGS) +03_noise_DEPENDENCIES = @LOL_DEPS@ + +04_texture_SOURCES = 04_texture.cpp 04_texture.lolfx +04_texture_CPPFLAGS = $(AM_CPPFLAGS) +04_texture_DEPENDENCIES = @LOL_DEPS@ + +05_easymesh_SOURCES = 05_easymesh.cpp +05_easymesh_CPPFLAGS = $(AM_CPPFLAGS) +05_easymesh_DEPENDENCIES = @LOL_DEPS@ + +06_sprite_SOURCES = 06_sprite.cpp 06_sprite.png +06_sprite_CPPFLAGS = $(AM_CPPFLAGS) +06_sprite_DEPENDENCIES = @LOL_DEPS@ +06_sprite_LDFLAGS = $(AM_LDFLAGS) +if USE_EMSCRIPTEN +06_sprite_LDFLAGS += --preload-file 06_sprite.png +endif + +08_fbo_SOURCES = 08_fbo.cpp 08_fbo.lolfx +08_fbo_CPPFLAGS = $(AM_CPPFLAGS) +08_fbo_DEPENDENCIES = @LOL_DEPS@ + +11_fractal_SOURCES = 11_fractal.cpp 11_fractal.lolfx +11_fractal_CPPFLAGS = $(AM_CPPFLAGS) +11_fractal_DEPENDENCIES = @LOL_DEPS@ + +12_voronoi_SOURCES = 12_voronoi.cpp 12_voronoi.lolfx \ + 12_voronoi_distance.lolfx 12_voronoi_setup.lolfx \ + 12_texture_to_screen.lolfx 12_distance.lolfx +12_voronoi_CPPFLAGS = $(AM_CPPFLAGS) +12_voronoi_DEPENDENCIES = @LOL_DEPS@ +