Skip to content

Instantly share code, notes, and snippets.

@DCubix
Created April 20, 2018 20:05
Show Gist options
  • Save DCubix/45f0ed2256f30643a4ecc731468593c1 to your computer and use it in GitHub Desktop.
Save DCubix/45f0ed2256f30643a4ecc731468593c1 to your computer and use it in GitHub Desktop.
Mesher
#include "mesher.h"
#include "../core/logging/log.h"
NS_BEGIN
void VertexFormat::put(const String& name, AttributeType type, bool normalized, i32 location) {
VertexAttribute attr;
attr.size = type;
attr.normalized = normalized;
attr.location = location;
attr.name = name;
m_attributes.push_back(attr);
m_stride += u32(type) * 4;
}
void VertexFormat::bind(ShaderProgram* shader) {
u32 off = 0;
bool shaderValid = shader != nullptr;
for (auto attr : m_attributes) {
i32 loc = attr.location;
if (shaderValid && attr.location == -1) {
loc = shader->getAttributeLocation(attr.name);
}
if (loc != -1) {
glEnableVertexAttribArray(loc);
glVertexAttribPointer(loc, attr.size, GL_FLOAT, attr.normalized, m_stride, ((void*) off));
}
off += attr.size * 4;
}
}
void VertexFormat::unbind(ShaderProgram* shader) {
if (shader == nullptr) return;
for (auto attr : m_attributes) {
i32 loc = attr.location;
if (attr.location == -1) {
loc = shader->getAttributeLocation(attr.name);
}
if (loc != -1) {
glDisableVertexAttribArray(loc);
}
}
}
Model::~Model() {
if (m_vbo) GLBuffer::destroy(m_vbo);
if (m_ibo && m_indexed) GLBuffer::destroy(m_ibo);
if (m_vao && m_useVertexArrays) GLVertexArray::destroy(m_vao);
}
Model::Model(bool dynamic, bool indexed, bool vao)
: m_dynamic(dynamic), m_indexed(indexed), m_useVertexArrays(vao),
m_vboSize(0), m_iboSize(0), m_vaoOk(false) {
m_format = uptr<VertexFormat>(new VertexFormat());
m_vbo = GLBuffer::create();
if (indexed) m_ibo = GLBuffer::create();
if (vao) m_vao = GLVertexArray::create();
m_format->put("vPosition", AttributeType::AttrVector3, false, 0);
m_format->put("vNormal", AttributeType::AttrVector3, false, 1);
m_format->put("vTangent", AttributeType::AttrVector3, false, 2);
m_format->put("vTexCoord", AttributeType::AttrVector2, false, 3);
m_format->put("vColor", AttributeType::AttrVector4, false, 4);
}
void Model::addVertex(const Vertex& vert) {
m_vertexData.push_back(vert);
}
void Model::addIndex(i32 index) {
m_indexData.push_back(index);
}
void Model::addTriangle(i32 i0, i32 i1, i32 i2) {
m_indexData.push_back(i0);
m_indexData.push_back(i1);
m_indexData.push_back(i2);
}
void Model::addData(const Vector<Vertex>& vertices, const Vector<i32>& indices) {
i32 off = m_vertexData.size();
m_vertexData.insert(m_vertexData.end(), vertices.begin(), vertices.end());
for (auto i : indices) {
m_indexData.push_back(off + i);
}
}
void Model::addFromFile(const String& file) {
Assimp::Importer imp;
const aiScene* scene = imp.ReadFile(
file.c_str(),
aiPostProcessSteps::aiProcess_Triangulate |
aiPostProcessSteps::aiProcess_FlipUVs |
aiPostProcessSteps::aiProcess_CalcTangentSpace
);
if (scene == nullptr || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE) {
LogError(imp.GetErrorString());
return;
}
addAIScene(scene);
}
void Model::addFromFile(VirtualFile* file) {
Assimp::Importer imp;
const aiScene* scene = imp.ReadFileFromMemory(
file->readAll(), file->size,
aiPostProcessSteps::aiProcess_Triangulate |
aiPostProcessSteps::aiProcess_FlipUVs |
aiPostProcessSteps::aiProcess_CalcTangentSpace
);
if (scene == nullptr) {
LogError(imp.GetErrorString());
return;
}
addAIScene(scene);
}
void Model::calculateNormals(PrimitiveType primitive) {
switch (primitive) {
case PrimitiveType::Points:
case PrimitiveType::Lines:
case PrimitiveType::LineLoop:
case PrimitiveType::LineStrip:
break;
case PrimitiveType::Triangles:
{
for (u32 i = 0; i < m_indexData.size(); i += 3) {
i32 i0 = index(i + 0);
i32 i1 = index(i + 1);
i32 i2 = index(i + 2);
triNormal(i0, i1, i2);
}
} break;
case PrimitiveType::TriangleFan:
{
for (u32 i = 0; i < m_indexData.size(); i += 2) {
i32 i0 = index(0);
i32 i1 = index(i);
i32 i2 = index(i + 1);
triNormal(i0, i1, i2);
}
} break;
case PrimitiveType::TriangleStrip:
{
for (u32 i = 0; i < m_indexData.size(); i += 2) {
i32 i0, i1, i2;
if (i % 2 == 0) {
i0 = index(i + 0);
i1 = index(i + 1);
i2 = index(i + 2);
} else {
i0 = index(i + 2);
i1 = index(i + 1);
i2 = index(i + 0);
}
triNormal(i0, i1, i2);
}
} break;
}
for (auto& v : m_vertexData) {
v.normal = v.normal.normalized();
}
}
void Model::calculateTangents(PrimitiveType primitive) {
switch (primitive) {
case PrimitiveType::Points:
case PrimitiveType::Lines:
case PrimitiveType::LineLoop:
case PrimitiveType::LineStrip:
break;
case PrimitiveType::Triangles:
{
for (u32 i = 0; i < m_indexData.size(); i += 3) {
i32 i0 = index(i + 0);
i32 i1 = index(i + 1);
i32 i2 = index(i + 2);
triTangent(i0, i1, i2);
}
} break;
case PrimitiveType::TriangleFan:
{
for (u32 i = 0; i < m_indexData.size(); i += 2) {
i32 i0 = index(0);
i32 i1 = index(i);
i32 i2 = index(i + 1);
triTangent(i0, i1, i2);
}
} break;
case PrimitiveType::TriangleStrip:
{
for (u32 i = 0; i < m_indexData.size(); i += 2) {
i32 i0, i1, i2;
if (i % 2 == 0) {
i0 = index(i + 0);
i1 = index(i + 1);
i2 = index(i + 2);
} else {
i0 = index(i + 2);
i1 = index(i + 1);
i2 = index(i + 0);
}
triTangent(i0, i1, i2);
}
} break;
}
for (auto& v : m_vertexData) {
v.tangent = v.tangent.normalized();
}
}
void Model::transformTexCoords(const Mat4& t) {
for (auto& v : m_vertexData) {
v.texCoord = (t * Vec4(v.texCoord.x, v.texCoord.y, 0.0f, 1.0f)).xy;
}
}
void Model::flush(i32 voff, i32 ioff) {
if (m_vertexData.empty()) return;
if (!m_vaoOk && m_useVertexArrays) {
glBindVertexArray(m_vao);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
m_format->bind();
if (m_indexed) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
glBindVertexArray(0);
m_vaoOk = true;
}
i32 vsize = m_format->stride() * m_vertexData.size();
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
if (vsize > m_vboSize) {
if (!m_dynamic) {
glBufferData(GL_ARRAY_BUFFER, vsize, m_vertexData.data(), GL_STATIC_DRAW);
} else {
glBufferData(GL_ARRAY_BUFFER, vsize, nullptr, GL_DYNAMIC_DRAW);
}
m_vboSize = vsize;
}
if (m_dynamic)
glBufferSubData(GL_ARRAY_BUFFER, voff, vsize, m_vertexData.data());
if (m_indexed) {
i32 isize = 4 * m_indexData.size();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
if (isize > m_iboSize) {
if (!m_dynamic) {
glBufferData(GL_ELEMENT_ARRAY_BUFFER, isize, m_indexData.data(), GL_STATIC_DRAW);
} else {
glBufferData(GL_ELEMENT_ARRAY_BUFFER, isize, nullptr, GL_DYNAMIC_DRAW);
}
m_iboSize = isize;
}
if (m_dynamic)
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, ioff, isize, m_indexData.data());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void Model::bind(ShaderProgram* shader) {
if (m_useVertexArrays) {
glBindVertexArray(m_vao);
} else {
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
m_format->bind(shader);
if (m_indexed) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
}
}
void Model::unbind(ShaderProgram* shader) {
if (m_useVertexArrays) {
glBindVertexArray(0);
} else {
glBindBuffer(GL_ARRAY_BUFFER, 0);
m_format->unbind(shader);
if (m_indexed) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
void Model::triNormal(i32 i0, i32 i1, i32 i2) {
Vec3 v0 = m_vertexData[i0].position;
Vec3 v1 = m_vertexData[i1].position;
Vec3 v2 = m_vertexData[i2].position;
Vec3 e0 = v1 - v0;
Vec3 e1 = v2 - v0;
Vec3 n = e0.cross(e1);
m_vertexData[i0].normal += n;
m_vertexData[i1].normal += n;
m_vertexData[i2].normal += n;
}
void Model::triTangent(i32 i0, i32 i1, i32 i2) {
Vec3 v0 = m_vertexData[i0].position;
Vec3 v1 = m_vertexData[i1].position;
Vec3 v2 = m_vertexData[i2].position;
Vec2 t0 = m_vertexData[i0].texCoord;
Vec2 t1 = m_vertexData[i1].texCoord;
Vec2 t2 = m_vertexData[i2].texCoord;
Vec3 e0 = v1 - v0;
Vec3 e1 = v2 - v0;
Vec2 dt1 = t1 - t0;
Vec2 dt2 = t2 - t0;
float dividend = dt1.perpDot(dt2);
float f = dividend == 0.0f ? 0.0f : 1.0f / dividend;
Vec3 t = Vec3();
t.x = (f * (dt2.y * e0.x - dt1.y * e1.x));
t.y = (f * (dt2.y * e0.y - dt1.y * e1.y));
t.z = (f * (dt2.y * e0.z - dt1.y * e1.z));
m_vertexData[i0].tangent += t;
m_vertexData[i1].tangent += t;
m_vertexData[i2].tangent += t;
}
void Model::addAIScene(const aiScene* scene) {
if (!indexed()) {
LogError("Cannot add data from file to non-indexed model.");
return;
}
i32 off = vertexCount();
const aiVector3D aiZeroVector(0.0f, 0.0f, 0.0f);
const aiColor4D aiOneVector4(1.0f, 1.0f, 1.0f, 1.0f);
for (u32 m = 0; m < scene->mNumMeshes; m++) {
aiMesh* mesh = scene->mMeshes[m];
bool hasPositions = mesh->HasPositions();
bool hasNormals = mesh->HasNormals();
bool hasUVs = mesh->HasTextureCoords(0);
bool hasTangents = mesh->HasTangentsAndBitangents();
bool hasColors = mesh->HasVertexColors(0);
for (u32 i = 0; i < mesh->mNumVertices; i++) {
Vertex v;
aiVector3D pos = hasPositions ? mesh->mVertices[i] : aiZeroVector;
aiVector3D normal = hasNormals ? mesh->mNormals[i] : aiZeroVector;
aiVector3D texCoord = hasUVs ? mesh->mTextureCoords[0][i] : aiZeroVector;
aiVector3D tangent = hasTangents ? mesh->mTangents[i] : aiZeroVector;
aiColor4D color = hasColors ? mesh->mColors[0][i] : aiOneVector4;
v.position = Vec3(pos.x, pos.y, pos.z);
v.normal = Vec3(normal.x, normal.y, normal.z);
v.texCoord = Vec2(texCoord.x, texCoord.y);
v.tangent = Vec3(tangent.x, tangent.y, tangent.z);
v.color = Vec4(color.r, color.g, color.b, color.a);
addVertex(v);
}
for (u32 i = 0; i < mesh->mNumFaces; i++) {
aiFace face = mesh->mFaces[i];
for (u32 j = 0; j < face.mNumIndices; j++) {
addIndex(off + face.mIndices[j]);
}
}
off += vertexCount();
}
}
NS_END
#ifndef MESHER_H
#define MESHER_H
#include "shader.h"
#include "../core/types.h"
#include "../math/vec.h"
#include "../core/filesys.h"
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
NS_BEGIN
enum AttributeType {
AttrFloat = 1,
AttrVector2,
AttrVector3,
AttrVector4
};
struct VertexAttribute {
String name;
i32 location;
u32 size;
bool normalized;
};
class VertexFormat {
public:
void put(const String& name, AttributeType type, bool normalized, i32 location = -1);
u32 stride() const { return m_stride; }
void bind(ShaderProgram* shader = nullptr);
void unbind(ShaderProgram* shader);
private:
Vector<VertexAttribute> m_attributes;
u32 m_stride;
};
struct Vertex {
Vec3 position;
Vec3 normal;
Vec3 tangent;
Vec2 texCoord;
Vec4 color;
Vertex()
: position(Vec3()), normal(Vec3()), tangent(Vec3()), texCoord(Vec2()), color(Vec4(1.0f))
{}
Vertex(const Vec3& pos)
: position(pos), normal(Vec3()), tangent(Vec3()), texCoord(Vec2()), color(Vec4(1.0f))
{}
Vertex(const Vec3& pos, const Vec3& nrm)
: position(pos), normal(nrm), tangent(Vec3()), texCoord(Vec2()), color(Vec4(1.0f))
{}
Vertex(const Vec3& pos, const Vec3& nrm, const Vec3& tan)
: position(pos), normal(nrm), tangent(tan), texCoord(Vec2()), color(Vec4(1.0f))
{}
Vertex(const Vec3& pos, const Vec3& nrm, const Vec3& tan, const Vec2& uv)
: position(pos), normal(nrm), tangent(tan), texCoord(uv), color(Vec4(1.0f))
{}
Vertex(const Vec3& pos, const Vec3& nrm, const Vec3& tan, const Vec2& uv, const Vec4& col)
: position(pos), normal(nrm), tangent(tan), texCoord(uv), color(col)
{}
Vertex(const Vec3& pos, const Vec2& uv)
: position(pos), texCoord(uv), normal(Vec3()), tangent(Vec3()), color(Vec4(1.0f))
{}
};
class Model {
public:
Model(bool dynamic = false, bool indexed = true, bool vao = true);
virtual ~Model();
void addVertex(const Vertex& vert);
void addIndex(i32 index);
void addTriangle(i32 i0, i32 i1, i32 i2);
void addData(const Vector<Vertex>& vertices, const Vector<i32>& indices);
void addFromFile(const String& file);
void addFromFile(VirtualFile* file);
void calculateNormals(PrimitiveType primitive = PrimitiveType::Triangles);
void calculateTangents(PrimitiveType primitive = PrimitiveType::Triangles);
void transformTexCoords(const Mat4& t);
void flush(i32 voff = 0, i32 ioff = 0);
void bind(ShaderProgram* shader = nullptr);
void unbind(ShaderProgram* shader = nullptr);
bool indexed() const { return m_indexed; }
bool useVertexArray() const { return m_useVertexArrays; }
const Vertex& vertex(u32 index) const {
return m_vertexData[index];
}
Vertex& vertex(u32 index) {
return m_vertexData[index];
}
i32 index(u32 index) const { return m_indexData[index]; }
u32 vertexCount() const { return m_vertexData.size(); }
u32 indexCount() const { return m_indexData.size(); }
private:
Vector<Vertex> m_vertexData;
Vector<i32> m_indexData;
uptr<VertexFormat> m_format;
bool m_dynamic, m_indexed, m_useVertexArrays, m_vaoOk;
GLuint m_vbo, m_ibo, m_vao;
i32 m_vboSize, m_iboSize;
void triNormal(i32 i0, i32 i1, i32 i2);
void triTangent(i32 i0, i32 i1, i32 i2);
void addAIScene(const aiScene* scene);
};
NS_END
#endif // MESHER_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment