Implement danglymeshes

This commit is contained in:
Vsevolod Kremianskii 2021-05-03 18:37:38 +07:00
parent 2212f0ff00
commit 1751023903
13 changed files with 126 additions and 6 deletions

View file

@ -260,9 +260,11 @@ void ActionExecutor::advanceCreatureOnPath(const shared_ptr<Creature> &creature,
} else if (_game->module()->area()->moveCreatureTowards(creature, dest, run, dt)) {
creature->setMovementType(run ? Creature::MovementType::Run : Creature::MovementType::Walk);
creature->setAppliedForce(glm::vec3(glm::normalize(glm::vec2(dest - origin)), 0.0f));
} else {
creature->setMovementType(Creature::MovementType::None);
creature->setAppliedForce(glm::vec3(0.0f));
}
}

View file

@ -638,6 +638,12 @@ void Creature::getOffhandDamage(int &min, int &max) const {
getWeaponDamage(InventorySlot::leftWeapon, min, max);
}
void Creature::setAppliedForce(glm::vec3 force) {
if (_sceneNode && _sceneNode->type() == SceneNodeType::Model) {
static_pointer_cast<ModelSceneNode>(_sceneNode)->setAppliedForce(force);
}
}
} // namespace game
} // namespace reone

View file

@ -196,10 +196,16 @@ public:
void getMainHandDamage(int &min, int &max) const;
void getOffhandDamage(int &min, int &max) const;
void setAttackTarget(std::shared_ptr<SpatialObject> target) { _combat.attackTarget = move(target); }
void setAttackTarget(std::shared_ptr<SpatialObject> target) { _combat.attackTarget = std::move(target); }
// END Combat
// Physics
void setAppliedForce(glm::vec3 force);
// END Physics
// Scripts
void runSpawnScript();

View file

@ -165,9 +165,11 @@ void Player::update(float dt) {
if (_area->moveCreature(partyLeader, dir, true, dt)) {
partyLeader->setMovementType(Creature::MovementType::Run);
partyLeader->setAppliedForce(glm::vec3(dir, 0.0f));
}
} else if (actions.isEmpty()) {
partyLeader->setMovementType(Creature::MovementType::None);
partyLeader->setAppliedForce(glm::vec3(0.0f));
}
}

View file

@ -1088,18 +1088,17 @@ void MdlReader::readDanglymesh(ModelNode &node) {
float period = readFloat();
ignore(4); // unknown
node._danglymesh->displacement = displacement;
node._danglymesh->displacement = 0.5f * displacement; // displacement is allegedly 1/2 meters per unit
node._danglymesh->tightness = tightness;
node._danglymesh->period = period;
size_t pos = tell();
seek(kMdlDataOffset + constraintArrayDef.offset);
for (uint32_t i = 0; i < constraintArrayDef.count; ++i) {
float number = readFloat();
float multiplier = readFloat();
vector<float> positionValues(readFloatArray(3));
ModelNode::DanglymeshConstraint constraint;
constraint.number = number;
constraint.multiplier = glm::clamp(multiplier / 255.0f, 0.0f, 1.0f);
constraint.position = glm::make_vec3(&positionValues[0]);
node._danglymesh->constraints.push_back(move(constraint));
}

View file

@ -69,7 +69,7 @@ public:
};
struct DanglymeshConstraint {
float number { 0.0f };
float multiplier { 0.0f };
glm::vec3 position { 0.0f };
};

View file

@ -42,6 +42,7 @@ static constexpr int kBindingPointIndexLighting = 3;
static constexpr int kBindingPointIndexSkeletal = 4;
static constexpr int kBindingPointIndexParticles = 5;
static constexpr int kBindingPointIndexGrass = 6;
static constexpr int kBindingPointIndexDanglymesh = 7;
static constexpr GLchar kShaderBaseHeader[] = R"END(
#version 330
@ -60,7 +61,11 @@ const int FEATURE_PARTICLES = 0x400;
const int FEATURE_WATER = 0x800;
const int FEATURE_HDR = 0x1000;
const int FEATURE_CUSTOMMAT = 0x2000;
const int FEATURE_BLUR = 0x4000;
const int FEATURE_TEXT = 0x8000;
const int FEATURE_GRASS = 0x10000;
const int FEATURE_FOG = 0x20000;
const int FEATURE_DANGLYMESH = 0x40000;
const int NUM_CUBE_FACES = 6;
const int MAX_BONES = 128;
@ -68,6 +73,7 @@ const int MAX_LIGHTS = 16;
const int MAX_PARTICLES = 32;
const int MAX_CHARS = 128;
const int MAX_GRASS_CLUSTERS = 256;
const int MAX_DANGLYMESH_CONSTRAINTS = 512;
const float PI = 3.14159265359;
const float GAMMA = 2.2;
@ -182,6 +188,12 @@ layout(std140) uniform Grass {
GrassCluster uGrassClusters[MAX_GRASS_CLUSTERS];
};
layout(std140) uniform Danglymesh {
vec4 uDanglymeshStride;
float uDanglymeshDisplacement;
vec4 uDanglymeshConstraints[MAX_DANGLYMESH_CONSTRAINTS];
};
bool isFeatureEnabled(int flag) {
return (uFeatureMask & flag) != 0;
}
@ -508,6 +520,12 @@ void main() {
} else {
P = vec4(aPosition, 1.0);
if (isFeatureEnabled(FEATURE_DANGLYMESH)) {
vec3 maxStride = vec3(uDanglymeshDisplacement * uDanglymeshConstraints[gl_VertexID / 4][gl_VertexID % 4]);
vec3 stride = clamp(uDanglymeshStride.xyz, -maxStride, maxStride);
P += vec4(stride, 0.0);
}
}
fragPosition = vec3(uGeneral.model * P);
@ -1192,6 +1210,7 @@ void Shaders::init() {
glGenBuffers(1, &_uboSkeletal);
glGenBuffers(1, &_uboParticles);
glGenBuffers(1, &_uboGrass);
glGenBuffers(1, &_uboDanglymesh);
for (auto &program : _programs) {
glUseProgram(program.second);
@ -1209,6 +1228,7 @@ void Shaders::init() {
_defaultUniforms.skeletal = make_shared<SkeletalUniforms>();
_defaultUniforms.particles = make_shared<ParticlesUniforms>();
_defaultUniforms.grass = make_shared<GrassUniforms>();
_defaultUniforms.danglymesh = make_shared<DanglymeshUniforms>();
_inited = true;
}
@ -1260,6 +1280,7 @@ void Shaders::initUBOs() {
static SkeletalUniforms defaultsSkeletal;
static ParticlesUniforms defaultsParticles;
static GrassUniforms defaultsGrass;
static DanglymeshUniforms defaultsDanglymesh;
initUBO("Combined", kBindingPointIndexCombined, _uboCombined, defaultsCombined, offsetof(ShaderUniforms, text));
initUBO("Text", kBindingPointIndexText, _uboText, defaultsText);
@ -1267,6 +1288,7 @@ void Shaders::initUBOs() {
initUBO("Skeletal", kBindingPointIndexSkeletal, _uboSkeletal, defaultsSkeletal);
initUBO("Particles", kBindingPointIndexParticles, _uboParticles, defaultsParticles);
initUBO("Grass", kBindingPointIndexGrass, _uboGrass, defaultsGrass);
initUBO("Danglymesh", kBindingPointIndexDanglymesh, _uboDanglymesh, defaultsDanglymesh);
}
template <class T>
@ -1324,6 +1346,10 @@ void Shaders::deinit() {
glDeleteBuffers(1, &_uboGrass);
_uboGrass = 0;
}
if (_uboDanglymesh) {
glDeleteBuffers(1, &_uboDanglymesh);
_uboDanglymesh = 0;
}
// Delete programs
for (auto &pair : _programs) {
@ -1383,6 +1409,10 @@ void Shaders::setUniforms(const ShaderUniforms &uniforms) {
glBindBufferBase(GL_UNIFORM_BUFFER, kBindingPointIndexGrass, _uboGrass);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(GrassUniforms), uniforms.grass.get());
}
if (uniforms.combined.featureMask & UniformFeatureFlags::danglymesh) {
glBindBufferBase(GL_UNIFORM_BUFFER, kBindingPointIndexDanglymesh, _uboDanglymesh);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(DanglymeshUniforms), uniforms.danglymesh.get());
}
}
void Shaders::setUniform(const string &name, const glm::mat4 &m) {

View file

@ -72,6 +72,7 @@ struct UniformFeatureFlags {
static constexpr int text = 0x8000;
static constexpr int grass = 0x10000;
static constexpr int fog = 0x20000;
static constexpr int danglymesh = 0x40000;
};
struct ShaderGeneral {
@ -187,6 +188,13 @@ struct TextUniforms {
ShaderCharacter chars[kMaxCharacters];
};
struct DanglymeshUniforms {
glm::vec4 stride { 0.0f };
float displacement { 0.0f };
char padding[12];
glm::vec4 constraints[kMaxDanglymeshConstraints];
};
struct ShaderUniforms {
CombinedUniforms combined;
@ -195,6 +203,7 @@ struct ShaderUniforms {
std::shared_ptr<SkeletalUniforms> skeletal;
std::shared_ptr<ParticlesUniforms> particles;
std::shared_ptr<GrassUniforms> grass;
std::shared_ptr<DanglymeshUniforms> danglymesh;
};
class Shaders : boost::noncopyable {
@ -248,6 +257,7 @@ private:
uint32_t _uboSkeletal { 0 };
uint32_t _uboParticles { 0 };
uint32_t _uboGrass { 0 };
uint32_t _uboDanglymesh { 0 };
// END UBO

View file

@ -29,6 +29,7 @@ constexpr int kMaxLights = 16;
constexpr int kMaxParticles = 32;
constexpr int kMaxCharacters = 128;
constexpr int kMaxGrassClusters = 256;
constexpr int kMaxDanglymeshConstraints = 512;
enum class PixelFormat {
Grayscale,

View file

@ -128,7 +128,36 @@ void ModelNodeSceneNode::update(float dt) {
}
}
}
// Danglymesh animation
shared_ptr<ModelNode::Danglymesh> danglymesh(_modelNode->danglymesh());
if (danglymesh) {
bool forceApplied = glm::length2(_danglymeshAnimation.force) > 0.0f;
if (forceApplied) {
// When force is applied, stride in the opposite direction from the applied force
glm::vec3 strideDir(-_danglymeshAnimation.force);
glm::vec3 maxStride(danglymesh->displacement);
_danglymeshAnimation.stride = glm::clamp(_danglymeshAnimation.stride + danglymesh->period * strideDir * dt, -maxStride, maxStride);
} else {
// When force is not applied, gradually nullify stride
float strideMag2 = glm::length2(_danglymeshAnimation.stride);
if (strideMag2 > 0.0f) {
glm::vec3 strideDir(-_danglymeshAnimation.stride);
_danglymeshAnimation.stride += danglymesh->period * strideDir * dt;
if ((strideDir.x > 0.0f && _danglymeshAnimation.stride.x > 0.0f) || (strideDir.x < 0.0f && _danglymeshAnimation.stride.x < 0.0f)) {
_danglymeshAnimation.stride.x = 0.0f;
}
if ((strideDir.y > 0.0f && _danglymeshAnimation.stride.y > 0.0f) || (strideDir.y < 0.0f && _danglymeshAnimation.stride.y < 0.0f)) {
_danglymeshAnimation.stride.y = 0.0f;
}
if ((strideDir.z > 0.0f && _danglymeshAnimation.stride.z > 0.0f) || (strideDir.z < 0.0f && _danglymeshAnimation.stride.z < 0.0f)) {
_danglymeshAnimation.stride.z = 0.0f;
}
}
}
}
}
SceneNode::update(dt);
}
@ -315,6 +344,17 @@ void ModelNodeSceneNode::drawSingle(bool shadowPass) {
uniforms.combined.general.fogFar = _sceneGraph->fogFar();
uniforms.combined.general.fogColor = glm::vec4(_sceneGraph->fogColor(), 1.0f);
}
shared_ptr<ModelNode::Danglymesh> danglymesh(_modelNode->danglymesh());
if (danglymesh) {
uniforms.combined.featureMask |= UniformFeatureFlags::danglymesh;
uniforms.danglymesh->stride = glm::vec4(_danglymeshAnimation.stride, 0.0f);
uniforms.danglymesh->displacement = danglymesh->displacement;
size_t i = 0;
for (i = 0; i < danglymesh->constraints.size(); ++i) {
uniforms.danglymesh->constraints[i / 4][i % 4] = danglymesh->constraints[i].multiplier;
}
}
}
Shaders::instance().activate(program, uniforms);
@ -375,6 +415,13 @@ bool ModelNodeSceneNode::isLightingEnabled() const {
return true;
}
void ModelNodeSceneNode::setAppliedForce(glm::vec3 force) {
if (_modelNode->danglymesh()) {
// Convert force from world to object space
_danglymeshAnimation.force = _absoluteTransformInv * glm::vec4(force, 0.0f);
}
}
glm::vec3 ModelNodeSceneNode::getOrigin() const {
return _absoluteTransform * glm::vec4(_modelNode->getCenterOfAABB(), 1.0f);
}

View file

@ -37,6 +37,8 @@ public:
void drawSingle(bool shadowPass);
void setAppliedForce(glm::vec3 force);
bool shouldRender() const;
bool shouldCastShadows() const;
@ -62,6 +64,11 @@ private:
std::shared_ptr<render::Texture> bumpmap;
} _textures;
struct DanglymeshAnimation {
glm::vec3 force { 0.0f }; /**< net force applied to this scene node */
glm::vec3 stride { 0.0f }; /**< how far have vertices traveled from the rest position? */
} _danglymeshAnimation;
const ModelSceneNode *_modelSceneNode;
const render::ModelNode *_modelNode;

View file

@ -370,6 +370,15 @@ void ModelSceneNode::signalEvent(const string &name) {
}
}
void ModelSceneNode::setAppliedForce(glm::vec3 force) {
for (auto &nodePair : _modelNodeByIndex) {
nodePair.second->setAppliedForce(force);
}
for (auto &attached : _attachedModels) {
attached.second->setAppliedForce(force);
}
}
} // namespace scene
} // namespace reone

View file

@ -50,6 +50,7 @@ public:
void refreshAABB();
void signalEvent(const std::string &name);
void setAppliedForce(glm::vec3 force);
bool isCulledOut() const { return _culledOut; }