Copyright 2008-2014 Matus Chochlik. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <vector>
#include <cassert>
namespace oglplus {
namespace se = ::oglplus::smart_enums;
class MetalProgram
{
private:
{
vs.Source("#version 330\n"
"uniform mat4 CameraMatrix, ProjectionMatrix;"
"uniform vec3 CameraPosition, LightPosition;"
"in vec4 Position;"
"in vec3 Normal, Tangent;"
"in vec2 TexCoord;"
"out vec3 vertNormal, vertTangent, vertBitangent;"
"out vec3 vertLightDir, vertViewDir;"
"out vec2 vertTexCoord;"
"void main(void)"
"{"
" gl_Position = Position;"
" vertLightDir = LightPosition - gl_Position.xyz;"
" vertViewDir = CameraPosition - gl_Position.xyz;"
" vertNormal = Normal;"
" vertTangent = Tangent;"
" vertBitangent = cross(vertNormal, vertTangent);"
" vertTexCoord = TexCoord * 100.0;"
" gl_Position = ProjectionMatrix * CameraMatrix * gl_Position;"
"}"
);
vs.Compile();
fs.Source("#version 330\n"
"const vec3 Color1 = vec3(0.5, 0.7, 0.6);"
"const vec3 Color2 = vec3(0.7, 0.9, 0.8);"
"uniform sampler2D MetalTex;"
"uniform float LightMultiplier;"
"in vec3 vertNormal, vertTangent, vertBitangent;"
"in vec3 vertLightDir, vertViewDir;"
"in vec2 vertTexCoord;"
"out vec3 fragColor;"
"void main(void)"
"{"
" vec3 Sample = texture(MetalTex, vertTexCoord).rgb;"
" vec3 LightColor = vec3(1.0, 1.0, 0.9);"
" vec3 Normal = normalize("
" 2.0*vertNormal + "
" (Sample.r - 0.5)*vertTangent + "
" (Sample.g - 0.5)*vertBitangent"
" );"
" vec3 LightRefl = reflect("
" -normalize(vertLightDir),"
" Normal"
" );"
" float Specular = LightMultiplier * pow(max(dot("
" normalize(LightRefl),"
" normalize(vertViewDir)"
" )+0.04, 0.0), 16+Sample.b*48)*pow(0.4+Sample.b*1.6, 4.0);"
" Normal = normalize(vertNormal*3.0 + Normal);"
" float Diffuse = LightMultiplier * pow(max(dot("
" normalize(Normal), "
" normalize(vertLightDir)"
" ), 0.0), 2.0);"
" float Ambient = 0.6;"
" vec3 Color = mix(Color1, Color2, Sample.b);"
" fragColor = "
" Color * Ambient +"
" LightColor * Color * Diffuse + "
" LightColor * Specular;"
"}"
);
fs.Compile();
prog.AttachShader(vs);
prog.AttachShader(fs);
prog.Link();
return prog;
}
const Program&
self(void)
const {
return *
this; }
public:
ProgramUniform<Mat4f> camera_matrix, projection_matrix;
ProgramUniform<Vec3f> camera_position;
ProgramUniform<GLfloat> light_multiplier;
MetalProgram(void)
, camera_matrix(self(), "CameraMatrix")
, projection_matrix(self(), "ProjectionMatrix")
, camera_position(self(), "CameraPosition")
, light_multiplier(self(), "LightMultiplier")
{ }
};
class MetalFloor
: public shapes::ShapeWrapper
{
private:
public:
MetalFloor(const MetalProgram& metal_prog)
: shapes::ShapeWrapper(
List(
"Position")(
"Normal")(
"Tangent")(
"TexCoord").Get(),
shapes::Plane(
Vec3f(200, 0, 0),
Vec3f(0, 0,-200)),
metal_prog
)
{
Texture::Active(0);
UniformSampler(metal_prog, "MetalTex").Set(0);
.Image2D(
images::BrushedMetalUByte(
512, 512,
5120,
-3, +3,
32, 128
)
).GenerateMipmap();
}
void Draw(MetalProgram& metal_prog)
{
metal_prog.Use();
shapes::ShapeWrapper::Use();
shapes::ShapeWrapper::Draw();
}
};
class CameraDriveProgram
{
private:
{
tfbs.Source(
"#version 330\n"
"uniform float Interval, Mass, TargetDistance, Elevation;"
"uniform uint CubeCenterIndex;"
"layout(std140) uniform CubePositionBlock {"
" vec3 CubePositions[512];"
"};"
"in vec3 Position, Velocity;"
"out vec4 tfbPosition, tfbVelocity;"
"bool is_camera(void)"
"{"
" return gl_VertexID == 0;"
"}"
"vec3 spring_force(vec3 P, vec3 Ps, float k, float l)"
"{"
" vec3 v = Ps - P;"
" float ds = (length(v) - l);"
" return k * sign(ds) * min(abs(ds), l) * normalize(v);"
"}"
"vec3 springs(void)"
"{"
" if(is_camera())"
" {"
" return spring_force("
" Position,"
" CubePositions[CubeCenterIndex],"
" 3.0,"
" TargetDistance"
" );"
" }"
" else"
" {"
" return spring_force("
" Position,"
" CubePositions[CubeCenterIndex],"
" 9.0,"
" 0.1"
" );"
" }"
"}"
"vec3 elevation(void)"
"{"
" if(is_camera())"
" {"
" float ds = (Elevation - Position.y);"
" float k = 7.0;"
" vec3 v = vec3(0.0, 1.0, 0.0);"
" return k * sign(ds) * min(abs(ds), Elevation) * v;"
" }"
" else return vec3(0.0, 0.0, 0.0);"
"}"
"vec3 drag(void)"
"{"
" return -0.08 * Velocity;"
"}"
"void main(void)"
"{"
" vec3 Force = drag() + elevation() + springs();"
" tfbVelocity = vec4(Velocity + (Force * Interval) / Mass, 1.0);"
" tfbPosition = vec4(Position + tfbVelocity.xyz * Interval,1.0);"
"}"
);
tfbs.Compile();
prog.AttachShader(tfbs);
const GLchar* var_names[2] = {"tfbPosition", "tfbVelocity"};
prog.Link();
return prog;
}
const Program&
self(void) {
return *
this; }
public:
ProgramUniform<GLfloat> interval;
CameraDriveProgram(void)
, interval(self(), "Interval")
{ }
};
class ChasingCamera
{
private:
Context gl;
Buffer tfb_positions, tfb_velocities;
{
Buffer::CopySubData(
0, 0,
2 * 4 * sizeof(GLfloat)
);
}
public:
ChasingCamera(
CameraDriveProgram& cam_prog,
GLfloat mass,
GLfloat elevation,
GLfloat target_distance
)
{
std::vector<GLfloat> vel_data(2*4, 0.0f);
std::vector<GLfloat> pos_data(2*4);
pos_data[0] = 0.0f;
pos_data[1] = elevation;
pos_data[2] = target_distance;
pos_data[3] = 1.0f;
pos_data[4] = 0.0f;
pos_data[5] = elevation;
pos_data[6] = 0.0f;
pos_data[7] = 1.0f;
cam_vao.Bind();
VertexArrayAttrib pos_vert_attr(cam_prog, "Position");
pos_vert_attr.Setup<
Vec4f>();
pos_vert_attr.Enable();
VertexArrayAttrib vel_vert_attr(cam_prog, "Velocity");
vel_vert_attr.Setup<
Vec4f>();
vel_vert_attr.Enable();
cam_prog.Use();
Uniform<GLfloat>(cam_prog,
"Mass").
Set(mass);
Uniform<GLfloat>(cam_prog,
"Elevation").
Set(elevation);
Uniform<GLfloat>(cam_prog,
"TargetDistance").
Set(target_distance);
}
void UpdatePhysics(CameraDriveProgram& cam_prog, double interval)
{
cam_vao.Bind();
cam_prog.Use();
GLuint n = 10;
cam_prog.interval.Set(interval / n);
for(GLuint i=0; i!=n; ++i)
{
{
TransformFeedback::Activator tfb(tfbmode);
}
CopyFeedback(tfb_positions, positions);
CopyFeedback(tfb_velocities, velocities);
}
BufferTypedMap<Vec4f> map(
);
cam_pos = map.At(0).xyz();
tgt_pos = map.At(1).xyz();
}
Vec3f Position(
void)
const
{
return cam_pos;
}
{
return tgt_pos;
}
{
}
};
class JellyPhysProgram
{
private:
{
tfbs.Source(
"#version 330\n"
"uniform vec3 ImpulseCenter;"
"uniform float ImpulseStrength, Interval, Mass;"
"uniform float SpringALength, SpringBLength, SpringCLength;"
"uniform float SpringAStrength, SpringBStrength, SpringCStrength;"
"layout(std140) uniform PositionBlock {"
" vec3 Positions[512];"
"};"
"struct VertexSpringIndices {"
" ivec4 SpringAX;"
" ivec4 SpringAY;"
" ivec4 SpringAZ;"
" ivec4 SpringBX;"
" ivec4 SpringBY;"
" ivec4 SpringBZ;"
" ivec4 SpringCUp;"
" ivec4 SpringCDn;"
"};"
"layout(std140) uniform SpringIndexBlock {"
" VertexSpringIndices VertexSprings[512];"
"};"
"in vec3 Position, Velocity;"
"out vec4 tfbPosition, tfbVelocity;"
"vec3 gravity(void)"
"{"
" float Acting = -max(sign(Position.y), 0.0);"
" vec3 GravAccel = vec3(0.0, Acting * 9.81, 0.0);"
" return Mass * GravAccel;"
"}"
"vec3 floor(void)"
"{"
" float IsUnder = -min(sign(Position.y), 0.0);"
" float GoingDown = -min(sign(Velocity.y), 0.0);"
" float Acting = IsUnder * GoingDown;"
" vec3 BounceVelocity = vec3(0.0, -Velocity.y, 0.0);"
" vec3 FrictionVelocity = vec3(-Velocity.x, 0.0, -Velocity.z);"
" vec3 Bounce = Acting * Mass * BounceVelocity / Interval;"
" vec3 Friction = IsUnder * Mass * FrictionVelocity / Interval;"
" return 1.95 * Bounce + 0.3 * Friction;"
"}"
"vec3 spring_force(vec3 P, vec3 Ps, float k, float l)"
"{"
" vec3 v = Ps - P;"
" float ds = (length(v) - l);"
" return k * sign(ds) * min(abs(ds), l) * normalize(v);"
"}"
"vec3 spring_X(int springVertexID, float k, float l)"
"{"
" if(springVertexID < 0) return vec3(0.0, 0.0, 0.0);"
" else return spring_force("
" Position,"
" Positions[springVertexID],"
" k, l"
" );"
"}"
"vec3 spring_X1(ivec4 indices, float k, float l)"
"{"
" return spring_X(indices.x, k, l*1)+"
" spring_X(indices.y, k, l*1)+"
" spring_X(indices.z, k, l*2)+"
" spring_X(indices.w, k, l*2);"
"}"
"vec3 spring_X2(ivec4 indices, float k, float l)"
"{"
" return spring_X(indices.x, k, l)+"
" spring_X(indices.y, k, l)+"
" spring_X(indices.z, k, l)+"
" spring_X(indices.w, k, l);"
"}"
"vec3 spring_A(ivec4 indices)"
"{"
" return spring_X1("
" indices, "
" SpringAStrength, "
" SpringALength"
" );"
"}"
"vec3 spring_B(ivec4 indices)"
"{"
" return spring_X2("
" indices, "
" SpringBStrength, "
" SpringBLength"
" );"
"}"
"vec3 spring_C(ivec4 indices)"
"{"
" return spring_X2("
" indices, "
" SpringCStrength, "
" SpringCLength"
" );"
"}"
"vec3 springs(void)"
"{"
" vec3 Result = vec3(0.0, 0.0, 0.0);"
" VertexSpringIndices vsi = VertexSprings[gl_VertexID];"
" Result += spring_A(vsi.SpringAX);"
" Result += spring_A(vsi.SpringAY);"
" Result += spring_A(vsi.SpringAZ);"
" Result += spring_B(vsi.SpringBX);"
" Result += spring_B(vsi.SpringBY);"
" Result += spring_B(vsi.SpringBZ);"
" Result += spring_C(vsi.SpringCUp);"
" Result += spring_C(vsi.SpringCDn);"
" return Result;"
"}"
"vec3 drag(void)"
"{"
" return -0.002 * SpringALength * Velocity;"
"}"
"vec3 impulse(void)"
"{"
" vec3 v = Position - ImpulseCenter;"
" return ImpulseStrength * normalize(v)/length(v);"
"}"
"void main(void)"
"{"
" vec3 Force = drag() + gravity() + springs() + floor() + impulse();"
" tfbVelocity = vec4(Velocity + (Force * Interval) / Mass, 1.0);"
" tfbPosition = vec4(Position + tfbVelocity.xyz * Interval,1.0);"
"}"
);
tfbs.Compile();
prog.AttachShader(tfbs);
const GLchar* var_names[2] = {"tfbPosition", "tfbVelocity"};
prog.Link();
return prog;
}
const Program&
self(void) {
return *
this; }
public:
ProgramUniform<Vec3f> impulse_center;
ProgramUniform<GLfloat> impulse_strength, interval, mass;
JellyPhysProgram(void)
, impulse_center(self(), "ImpulseCenter")
, impulse_strength(self(), "ImpulseStrength")
, interval(self(), "Interval")
, mass(self(), "Mass")
{ }
};
class JellyDrawProgram
{
private:
{
vs.Source(
"#version 330\n"
"uniform vec3 LightPosition;"
"in vec3 Position;"
"out vec3 vertLightDir;"
"void main(void)"
"{"
" gl_Position = vec4(Position, 1.0);"
" vertLightDir = normalize(LightPosition - Position);"
"}"
);
vs.Compile();
gs.Source(
"#version 330\n"
"layout(triangles_adjacency) in;"
"layout(triangle_strip, max_vertices = 3) out;"
"uniform mat4 CameraMatrix, ProjectionMatrix;"
"in vec3 vertLightDir[6];"
"out vec3 geomLightDir, geomNormal;"
"void make_vertex(int index)"
"{"
" gl_Position = "
" ProjectionMatrix*"
" CameraMatrix*"
" gl_in[index].gl_Position;"
" geomLightDir = vertLightDir[index];"
" EmitVertex();"
"}"
"vec3 face_normal(int a, int b, int c)"
"{"
" return normalize(cross("
" gl_in[c].gl_Position.xyz-"
" gl_in[a].gl_Position.xyz,"
" gl_in[b].gl_Position.xyz-"
" gl_in[a].gl_Position.xyz"
" ));"
"}"
"void main(void)"
"{"
" vec3 fn = face_normal(0, 2, 4);"
" vec3 fn1 = face_normal(1, 2, 0);"
" vec3 fn3 = face_normal(3, 4, 2);"
" vec3 fn5 = face_normal(5, 0, 4);"
" const float a = 0.400, b = (1.0 - a) * 0.5;"
" geomNormal = fn*a + fn5*b + fn1*b;"
" make_vertex(0);"
" geomNormal = fn*a + fn1*b + fn3*b;"
" make_vertex(2);"
" geomNormal = fn*a + fn3*b + fn5*b;"
" make_vertex(4);"
" EndPrimitive();"
"}"
);
gs.Compile();
fs.Source(
"#version 330\n"
"uniform vec3 AmbientColor, DiffuseColor;"
"uniform float LightMultiplier;"
"in vec3 geomLightDir, geomNormal;"
"out vec3 fragColor;"
"void main(void)"
"{"
" float Ambient = 0.8;"
" float Diffuse = LightMultiplier * sqrt(max(dot("
" geomLightDir,"
" geomNormal"
" ) - 0.1, 0.0));"
" fragColor = "
" Ambient * AmbientColor+"
" Diffuse * DiffuseColor;"
"}"
);
fs.Compile();
prog.AttachShader(vs);
prog.AttachShader(gs);
prog.AttachShader(fs);
prog.Link();
return prog;
}
const Program&
self(void) {
return *
this; }
public:
ProgramUniform<Mat4f> camera_matrix, projection_matrix;
ProgramUniform<Vec3f> ambient_color, diffuse_color;
ProgramUniform<GLfloat> light_multiplier;
JellyDrawProgram(void)
, camera_matrix(self(), "CameraMatrix")
, projection_matrix(self(), "ProjectionMatrix")
, ambient_color(self(), "AmbientColor")
, diffuse_color(self(), "DiffuseColor")
, light_multiplier(self(), "LightMultiplier")
{ }
};
class ShadowProgram
{
private:
{
vs.Source(
"#version 330\n"
"uniform vec3 LightPosition;"
"in vec3 Position;"
"out vec3 vertLightDir;"
"void main(void)"
"{"
" gl_Position = vec4(Position, 1.0);"
" vertLightDir = LightPosition - Position;"
"}"
);
vs.Compile();
gs.Source(
"#version 330\n"
"layout(triangles_adjacency) in;"
"layout(triangle_strip, max_vertices = 12) out;"
"uniform mat4 CameraMatrix, ProjectionMatrix;"
"in vec3 vertLightDir[6];"
"void make_near_vertex(int index)"
"{"
" gl_Position = "
" ProjectionMatrix*"
" CameraMatrix*"
" gl_in[index].gl_Position;"
" EmitVertex();"
"}"
"void make_far_vertex(int index)"
"{"
" vec3 Pos = gl_in[index].gl_Position.xyz;"
" Pos -= vertLightDir[index];"
" gl_Position = "
" ProjectionMatrix*"
" CameraMatrix*"
" vec4(Pos, 1.0);"
" EmitVertex();"
"}"
"void make_plane(int a, int b)"
"{"
" make_near_vertex(a);"
" make_near_vertex(b);"
" make_far_vertex(a);"
" make_far_vertex(b);"
" EndPrimitive();"
"}"
"vec3 face_normal(int a, int b, int c)"
"{"
" return cross("
" gl_in[c].gl_Position.xyz-"
" gl_in[a].gl_Position.xyz,"
" gl_in[b].gl_Position.xyz-"
" gl_in[a].gl_Position.xyz"
" );"
"}"
"vec3 face_light_dir(int a, int b, int c)"
"{"
" return ("
" vertLightDir[a]+"
" vertLightDir[b]+"
" vertLightDir[c] "
" );"
"}"
"void main(void)"
"{"
" vec3 ld = face_light_dir(0, 2, 4);"
" vec3 fn = face_normal(0, 2, 4);"
" if(dot(fn, ld) >= 0.0)"
" {"
" make_plane(2, 0);"
" make_plane(4, 2);"
" make_plane(0, 4);"
" }"
"}"
);
gs.Compile();
prog.AttachShader(vs);
prog.AttachShader(gs);
prog.Link();
return prog;
}
const Program&
self(void) {
return *
this; }
public:
ProgramUniform<Mat4f> camera_matrix, projection_matrix;
ShadowProgram(void)
, camera_matrix(self(), "CameraMatrix")
, projection_matrix(self(), "ProjectionMatrix")
{ }
};
class JellyCube
{
private:
const GLsizei vertex_count, face_index_count, shadow_index_count;
Context gl;
Buffer positions, velocities, indices, shadow_indices;
Buffer tfb_positions, tfb_velocities;
{
Buffer::CopySubData(
0, 0,
vertex_count * 4 * sizeof(GLfloat)
);
}
void InitPositions(
JellyPhysProgram& phys_prog,
JellyDrawProgram& draw_prog,
ShadowProgram& shadow_prog,
CameraDriveProgram& cam_prog,
const GLfloat size,
const GLfloat mass,
const GLuint n,
)
{
const GLfloat s_2 = size * 0.5;
const GLfloat s_n = size / (n - 1);
std::vector<GLfloat> pos_data(vertex_count * 4);
auto pi = pos_data.begin(), pe = pos_data.end();
for(GLuint z=0; z!=n; ++z)
{
const GLfloat z_offs = -s_2 + s_n*z;
for(GLuint y=0; y!=n; ++y)
{
const GLfloat y_offs = -s_2 + s_n*y;
for(GLuint x=0; x!=n; ++x)
{
const GLfloat x_offs = -s_2 + s_n*x;
Vec4f initial(x_offs, y_offs, z_offs, 1.0f);
Vec4f pos = transform * initial;
assert(pi != pe);
*pi = pos.x() ; ++pi;
assert(pi != pe);
*pi = pos.y() ; ++pi;
assert(pi != pe);
*pi = pos.z(); ++pi;
assert(pi != pe);
*pi = 1.0f; ++pi;
}
}
}
assert(pi == pe);
draw_vao.Bind();
VertexArrayAttrib draw_vert_attr(draw_prog, "Position");
draw_vert_attr.Setup<
Vec4f>();
draw_vert_attr.Enable();
phys_vao.Bind();
VertexArrayAttrib phys_vert_attr(phys_prog, "Position");
phys_vert_attr.Setup<
Vec4f>();
phys_vert_attr.Enable();
shadow_vao.Bind();
VertexArrayAttrib shadow_vert_attr(shadow_prog, "Position");
shadow_vert_attr.Setup<
Vec4f>();
shadow_vert_attr.Enable();
phys_prog.Use();
phys_prog.mass.Set(mass / vertex_count);
Uniform<GLfloat>(phys_prog,
"SpringALength").
Set(s_n);
Uniform<GLfloat>(phys_prog,
"SpringBLength").
Set(sqrt(2.0f)*s_n);
Uniform<GLfloat>(phys_prog,
"SpringCLength").
Set(sqrt(3.0f)*s_n);
GLfloat k = 1.1;
Uniform<GLfloat>(phys_prog,
"SpringAStrength").
Set(mass * 4.2f * k);
Uniform<GLfloat>(phys_prog,
"SpringBStrength").
Set(mass * 4.0f * k);
Uniform<GLfloat>(phys_prog,
"SpringCStrength").
Set(mass * 3.8f * k);
cam_prog.Use();
Uniform<GLuint>(cam_prog,
"CubeCenterIndex").
Set(vertex_count/2);
}
void InitVelocities(const JellyPhysProgram& phys_prog)
{
const std::vector<GLfloat> vel_data(vertex_count * 4, 0.0f);
phys_vao.Bind();
VertexArrayAttrib vert_attr(phys_prog, "Velocity");
vert_attr.Setup<
Vec4f>();
vert_attr.Enable();
}
int FaceIndex(
const GLuint n,
const GLuint n_1,
const GLuint f,
int i,
int j
) const
{
if(i<0)
{
i += n_1;
assert(i <=int(n_1));
assert(j >= 0);
assert(j <=int(n_1));
switch(f)
{
case 0: return FaceIndex(n, n_1, 4, i, j);
case 1: return FaceIndex(n, n_1, 5, i, j);
case 2: return FaceIndex(n, n_1, 1, n_1-j, i);
case 3: return FaceIndex(n, n_1, 1, j, n_1-i);
case 4: return FaceIndex(n, n_1, 1, i, j);
case 5: return FaceIndex(n, n_1, 0, i, j);
}
}
else if(i>int(n_1))
{
i -= n_1;
assert(i <= int(n_1));
assert(j >= 0);
assert(j <= int(n_1));
switch(f)
{
case 0: return FaceIndex(n, n_1, 5, i, j);
case 1: return FaceIndex(n, n_1, 4, i, j);
case 2: return FaceIndex(n, n_1, 0, j, n_1-i);
case 3: return FaceIndex(n, n_1, 0, n_1-j, i);
case 4: return FaceIndex(n, n_1, 0, i, j);
case 5: return FaceIndex(n, n_1, 1, i, j);
}
}
else if(j<0)
{
j += n_1;
assert(j <= int(n_1));
assert(i >= 0);
assert(i <= int(n_1));
switch(f)
{
case 0: return FaceIndex(n, n_1, 3, j, n_1-i);
case 1: return FaceIndex(n, n_1, 3, n_1-j, i);
case 2: return FaceIndex(n, n_1, 4, i, j);
case 3: return FaceIndex(n, n_1, 5, n_1-i, n_1-j);
case 4: return FaceIndex(n, n_1, 3, i, j);
case 5: return FaceIndex(n, n_1, 3, n_1-i, n_1-j);
}
}
else if(j>int(n_1))
{
j -= n_1;
assert(j <= int(n_1));
assert(i >= 0);
assert(i <= int(n_1));
switch(f)
{
case 0: return FaceIndex(n, n_1, 2, n_1-j, i);
case 1: return FaceIndex(n, n_1, 2, j, n_1-i);
case 2: return FaceIndex(n, n_1, 5, n_1-i, n_1-j);
case 3: return FaceIndex(n, n_1, 4, i, j);
case 4: return FaceIndex(n, n_1, 2, i, j);
case 5: return FaceIndex(n, n_1, 2, n_1-i, n_1-j);
}
}
assert(f < 6);
assert(i >= 0);
assert(j >= 0);
assert(i <= int(n_1));
assert(j <= int(n_1));
int get_x[6] = { int(n_1), 0, i, i, i, int(n_1)-i };
int get_y[6] = { j, j, int(n_1), 0, j, j };
int get_z[6] = { int(n_1)-i, i, int(n_1)-j, j, int(n_1), 0 };
int x = get_x[f];
int y = get_y[f];
int z = get_z[f];
return z*n*n + y*n + x;
}
void InitIndexData(
std::vector<GLuint>& index_data,
const GLuint n,
const GLuint n_1,
const GLuint k
)
{
assert(k != 0);
assert(k < n);
assert(n_1 % k == 0);
auto ii = index_data.begin(), ie = index_data.end();
for(GLuint f=0; f!=6; ++f)
{
for(GLuint j=0; j!=n_1; j+=k)
{
assert(ii != ie);
*ii++ = FaceIndex(n, n_1, f, 0, j+0);
assert(ii != ie);
*ii++ = FaceIndex(n, n_1, f, -k, j+k);
assert(ii != ie);
*ii++ = FaceIndex(n, n_1, f, 0, j+k);
assert(ii != ie);
*ii++ = FaceIndex(n, n_1, f, +k, j-k);
for(GLuint i=k; i!=n_1; i+=k)
{
assert(ii != ie);
*ii++ = FaceIndex(n, n_1, f, i+0, j+0);
assert(ii != ie);
*ii++ = FaceIndex(n, n_1, f, i-k, j+2*k);
assert(ii != ie);
*ii++ = FaceIndex(n, n_1, f, i+0, j+k);
assert(ii != ie);
*ii++ = FaceIndex(n, n_1, f, i+k, j-k);
}
assert(ii != ie);
*ii++ = FaceIndex(n, n_1, f, n_1+0, j+0);
assert(ii != ie);
*ii++ = FaceIndex(n, n_1, f, n_1-k, j+2*k);
assert(ii != ie);
*ii++ = FaceIndex(n, n_1, f, n_1+0, j+k);
assert(ii != ie);
*ii++ = FaceIndex(n, n_1, f, n_1+k, j+0);
assert(ii != ie);
*ii++ = vertex_count;
}
}
assert(ii == ie);
}
void InitIndices(const GLuint n)
{
const GLuint n_1 = n-1;
std::vector<GLuint> index_data(face_index_count);
InitIndexData(index_data, n, n_1, 1);
draw_vao.Bind();
shadow_vao.Bind();
}
GLint SpringIndex(int x, int y, int z, int n) const
{
if(x < 0) return -1;
if(x >= n) return -1;
if(y < 0) return -1;
if(y >= n) return -1;
if(z < 0) return -1;
if(z >= n) return -1;
GLint index = z*n*n + y*n + x;
assert(index >= 0);
assert(index < GLint(vertex_count));
return index;
}
void InitSprings(const JellyPhysProgram& phys_prog, const GLuint n)
{
std::vector<GLint> idx_data(vertex_count * (4*8));
auto ii = idx_data.begin(), ie = idx_data.end();
for(GLuint z=0; z!=n; ++z)
{
for(GLuint y=0; y!=n; ++y)
{
for(GLuint x=0; x!=n; ++x)
{
assert(ii != ie);
*ii++ = SpringIndex(x-1, y+0, z+0, n);
assert(ii != ie);
*ii++ = SpringIndex(x+1, y+0, z+0, n);
assert(ii != ie);
*ii++ = SpringIndex(x-2, y+0, z+0, n);
assert(ii != ie);
*ii++ = SpringIndex(x+2, y+0, z+0, n);
assert(ii != ie);
*ii++ = SpringIndex(x+0, y-1, z+0, n);
assert(ii != ie);
*ii++ = SpringIndex(x+0, y+1, z+0, n);
assert(ii != ie);
*ii++ = SpringIndex(x+0, y-2, z+0, n);
assert(ii != ie);
*ii++ = SpringIndex(x+0, y+2, z+0, n);
assert(ii != ie);
*ii++ = SpringIndex(x+0, y+0, z-1, n);
assert(ii != ie);
*ii++ = SpringIndex(x+0, y+0, z+1, n);
assert(ii != ie);
*ii++ = SpringIndex(x+0, y+0, z-2, n);
assert(ii != ie);
*ii++ = SpringIndex(x+0, y+0, z+2, n);
assert(ii != ie);
*ii++ = SpringIndex(x+0, y+1, z-1, n);
assert(ii != ie);
*ii++ = SpringIndex(x+0, y+1, z+1, n);
assert(ii != ie);
*ii++ = SpringIndex(x+0, y-1, z+1, n);
assert(ii != ie);
*ii++ = SpringIndex(x+0, y-1, z-1, n);
assert(ii != ie);
*ii++ = SpringIndex(x-1, y+0, z+1, n);
assert(ii != ie);
*ii++ = SpringIndex(x+1, y+0, z+1, n);
assert(ii != ie);
*ii++ = SpringIndex(x+1, y+0, z-1, n);
assert(ii != ie);
*ii++ = SpringIndex(x-1, y+0, z-1, n);
assert(ii != ie);
*ii++ = SpringIndex(x+1, y-1, z+0, n);
assert(ii != ie);
*ii++ = SpringIndex(x+1, y+1, z+0, n);
assert(ii != ie);
*ii++ = SpringIndex(x-1, y+1, z+0, n);
assert(ii != ie);
*ii++ = SpringIndex(x-1, y-1, z+0, n);
assert(ii != ie);
*ii++ = SpringIndex(x-1, y+1, z-1, n);
assert(ii != ie);
*ii++ = SpringIndex(x+1, y+1, z-1, n);
assert(ii != ie);
*ii++ = SpringIndex(x+1, y+1, z+1, n);
assert(ii != ie);
*ii++ = SpringIndex(x-1, y+1, z+1, n);
assert(ii != ie);
*ii++ = SpringIndex(x-1, y-1, z-1, n);
assert(ii != ie);
*ii++ = SpringIndex(x+1, y-1, z-1, n);
assert(ii != ie);
*ii++ = SpringIndex(x+1, y-1, z+1, n);
assert(ii != ie);
*ii++ = SpringIndex(x-1, y-1, z+1, n);
}
}
}
assert(ii == ie);
phys_prog.Use();
}
public:
JellyCube(
JellyPhysProgram& phys_prog,
JellyDrawProgram& draw_prog,
ShadowProgram& shadow_prog,
CameraDriveProgram& cam_prog,
const GLfloat size,
const GLfloat mass,
const GLuint n,
): vertex_count(n*n*n)
, face_index_count(6*(n-1)*(n*4+1))
, shadow_index_count(face_index_count)
{
assert(size > 0.0f);
assert(n > 1);
InitPositions(
phys_prog,
draw_prog,
shadow_prog,
cam_prog,
size,
mass,
n,
transform
);
InitIndices(n);
InitVelocities(phys_prog);
InitSprings(phys_prog, n);
}
void UpdatePhysics(JellyPhysProgram& phys_prog, double interval)
{
phys_vao.Bind();
phys_prog.Use();
GLuint n = 20;
phys_prog.interval.Set(interval / n);
for(GLuint i=0; i!=n; ++i)
{
{
TransformFeedback::Activator tfb(tfbmode);
}
CopyFeedback(tfb_positions, positions);
CopyFeedback(tfb_velocities, velocities);
}
}
void Shadow(ShadowProgram& shadow_prog)
{
shadow_vao.Bind();
shadow_prog.Use();
gl.PrimitiveRestartIndex(vertex_count);
}
void Draw(JellyDrawProgram& draw_prog)
{
draw_vao.Bind();
draw_prog.Use();
gl.PrimitiveRestartIndex(vertex_count);
draw_prog.ambient_color.Set(
Vec3f(0.3, 0.4, 0.2));
draw_prog.diffuse_color.Set(
Vec3f(0.6, 1.0, 0.4));
draw_prog.ambient_color.Set(
Vec3f(0.1, 0.2, 0.0));
draw_prog.diffuse_color.Set(
Vec3f(0.3, 0.4, 0.2));
}
};
class BulletTimeTrigger
{
private:
double _remaining;
bool _status;
bool _started(void) const
{
return _remaining > 0.0;
}
bool _can_restart(void) const
{
return _remaining < -2.0;
}
public:
BulletTimeTrigger(void)
: _remaining(0.0)
, _status(false)
{ }
void UpdateAndStartIf(double interval, bool can_start, double duration)
{
_remaining -= interval;
if(can_start && _can_restart())
_remaining = duration;
}
bool On(void)
{
if(_started() && !_status)
{
_status = true;
return true;
}
return false;
}
bool Off(void)
{
if(!_started() && _status)
{
_status = false;
return true;
}
return false;
}
};
class JellyExample : public Example
{
private:
double prev_impulse_strength;
Context gl;
MetalProgram metal_prog;
MetalFloor floor;
CameraDriveProgram cam_prog;
JellyPhysProgram phys_prog;
JellyDrawProgram draw_prog;
ShadowProgram shadow_prog;
ChasingCamera camera;
JellyCube jelly_cube;
BulletTimeTrigger bt_trg;
public:
JellyExample(const ExampleParams&)
: prev_impulse_strength(0.0)
, floor(metal_prog)
, camera(cam_prog, 0.05f, 3.5f, 10.0f)
, jelly_cube(
phys_prog,
draw_prog,
shadow_prog,
cam_prog,
2.0f, 0.5f,
8,
)
{
phys_prog.impulse_strength.Set(0.0f);
Vec3f light_pos(20.0f, 30.0f, 25.0f);
ProgramUniform<Vec3f>(metal_prog,
"LightPosition").
Set(light_pos);
ProgramUniform<Vec3f>(draw_prog,
"LightPosition").
Set(light_pos);
ProgramUniform<Vec3f>(shadow_prog,
"LightPosition").
Set(light_pos);
gl.ClearColor(0.6f, 0.6f, 0.6f, 0.0f);
gl.ClearDepth(1.0f);
gl.ClearStencil(1);
gl.PolygonOffset(-0.01, -1.0);
}
void Reshape(GLuint width, GLuint height)
{
gl.Viewport(width, height);
Degrees(70),
double(width)/height,
1, 200
);
metal_prog.projection_matrix.Set(perspective);
draw_prog.projection_matrix.Set(perspective);
shadow_prog.projection_matrix.Set(perspective);
}
void UpdateImpulse(
double time,
Vec3f pos)
{
double impulse_strength = 100.0 * (-
SineWave(time / 5.0) - 0.99);
if(impulse_strength < 0.0) impulse_strength = 0.0;
impulse_strength = pow(impulse_strength, 12.0) * 2.0;
if(impulse_strength != 0.0)
{
if(prev_impulse_strength == 0.0)
{
phys_prog.impulse_center.Set(
Vec3f(
+4.5f-float(std::rand())/float(RAND_MAX)*9.0+pos.x(),
-4.5f-float(std::rand())/float(RAND_MAX),
+4.5f-float(std::rand())/float(RAND_MAX)*9.0+pos.z()
));
}
phys_prog.impulse_strength.Set(impulse_strength);
}
prev_impulse_strength = impulse_strength;
}
void Render(ExampleClock& clock)
{
gl.Clear().ColorBuffer().DepthBuffer().StencilBuffer();
double interval = clock.Interval().Seconds();
jelly_cube.UpdatePhysics(phys_prog, interval);
camera.UpdatePhysics(cam_prog, interval);
bt_trg.UpdateAndStartIf(
clock.Interval().Seconds(),
camera.Target().y() < 1.0f &&
clock.RealTime().Second() < 20,
1.5
);
if(bt_trg.On()) clock.Pace(0.1);
if(bt_trg.Off())clock.Pace(1.0);
auto cam_matrix = camera.Matrix();
shadow_prog.camera_matrix.Set(cam_matrix);
metal_prog.camera_matrix.Set(cam_matrix);
draw_prog.camera_matrix.Set(cam_matrix);
metal_prog.camera_position.Set(camera.Position());
metal_prog.light_multiplier.Set(0.2);
draw_prog.light_multiplier.Set(0.2);
gl.ColorMask(true, true, true, true);
gl.DepthMask(true);
floor.Draw(metal_prog);
jelly_cube.Draw(draw_prog);
gl.ColorMask(false, false, false, false);
gl.DepthMask(false);
jelly_cube.Shadow(shadow_prog);
metal_prog.light_multiplier.Set(1.0);
draw_prog.light_multiplier.Set(1.0);
gl.ColorMask(true, true, true, true);
gl.DepthMask(true);
gl.Clear().DepthBuffer();
floor.Draw(metal_prog);
jelly_cube.Draw(draw_prog);
UpdateImpulse(clock.Now().Seconds(), camera.Target());
}
bool Continue(
const ExampleClock& clock)
{
return clock.RealTime().Minutes() < 2.0;
}
double HeatUpTime(void) const
{
return 0.0;
}
double ScreenshotTime(void) const
{
return 2.0;
}
double FrameTime(void) const
{
return 1.0/60.0;
}
};
void setupExample(ExampleParams& ){ }
std::unique_ptr<ExampleThread> makeExampleThread(
Example& ,
unsigned ,
const ExampleParams&
){ return std::unique_ptr<ExampleThread>(); }
std::unique_ptr<Example> makeExample(const ExampleParams& params)
{
return std::unique_ptr<Example>(new JellyExample(params));
}
}