Browse Source

Add support for loading and rendering skinned meshes

master
C. J. Howard 6 years ago
parent
commit
37e109db4d
9 changed files with 202 additions and 27 deletions
  1. +1
    -1
      data
  2. +1
    -1
      lib/emergent
  3. +29
    -1
      src/game/ant.cpp
  4. +4
    -1
      src/game/ant.hpp
  5. +83
    -0
      src/model-loader.cpp
  6. +19
    -0
      src/model-loader.hpp
  7. +58
    -22
      src/render-passes.cpp
  8. +6
    -1
      src/render-passes.hpp
  9. +1
    -0
      src/states/play-state.cpp

+ 1
- 1
data

@ -1 +1 @@
Subproject commit c08b221aed4d3dbe82e8ffe5bc67d7b786993b28
Subproject commit e0fb2511f4d08bb4823535ebdaa79de3cf2caf59

+ 1
- 1
lib/emergent

@ -1 +1 @@
Subproject commit 18ec684943f9530a1a3851271dc1805a18e2b102
Subproject commit 0d4b84100382a6053dfd2da5e7707bc28477ac87

+ 29
- 1
src/game/ant.cpp View File

@ -25,9 +25,37 @@ Ant::Ant(Colony* colony):
colony(colony),
state(Ant::State::IDLE),
transform(Transform::getIdentity()),
skeletonPose(nullptr)
pose(nullptr)
{
pose = new Pose(colony->getAntModel()->getSkeleton());
for (std::size_t i = 0; i < pose->getSkeleton()->getBoneCount(); ++i)
{
pose->setRelativeTransform(i, pose->getSkeleton()->getBindPose()->getRelativeTransform(i));
}
pose->concatenate();
modelInstance.setModel(colony->getAntModel());
modelInstance.setPose(pose);
}
Ant::~Ant()
{
delete pose;
}
void Ant::rotateHead()
{
const Bone* headBone = pose->getSkeleton()->getBone("left-flagellum");
if (headBone != nullptr)
{
std::size_t boneIndex = headBone->getIndex();
Transform transform = pose->getRelativeTransform(boneIndex);
transform.rotation = glm::normalize(transform.rotation * glm::angleAxis(glm::radians(5.0f), Vector3(0.0f, 1.0f, 0.0f)));
pose->setRelativeTransform(boneIndex, transform);
pose->concatenate();
}
}
void Ant::move(const Vector3& velocity)

+ 4
- 1
src/game/ant.hpp View File

@ -65,6 +65,9 @@ public:
* Creates an instance of Ant.
*/
Ant(Colony* colony);
~Ant();
void rotateHead();
void move(const Vector3& velocity);
@ -96,7 +99,7 @@ private:
Transform transform;
ModelInstance modelInstance;
Pose* skeletonPose;
Pose* pose;
};
inline const Colony* Ant::getColony() const

+ 83
- 0
src/model-loader.cpp View File

@ -88,6 +88,7 @@ Model* ModelLoader::load(const std::string& filename)
// Allocate model data
ModelData* modelData = new ModelData();
SkeletonData* skeletonData = nullptr;
// Allocate material groups
read32(&modelData->groupCount, &bufferOffset);
@ -168,6 +169,43 @@ Model* ModelLoader::load(const std::string& filename)
read32(&modelData->indexData[i], &bufferOffset);
}
// Read skeleton data
if (modelData->vertexFormat & WEIGHTS)
{
// Allocate skeleton data
skeletonData = new SkeletonData();
// Read bone count
read16(&skeletonData->boneCount, &bufferOffset);
// Allocate bones
skeletonData->boneData = new BoneData[skeletonData->boneCount];
// Read bones
for (std::size_t i = 0; i < skeletonData->boneCount; ++i)
{
BoneData* boneData = &skeletonData->boneData[i];
boneData->children = nullptr;
readString(&boneData->name, &bufferOffset);
read16(&boneData->parent, &bufferOffset);
read16(&boneData->childCount, &bufferOffset);
boneData->children = new std::uint16_t[boneData->childCount];
for (std::size_t j = 0; j < boneData->childCount; ++j)
{
read16(&boneData->children[j], &bufferOffset);
}
read32(&boneData->translation.x, &bufferOffset);
read32(&boneData->translation.y, &bufferOffset);
read32(&boneData->translation.z, &bufferOffset);
read32(&boneData->rotation.w, &bufferOffset);
read32(&boneData->rotation.x, &bufferOffset);
read32(&boneData->rotation.y, &bufferOffset);
read32(&boneData->rotation.z, &bufferOffset);
read32(&boneData->length, &bufferOffset);
}
}
// Free file data buffer
delete[] buffer;
@ -294,12 +332,39 @@ Model* ModelLoader::load(const std::string& filename)
model->addGroup(modelGroup);
}
// Create skeleton
if (skeletonData != nullptr)
{
// Allocate skeleton
Skeleton* skeleton = new Skeleton();
// Construct bone hierarchy from bone data
constructBoneHierarchy(skeleton->getRootBone(), skeletonData->boneData, 0);
// Calculate bind pose
skeleton->calculateBindPose();
// Add skeleton to model
model->setSkeleton(skeleton);
}
// Delete model data groups
delete[] modelData->groups;
// Delete model data
delete modelData;
// Delete skeleton data
if (skeletonData != nullptr)
{
for (std::size_t i = 0; i < skeletonData->boneCount; ++i)
{
delete[] skeletonData->boneData[i].children;
}
delete[] skeletonData->boneData;
delete skeletonData;
}
return model;
}
@ -307,3 +372,21 @@ void ModelLoader::setMaterialLoader(MaterialLoader* materialLoader)
{
this->materialLoader = materialLoader;
}
void ModelLoader::constructBoneHierarchy(Bone* bone, const BoneData* data, std::uint16_t index)
{
bone->setName(data[index].name);
Transform transform;
transform.translation = data[index].translation;
transform.rotation = data[index].rotation;
transform.scale = Vector3(1.0f);
bone->setRelativeTransform(transform);
bone->setLength(data[index].length);
for (std::uint16_t i = 0; i < data[index].childCount; ++i)
{
constructBoneHierarchy(bone->createChild(), data, data[index].children[i]);
}
}

+ 19
- 0
src/model-loader.hpp View File

@ -63,6 +63,25 @@ private:
std::uint32_t* indexData;
};
struct BoneData
{
std::string name;
std::uint16_t parent;
std::uint16_t childCount;
std::uint16_t* children;
Vector3 translation;
Quaternion rotation;
float length;
};
struct SkeletonData
{
std::uint16_t boneCount;
BoneData* boneData;
};
static void constructBoneHierarchy(Bone* bone, const BoneData* data, std::uint16_t index);
MaterialLoader* materialLoader;
};

+ 58
- 22
src/render-passes.cpp View File

@ -305,6 +305,9 @@ LightingRenderPass::LightingRenderPass():
0.0f, 0.0f, 0.5f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f);
maxBoneCount = 64;
matrixPaletteParam = parameterSet.addParameter("matrixPalette", ShaderParameter::Type::MATRIX_4, maxBoneCount);
modelParam = parameterSet.addParameter("modelMatrix", ShaderParameter::Type::MATRIX_4, 1);
modelViewParam = parameterSet.addParameter("modelViewMatrix", ShaderParameter::Type::MATRIX_4, 1);
modelViewProjectionParam = parameterSet.addParameter("modelViewProjectionMatrix", ShaderParameter::Type::MATRIX_4, 1);
@ -315,7 +318,7 @@ LightingRenderPass::LightingRenderPass():
directionalLightColorsParam = parameterSet.addParameter("directionalLightColors", ShaderParameter::Type::VECTOR_3, 1);
directionalLightDirectionsParam = parameterSet.addParameter("directionalLightDirections", ShaderParameter::Type::VECTOR_3, 1);
albedoOpacityMapParam = parameterSet.addParameter("albedoOpacityMap", ShaderParameter::Type::INT, 1);
metalnessRoughnessMapParam = parameterSet.addParameter("metalnessRoughness", ShaderParameter::Type::INT, 1);
metalnessRoughnessMapParam = parameterSet.addParameter("metalnessRoughnessMap", ShaderParameter::Type::INT, 1);
normalOcclusionMapParam = parameterSet.addParameter("normalOcclusionMap", ShaderParameter::Type::INT, 1);
diffuseCubemapParam = parameterSet.addParameter("diffuseCubemap", ShaderParameter::Type::INT, 1);
specularCubemapParam = parameterSet.addParameter("specularCubemap", ShaderParameter::Type::INT, 1);
@ -348,15 +351,22 @@ bool LightingRenderPass::load(const RenderContext* renderContext)
std::cerr << "Failed to load cubemap" << std::endl;
}
// Load lighting shader
// Load unskinned shader
shaderLoader.undefine();
shaderLoader.define("TEXTURE_COUNT", 0);
shaderLoader.define("VERTEX_POSITION", EMERGENT_VERTEX_POSITION);
shaderLoader.define("VERTEX_NORMAL", EMERGENT_VERTEX_NORMAL);
shaderLoader.define("VERTEX_TEXCOORD", EMERGENT_VERTEX_TEXCOORD);
unskinnedShader = shaderLoader.load("data/shaders/lit-object.glsl", &parameterSet);
// Load skinned shader
shaderLoader.define("SKINNED");
shaderLoader.define("MAX_BONE_COUNT", maxBoneCount);
shaderLoader.define("VERTEX_BONE_INDICES", EMERGENT_VERTEX_BONE_INDICES);
shaderLoader.define("VERTEX_BONE_WEIGHTS", EMERGENT_VERTEX_BONE_WEIGHTS);
skinnedShader = shaderLoader.load("data/shaders/lit-object.glsl", &parameterSet);
lightingShader = shaderLoader.load("data/shaders/lit-object.glsl", &parameterSet);
if (!lightingShader)
if (!unskinnedShader || !skinnedShader)
{
return false;
}
@ -368,8 +378,11 @@ bool LightingRenderPass::load(const RenderContext* renderContext)
void LightingRenderPass::unload()
{
delete lightingShader;
lightingShader = nullptr;
delete unskinnedShader;
delete skinnedShader;
unskinnedShader = nullptr;
skinnedShader = nullptr;
for (auto it = shaderCache.begin(); it != shaderCache.end(); ++it)
{
@ -789,34 +802,20 @@ void LightingRenderPass::render(const RenderContext* renderContext)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Bind shader
Shader* shader = lightingShader;
shader->bind();
// Pass texture units to shader
shader->setParameter(albedoOpacityMapParam, 0);
shader->setParameter(normalOcclusionMapParam, 2);
int directionalLightCount = 1;
Vector3 directionalLightColors[3];
Vector3 directionalLightDirections[3];
directionalLightColors[0] = Vector3(1);
directionalLightDirections[0] = glm::normalize(Vector3(camera.getView() * -Vector4(0, 0, -1, 0)));
shader->setParameter(directionalLightCountParam, directionalLightCount);
shader->setParameter(directionalLightColorsParam, 0, &directionalLightColors[0], directionalLightCount);
shader->setParameter(directionalLightDirectionsParam, 0, &directionalLightDirections[0], directionalLightCount);
shader->setParameter(cameraPositionParam, camera.getTranslation());
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_CUBE_MAP, diffuseCubemap->getTextureID());
shader->setParameter(diffuseCubemapParam, 3);
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_CUBE_MAP, specularCubemap->getTextureID());
shader->setParameter(specularCubemapParam, 4);
Shader* shader = nullptr;
Texture* albedoOpacityMap = nullptr;
Texture* metalnessRoughnessMap = nullptr;
Texture* normalOcclusionMap = nullptr;
@ -837,6 +836,43 @@ void LightingRenderPass::render(const RenderContext* renderContext)
// Skip render operations with unsupported vertex formats
// Select shader
Shader* targetShader = nullptr;
if (operation.pose != nullptr)
{
targetShader = skinnedShader;
}
else
{
targetShader = unskinnedShader;
}
// Switch shader if necessary
if (shader != targetShader)
{
shader = targetShader;
// Bind shader
shader->bind();
// Pass static params
shader->setParameter(albedoOpacityMapParam, 0);
shader->setParameter(metalnessRoughnessMapParam, 1);
shader->setParameter(normalOcclusionMapParam, 2);
shader->setParameter(diffuseCubemapParam, 3);
shader->setParameter(specularCubemapParam, 4);
shader->setParameter(directionalLightCountParam, directionalLightCount);
shader->setParameter(directionalLightColorsParam, 0, &directionalLightColors[0], directionalLightCount);
shader->setParameter(directionalLightDirectionsParam, 0, &directionalLightDirections[0], directionalLightCount);
shader->setParameter(cameraPositionParam, camera.getTranslation());
}
// Pass matrix palette
if (operation.pose != nullptr)
{
shader->setParameter(matrixPaletteParam, 0, operation.pose->getMatrixPalette(), operation.pose->getSkeleton()->getBoneCount());
}
// Bind albedo-opacity map
if (material->albedoOpacityMap != albedoOpacityMap)
{

+ 6
- 1
src/render-passes.hpp View File

@ -131,6 +131,7 @@ private:
bool loadShader(const RenderOperation& operation);
ShaderParameterSet parameterSet;
const ShaderParameter* matrixPaletteParam;
const ShaderParameter* modelParam;
const ShaderParameter* modelViewParam;
const ShaderParameter* modelViewProjectionParam;
@ -146,9 +147,13 @@ private:
const ShaderParameter* diffuseCubemapParam;
const ShaderParameter* specularCubemapParam;
Shader* unskinnedShader;
Shader* skinnedShader;
int maxBoneCount;
ShaderLoader shaderLoader;
std::map<std::size_t, Shader*> shaderCache;
Shader* lightingShader;
//Shader* lightingShader;
Matrix4 biasMatrix;
GLuint shadowMap;

+ 1
- 0
src/states/play-state.cpp View File

@ -195,6 +195,7 @@ void PlayState::execute()
if (pickAnt != nullptr)
{
pickAnt->getModelInstance()->setTranslation(pick);
pickAnt->rotateHead();
}
// Update colony

Loading…
Cancel
Save