diff --git a/CMakeLists.txt b/CMakeLists.txt index a47c3d0..04b3ba7 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ set(utils util.cpp) set(math quaternion.cpp vector.cpp matrix.cpp ray.cpp) set(render particleEmitter.cpp camera.cpp light.cpp material.cpp mesh.cpp model.cpp node.cpp quad.cpp box.cpp root.cpp shader.cpp texture.cpp) set(model modelReader.cpp assimpModelReader.cpp vbModelReader.cpp) -set(anim animationController.cpp animationChannel.cpp animation.cpp animatable.cpp) +set(anim animationController.cpp animationChannel.cpp animation.cpp keyframeChannel.cpp animatable.cpp driver.cpp) set(armature skeleton.cpp bone.cpp ikSolver.cpp) set(library ${render} ${math} ${model} ${utils} ${gui} ${armature} ${assetManager} ${anim}) diff --git a/animatable.h b/animatable.h index 31d2702..9f05df2 100644 --- a/animatable.h +++ b/animatable.h @@ -8,14 +8,10 @@ namespace vb01{ class Animatable{ public: - enum Type{NODE, BONE}; - Animatable(); virtual void animate(float, KeyframeChannel){} - inline Type getType(){return type;} private: protected: - Type type; }; } diff --git a/animation.h b/animation.h index 8e546ae..1bba071 100644 --- a/animation.h +++ b/animation.h @@ -3,39 +3,13 @@ #include #include +#include "keyframeChannel.h" namespace vb01{ class Animatable; class Animation{ public: - struct KeyframeChannel{ - enum Type{ - POS_X, - POS_Y, - POS_Z, - ROT_W, - ROT_X, - ROT_Y, - ROT_Z, - SCALE_X, - SCALE_Y, - SCALE_Z, - VALUE - }; - struct Keyframe{ - enum Interpolation{CONSTANT, LINEAR, BEZIER}; - - float value; - int frame; - Interpolation interpolation; - }; - - Type type; - Animatable *animatable = nullptr; - std::vector keyframes; - }; - Animation(std::string); ~Animation(); void update(); @@ -49,11 +23,6 @@ namespace vb01{ std::string name; std::vector keyframeChannels; }; - - typedef Animation::KeyframeChannel KeyframeChannel; - typedef Animation::KeyframeChannel::Type KeyframeChannelType; - typedef Animation::KeyframeChannel::Keyframe Keyframe; - typedef Animation::KeyframeChannel::Keyframe::Interpolation KeyframeInterpolation; } #endif diff --git a/animationController.cpp b/animationController.cpp index 885877f..6c59585 100644 --- a/animationController.cpp +++ b/animationController.cpp @@ -14,55 +14,29 @@ namespace vb01{ void AnimationController::update(){ for(AnimationChannel *channel : channels){ channel->update(); - transform(channel); } } - Keyframe AnimationController::findKeyframe(AnimationChannel *channel, KeyframeChannel keyframeChannel, bool last){ - int pastKeyframeId = -1; - for(int i = 0; i < keyframeChannel.keyframes.size(); i++){ - Keyframe keyframe = keyframeChannel.keyframes[i]; - if(channel->getCurrentFrame() >= keyframe.frame){ - pastKeyframeId = i; - } - } - return keyframeChannel.keyframes[pastKeyframeId + (last ? 0 : 1)]; - } - void AnimationController::transform(AnimationChannel *channel){ Animation *animation = getAnimation(channel->getAnimationName()); for(Animatable *animatable : channel->getAnimatables()){ vector keyframeChannels = animation->getKeyframeChannelsByAnimatable(animatable); for(KeyframeChannel keyframeChannel : keyframeChannels){ - Keyframe pastKeyframe = findKeyframe(channel, keyframeChannel, true); - Keyframe nextKeyframe = findKeyframe(channel, keyframeChannel, false); + int currentFrame = channel->getCurrentFrame(); + Keyframe pastKeyframe = KeyframeChannel::findKeyframe(currentFrame, keyframeChannel, true); + Keyframe nextKeyframe = KeyframeChannel::findKeyframe(currentFrame, keyframeChannel, false); int pastFrame = pastKeyframe.frame; int nextFrame = nextKeyframe.frame; - float ratio = (float)(max(0, channel->getCurrentFrame() - pastFrame)) / (nextFrame - pastFrame); + + float ratio = (float)(max(0, currentFrame - pastFrame)) / (nextFrame - pastFrame); Keyframe::Interpolation interp = pastKeyframe.interpolation; - float value = interpolate(pastKeyframe.value, nextKeyframe.value, interp, ratio); + float value = KeyframeChannel::interpolate(pastKeyframe.value, nextKeyframe.value, interp, ratio); animatable->animate(value, keyframeChannel); } } } - float AnimationController::interpolate(float pastValue, float nextValue, Keyframe::Interpolation mode, float ratio){ - float currentValue; - switch(mode){ - case Keyframe::CONSTANT: - currentValue = pastValue; - break; - case Keyframe::LINEAR: - currentValue = pastValue + (nextValue - pastValue) * ratio; - break; - case Keyframe::BEZIER: - currentValue = pastValue + (nextValue - pastValue) * ratio; - break; - } - return currentValue; - } - Animation* AnimationController::getAnimation(string name){ Animation *anim = nullptr; for(Animation *a : animations) diff --git a/animationController.h b/animationController.h index 500a5b7..7648610 100644 --- a/animationController.h +++ b/animationController.h @@ -26,8 +26,6 @@ namespace vb01{ std::vector channels; void transform(AnimationChannel*); - Keyframe findKeyframe(AnimationChannel*, KeyframeChannel, bool); - float interpolate(float, float, Keyframe::Interpolation, float); protected: void onAnimationEnd(std::string){} void onAnimationStart(std::string){} diff --git a/bone.cpp b/bone.cpp index 40ec8b0..58915d1 100644 --- a/bone.cpp +++ b/bone.cpp @@ -7,7 +7,6 @@ namespace vb01{ Bone::Bone(string name, float length, Vector3 pos, Quaternion rot, Vector3 scale) : Node(pos, rot, scale, name){ this->name = name; this->length = length; - Animatable::type = Animatable::BONE; } void Bone::lookAt(Vector3 newDir, Vector3 newUp){ @@ -118,4 +117,41 @@ namespace vb01{ setPoseRot(newRot); setPoseScale(newScale); } + + float Bone::getDriverValue(Driver::VariableType type){ + float driverValue; + switch(type){ + case Driver::POS_X: + driverValue = posePos.x; + break; + case Driver::POS_Y: + driverValue = posePos.y; + break; + case Driver::POS_Z: + driverValue = posePos.z; + break; + case Driver::ROT_W: + driverValue = poseRot.w; + break; + case Driver::ROT_X: + driverValue = poseRot.x; + break; + case Driver::ROT_Y: + driverValue = poseRot.y; + break; + case Driver::ROT_Z: + driverValue = poseRot.z; + break; + case Driver::SCALE_X: + driverValue = poseScale.x; + break; + case Driver::SCALE_Y: + driverValue = poseScale.y; + break; + case Driver::SCALE_Z: + driverValue = poseScale.z; + break; + } + return driverValue; + } } diff --git a/bone.h b/bone.h index 44251cf..a0e7da1 100644 --- a/bone.h +++ b/bone.h @@ -35,6 +35,7 @@ namespace vb01{ private: void updateBoneInfo(); void animate(float, KeyframeChannel); + virtual float getDriverValue(Driver::VariableType); float length; Bone *ikTarget = nullptr; diff --git a/driver.cpp b/driver.cpp new file mode 100644 index 0000000..d9598d7 --- /dev/null +++ b/driver.cpp @@ -0,0 +1,25 @@ +#include "driver.h" +#include "animatable.h" + +#include + +using namespace std; + +namespace vb01{ + Driver::Driver(){} + + void Driver::drive(float driverValue){ + Keyframe pastKeyframe = KeyframeChannel::findKeyframe(driverValue, keyframeChannel, true); + Keyframe nextKeyframe = KeyframeChannel::findKeyframe(driverValue, keyframeChannel, false); + float pastFrame = pastKeyframe.frame; + float nextFrame = nextKeyframe.frame; + + float ratio = (float)(driverValue - pastFrame) / (nextFrame - pastFrame); + if(ratio < 0) + ratio = 0; + + Keyframe::Interpolation interp = pastKeyframe.interpolation; + float value = KeyframeChannel::interpolate(pastKeyframe.value, nextKeyframe.value, interp, ratio); + target->animate(value, keyframeChannel); + } +} diff --git a/driver.h b/driver.h new file mode 100644 index 0000000..eeb0026 --- /dev/null +++ b/driver.h @@ -0,0 +1,34 @@ +#ifndef DRIVER_H +#define DRIVER + +#include "keyframeChannel.h" + +namespace vb01{ + class Animatable; + + class Driver{ + public: + enum VariableType{ + POS_X, + POS_Y, + POS_Z, + ROT_W, + ROT_X, + ROT_Y, + ROT_Z, + SCALE_X, + SCALE_Y, + SCALE_Z + }; + + Driver(); + inline VariableType getType(){return type;} + void drive(float); + private: + Animatable *target; + VariableType type; + KeyframeChannel keyframeChannel; + }; +} + +#endif diff --git a/keyframeChannel.cpp b/keyframeChannel.cpp new file mode 100644 index 0000000..d07ed65 --- /dev/null +++ b/keyframeChannel.cpp @@ -0,0 +1,33 @@ +#include "keyframeChannel.h" + +namespace vb01{ + float KeyframeChannel::interpolate(float pastValue, float nextValue, Keyframe::Interpolation mode, float ratio){ + float currentValue; + switch(mode){ + case Keyframe::CONSTANT: + currentValue = pastValue; + break; + case Keyframe::LINEAR: + currentValue = pastValue + (nextValue - pastValue) * ratio; + break; + case Keyframe::BEZIER: + currentValue = pastValue + (nextValue - pastValue) * ratio; + break; + } + return currentValue; + } + + Keyframe KeyframeChannel::findKeyframe(float time, KeyframeChannel keyframeChannel, bool last){ + int pastKeyframeId = -1; + for(int i = 0; i < keyframeChannel.keyframes.size(); i++){ + Keyframe keyframe = keyframeChannel.keyframes[i]; + if(time >= keyframe.frame){ + pastKeyframeId = i; + } + } + return keyframeChannel.keyframes[pastKeyframeId + (last ? 0 : 1)]; + } + + void KeyframeChannel::transform(){ + } +} diff --git a/keyframeChannel.h b/keyframeChannel.h new file mode 100644 index 0000000..5558d25 --- /dev/null +++ b/keyframeChannel.h @@ -0,0 +1,46 @@ +#ifndef KEYFRAME_CHANNEL_H +#define KEYFRAME_CHANNEL_H + +#include + +namespace vb01{ + class Animatable; + + struct KeyframeChannel{ + enum Type{ + POS_X, + POS_Y, + POS_Z, + ROT_W, + ROT_X, + ROT_Y, + ROT_Z, + SCALE_X, + SCALE_Y, + SCALE_Z, + SHAPE_KEY_MIN, + SHAPE_KEY_VALUE, + SHAPE_KEY_MAX + }; + struct Keyframe{ + enum Interpolation{CONSTANT, LINEAR, BEZIER}; + + float value, frame; + Interpolation interpolation; + }; + + Type type; + Animatable *animatable = nullptr; + std::vector keyframes; + + static float interpolate(float, float, Keyframe::Interpolation, float); + static Keyframe findKeyframe(float, KeyframeChannel, bool); + static void transform(); + }; + + typedef KeyframeChannel::Type KeyframeChannelType; + typedef KeyframeChannel::Keyframe Keyframe; + typedef KeyframeChannel::Keyframe::Interpolation KeyframeInterpolation; +} + +#endif diff --git a/mesh.cpp b/mesh.cpp index 3b9f848..822dbad 100755 --- a/mesh.cpp +++ b/mesh.cpp @@ -1,22 +1,37 @@ -#include"root.h" -#include"node.h" -#include"box.h" -#include"texture.h" -#include"skeleton.h" -#include -#include -#include -#include -#include -#include +#include "root.h" +#include "node.h" +#include "box.h" +#include "texture.h" +#include "skeleton.h" +#include "animation.h" +#include +#include +#include +#include +#include +#include using namespace std; using namespace glm; namespace vb01{ + void Mesh::ShapeKey::animate(float value, KeyframeChannel keyframeChannel){ + switch(keyframeChannel.type){ + case KeyframeChannel::SHAPE_KEY_MIN: + this->minValue = value; + break; + case KeyframeChannel::SHAPE_KEY_VALUE: + this->value = value; + break; + case KeyframeChannel::SHAPE_KEY_MAX: + this->maxValue = value; + break; + } + } + Mesh::Mesh(){} - Mesh::Mesh(Vertex *vertices, unsigned int *indices, int numTris, string *vertexGroups, int numVertexGroups, string *shapeKeys, int numShapeKeys, string name){ + Mesh::Mesh(Vertex *vertices, unsigned int *indices, int numTris, string *vertexGroups, int numVertexGroups, ShapeKey *shapeKeys, int numShapeKeys, string name) : Animatable(){ this->vertices = vertices; this->indices = indices; this->numTris = numTris; @@ -25,10 +40,6 @@ namespace vb01{ this->shapeKeys = shapeKeys; this->numShapeKeys = numShapeKeys; this->name = name; - - shapeKeyFactors = new float[numShapeKeys]; - for(int i = 0; i < numShapeKeys; i++) - shapeKeyFactors[i] = 0; } Mesh::~Mesh(){ @@ -164,7 +175,12 @@ namespace vb01{ void Mesh::updateShapeKeys(Shader *shader){ shader->editShader(Shader::VERTEX_SHADER, 5, "const int numShapeKeys=" + to_string(numShapeKeys) + ";"); for(int i = 0; i < numShapeKeys; i++){ - shader->setFloat(shapeKeyFactors[i], "shapeKeyFactors[" + to_string(i) + "]"); + if(shapeKeys[i].value < shapeKeys[i].minValue) + shapeKeys[i].value = shapeKeys[i].minValue; + else if(shapeKeys[i].value > shapeKeys[i].maxValue) + shapeKeys[i].value = shapeKeys[i].maxValue; + + shader->setFloat(shapeKeys[i].value, "shapeKeyFactors[" + to_string(i) + "]"); } } diff --git a/mesh.h b/mesh.h index 36f6c86..252dc69 100755 --- a/mesh.h +++ b/mesh.h @@ -1,12 +1,12 @@ #ifndef MESH_H #define MESH_H -#include"vector.h" -#include"util.h" -#include -#include -#include"material.h" - +#include "vector.h" +#include "util.h" +#include "material.h" +#include "animatable.h" +#include +#include namespace vb01{ class Node; @@ -14,8 +14,13 @@ namespace vb01{ class Texture; class Skeleton; - class Mesh{ + class Mesh : public Animatable{ public: + struct ShapeKey{ + float minValue, value, maxValue; + + void animate(float, KeyframeChannel); + }; struct Vertex{ Vector3 pos, norm, tan, biTan; Vector2 uv; @@ -24,7 +29,7 @@ namespace vb01{ Vector3 shapeKeyOffsets[100]; }; - Mesh(Vertex*, u32*, int, std::string *vg = nullptr, int = 0, std::string *sk = nullptr, int = 0, std::string = ""); + Mesh(Vertex*, u32*, int, std::string *vg = nullptr, int = 0, ShapeKey *sk = nullptr, int = 0, std::string = ""); ~Mesh(); void construct(); virtual void update(); @@ -66,9 +71,9 @@ namespace vb01{ bool staticVerts = true, castShadow = false, reflect = false, wireframe = false; Vertex *vertices; - std::string *vertexGroups = nullptr, *shapeKeys = nullptr; + std::string *vertexGroups = nullptr; + ShapeKey *shapeKeys = nullptr; u32 *indices, VAO, VBO, EBO; - float *shapeKeyFactors = nullptr; int numTris, numVertexGroups = 0, numShapeKeys = 0; }; } diff --git a/node.cpp b/node.cpp index 18a1593..0a3c683 100755 --- a/node.cpp +++ b/node.cpp @@ -21,7 +21,6 @@ namespace vb01{ this->orientation = orientation; this->name = name; this->controller = controller; - Animatable::type = Animatable::NODE; globalAxis[0] = Vector3::VEC_I; globalAxis[1] = Vector3::VEC_J; @@ -58,6 +57,45 @@ namespace vb01{ if(controller) controller->update(); } + for(Driver &driver : drivers) + driver.drive(getDriverValue(driver.getType())); + } + + float Node::getDriverValue(Driver::VariableType type){ + float driverValue; + switch(type){ + case Driver::POS_X: + driverValue = pos.x; + break; + case Driver::POS_Y: + driverValue = pos.y; + break; + case Driver::POS_Z: + driverValue = pos.z; + break; + case Driver::ROT_W: + driverValue = orientation.w; + break; + case Driver::ROT_X: + driverValue = orientation.x; + break; + case Driver::ROT_Y: + driverValue = orientation.y; + break; + case Driver::ROT_Z: + driverValue = orientation.z; + break; + case Driver::SCALE_X: + driverValue = scale.x; + break; + case Driver::SCALE_Y: + driverValue = scale.y; + break; + case Driver::SCALE_Z: + driverValue = scale.z; + break; + } + return driverValue; } void Node::attachChild(Node *child){ diff --git a/node.h b/node.h index 7258136..1eae93f 100755 --- a/node.h +++ b/node.h @@ -5,6 +5,7 @@ #include "vector.h" #include "root.h" #include "animatable.h" +#include "driver.h" #include #include @@ -73,8 +74,10 @@ namespace vb01{ Quaternion adjustRot(std::vector, Quaternion, bool); AnimationController *controller = nullptr; + std::vector drivers; protected: virtual void animate(float, KeyframeChannel); + virtual float getDriverValue(Driver::VariableType); bool visible = true; Vector3 pos, scale, globalAxis[3]; diff --git a/util.cpp b/util.cpp index 6b902d7..4f70b31 100755 --- a/util.cpp +++ b/util.cpp @@ -14,7 +14,7 @@ namespace vb01{ inFile.close(); } - void getLineData(string &line, string data[], int numData, int offset){ + void getLineData(string line, string data[], int numData, int offset){ int offsetComma = 0, c1 = 0; for(int i = 0; i < line.length() && offsetComma < offset; i++) if(line.c_str()[i] == ' '){ diff --git a/util.h b/util.h index fcde55c..5369f17 100755 --- a/util.h +++ b/util.h @@ -20,7 +20,7 @@ namespace vb01{ static const float PI = 4 * atan(1); void readFile(std::string, std::vector&, int = 0, int = -1); - void getLineData(std::string&, std::string[], int, int = 0); + void getLineData(std::string, std::string[], int, int = 0); int getCharId(std::string, char, bool = false); inline s64 getTime(){return s64(std::chrono::system_clock::now().time_since_epoch() / std::chrono::milliseconds(1));} diff --git a/vbModelReader.cpp b/vbModelReader.cpp index 64337c2..8be24df 100644 --- a/vbModelReader.cpp +++ b/vbModelReader.cpp @@ -344,7 +344,7 @@ namespace vb01{ } } - void VbModelReader::readShapeKeys(Mesh::Vertex *vertices, vector &vertIds, vector &meshData, string *shapeKeys, int numShapes, int numVertPos){ + void VbModelReader::readShapeKeys(Mesh::Vertex *vertices, vector &vertIds, vector &meshData, Mesh::ShapeKey *shapeKeys, int numShapes, int numVertPos){ /* for(int i = 0; i < vertIds.size(); i++) vertices[i].shapeKeyOffsets = new Vector3[numShapes]; @@ -352,9 +352,17 @@ namespace vb01{ int shapeKeyStartLine = 0; for(int i = 0; i < numShapes; i++){ - shapeKeys[i] = meshData[shapeKeyStartLine]; - Vector3 *shapeKeyVertPos = new Vector3[numVertPos]; + string line = meshData[shapeKeyStartLine]; + int colonId = line.find_first_of(':'); + Mesh::ShapeKey shapeKey; + string data[2]; + getLineData(line.substr(colonId + 1), data, 2); + shapeKey.minValue = atof(data[0].c_str()); + shapeKey.value = shapeKey.minValue; + shapeKey.maxValue = atof(data[1].c_str()); + shapeKeys[i] = shapeKey; + Vector3 *shapeKeyVertPos = new Vector3[numVertPos]; for(int j = 0; j < numVertPos; j++){ string line = meshData[shapeKeyStartLine + 1 + j]; string dataLine[3]; @@ -407,7 +415,7 @@ namespace vb01{ Mesh::Vertex *vertices = new Mesh::Vertex[numFaces * numVertsPerFace]; u32 *indices = new u32[numFaces * numVertsPerFace]; string *groups = new string[numBones]; - string *shapeKeys = new string[numShapes]; + Mesh::ShapeKey *shapeKeys = new Mesh::ShapeKey[numShapes]; vector meshSubData = vector(meshData.begin() + vertexStartLine, meshData.begin() + vertexStartLine + numVertices); readVertices(vertPos, vertNorm, vertWeights, meshSubData, numBones); diff --git a/vbModelReader.h b/vbModelReader.h index d9fd032..5b55b1b 100644 --- a/vbModelReader.h +++ b/vbModelReader.h @@ -30,7 +30,7 @@ namespace vb01{ void readVertices(std::vector&, std::vector&, std::vector&, std::vector&, int); void readFaces(std::vector&, std::vector&, std::vector&, std::vector&, std::vector&, int, Mesh::Vertex*, u32*); void readVertexGroups(std::string*, std::vector&, int); - void readShapeKeys(Mesh::Vertex*, std::vector&, std::vector&, std::string*, int, int); + void readShapeKeys(Mesh::Vertex*, std::vector&, std::vector&, Mesh::ShapeKey*, int, int); void buildMesh(std::vector&); Mesh* readMeshes(std::vector&); void readLights(std::vector&); diff --git a/vbModelReaderTest.cpp b/vbModelReaderTest.cpp index f127a3b..334dbaf 100644 --- a/vbModelReaderTest.cpp +++ b/vbModelReaderTest.cpp @@ -143,6 +143,12 @@ namespace vb01{ meshData.push_back(to_string(i) + " " + uvString + tanString + biTanString); } meshData.push_back("shapeKeys: 0"); + meshData.push_back("Key 1: 0 1"); + for(int i = 0; i < vertPos.size(); i++){ + Vector3 pos = vertPos[i]; + string posString = to_string(pos.x) + " " + to_string(pos.y) + " " + to_string(pos.z) + " "; + meshData.push_back(posString); + } meshData.push_back("animations: 0"); }