Skip to content
Snippets Groups Projects
Commit 179cdab1 authored by Kersten Schuster's avatar Kersten Schuster
Browse files

Solutions to A04 and A05.

parent ba393ea2
Branches master
No related tags found
No related merge requests found
/// ===============================================================================================
/// Task 1.a
/// Build the line mesh
/// Draw the line mesh
///
/// Your job is to:
/// - build a line mesh for one single line
/// - draw the line mesh (i.e. drawing the VertexArray after binding it)
///
/// Notes:
/// - create an ArrayBuffer, define the attribute(s), set the data, create a VertexArray
/// - store the VertexArray to the member variable mMeshLine
/// - the primitive type is GL_LINES
/// - for the drawing, you have to set some uniforms for mShaderLine
/// - uViewMatrix and uProjectionMatrix are automatically set
///
///
void Assignment04::buildLineMesh()
{
// Note that it is okay to not use this method at all and assemble
// the AB in drawLine(...) every time (from the two tg::pos3 and the color)
auto ab = ArrayBuffer::create();
ab->defineAttribute<float>("aPosition");
ab->bind().setData(std::vector<float>({0.0f, 1.0f}));
mMeshLine = VertexArray::create(ab, GL_LINES);
}
void Assignment04::drawLine(tg::pos3 from, tg::pos3 to, tg::color3 color)
{
auto shader = mShaderLine->use();
shader.setUniform("uFrom", from);
shader.setUniform("uTo", to);
shader.setUniform("uColor", color);
mMeshLine->bind().draw();
}
/// ============= STUDENT CODE END =============
/// ===============================================================================================
/// ===============================================================================================
/// Task 1.b
/// Draw the rigid body
///
/// Your job is to:
/// - compute the model matrix of the box or sphere
/// - pass it to the shader as uniform
/// - draw the appropriate VertexArray
///
/// Notes:
/// - a rigid body can consist of several shapes with one common
/// transformation matrix (rbTransform)
/// - furthermore, every shape is transformed by its own matrix (s->transform)
/// and some scaling that is defined by the radius (sphere) or halfExtent (box)
/// - the needed VertexArrays are stored in mMeshSphere and mMeshCube
///
/// ============= STUDENT CODE BEGIN =============
auto rbTransform = mRigidBody.getTransform();
for (auto const& s : mRigidBody.shapes)
{
auto sphere = std::dynamic_pointer_cast<SphereShape>(s);
auto box = std::dynamic_pointer_cast<BoxShape>(s);
// draw spheres
if (sphere)
{
auto model = s->transform * tg::scaling<3>(sphere->radius);
shader.setUniform("uModelMatrix", rbTransform * model);
mMeshSphere->bind().draw();
}
// draw boxes
if (box)
{
auto model = s->transform * tg::scaling(box->halfExtent);
shader.setUniform("uModelMatrix", rbTransform * model);
mMeshCube->bind().draw();
}
}
/// ============= STUDENT CODE END =============
/// ===============================================================================================
/// ===============================================================================================
/// Task 2.a
/// Calculate inertia and mass via sampling
///
/// Your job is to:
/// - calculate mass (write to variable 'mass')
/// - calculate center of gravity (write to variable 'cog')
/// - calculate inertia matrix (write to variable 'inertia')
///
/// Notes:
/// - sanity check for inertia: it should be roughly diagonal (sides are |.| < 0.5)
///
/// ============= STUDENT CODE BEGIN =============
// calculate center of gravity
for (auto const& s : samples)
{
mass += s.mass;
cog += s.offset * s.mass;
}
cog /= mass;
// calculate inertia (via sampling)
for (auto const& s : samples)
{
auto m = s.mass;
auto r = s.offset - cog;
auto I = tg::mat3::zero;
// diagonal
I[0][0] = r.y * r.y + r.z * r.z;
I[1][1] = r.z * r.z + r.x * r.x;
I[2][2] = r.x * r.x + r.y * r.y;
// sides
I[1][0] = I[0][1] = -r.x * r.y;
I[0][2] = I[2][0] = -r.z * r.x;
I[2][1] = I[1][2] = -r.y * r.z;
// add to total inertia
inertia += I * m;
}
/// ============= STUDENT CODE END =============
/// ===============================================================================================
/// ===============================================================================================
/// Task 2.b
///
/// Your job is to:
/// - update the linear momentum and position
/// - update the angular momentum and position
///
/// Notes:
/// - you can test your angular velocity code with the following settings for omega:
/// (be sure to remove test code before uploading)
/// omega = tg::vec3(0, 1, 0) // slow counter-clockwise rotation around Y
/// omega = tg::vec3(5, 0, 0) // faster rotation around X (the long side for the pendulum)
///
///
/// ============= STUDENT CODE BEGIN =============
// linear part
linearMomentum += linearForces * elapsedSeconds;
linearPosition += linearVelocity() * elapsedSeconds;
// angular part
angularMomentum += angularForces * elapsedSeconds;
// apply rotational velocity
auto Rx = angularPosition[0];
auto Ry = angularPosition[1];
auto Rz = angularPosition[2];
Rx += cross(omega, Rx) * elapsedSeconds;
Ry += cross(omega, Ry) * elapsedSeconds;
Rz += cross(omega, Rz) * elapsedSeconds;
angularPosition = {Rx, Ry, Rz};
/// ============= STUDENT CODE END =============
/// ===============================================================================================
/// ===============================================================================================
/// Task 2.c
///
/// Your job is to:
/// - implement the bodies of the six methods defined below
///
/// Notes:
/// - in some of the methods you might need to call one of the others
/// - if needed, you should use the tg functions cross, inverse and transpose
///
/// ============= STUDENT CODE BEGIN =============
tg::vec3 RigidBody::linearVelocity() const
{
return linearMomentum / mass;
}
tg::vec3 RigidBody::angularVelocity() const
{
return invInertiaWorld() * angularMomentum;
}
tg::vec3 RigidBody::velocityInPoint(tg::pos3 worldPos) const
{
return linearVelocity() + cross(angularVelocity(), worldPos - linearPosition);
}
tg::mat3 RigidBody::invInertiaWorld() const
{
return angularPosition * inverse(inertia) * transpose(angularPosition);
}
void RigidBody::applyImpulse(tg::vec3 impulse, tg::pos3 worldPos)
{
linearMomentum += impulse;
angularMomentum += cross(worldPos - linearPosition, impulse);
}
void RigidBody::addForce(tg::vec3 force, tg::pos3 worldPos)
{
linearForces += force;
angularForces += cross(worldPos - linearPosition, force);
}
/// ============= STUDENT CODE END =============
/// ===============================================================================================
File added
File added
/// ===============================================================================================
/// Task 1.a
/// Draw the line mesh
///
/// Your job is to:
/// - define the vertex attributes and uniforms that you need for the rendering
/// - compute the position, transform it to screen space and pass it on
///
/// Notes:
/// - gl_Position is four-dimensional
///
/// ============= STUDENT CODE BEGIN =============
in float aPosition; // 0.0 and 1.0
uniform vec3 uFrom;
uniform vec3 uTo;
void main()
{
vec3 pos = mix(uFrom, uTo, aPosition); // optimized version of "aPosition < 0.5 ? uFrom : uTo"
gl_Position = uProjectionMatrix * uViewMatrix * vec4(pos, 1.0);
}
/// ============= STUDENT CODE END =============
/// ===============================================================================================
\ No newline at end of file
///
/// Create the meshes for this chunk. (one per material)
/// The return value is a map from material to mesh.
///
/// Your job is to:
/// - enhance the performance by (re-)creating meshes only if needed
/// (Dirty-flagging can be done using mIsDirty)
/// - create faces for all visible blocks
/// - adapt Vertices.hh (vertex type) and terrain.vsh (vertex shader)
///
/// - Advanced: do not create faces that are never visible from the outside
/// - no face between two solids
/// - no face between two translucent materials of the same type
/// - Advanced: compute some fake ambient occlusion for every vertex
/// (also optimize the triangulation of the quads depending on the ao values)
/// - Advanced: Pack vertex data in 16 byte or less (e.g. a single (tg::)pos4)
///
///
/// Notes:
/// - pre-filled code is meant as a starting helper; you may change everything
/// you want inside the strips, e.g. implement helper functions (see Chunk.hh)
/// - for every material (except air) in a chunk, one mesh must be created
/// - it is up to you whether you create an indexed mesh or not
/// - local coordinates: 0..size-1 -> block query: block(localPos)
/// - global coordinates: chunkPos + localPos -> block query: queryBlock(globalPos)
/// - read Chunk.hh for helpers and explanations
/// - for AO see screenshots, "Fake Ambient Occlusion.jpg", and "Fake AO - Triangulation.jpg"
/// - don't forget that some faces need information from neighboring chunks (global queries)
///
/// ============= STUDENT CODE BEGIN =============
std::map<int, SharedVertexArray> Chunk::queryMeshes()
{
if (!isDirty())
return mMeshes;
GLOW_ACTION(); // time this method (shown on shutdown)
// clear list of cached meshes
mMeshes.clear();
std::set<int> built; // track already built materials
// ensure that each material is accounted for
for (auto z = 0; z < size; ++z)
for (auto y = 0; y < size; ++y)
for (auto x = 0; x < size; ++x)
{
tg::ivec3 rp = {x, y, z};
auto const &b = block(rp); // get block
// if block material is not air and not already built
if (!b.isAir() && !built.count(b.mat))
{
// mark as built
built.insert(b.mat);
// create VAO
auto vao = buildMeshFor(b.mat);
if (vao) // might be nullptr if fully surrounded
mMeshes[b.mat] = vao;
}
}
mIsDirty = false;
glow::info() << "Rebuilding mesh for " << chunkPos;
return mMeshes;
}
SharedVertexArray Chunk::buildMeshFor(int mat) const
{
GLOW_ACTION(); // time this method (shown on shutdown)
// assemble data
std::vector<TerrainVertex> vertices;
// optimized packed vertex
auto vert = [](tg::pos3 pos, int dir, float ao) {
TerrainVertex v;
v.pos = tg::pos4(pos, ao * 0.5f + dir);
return v;
};
for (auto z = 0; z < size; ++z)
for (auto y = 0; y < size; ++y)
for (auto x = 0; x < size; ++x)
{
tg::ivec3 rp = {x, y, z}; // local position
auto gp = chunkPos + rp; // global position
auto const &blk = block(rp);
if (blk.mat != mat)
continue; // consider only current material
for (auto s : {-1, 1})
for (auto dir : {0, 1, 2})
{
// face normal
auto n = s * tg::ivec3(dir == 0, dir == 1, dir == 2);
// neighbor position and block
auto np = rp + n;
auto nblk = queryBlock(chunkPos + np); // global query
// check if we have to gen a face
bool hasFace = !nblk.isSolid() && nblk.mat != mat;
if (hasFace)
{
auto dn = tg::vec3(n);
auto dt = cross(dn, tg::vec3(dir == 1, dir == 2, dir == 0));
auto db = cross(dt, dn);
auto pc = tg::pos3(gp) + tg::vec3(0.5f) + tg::vec3(dn) * 0.5f;
auto pdir = dir + (s + 1) / 2 * 3; // packed dir 0,1,2 negative, 3,4,5 positive
// Calculate position
auto p00 = pc - dt * 0.5f - db * 0.5f;
auto p01 = pc - dt * 0.5f + db * 0.5f;
auto p10 = pc + dt * 0.5f - db * 0.5f;
auto p11 = pc + dt * 0.5f + db * 0.5f;
// Ambient Occlusion trick
auto a00 = aoAt(gp + n, -tg::ivec3(dt), -tg::ivec3(db));
auto a01 = aoAt(gp + n, -tg::ivec3(dt), +tg::ivec3(db));
auto a10 = aoAt(gp + n, +tg::ivec3(dt), -tg::ivec3(db));
auto a11 = aoAt(gp + n, +tg::ivec3(dt), +tg::ivec3(db));
// Create face
// (choose optimal triangulation based on ambient occlusion)
if (tg::abs(a01 - a10) > tg::abs(a00 - a11))
{
vertices.push_back(vert(p00, pdir, a00));
vertices.push_back(vert(p01, pdir, a01));
vertices.push_back(vert(p11, pdir, a11));
vertices.push_back(vert(p00, pdir, a00));
vertices.push_back(vert(p11, pdir, a11));
vertices.push_back(vert(p10, pdir, a10));
}
else
{
vertices.push_back(vert(p00, pdir, a00));
vertices.push_back(vert(p01, pdir, a01));
vertices.push_back(vert(p10, pdir, a10));
vertices.push_back(vert(p01, pdir, a01));
vertices.push_back(vert(p11, pdir, a11));
vertices.push_back(vert(p10, pdir, a10));
}
}
}
}
if (vertices.empty())
return nullptr; // no visible faces
auto ab = ArrayBuffer::create(TerrainVertex::attributes());
ab->bind().setData(vertices);
glow::info() << "Created " << vertices.size() << " verts for mat " << mat << " in chunk " << chunkPos;
return VertexArray::create(ab);
}
float Chunk::aoAt(tg::ipos3 pos, tg::ivec3 dx, tg::ivec3 dy) const
{
// block(pos) is non-solid
// query three relevant materials
auto s10 = queryBlock(pos + dx).isSolid();
auto s01 = queryBlock(pos + dy).isSolid();
auto s11 = queryBlock(pos + dx + dy).isSolid();
if (s10 && s01)
s11 = true; // corner case
return (3 - s10 - s01 - s11) / 3.0f;
}
#endif /// ============= STUDENT CODE END =============
/// All Tasks
///
/// You can use this space for declarations of helper functions
///
/// ============= STUDENT CODE BEGIN =============
/// Builds the mesh for a given material
/// Builds the mesh for a given material
glow::SharedVertexArray buildMeshFor(int mat) const;
/// Returns the ambient occlusion at a given position
float aoAt(tg::ipos3 pos, tg::ivec3 dx, tg::ivec3 dy) const;
/// ============= STUDENT CODE END =============
///
/// The vertex type used for all terrain meshes
///
/// Your job is to:
/// - create/modify the vertex type to fit your needs
///
/// - Advanced: Pack everything you need in 16 byte or less (e.g. a single (tg::)pos4)
///
/// Notes:
/// - do not upload tangents or texture coords!
/// (those can -and have to- be computed in the shader)
///
/// Grading:
/// 3p if <= 16 byte per vertex
/// 2p if <= 28 byte per vertex
/// 1p otherwise
///
/// (~4.9 byte is theoretical minimum but <= 16 is fine for full points)
///
/// ============= STUDENT CODE BEGIN =============
struct TerrainVertex
{
tg::pos4 pos;
static std::vector<glow::ArrayBufferAttribute> attributes()
{
return {
{&TerrainVertex::pos, "aPosition"}, //
};
}
};
/// ============= STUDENT CODE END =============
File added
File added
///
/// Vertex shader code for all terrain materials
///
/// Your job is to:
/// - define vertex attribute(s) for the data you need
/// - compute tangent and texture coordinate
/// - write to all defined out-variables (vWorldPos, ...)
///
/// - Advanced: unpack position, ao value and normal from your optimized vertex format
///
/// Notes:
/// - don't forget to divide texture coords by uTextureScale
///
/// ============= STUDENT CODE BEGIN =============
in vec4 aPosition;
void main()
{
vec3 pos = aPosition.xyz;
float ao = fract(aPosition.w) * 2;
int pdir = int(aPosition.w);
int s = pdir / 3 * 2 - 1;
int dir = pdir % 3;
vec3 N = vec3(float(dir == 0), float(dir == 1), float(dir == 2)) * float(s);
// derive tangent
vec3 T = normalize(cross(abs(N.x) > abs(N.y) + 0.1 ? vec3(0,1,0) : vec3(1,0,0), N));
vec3 B = normalize(cross(T, N));
// derive UV
vTexCoord = vec2(
dot(pos, T),
dot(pos, B)
) / uTextureScale;
vAO = ao;
vNormal = N;
vTangent = T;
vWorldPos = pos;
vViewPos = vec3(uView * vec4(pos, 1.0));
vScreenPos = uProj * vec4(vViewPos, 1.0);
gl_Position = vScreenPos;
}
/// ============= STUDENT CODE END =============
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment