diff options
Diffstat (limited to 'Source/Graphics/skeleton.cpp')
-rw-r--r-- | Source/Graphics/skeleton.cpp | 1603 |
1 files changed, 1603 insertions, 0 deletions
diff --git a/Source/Graphics/skeleton.cpp b/Source/Graphics/skeleton.cpp new file mode 100644 index 00000000..8428f909 --- /dev/null +++ b/Source/Graphics/skeleton.cpp @@ -0,0 +1,1603 @@ +//----------------------------------------------------------------------------- +// Name: skeleton.cpp +// Developer: Wolfire Games LLC +// Description: +// License: Read below +//----------------------------------------------------------------------------- +// +// +// Copyright 2022 Wolfire Games LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//----------------------------------------------------------------------------- +#include "skeleton.h" + +#include <Internal/filesystem.h> +#include <Internal/timer.h> +#include <Internal/snprintf.h> + +#include <Graphics/model.h> +#include <Graphics/models.h> +#include <Graphics/camera.h> +#include <Graphics/pxdebugdraw.h> + +#include <Physics/bulletworld.h> +#include <Physics/bulletobject.h> +#include <Physics/physics.h> + +#include <Utility/compiler_macros.h> +#include <Utility/assert.h> + +#include <Math/vec3math.h> +#include <Math/enginemath.h> + +#include <Asset/Asset/skeletonasset.h> +#include <Scripting/angelscript/ascollisions.h> +#include <GUI/gui.h> +#include <Logging/logdata.h> +#include <Main/engine.h> + +#include <btBulletDynamicsCommon.h> + +#include <cmath> +#include <set> + +struct SelectedJoint { + std::vector<int> bones; +}; + +static std::string LabelFromBone(int bone, const Skeleton::SimpleIKBoneMap &simple_ik_bones, const std::vector<int> &parents) { + for(Skeleton::SimpleIKBoneMap::const_iterator iter = simple_ik_bones.begin(); iter != simple_ik_bones.end(); ++iter){ + const SimpleIKBone &ik_bone = iter->second; + const char *label = iter->first.c_str(); + int curr_bone = ik_bone.bone_id; + for(int i=0; i<ik_bone.chain_length; ++i){ + if(curr_bone == bone){ + const int BUF_SIZE = 256; + char temp[BUF_SIZE]; + snprintf(temp, BUF_SIZE, "%s%d", label, i); + return std::string(temp); + } + curr_bone = parents[curr_bone]; + if(curr_bone == -1){ + break; + } + } + } + return "unknown_bone"; +} + +void Skeleton::CreateHinge(const SelectedJoint &selected_joint, const vec3 &force_axis, float *initial_angle_ptr) { + int shared_point = -1; + Bone *joint_bones[2]; + joint_bones[0] = &bones[selected_joint.bones[0]]; + joint_bones[1] = &bones[selected_joint.bones[1]]; + for(int i=0; i<2; i++){ + for(int j=0; j<2; j++) { + if(joint_bones[0]->points[i] == joint_bones[1]->points[j]){ + shared_point = joint_bones[0]->points[i]; + } + } + } + if(shared_point == -1) { + std::vector<int> point_ids(2, -1); + for(unsigned i=0; i<points.size(); ++i){ + if(joint_bones[0]->points[0] == (int)i){ + point_ids[0] = i; + } + if(joint_bones[0]->points[1] == (int)i){ + point_ids[1] = i; + } + } + if(!parents.empty() && parents[point_ids[0]] == point_ids[1]){ + shared_point = point_ids[1]; + } else { + shared_point = point_ids[0]; + } + } + + std::vector<BulletObject*> selected_bullet_bones(2); + GetPhysicsObjectsFromSelectedJoint(selected_bullet_bones, selected_joint); + DeleteJointOnBones(selected_bullet_bones); + + vec3 anchor = points[shared_point]; + vec3 axis = force_axis; + if(axis == vec3(0.0f)) { + axis = normalize(ActiveCameras::Get()->GetPos() - anchor); + } + + btTypedConstraint* bt_joint = bullet_world->AddHingeJoint(selected_bullet_bones[0], selected_bullet_bones[1], anchor, axis, 0.0f, (float)PI*0.5f, initial_angle_ptr); + + btTypedConstraint* fixed_joint = + bullet_world->AddFixedJoint(selected_bullet_bones[0], + selected_bullet_bones[1], + anchor); + + physics_joints.resize(physics_joints.size()+1); + physics_joints.back().type = HINGE_JOINT; + physics_joints.back().anchor = anchor; + physics_joints.back().bt_joint = bt_joint; + physics_joints.back().fixed_joint = fixed_joint; + physics_joints.back().bt_bone[0] = selected_bullet_bones[0]; + physics_joints.back().bt_bone[1] = selected_bullet_bones[1]; + physics_joints.back().stop_angle[0] = 0.0f; + physics_joints.back().stop_angle[1] = (float)PI*0.5f; + + mat4 rotation1 = selected_bullet_bones[0]->GetRotation(); + mat4 rotation2 = selected_bullet_bones[1]->GetRotation(); + + physics_joints.back().initial_axis = axis; +} + +void Skeleton::SetGravity( bool enable ) { + if(enable){ + for(unsigned i=0; i<physics_bones.size(); i++){ + if(!physics_bones[i].bullet_object){ + continue; + } + physics_bones[i].bullet_object->SetGravity(true); + //physics_bones[i].bullet_object->SetDamping(0.0f); + } + } else { + for(unsigned i=0; i<physics_bones.size(); i++){ + if(!physics_bones[i].bullet_object){ + continue; + } + physics_bones[i].bullet_object->SetGravity(true); + //physics_bones[i].bullet_object->SetDamping(1.0f); + } + } +} + +void Skeleton::UnlinkFromBulletWorld() { + for(unsigned i=0; i<physics_bones.size(); i++){ + if(physics_bones[i].bullet_object){ + bullet_world->UnlinkObject(physics_bones[i].bullet_object); + } + } + for(unsigned i=0; i<physics_joints.size(); i++){ + PhysicsJoint &joint = physics_joints[i]; + if(joint.bt_joint){ + bullet_world->UnlinkConstraint(joint.bt_joint); + } + if(joint.fixed_joint && joint.fixed_joint_enabled){ + bullet_world->UnlinkConstraint(joint.fixed_joint); + } + } + for(unsigned i=0; i<null_constraints.size(); i++){ + bullet_world->UnlinkConstraint(null_constraints[i]); + } +} + +void Skeleton::LinkToBulletWorld() { + int num = 0; + for(unsigned i=0; i<physics_bones.size(); i++){ + if(!physics_bones[i].bullet_object || fixed_obj[i]){ + continue; + } + bullet_world->LinkObject(physics_bones[i].bullet_object); + ++num; + } + num = 0; + int num2 = 0; + int num3 = 0; + for(unsigned i=0; i<physics_joints.size(); i++){ + PhysicsJoint &joint = physics_joints[i]; + if(joint.bt_joint){ + bullet_world->LinkConstraint(joint.bt_joint); + if(joint.type == FIXED_JOINT){ + ++num3; + } else { + ++num; + } + } + if(joint.fixed_joint && joint.fixed_joint_enabled){ + bullet_world->LinkConstraint(joint.fixed_joint); + ++num2; + } + } + num = 0; + for(unsigned i=0; i<null_constraints.size(); i++){ + bullet_world->LinkConstraint(null_constraints[i]); + ++num; + } +} + +void Skeleton::AddModifiedSphere(btCompoundShape* compound_shape, const vec3 &scale, const vec3 &offset, const quaternion &rotation, const mat4 &capsule_transform, const vec3& skel_offset, float body_scale) { + quaternion quat(rotation[1], rotation[3], -rotation[2], rotation[0]); + float radius = (scale[0] + scale[1] + scale[2])/3.0f*body_scale; + vec3 modified_scale = scale / radius*body_scale; + modified_scale = vec3(modified_scale[0],modified_scale[2],modified_scale[1]); + btVector3 position(0.0f,0.0f,0.0f); + btMultiSphereShape* sphere_shape = new btMultiSphereShape(&position, &radius, 1); + child_shapes.push_back(sphere_shape); + sphere_shape->setLocalScaling(btVector3(modified_scale[0], modified_scale[1], modified_scale[2])); + + quat = invert(QuaternionFromMat4(capsule_transform)) * quat; + + btTransform trans; + trans.setIdentity(); + trans.setRotation(btQuaternion(quat.entries[0],quat.entries[1],quat.entries[2],quat.entries[3])); + vec3 test = invert(capsule_transform) * ((vec3(offset[0], offset[2], -offset[1]) - skel_offset)*body_scale); + trans.setOrigin(btVector3(test[0], test[1], test[2])); + compound_shape->addChildShape(trans,sphere_shape); + + /*mat4 offset_mat; + offset_mat.SetTranslationPart(test); + mat4 transform = capsule_transform * offset_mat * Mat4FromQuaternion(quat); + transform.AddTranslation(vec3(0,20,0)); + DebugDraw::Instance()->AddTransformedWireScaledSphere(transform, radius, modified_scale, vec4(1.0f), _persistent);*/ +} + +void Skeleton::AddModifiedCapsule(btCompoundShape* compound_shape, const vec3 &scale, const vec3 &offset, const quaternion &rotation, const mat4 &capsule_transform, const vec3& skel_offset, float body_scale) { + quaternion quat(rotation[1], rotation[3], -rotation[2], rotation[0]); + float radius[2] = {scale[0]*body_scale, scale[0]*body_scale}; + btVector3 position[2]; + position[0] = btVector3(0.0f,-scale[2]*body_scale,0.0f); + position[1] = btVector3(0.0f,scale[2]*body_scale,0.0f); + btMultiSphereShape* sphere_shape = new btMultiSphereShape(position, radius, 2); + child_shapes.push_back(sphere_shape); + + quat = invert(QuaternionFromMat4(capsule_transform)) * quat; + + btTransform trans; + trans.setIdentity(); + trans.setRotation(btQuaternion(quat.entries[0],quat.entries[1],quat.entries[2],quat.entries[3])); + vec3 test = invert(capsule_transform) * ((vec3(offset[0], offset[2], -offset[1]) - skel_offset)*body_scale); + trans.setOrigin(btVector3(test[0], test[1], test[2])); + compound_shape->addChildShape(trans,sphere_shape); + + vec3 vec_pos[2]; + vec_pos[0] = vec3(position[0][0], position[0][1], position[0][2]); + vec_pos[1] = vec3(position[1][0], position[1][1], position[1][2]); + vec_pos[0] = test + quat * vec_pos[0]; + vec_pos[1] = test + quat * vec_pos[1]; + + /*for(int i=0; i<2; ++i){ + DebugDraw::Instance()->AddWireSphere(capsule_transform * vec_pos[i] + vec3(0,20,0), radius[i], vec4(1.0f), _persistent); + }*/ +} + +void Skeleton::AddModifiedBox(btCompoundShape* compound_shape, const vec3 &scale, const vec3 &offset, const quaternion &rotation, const mat4 &capsule_transform, const vec3& skel_offset, float body_scale) { + quaternion quat(rotation[1], rotation[3], -rotation[2], rotation[0]); + vec3 new_scale(scale[0]*body_scale,scale[2]*body_scale,scale[1]*body_scale); + btBoxShape* box_shape = new btBoxShape(btVector3(new_scale[0], new_scale[1], new_scale[2])); + child_shapes.push_back(box_shape); + + quat = invert(QuaternionFromMat4(capsule_transform)) * quat; + + btTransform trans; + trans.setIdentity(); + trans.setRotation(btQuaternion(quat.entries[0],quat.entries[1],quat.entries[2],quat.entries[3])); + vec3 test = invert(capsule_transform) * ((vec3(offset[0], offset[2], -offset[1]) - skel_offset)*body_scale); + trans.setOrigin(btVector3(test[0], test[1], test[2])); + compound_shape->addChildShape(trans,box_shape); +} + +namespace FZX { + const char* key_words[] = {"l","r","m","capsule", "box", "sphere","eartip","earbase","head","chest","upperarm","forearm","abdomen","hip","thigh","shin","foot","tail1","tail2","tail3","tail4","tail5","tail6"}; + enum {left,right,mirrored,capsule,box,sphere,eartip,earbase,head,chest,upperarm,forearm,abdomen,hip,thigh,shin,foot,tail1,tail2,tail3,tail4,tail5,tail6}; + const int num_key_words = sizeof(key_words)/sizeof(key_words[0]); +} + +static int GetChainMember(const std::vector<int> &parents, int tip, int depth){ + int bone = tip; + for(int i=0; i<depth; ++i){ + bone = parents[bone]; + } + return bone; +} + +enum ShapeType {NONE, SPHERE, CAPSULE, BOX}; +struct CustomShape { + ShapeType shape; + quaternion rotation; + vec3 scale; + vec3 location; + CustomShape():shape(NONE){} +}; +const int MAX_SHAPES_PER_BONE = 5; + +void GetShapeCOMAndVolume(const CustomShape &custom_shape, const vec3& skel_offset, float body_scale, vec3 *com, float *volume) { + const vec3 &offset = custom_shape.location; + *com = (vec3(offset[0], offset[2], -offset[1]) - skel_offset) * body_scale; + switch(custom_shape.shape) { + case CAPSULE: { + float radius = custom_shape.scale[0]*body_scale; + float sphere_volume = 4.0f/3.0f*3.1417f*pow(radius,3); + float cylinder_volume = 3.1417f*pow(radius,2)*custom_shape.scale[2]*2.0f; + *volume = sphere_volume + cylinder_volume; + break;} + case SPHERE: { + vec3 scale = custom_shape.scale * body_scale; + *volume = 4.0f/3.0f*3.1417f*scale[0]*scale[1]*scale[2]; + break;} + case BOX: { + vec3 scale = custom_shape.scale * body_scale * 2.0f; + *volume = scale[0]*scale[1]*scale[2]; + break;} + case NONE: + LOGW << "Requested volume of NONE shape" << std::endl; + break; + } +} + +static void ShapesFromFZX(const FZXAssetRef& fzx_ref, + std::vector<CustomShape> *custom_shapes_ptr, + std::vector<int> *num_custom_shapes_ptr, + const Skeleton::SimpleIKBoneMap &simple_ik_bones, + const std::vector<int> &parents, + const std::vector<int> &symmetry) +{ + std::vector<CustomShape> &custom_shapes = *custom_shapes_ptr; + std::vector<int> &num_custom_shapes = *num_custom_shapes_ptr; + for(int i=0, len=fzx_ref->objects.size(); i<len; ++i){ + const FZXObject &object = fzx_ref->objects[i]; + // Parse label string + const std::string &label = object.label; + int last_space_index = 0; + int side = -1; + int bodypart = -1; + int shape = -1; + bool mirrored = false; + for(int j=0, len=label.size()+1; j<len; ++j){ + if(label[j] == ' ' || label[j] == '\0'){ + std::string sub_str_buf; + sub_str_buf.resize(j-last_space_index); + int count = 0; + for(int k=last_space_index; k<j; ++k){ + char c = label[k]; + if(c >= 'A' && c <= 'Z'){ + c -= ('A' - 'a'); + } + sub_str_buf[count] = c; + ++count; + } + for(int k=0; k<FZX::num_key_words; ++k){ + bool word_match = (strcmp(FZX::key_words[k], sub_str_buf.c_str())==0); + if(word_match){ + switch(k){ + case FZX::left: + case FZX::right: + side = k; break; + case FZX::capsule: + case FZX::box: + case FZX::sphere: + shape = k; break; + case FZX::mirrored: + mirrored = true; break; + default: + bodypart = k; break; + }; + } + } + last_space_index = j+1; + } + } + const bool print_info = false; + if(print_info){ + if(side != -1){ + LOGI << "Side: " << FZX::key_words[side] << std::endl; + } else { + LOGI << "Side: undefined" << std::endl; + } + if(shape != -1){ + LOGI << "Shape: " << FZX::key_words[shape] << std::endl; + } else { + LOGI << "Shape: undefined" << std::endl; + } + if(bodypart != -1){ + LOGI << "Bodypart: " << FZX::key_words[bodypart] << std::endl; + } else { + LOGI << "Bodypart: undefined" << std::endl; + } + } + if(shape != -1 && bodypart != -1){ + int bone = -1; + switch(bodypart){ + case FZX::eartip: + if(side == FZX::right){ + bone = simple_ik_bones.find("rightear")->second.bone_id; + } else if(side == FZX::left){ + bone = simple_ik_bones.find("leftear")->second.bone_id; + } + break; + case FZX::earbase: + if(side == FZX::right){ + bone = GetChainMember(parents, simple_ik_bones.find("rightear")->second.bone_id, 1); + } else if(side == FZX::left){ + bone = GetChainMember(parents, simple_ik_bones.find("leftear")->second.bone_id, 1); + } + break; + case FZX::upperarm: + if(side == FZX::right){ + bone = GetChainMember(parents, simple_ik_bones.find("rightarm")->second.bone_id, 5); + } else if(side == FZX::left){ + bone = GetChainMember(parents, simple_ik_bones.find("leftarm")->second.bone_id, 5); + } + break; + case FZX::forearm: + if(side == FZX::right){ + bone = GetChainMember(parents, simple_ik_bones.find("rightarm")->second.bone_id, 3); + } else if(side == FZX::left){ + bone = GetChainMember(parents, simple_ik_bones.find("leftarm")->second.bone_id, 3); + } + break; + case FZX::head: + bone = simple_ik_bones.find("head")->second.bone_id; + break; + case FZX::chest: + bone = simple_ik_bones.find("torso")->second.bone_id; + break; + case FZX::abdomen: + bone = GetChainMember(parents, simple_ik_bones.find("torso")->second.bone_id, 1); + break; + case FZX::hip: + bone = GetChainMember(parents, simple_ik_bones.find("torso")->second.bone_id, 2); + break; + case FZX::tail1: + bone = GetChainMember(parents, simple_ik_bones.find("tail")->second.bone_id, simple_ik_bones.find("tail")->second.chain_length-1); + break; + case FZX::tail2: + bone = GetChainMember(parents, simple_ik_bones.find("tail")->second.bone_id, simple_ik_bones.find("tail")->second.chain_length-2); + break; + case FZX::tail3: + bone = GetChainMember(parents, simple_ik_bones.find("tail")->second.bone_id, simple_ik_bones.find("tail")->second.chain_length-3); + break; + case FZX::tail4: + bone = GetChainMember(parents, simple_ik_bones.find("tail")->second.bone_id, simple_ik_bones.find("tail")->second.chain_length-4); + break; + case FZX::tail5: + bone = GetChainMember(parents, simple_ik_bones.find("tail")->second.bone_id, simple_ik_bones.find("tail")->second.chain_length-5); + break; + case FZX::tail6: + bone = GetChainMember(parents, simple_ik_bones.find("tail")->second.bone_id, simple_ik_bones.find("tail")->second.chain_length-6); + break; + case FZX::thigh: + if(side == FZX::right){ + bone = GetChainMember(parents, simple_ik_bones.find("right_leg")->second.bone_id, 5); + } else if(side == FZX::left){ + bone = GetChainMember(parents, simple_ik_bones.find("left_leg")->second.bone_id, 5); + } + break; + case FZX::shin: + if(side == FZX::right){ + bone = GetChainMember(parents, simple_ik_bones.find("right_leg")->second.bone_id, 3); + } else if(side == FZX::left){ + bone = GetChainMember(parents, simple_ik_bones.find("left_leg")->second.bone_id, 3); + } + break; + case FZX::foot: + if(side == FZX::right){ + bone = GetChainMember(parents, simple_ik_bones.find("right_leg")->second.bone_id, 1); + } else if(side == FZX::left){ + bone = GetChainMember(parents, simple_ik_bones.find("left_leg")->second.bone_id, 1); + } + break; + } + if(bone != -1){ + CustomShape &custom_shape = custom_shapes[bone*MAX_SHAPES_PER_BONE+num_custom_shapes[bone]]; + switch(shape){ + case FZX::sphere: + custom_shape.shape = SPHERE; + break; + case FZX::capsule: + custom_shape.shape = CAPSULE; + break; + case FZX::box: + custom_shape.shape = BOX; + break; + } + custom_shape.location = object.location; + custom_shape.scale = object.scale; + custom_shape.rotation = object.rotation; + int symmetry_bone = symmetry[bone]; + if(mirrored && symmetry_bone != -1){ + CustomShape &sym_custom_shape = custom_shapes[symmetry_bone*MAX_SHAPES_PER_BONE+num_custom_shapes[symmetry_bone]]; + sym_custom_shape = custom_shape; + sym_custom_shape.location[0] *= -1.0f; + sym_custom_shape.rotation[2] *= -1.0f; + sym_custom_shape.rotation[3] *= -1.0f; + ++num_custom_shapes[symmetry_bone]; + } + ++num_custom_shapes[bone]; + } + } + } +} + +void Skeleton::CreatePhysicsSkeleton(float scale, const FZXAssetRef& fzx_ref) { + // Parse FZX file to get collision shapes for each bone + std::vector<int> num_custom_shapes(bones.size(), 0); + std::vector<CustomShape> custom_shapes; + custom_shapes.resize(bones.size()*MAX_SHAPES_PER_BONE); + if(fzx_ref.valid()){ + ShapesFromFZX(fzx_ref, &custom_shapes, &num_custom_shapes, simple_ik_bones, parents, skeleton_asset_ref->GetData().symmetry); + } + + std::map<int,std::vector<BulletObject*> > joints; + std::map<int,std::vector<BulletObject*> >::iterator iter; + for(unsigned i=0; i<bones.size(); i++){ + Bone* bone = &bones[i]; + vec3 bone_points[2]; + bone_points[0] = points[bone->points[0]]; + bone_points[1] = points[bone->points[1]]; + + //DebugDraw::Instance()->AddLine(bone_points[0]+vec3(0,20,0), bone_points[1]+vec3(0,20,0), vec4(1.0f), _persistent); + + vec3 old_midpoint = (bone_points[1] + bone_points[0])*0.5f; + + BulletObject* b_capsule = NULL; + if(!fzx_ref.valid()){ + b_capsule = bullet_world->CreateCapsule(bone_points[0], bone_points[1], 0.05f, physics_bones[i].physics_mass); + } else { + if(num_custom_shapes[i] == 0){ + b_capsule = bullet_world->CreateCapsule(bone_points[0], + bone_points[1], + 0.05f, + physics_bones[i].physics_mass, + BW_NO_DYNAMIC_COLLISIONS | BW_NO_STATIC_COLLISIONS); + b_capsule->SetVisibility(true); + } else { + mat4 capsule_transform = BulletWorld::GetCapsuleTransform(bone_points[0],bone_points[1]); + btCompoundShape* compound_shape = new btCompoundShape(); + SharedShapePtr shape(compound_shape); + float total_volume = 0.0f; + vec3 com; + for(int j=0; j<num_custom_shapes[i]; ++j){ + CustomShape custom_shape = custom_shapes[i*MAX_SHAPES_PER_BONE+j]; + vec3 shape_com; + float shape_volume; + GetShapeCOMAndVolume(custom_shape, skeleton_asset_ref->GetData().old_model_center, scale, &shape_com, &shape_volume); + com += shape_com * shape_volume; + total_volume += shape_volume; + } + com /= total_volume; + capsule_transform.SetTranslationPart(com); + for(int j=0; j<num_custom_shapes[i]; ++j){ + CustomShape custom_shape = custom_shapes[i*MAX_SHAPES_PER_BONE+j]; + switch (custom_shape.shape) { + case CAPSULE: + AddModifiedCapsule(compound_shape, custom_shape.scale, custom_shape.location, custom_shape.rotation, capsule_transform, skeleton_asset_ref->GetData().old_model_center, scale); + break; + + case SPHERE: + AddModifiedSphere(compound_shape, custom_shape.scale, custom_shape.location, custom_shape.rotation, capsule_transform, skeleton_asset_ref->GetData().old_model_center, scale); + break; + + case BOX: + AddModifiedBox(compound_shape, custom_shape.scale, custom_shape.location, custom_shape.rotation, capsule_transform, skeleton_asset_ref->GetData().old_model_center, scale); + break; + + case NONE: + // This should never happen, if it does something's gone wrong ShapesFromFZX + LOG_ASSERT_MSG(false, "Unhandled case of custom shape"); + break; + } + } + b_capsule = bullet_world->CreateRigidBody(shape, physics_bones[i].physics_mass); + b_capsule->SetVisibility(true); + b_capsule->SetTransform(capsule_transform); + } + } + + /*if(!fixed_obj[i] && has_verts_assigned[i]){ + LOGI < "Creating bone " << LabelFromBone(i, simple_ik_bones, parents) << " with mass " << physics_bones[i].physics_mass << std::endl; + }*/ + + b_capsule->SetGravity(false); + b_capsule->color = vec3(0.8f); + b_capsule->com_offset = invert(b_capsule->GetTransform()) * (old_midpoint) * -1.0f; + + PhysicsBone& physics_bone = physics_bones[i]; + physics_bone.bullet_object = b_capsule; + physics_bone.bone = i; + physics_bone.initial_position = b_capsule->GetPosition(); + physics_bone.initial_rotation = b_capsule->GetRotation(); + + BulletObject* object = b_capsule; + joints[bone->points[0]].push_back(object); + joints[bone->points[1]].push_back(object); + } + + int editor; + btTypedConstraint* bt_joint; + physics_joints.clear(); + for(iter = joints.begin(); iter != joints.end(); ++iter){ + editor = iter->first; + std::vector<BulletObject*> &shared_objects = iter->second; + for(unsigned i=0; i<shared_objects.size(); i++){ + for(unsigned j=i+1; j<shared_objects.size(); j++){ + bt_joint = bullet_world->AddBallJoint(shared_objects[i], + shared_objects[j], + points[editor]); + + physics_joints.resize(physics_joints.size()+1); + physics_joints.back().type = BALL_JOINT; + physics_joints.back().bt_joint = bt_joint; + physics_joints.back().bt_bone[0] = shared_objects[i]; + physics_joints.back().bt_bone[1] = shared_objects[j]; + } + } + } +} + +int Skeleton::GetAttachedBone( BulletObject* object ) { + for(unsigned i=0; i<physics_bones.size(); i++){ + if(physics_bones[i].bullet_object == object) { + return physics_bones[i].bone; + } + } + return -1; +} + +int Skeleton::Read( const std::string &path, float scale, float mass_scale, const FZXAssetRef &fzx_ref) { + //skeleton_asset_ref = SkeletonAssets::Instance()->ReturnRef(path); + skeleton_asset_ref = Engine::Instance()->GetAssetManager()->LoadSync<SkeletonAsset>(path); + const SkeletonFileData &data = skeleton_asset_ref->GetData(); + + RiggingStage rigging_stage = data.rigging_stage; + + // Load points + int num_points = data.points.size(); + for(int i=0; i<num_points; i++){ + points.push_back(data.points[i] * scale); + } + + // Load bones that connect points + int num_bones = data.bone_ends.size()/2; + bones.resize(num_bones); + for(int i=0; i<num_bones; i++){ + bones[i].points[0] = data.bone_ends[i*2+0]; + bones[i].points[1] = data.bone_ends[i*2+1]; + } + + // Load mass of each bone + if(!data.bone_mass.empty()){ + for(int i=0; i<num_bones; i++){ + bones[i].mass = data.bone_mass[i] * mass_scale * mass_scale * mass_scale; + } + } + + // Load center of mass for each bone + if(!data.bone_com.empty()){ + for(int i=0; i<num_bones; i++){ + bones[i].center_of_mass = data.bone_com[i] * scale; + } + } + + simple_ik_bones = data.simple_ik_bones; + + if(rigging_stage == _control_joints){ + PhysicsDispose(); + col_bullet_world.reset(new BulletWorld()); + col_bullet_world->Init(); + physics_bones.resize(bones.size()); + + has_verts_assigned.clear(); + has_verts_assigned.resize(physics_bones.size(), false); + for(unsigned i=0; i<data.model_bone_ids.size(); ++i){ + for(int j=0; j<4; ++j){ + if(data.model_bone_weights[i][j] > 0.0f){ + has_verts_assigned[int(data.model_bone_ids[i][j])] = true; + } + } + } + + parents = data.hier_parents; + + for(unsigned i=0; i<physics_bones.size(); ++i){ + physics_bones[i].physics_mass = bones[i].mass; + } + + // Identify objects with fixed joint to parent + fixed_obj.resize(physics_bones.size(), false); + for(unsigned i=0; i<data.joints.size(); ++i){ + const JointData& joint = data.joints[i]; + if(joint.type == _fixed_joint){ + int id[2]; + id[0] = joint.bone_id[0]; + id[1] = joint.bone_id[1]; + if(parents[id[0]] == id[1]){ + fixed_obj[id[0]] = true; + physics_bones[id[1]].physics_mass += bones[id[0]].mass; + } else if(parents[id[1]] == id[0]){ + fixed_obj[id[1]] = true; + physics_bones[id[0]].physics_mass += bones[id[1]].mass; + } + } + } + + // Remove hands and toes from physics world + int toe_bones[2]; + toe_bones[0] = simple_ik_bones["left_leg"].bone_id; + toe_bones[1] = simple_ik_bones["right_leg"].bone_id; + int hand_bones[2]; + hand_bones[0] = simple_ik_bones["leftarm"].bone_id; + hand_bones[1] = simple_ik_bones["rightarm"].bone_id; + for(int i=0, len=physics_bones.size(); i<len; ++i){ + int parent = parents[i]; + int grandparent = -1; + if(parent != -1){ + grandparent = parents[parent]; + } + if(i == toe_bones[0] || i == toe_bones[1]){ + fixed_obj[i] = true; + physics_bones[parent].physics_mass += bones[i].mass; + } + if(i == hand_bones[0] || i == hand_bones[1]){ + fixed_obj[i] = true; + physics_bones[parents[grandparent]].physics_mass += bones[i].mass; + } + if(parent == hand_bones[0] || parent == hand_bones[1]){ + fixed_obj[i] = true; + physics_bones[parents[parents[grandparent]]].physics_mass += bones[i].mass; + } + if(grandparent == hand_bones[0] || grandparent == hand_bones[1]){ + fixed_obj[i] = true; + physics_bones[parents[parents[parents[grandparent]]]].physics_mass += bones[i].mass; + } + } + CreatePhysicsSkeleton(scale, fzx_ref); + + // Add joints + int num_joints = data.joints.size(); + for(int i=0; i<num_joints; i++){ + const JointData &joint = data.joints[i]; + SelectedJoint selected; + + if(joint.bone_id[0] != joint.bone_id[1]) { + selected.bones.push_back(joint.bone_id[0]); + selected.bones.push_back(joint.bone_id[1]); + + float initial_angle = 0.0f; + if(joint.type == _hinge_joint) { + CreateHinge(selected, joint.axis, &initial_angle); + } + else if(joint.type == _amotor_joint) { + CreateRotationalConstraint(selected); + } + else if(joint.type == _fixed_joint) { + CreateFixed(selected); + } + PhysicsJoint& physics_joint = physics_joints.back(); + physics_joint.initial_angle = initial_angle; + physics_joint.stop_angle[0] = joint.stop_angle[0]; + physics_joint.stop_angle[1] = joint.stop_angle[1]; + if(joint.type == _amotor_joint) { + physics_joint.stop_angle[2] = joint.stop_angle[2]; + physics_joint.stop_angle[3] = joint.stop_angle[3]; + physics_joint.stop_angle[4] = joint.stop_angle[4]; + physics_joint.stop_angle[5] = joint.stop_angle[5]; + } + if(joint.type != _fixed_joint) { + ApplyJointRange(&physics_joint); + } + } else { + LOGW << "Error when setting up joints in skeleton from path \"" << path << "\", trying to create a joint between bone " << joint.bone_id[0] << " and itself." << std::endl; + } + } + } + + std::map<BulletObject*, int> bo_id; + for(unsigned i=0; i<physics_bones.size(); ++i){ + bo_id[physics_bones[i].bullet_object] = i; + } + + // Remove bones that are fixed in place + for(unsigned i=0; i<physics_bones.size(); ++i){ + if(fixed_obj[i] && has_verts_assigned[i]){ + PhysicsBone& pbi = physics_bones[i]; + for(unsigned j=0; j<physics_joints.size(); ++j){ + PhysicsJoint& joint = physics_joints[j]; + int id[2]; + id[0] = bo_id[joint.bt_bone[0]]; + id[1] = bo_id[joint.bt_bone[1]]; + if(id[0] != (int)i && id[1] != (int)i) { + continue; + } + int other = id[0]==(int)i?id[1]:id[0]; + if(other == parents[i]){ + DeleteJoint(&joint); + } else { + ShiftJoint(joint, + id[0]==(int)i?parents[i]:id[0], + id[1]==(int)i?parents[i]:id[1]); + } + } + PhysicsBone& pb = physics_bones[parents[i]]; + pb.bullet_object->color = vec3(1.0f,0.0f,0.0f); + pbi.bullet_object->color = vec3(0.5f,0.5f,0.5f); + } + } + + // Delete joints that involve fixed objects + for(unsigned i=0; i<physics_bones.size(); ++i){ + if(fixed_obj[i] && has_verts_assigned[i]){ + PhysicsBone& pbi = physics_bones[i]; + for(unsigned j=0; j<physics_joints.size(); ++j){ + PhysicsJoint& joint = physics_joints[j]; + if(joint.bt_bone[0] == pbi.bullet_object || joint.bt_bone[1] == pbi.bullet_object) { + DeleteJoint(&joint); + } + } + bullet_world->UnlinkObjectPermanent(pbi.bullet_object); + } + } + + // Remove invisible bones from parent hierarchy as well as physics world + for(unsigned i=0; i<parents.size(); ++i){ + while(parents[i] != -1 && !has_verts_assigned[parents[i]]){ + parents[i] = parents[parents[i]]; + } + } + for(unsigned i=0; i<physics_bones.size(); ++i){ + if(!has_verts_assigned[i]){ + PhysicsBone& pbi = physics_bones[i]; + for(unsigned j=0; j<physics_joints.size(); ++j){ + PhysicsJoint& joint = physics_joints[j]; + if(joint.bt_bone[0] == pbi.bullet_object || + joint.bt_bone[1] == pbi.bullet_object) + { + DeleteJoint(&joint); + } + } + bullet_world->RemoveObject(&pbi.bullet_object); + } + } + + // Add constraints that do nothing but prevent collision detection between bones + // that start out overlapping + AddNullConstraints(); + + phys_id.clear(); + for(unsigned i=0; i<physics_bones.size(); ++i){ + phys_id[physics_bones[i].bullet_object] = i; + } + + return (int)rigging_stage; +} + +void Skeleton::ReduceROM(float how_much) { + std::map<BulletObject*, int> boid; + for(unsigned i=0; i<physics_bones.size(); ++i){ + boid[physics_bones[i].bullet_object] = i; + } + std::vector<int> affected(physics_bones.size(), 1); + if(simple_ik_bones.find("tail") != simple_ik_bones.end()){ + const SimpleIKBone &ik_bone = simple_ik_bones["tail"]; + int bone = ik_bone.bone_id; + int length = ik_bone.chain_length; + for(int i=0; i<length; ++i){ + affected[bone] = 0; + bone = parents[bone]; + } + } + for(unsigned i=0; i<physics_joints.size(); i++){ + if(physics_joints[i].type == ROTATION_JOINT && affected[boid[physics_joints[i].bt_bone[0]]] == 1 && affected[boid[physics_joints[i].bt_bone[1]]] == 1){ + float mid = (physics_joints[i].stop_angle[0]+ + physics_joints[i].stop_angle[1])*0.5f; + physics_joints[i].stop_angle[0] = mix(physics_joints[i].stop_angle[0],mid,how_much); + physics_joints[i].stop_angle[1] = mix(physics_joints[i].stop_angle[1],mid,how_much); + + mid = (physics_joints[i].stop_angle[2]+ + physics_joints[i].stop_angle[3])*0.5f; + physics_joints[i].stop_angle[2] = mix(physics_joints[i].stop_angle[2],mid,how_much); + physics_joints[i].stop_angle[3] = mix(physics_joints[i].stop_angle[3],mid,how_much); + + mid = (physics_joints[i].stop_angle[4]+ + physics_joints[i].stop_angle[5])*0.5f; + physics_joints[i].stop_angle[4] = mix(physics_joints[i].stop_angle[4],mid,how_much); + physics_joints[i].stop_angle[5] = mix(physics_joints[i].stop_angle[5],mid,how_much); + + ApplyJointRange(&physics_joints[i]); + } + } +} + +void Skeleton::CreateFixed(const SelectedJoint &selected_joint) { + std::vector<BulletObject*> selected_bullet_bones(2); + GetPhysicsObjectsFromSelectedJoint(selected_bullet_bones,selected_joint); + + //It doesn't make sense to create a joint between a bone and itself. + assert(selected_bullet_bones[0] != selected_bullet_bones[1]); + + DeleteJointOnBones(selected_bullet_bones); + + btTypedConstraint* bt_joint = + bullet_world->AddFixedJoint(selected_bullet_bones[0], + selected_bullet_bones[1], + (selected_bullet_bones[0]->GetPosition()+selected_bullet_bones[1]->GetPosition())*0.5f); + + physics_joints.resize(physics_joints.size()+1); + physics_joints.back().type = FIXED_JOINT; + physics_joints.back().bt_joint = bt_joint; + physics_joints.back().fixed_joint = NULL; + physics_joints.back().bt_bone[0] = selected_bullet_bones[0]; + physics_joints.back().bt_bone[1] = selected_bullet_bones[1]; +} + +vec3 Skeleton::GetCenterOfMass() { + vec3 center(0.0f); + float total_mass = 0.0f; + for(unsigned i=0; i<physics_bones.size(); i++){ + if(!physics_bones[i].bullet_object){ + continue; + } + center += physics_bones[i].bullet_object->GetPosition() * + physics_bones[i].bullet_object->GetMass(); + total_mass += physics_bones[i].bullet_object->GetMass(); + } + center /= total_mass; + return center; +} + +void Skeleton::CreateBallJoint(const SelectedJoint &selected_joint) { + int shared_point = -1; + Bone* joint_bones[2]; + joint_bones[0] = &bones[selected_joint.bones[0]]; + joint_bones[1] = &bones[selected_joint.bones[1]]; + for(int i=0; i<2; i++){ + for(int j=0; j<2; j++) { + if(joint_bones[0]->points[i] == joint_bones[1]->points[j]){ + shared_point = joint_bones[0]->points[i]; + } + } + } + if(shared_point == -1) { + std::vector<int> point_ids(2, -1); + for(unsigned i=0; i<points.size(); ++i){ + if(joint_bones[0]->points[0] == (int)i){ + point_ids[0] = i; + } + if(joint_bones[0]->points[1] == (int)i){ + point_ids[1] = i; + } + } + if(!parents.empty() && parents[point_ids[0]] == point_ids[1]){ + shared_point = point_ids[1]; + } else { + shared_point = point_ids[0]; + } + } + + std::vector<BulletObject*> selected_bullet_bones(2); + GetPhysicsObjectsFromSelectedJoint(selected_bullet_bones, selected_joint); + DeleteJointOnBones(selected_bullet_bones); + + vec3 anchor = points[shared_point]; + + btTypedConstraint *bt_joint = bullet_world->AddBallJoint(selected_bullet_bones[0], + selected_bullet_bones[1], + anchor); + + physics_joints.resize(physics_joints.size()+1); + physics_joints.back().type = BALL_JOINT; + physics_joints.back().anchor = anchor; + physics_joints.back().bt_joint = bt_joint; + physics_joints.back().bt_bone[0] = selected_bullet_bones[0]; + physics_joints.back().bt_bone[1] = selected_bullet_bones[1]; + + mat4 rotation1 = selected_bullet_bones[0]->GetRotation(); + mat4 rotation2 = selected_bullet_bones[1]->GetRotation(); +} + +void Skeleton::CreateRotationalConstraint(const SelectedJoint &selected_joint) { + int shared_point = -1; + Bone* joint_bones[2]; + joint_bones[0] = &bones[selected_joint.bones[0]]; + joint_bones[1] = &bones[selected_joint.bones[1]]; + for(int i=0; i<2; i++){ + for(int j=0; j<2; j++) { + if(joint_bones[0]->points[i] == joint_bones[1]->points[j]){ + shared_point = joint_bones[0]->points[i]; + } + } + } + if(shared_point == -1) { + std::vector<int> point_ids(2, -1); + for(unsigned i=0; i<points.size(); ++i){ + if(joint_bones[0]->points[0] == (int)i){ + point_ids[0] = i; + } + if(joint_bones[0]->points[1] == (int)i){ + point_ids[1] = i; + } + } + if(!parents.empty() && parents[point_ids[0]] == point_ids[1]){ + shared_point = point_ids[1]; + } else { + shared_point = point_ids[0]; + } + } + + std::vector<BulletObject*> selected_bullet_bones(2); + GetPhysicsObjectsFromSelectedJoint(selected_bullet_bones, selected_joint); + DeleteJointOnBones(selected_bullet_bones); + + vec3 anchor = points[shared_point]; + + btTypedConstraint* bt_joint = + bullet_world->AddAngleConstraints(selected_bullet_bones[0], + selected_bullet_bones[1], + anchor, + (float)PI*0.25f); + + btTypedConstraint* fixed_joint = + bullet_world->AddFixedJoint(selected_bullet_bones[0], + selected_bullet_bones[1], + anchor); + + physics_joints.resize(physics_joints.size()+1); + physics_joints.back().type = ROTATION_JOINT; + physics_joints.back().anchor = anchor; + physics_joints.back().bt_joint = bt_joint; + physics_joints.back().fixed_joint = fixed_joint; + physics_joints.back().bt_bone[0] = selected_bullet_bones[0]; + physics_joints.back().bt_bone[1] = selected_bullet_bones[1]; + physics_joints.back().stop_angle[0] = -(float)PI*0.25f; + physics_joints.back().stop_angle[1] = (float)PI*0.25f; + physics_joints.back().stop_angle[2] = -(float)PI*0.25f; + physics_joints.back().stop_angle[3] = (float)PI*0.25f; + physics_joints.back().stop_angle[4] = -(float)PI*0.25f; + physics_joints.back().stop_angle[5] = (float)PI*0.25f; + + mat4 rotation1 = selected_bullet_bones[0]->GetRotation(); + mat4 rotation2 = selected_bullet_bones[1]->GetRotation(); + + physics_joints.back().initial_axis = bullet_world->GetD6Axis(bt_joint,0); + physics_joints.back().initial_axis2 = bullet_world->GetD6Axis(bt_joint,2); +} + +void Skeleton::ApplyJointRange(PhysicsJoint *joint) { + if(joint->type == HINGE_JOINT) { + btTypedConstraint* bt_joint = joint->bt_joint; + if(bt_joint){ + btHingeConstraint* hinge = (btHingeConstraint*)bt_joint; + if(joint->stop_angle[0]<joint->stop_angle[1]) { + hinge->setLimit(joint->stop_angle[0] + joint->initial_angle, joint->stop_angle[1] + joint->initial_angle); + } else { + hinge->setLimit(joint->stop_angle[1] + joint->initial_angle, joint->stop_angle[0] + joint->initial_angle); + } + } + } + if(joint->type == ROTATION_JOINT) { + btTypedConstraint* bt_joint = joint->bt_joint; + if(bt_joint){ + btGeneric6DofConstraint* constraint = (btGeneric6DofConstraint*)bt_joint; + constraint->setAngularLowerLimit(btVector3(joint->stop_angle[0], + joint->stop_angle[2], + joint->stop_angle[4])); + constraint->setAngularUpperLimit(btVector3(joint->stop_angle[1], + joint->stop_angle[3], + joint->stop_angle[5])); + } + } +} + +void Skeleton::DeleteJoint(PhysicsJoint *joint){ + if(joint->bt_joint){ + bullet_world->RemoveJoint(&joint->bt_joint); + } + if(joint->fixed_joint){ + bullet_world->RemoveJoint(&joint->fixed_joint); + } +} + +void Skeleton::RefreshFixedJoints(const std::vector<BoneTransform> &mats){ + for(unsigned i=0; i<physics_joints.size(); ++i){ + PhysicsJoint& joint = physics_joints[i]; + if(joint.fixed_joint){ + BoneTransform a = mats[phys_id[joint.bt_bone[0]]]; + a.origin += a.rotation * joint.bt_bone[0]->com_offset; + BoneTransform b = mats[phys_id[joint.bt_bone[1]]]; + b.origin += b.rotation * joint.bt_bone[1]->com_offset; + BulletWorld::UpdateFixedJoint(joint.fixed_joint, + a.GetMat4(), + b.GetMat4()); + } + } +} + +void Skeleton::PhysicsDispose() { + // Remove child shapes + for (unsigned i = 0; i < child_shapes.size(); i++) { + delete child_shapes[i]; + } + child_shapes.clear(); + + // Remove joints + for(unsigned i=0; i<physics_joints.size(); i++){ + PhysicsJoint &joint = physics_joints[i]; + if(joint.bt_joint){ + bullet_world->RemoveJoint(&joint.bt_joint); + } + if(joint.fixed_joint){ + bullet_world->RemoveJoint(&joint.fixed_joint); + } + } + physics_joints.clear(); + + // Remove null constraints + for(unsigned i=0; i<null_constraints.size(); i++){ + bullet_world->RemoveJoint(&null_constraints[i]); + } + null_constraints.clear(); + + // Remove objects + for(unsigned i=0; i<physics_bones.size(); i++){ + bullet_world->RemoveObject(&physics_bones[i].bullet_object); + col_bullet_world->RemoveObject(&physics_bones[i].col_bullet_object); + } + physics_bones.clear(); + + // Remove local physics worlds + if(col_bullet_world.get()){ + col_bullet_world->Dispose(); + col_bullet_world.reset(NULL); + } +} + +void Skeleton::Dispose() { + points.clear(); + bones.clear(); + + PhysicsDispose(); +} + +void Skeleton::SetBulletWorld( BulletWorld *_bullet_world ) { + bullet_world = _bullet_world; +} + +Skeleton::~Skeleton() { + Dispose(); +} + +void Skeleton::GetPhysicsObjectsFromSelectedJoint( + std::vector<BulletObject*> &selected_bullet_bones, + const SelectedJoint & selected_joint ) +{ + for(unsigned i=0; i<physics_bones.size(); i++){ + if(physics_bones[i].bone == selected_joint.bones[0]) { + selected_bullet_bones[0] = physics_bones[i].bullet_object; + } + if(physics_bones[i].bone == selected_joint.bones[1]) { + selected_bullet_bones[1] = physics_bones[i].bullet_object; + } + } +} + +void Skeleton::DeleteJointOnBones( std::vector<BulletObject*> &selected_bullet_bones ) { + std::vector<PhysicsJoint>::iterator iter; + for(iter = physics_joints.begin(); iter != physics_joints.end();){ + if(((*iter).bt_bone[0] == selected_bullet_bones[0] && + (*iter).bt_bone[1] == selected_bullet_bones[1]) || + ((*iter).bt_bone[1] == selected_bullet_bones[0] && + (*iter).bt_bone[0] == selected_bullet_bones[1])) { + DeleteJoint(&(*iter)); + iter = physics_joints.erase(iter); + } else { + ++iter; + } + } +} + +std::vector<float> Skeleton::GetBoneMasses() { + std::vector<float> bone_masses(physics_bones.size(),0.0f); + for(unsigned i=0; i<bone_masses.size(); i++){ + if(physics_bones[i].bullet_object){ + bone_masses[i] = physics_bones[i].bullet_object->GetMass(); + } + } + return bone_masses; +} + +struct JointBoneIDs { + int id[2]; +}; + +void Skeleton::SetGFStrength(PhysicsJoint& joint, float _strength) { + if(joint.fixed_joint){ + if(_strength == 0.0f){ + if(joint.fixed_joint_enabled){ + bullet_world->UnlinkConstraint(joint.fixed_joint); + joint.fixed_joint_enabled = false; + } + } else { + if(!joint.fixed_joint_enabled){ + bullet_world->LinkConstraint(joint.fixed_joint); + joint.fixed_joint_enabled = true; + } + bullet_world->SetJointStrength(joint.fixed_joint, _strength); + } + } +} + +void Skeleton::AddNullConstraints() { + std::map<BulletObject*, std::set<BulletObject*> > connected; + + for(unsigned i=0; i<physics_joints.size(); ++i){ + const PhysicsJoint& pj = physics_joints[i]; + connected[pj.bt_bone[0]].insert(pj.bt_bone[1]); + connected[pj.bt_bone[1]].insert(pj.bt_bone[0]); + } + + for(unsigned i=0; i<physics_bones.size(); i++){ + if(fixed_obj[i]){ + continue; + } + const PhysicsBone& pbi = physics_bones[i]; + std::set<BulletObject*> &connected_set = connected[pbi.bullet_object]; + for(unsigned j=i+1; j<physics_bones.size(); j++){ + if(fixed_obj[j]){ + continue; + } + const PhysicsBone& pbj = physics_bones[j]; + if(!pbi.bullet_object || !pbj.bullet_object){ + continue; + } + if(connected_set.find(pbj.bullet_object) == connected_set.end() && + bullet_world->CheckCollision(pbi.bullet_object, pbj.bullet_object)) + { + BulletObject *obj_a = pbi.bullet_object; + BulletObject *obj_b = pbj.bullet_object; + if(obj_a->collision_flags & obj_b->collision_group && + obj_b->collision_flags & obj_a->collision_group) + { + btTypedConstraint* constraint = + bullet_world->AddNullConstraint(obj_a, obj_b); + null_constraints.push_back(constraint); + LOGD << "Creating null constraint between " << LabelFromBone(pbi.bone, simple_ik_bones, parents) << " and " << LabelFromBone(pbj.bone, simple_ik_bones, parents) << std::endl; + //bullet_world->CheckCollision(pbi.bullet_object, pbj.bullet_object); + } + } + } + } + LOGD << null_constraints.size() << " null constraints." << std::endl; +} + +void Skeleton::ShiftJoint( PhysicsJoint& joint, int obj_a, int obj_b ) { + if(joint.type != HINGE_JOINT && joint.type != ROTATION_JOINT){ + return; + } + btTypedConstraint* bt_joint; + if(joint.type == HINGE_JOINT){ + bt_joint = bullet_world->AddHingeJoint( + physics_bones[obj_a].bullet_object, + physics_bones[obj_b].bullet_object, + joint.anchor, + joint.initial_axis, + joint.stop_angle[0], + joint.stop_angle[1], + &joint.initial_angle); + } + else if(joint.type == ROTATION_JOINT){ + bt_joint = bullet_world->AddAngleConstraints( + physics_bones[obj_a].bullet_object, + physics_bones[obj_b].bullet_object, + joint.anchor, + (float)PI * 0.25f); + } + else { + // because above we returned if joint.type was not one of these two + __builtin_unreachable(); + } + btTypedConstraint* fixed_joint = bullet_world->AddFixedJoint( + physics_bones[obj_a].bullet_object, + physics_bones[obj_b].bullet_object, + joint.anchor); + + DeleteJoint(&joint); + joint.bt_joint = bt_joint; + joint.fixed_joint = fixed_joint; + joint.bt_bone[0] = physics_bones[obj_a].bullet_object; + joint.bt_bone[1] = physics_bones[obj_b].bullet_object; + + mat4 rotation1 = physics_bones[obj_a].bullet_object->GetRotation(); + mat4 rotation2 = physics_bones[obj_b].bullet_object->GetRotation(); + + if(joint.type == ROTATION_JOINT){ + joint.initial_axis = bullet_world->GetD6Axis(bt_joint,0); + joint.initial_axis2 = bullet_world->GetD6Axis(bt_joint,2); + } +} + +void Skeleton::UpdateTwistBones(bool update_transform) { + /*for(unsigned i=0; i<physics_bones.size(); ++i){ + if(fixed_obj[i]){ + const PhysicsBone& pbone = physics_bones[parents[i]]; + mat4 initial_parent_mat = pbone.initial_rotation; + initial_parent_mat.SetTranslationPart(pbone.initial_position); + mat4 initial_mat = physics_bones[i].initial_rotation; + initial_mat.SetTranslationPart(physics_bones[i].initial_position); + mat4 offset = invert(initial_parent_mat) * initial_mat; + + int child_id = -1; + for(unsigned j=0; j<physics_bones.size(); ++j){ + if(i==j){ + continue; + } + if(parents[j] == (int)i && + physics_bones[j].bullet_object) + { + child_id = j; + break; + } + } + + if(child_id == -1){ + continue; + } + const PhysicsBone& cbone = physics_bones[child_id]; + mat4 child_mat = cbone.bullet_object->GetTransform(); + mat4 initial_child_mat = cbone.initial_rotation; + initial_child_mat.SetTranslationPart(cbone.initial_position); + + mat4 parent_transform = pbone.bullet_object->GetTransform(); + parent_transform.AddTranslation(parent_transform.GetRotatedvec3(pbone.bullet_object->com_offset*-1.0f)); + + mat4 child_mat_local = + invert(parent_transform) * child_mat; + vec3 vec_x = child_mat_local.GetColumnVec3(0); + vec3 vec_z = vec3(0.0f,0.0f,1.0f); + vec3 vec_y = normalize(cross(vec_z, vec_x)); + vec_x = normalize(cross(vec_y, vec_z)); + mat4 rot; + rot.SetColumn(0, vec_x); + rot.SetColumn(1, vec_y); + + mat4 new_mat = parent_transform * offset * rot; + + physics_bones[i].bullet_object->SetTransform(new_mat); + + if(update_transform){ + physics_bones[i].bullet_object->UpdateTransform(); + } + } + }*/ + + for(unsigned i=0; i<physics_bones.size(); ++i){ + if(fixed_obj[i] && physics_bones[i].bullet_object){ + int bone = parents[i]; + for(int j=0; j<4; ++j){ + if(!fixed_obj[bone] && has_verts_assigned[bone]){ + break; + } + bone = parents[bone]; + } + const PhysicsBone& pbone = physics_bones[bone]; + mat4 initial_parent_mat = pbone.initial_rotation; + initial_parent_mat.SetTranslationPart(pbone.initial_position); + + mat4 initial_mat = physics_bones[i].initial_rotation; + initial_mat.SetTranslationPart(physics_bones[i].initial_position); + mat4 offset = invert(initial_parent_mat) * initial_mat; + + mat4 curr_parent_mat = pbone.bullet_object->GetRotation(); + curr_parent_mat.SetTranslationPart(pbone.bullet_object->GetPosition()); + + physics_bones[i].bullet_object->SetRotation(pbone.bullet_object->GetTransform() * offset); + physics_bones[i].bullet_object->SetPosition(curr_parent_mat * offset * vec3()); + + if(update_transform){ + physics_bones[i].bullet_object->UpdateTransform(); + } + } + } +} + +void Skeleton::CheckForNAN() +{ + for(unsigned i=0; i<physics_bones.size(); i++){ + if(physics_bones[i].bullet_object){ + physics_bones[i].bullet_object->CheckForNAN(); + } + } +} + +void Skeleton::AlternateHull( const std::string &model_name, const vec3 &old_center, float model_char_scale ) { + std::string alt_model = model_name.substr(0, model_name.size()-4) + "_hulls.obj"; + LOGD << "Checking for " << alt_model << std::endl; + if(!FileExists(alt_model.c_str(), kDataPaths|kModPaths)){ + return; + } + int hull_id = Models::Instance()->loadModel(alt_model,_MDL_SIMPLE); + const Model &hull_model = Models::Instance()->GetModel(hull_id); + + // Create map containing all the neighboring vertices for each vertex + std::map<int, std::set<int> > vert_connections; + unsigned index = 0; + const GLuint *tri_faces; + for(int i=0, len=hull_model.faces.size()/3; i<len; ++i){ + tri_faces = &hull_model.faces[index]; + for(unsigned j=0; j<3; ++j){ + vert_connections[tri_faces[j]].insert(tri_faces[(j+1)%3]); + vert_connections[tri_faces[j]].insert(tri_faces[(j+2)%3]); + } + index += 3; + } + + // For each vertex, propagate group id to all connected vertices + std::vector<std::vector<int> > groups; + std::vector<int> vert_remap(hull_model.vertices.size()/3); + std::map<int, int> group_id; + std::queue<int> verts_to_process; + int the_label = 0; + int remap_id; + for(int i=0, len=hull_model.vertices.size()/3; i<len; ++i){ + if(group_id.find(i) != group_id.end()){ + continue; + } + remap_id = 0; + verts_to_process.push(i); + while(!verts_to_process.empty()){ + int vert_id = verts_to_process.front(); + verts_to_process.pop(); + if(group_id.find(vert_id) != group_id.end()){ + continue; + } + group_id.insert(std::pair<int, int>(vert_id, the_label)); + groups.resize(the_label+1); + groups[the_label].push_back(vert_id); + vert_remap[vert_id] = remap_id; + ++remap_id; + const std::set<int> &connections = vert_connections[vert_id]; + std::set<int>::const_iterator iter = connections.begin(); + for(; iter != vert_connections[vert_id].end(); ++iter){ + verts_to_process.push((*iter)); + } + } + ++the_label; + } + + // Calculate the average position of each group + std::vector<vec3> group_centers(groups.size()); + for(unsigned i=0; i<groups.size(); ++i){ + const std::vector<int> &group = groups[i]; + vec3 center_accum(0.0f); + for(unsigned j=0; j<group.size(); ++j){ + const int vert_index = group[j] * 3; + center_accum += vec3(hull_model.vertices[vert_index+0], + hull_model.vertices[vert_index+1], + hull_model.vertices[vert_index+2]); + } + group_centers[i] = center_accum / (float)group.size(); + group_centers[i] -= old_center; + group_centers[i] *= model_char_scale; + } + + std::vector<int> group_closest_bone(groups.size(), -1); + std::vector<float> group_closest_dist(groups.size(), 0.0f); + + + for(unsigned i=0; i<physics_bones.size(); ++i){ + if(fixed_obj[i] || !has_verts_assigned[i]){ + continue; + } + int closest_group = -1; + float closest_dist = 0.0f; + float dist; + for(unsigned j=0; j<groups.size(); ++j){ + dist = distance_squared(group_centers[j], physics_bones[i].initial_position); + if(closest_group == -1 || dist < closest_dist){ + closest_group = j; + closest_dist = dist; + } + } + if(group_closest_bone[closest_group] == -1 || + closest_dist < group_closest_dist[closest_group]) + { + group_closest_dist[closest_group] = closest_dist; + group_closest_bone[closest_group] = i; + } + } + + std::vector<std::vector<vec3> > groups_verts(groups.size()); + for(unsigned i=0; i<groups.size(); ++i){ + std::vector<vec3> &group_verts = groups_verts[i]; + std::vector<int> &group = groups[i]; + group_verts.resize(group.size()); + int vert_index; + for(unsigned j=0; j<group_verts.size(); ++j){ + vert_index = group[j]*3; + group_verts[j] = vec3(hull_model.vertices[vert_index+0], + hull_model.vertices[vert_index+1], + hull_model.vertices[vert_index+2]); + group_verts[j] -= old_center; + group_verts[j] *= model_char_scale; + } + } + + for(unsigned i=0; i<groups.size(); ++i){ + if(group_closest_bone[i] != -1){ + std::vector<int> faces; + for(int j=0, len=hull_model.faces.size()/3; j<len; ++j){ + if(group_id[hull_model.faces[j*3]] == (int)i){ + faces.push_back(vert_remap[hull_model.faces[j*3+0]]); + faces.push_back(vert_remap[hull_model.faces[j*3+1]]); + faces.push_back(vert_remap[hull_model.faces[j*3+2]]); + } + } + PhysicsBone &pb = physics_bones[group_closest_bone[i]]; + std::vector<vec3> &group_verts = groups_verts[i]; + mat4 mat; + mat.SetTranslationPart(pb.initial_position*-1.0f); + mat = transpose(pb.initial_rotation) * mat; + for(unsigned j=0; j<group_verts.size(); ++j){ + group_verts[j] = mat*group_verts[j]; + } + + pb.col_bullet_object = + col_bullet_world->CreateConvexObject(group_verts, faces); + pb.col_bullet_object->SetVisibility(true); + pb.col_bullet_object->owner_object = (Object*)&physics_bones[group_closest_bone[i]]; + } + } +} + +void Skeleton::GetSweptSphereCollisionCharacter( const vec3 & pos, const vec3 & pos2, float radius, SphereCollision & as_col ) { + SweptSlideCallback cb; + col_bullet_world->GetSweptSphereCollisions(pos, pos2, radius, cb); + as_col.adjusted_position = col_bullet_world->ApplySphereSlide(pos2, + radius, + cb.collision_info); + if(cb.true_closest_hit_fraction != 1.0f){ + LOGI << "Hit something" << std::endl; + } + as_col.SetFromCallback(pos, pos2, cb); +} + +void Skeleton::UpdateCollisionWorldAABB() { + col_bullet_world->UpdateAABB(); +} + +const btCollisionObject* Skeleton::CheckRayCollision( const vec3 & start, const vec3 & end, vec3* point, vec3* normal ) { + return col_bullet_world->CheckRayCollision(start, end, point, normal, false); +} + +int Skeleton::CheckRayCollisionBone( const vec3 & start, const vec3 & end ) { + SimpleRayResultCallbackInfo cb; + col_bullet_world->CheckRayCollisionInfo(start, end, cb, false); + if(cb.contact_info.size() == 0){ + LOGI << "Did not hit anything!" << std::endl; + return -1; + } + float closest_dist = FLT_MAX; + int closest_hit = -1; + for(int contact_index=0, len=cb.contact_info.size(); contact_index<len; ++contact_index){ + BulletObject* bo = cb.contact_info[contact_index].object; + for(unsigned bone_index=0; bone_index<physics_bones.size(); ++bone_index){ + if(physics_bones[bone_index].col_bullet_object == bo){ + float dist = distance_squared(start, cb.contact_info[contact_index].point); + if(dist < closest_dist){ + closest_hit = bone_index; + closest_dist = dist; + } + } + } + } + if(closest_hit != -1){ + return closest_hit; + } + + LOGI << "Hit unknown bone!" << std::endl; + return -1; +} + +PhysicsBone::PhysicsBone() : + bone(0), + bullet_object(NULL), + col_bullet_object(NULL), + display_scale(1.0f) +{} |