Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/WolfireGames/overgrowth.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Objects/riggedobject.cpp')
-rw-r--r--Source/Objects/riggedobject.cpp4922
1 files changed, 4922 insertions, 0 deletions
diff --git a/Source/Objects/riggedobject.cpp b/Source/Objects/riggedobject.cpp
new file mode 100644
index 00000000..90800644
--- /dev/null
+++ b/Source/Objects/riggedobject.cpp
@@ -0,0 +1,4922 @@
+//-----------------------------------------------------------------------------
+// Name: riggedobject.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 "riggedobject.h"
+
+#include <Graphics/graphics.h>
+#include <Graphics/particles.h>
+#include <Graphics/camera.h>
+#include <Graphics/shaders.h>
+#include <Graphics/textures.h>
+#include <Graphics/models.h>
+#include <Graphics/particles.h>
+#include <Graphics/sky.h>
+#include <Graphics/palette.h>
+#include <Graphics/pxdebugdraw.h>
+#include <Graphics/simplify.hpp>
+
+#include <Internal/timer.h>
+#include <Internal/datemodified.h>
+#include <Internal/collisiondetection.h>
+
+#include <Objects/group.h>
+#include <Objects/movementobject.h>
+#include <Objects/itemobject.h>
+#include <Objects/envobject.h>
+#include <Objects/decalobject.h>
+#include <Objects/lightvolume.h>
+
+#include <XML/xml_helper.h>
+#include <XML/level_loader.h>
+
+#include <Scripting/angelscript/ascontext.h>
+#include <Scripting/angelscript/add_on/scriptarray/scriptarray.h>
+
+#include <Physics/bulletobject.h>
+#include <Physics/bulletworld.h>
+#include <Physics/physics.h>
+
+#include <Asset/Asset/skeletonasset.h>
+#include <Asset/Asset/fzx_file.h>
+#include <Asset/Asset/material.h>
+#include <Asset/Asset/attacks.h>
+
+#include <GUI/gui.h>
+#include <GUI/IMUI/imui.h>
+
+#include <Internal/varstring.h>
+#include <Internal/checksum.h>
+#include <Internal/memwrite.h>
+#include <Internal/filesystem.h>
+#include <Internal/profiler.h>
+#include <Internal/common.h>
+#include <Internal/filesystem.h>
+
+#include <Math/vec2math.h>
+#include <Math/vec3math.h>
+#include <Math/vec4math.h>
+
+#include <Timing/timingevent.h>
+#include <Timing/intel_gl_perf.h>
+
+#include <Main/engine.h>
+#include <Main/scenegraph.h>
+
+#include <Online/online.h>
+#include <Online/online_datastructures.h>
+
+#include <Compat/fileio.h>
+#include <Logging/logdata.h>
+#include <Utility/assert.h>
+#include <Threading/thread_sanity.h>
+#include <Sound/sound.h>
+#include <Editors/map_editor.h>
+#include <Memory/allocation.h>
+
+#include <tinyxml.h>
+
+#include <forward_list>
+#include <cmath>
+#include <sstream>
+#include <cmath>
+
+#ifdef PLATFORM_LINUX
+ #include <malloc.h>
+#endif
+const bool _draw_char_collision_world = false;
+bool GPU_skinning = false;
+
+extern AnimationConfig animation_config;
+extern bool g_simple_shadows;
+extern bool g_level_shadows;
+extern char* global_shader_suffix;
+extern bool g_no_decals;
+extern bool g_no_reflection_capture;
+extern bool g_character_decals_enabled;
+
+extern bool g_debug_runtime_disable_morph_target_pre_draw_camera;
+extern bool g_debug_runtime_disable_rigged_object_draw;
+extern bool g_debug_runtime_disable_rigged_object_pre_draw_camera;
+extern bool g_debug_runtime_disable_rigged_object_pre_draw_frame;
+
+const int kMaxBones = 200;
+
+//-----------------------------------------------------------------------------
+//Functions
+//-----------------------------------------------------------------------------
+
+struct RiggedObjectGLState {
+ GLState gl_state;
+ RiggedObjectGLState() {
+ gl_state.depth_test = true;
+ gl_state.cull_face = true;
+ gl_state.depth_write = true;
+ gl_state.blend = false;
+ }
+};
+static const RiggedObjectGLState rigged_object_gl_state;
+
+
+
+float RiggedObject::GetRootBoneVelocity() {
+ return velocity;
+}
+
+const std::vector<mat4>& RiggedObject::GetViewBones() const
+{
+ return display_bone_matrices;
+}
+
+RiggedObject::RiggedObject() :
+ static_char(false),
+ primary_weapon_id(-1),
+ total_rotation(0.0f),
+ character_script_getter(NULL),
+ as_context(NULL),
+ char_id(-1),
+ lod_level(0),
+ shadow_group_id(-1),
+ char_scale(1.0f),
+ transformed_vertex_getter_(this),
+ blood_surface(&transformed_vertex_getter_),
+ vel_vbo(V_MIBIBYTE, kVBOFloat | kVBODynamic),
+ fur_transform_vec_vbo0(V_MIBIBYTE, kVBOFloat | kVBODynamic),
+ fur_transform_vec_vbo1(V_MIBIBYTE, kVBOFloat | kVBODynamic),
+ fur_transform_vec_vbo2(V_MIBIBYTE, kVBOFloat | kVBODynamic),
+ fur_tex_transform_vbo(V_MIBIBYTE, kVBOFloat | kVBODynamic),
+ last_draw_time(0.0f),
+ shader("envobject #CHARACTER"),
+ water_cube("character_accumulate #WATER_CUBE"),
+ water_cube_expand("character_accumulate #EXPAND"),
+ update_camera_pos(true)
+{
+ for( int i = 0; i < kLodLevels; i++ ) {
+ transform_vec_vbo0[i] = new VBORingContainer(V_MIBIBYTE, kVBOFloat | kVBODynamic);
+ transform_vec_vbo1[i] = new VBORingContainer(V_MIBIBYTE, kVBOFloat | kVBODynamic);
+ transform_vec_vbo2[i] = new VBORingContainer(V_MIBIBYTE, kVBOFloat | kVBODynamic);
+ tex_transform_vbo[i] = new VBORingContainer(V_MIBIBYTE, kVBOFloat | kVBODynamic);
+ need_vbo_update[i] = true;
+ }
+
+ palette_colors.resize(max_palette_elements, vec3(1.0f));
+ palette_colors_srgb.resize(max_palette_elements, vec3(1.0f));
+ for(int i=0; i<4; ++i){
+ model_id[i] = -1;
+ }
+
+ anim_update_period = 2;
+ max_time_until_next_anim_update = anim_update_period;
+
+ time_until_next_anim_update = 0;
+ curr_anim_update_time = -1;
+ prev_anim_update_time = -1;
+
+ collidable = false;
+
+ animated = false;
+
+ stab_texture = Engine::Instance()->GetAssetManager()->LoadSync<TextureAsset>("Data/Textures/stabdecal.tga");
+}
+
+RiggedObject::~RiggedObject() {
+ #ifdef USE_SSE
+ #ifdef _WIN32
+ _aligned_free(simd_bone_mats);
+ #else
+ free(simd_bone_mats);
+ #endif
+ #endif
+ for(int i=0; i<4; ++i){
+ Models::Instance()->DeleteModel(model_id[i]);
+ }
+ for(unsigned i=0; i<morph_targets.size(); ++i){
+ morph_targets[i].Dispose();
+ }
+
+ // TODO: why do we get a black screen when returning to the main menu on Mac
+ // if the velocity vbo gets disposed?
+ // Maybe something to do with motion blur being enabled or not?
+ //vel_vbo.is_valid = false;
+
+ for( int i = 0; i < kLodLevels; i++ ) {
+ delete transform_vec_vbo0[i];
+ delete transform_vec_vbo1[i];
+ delete transform_vec_vbo2[i];
+ delete tex_transform_vbo[i];
+ }
+ blood_surface.Dispose();
+}
+
+mat4 ASGetBindMatrix(Skeleton* skeleton, int which_bone){
+ mat4 mat;
+ const PhysicsBone& p_bone = skeleton->physics_bones[which_bone];
+ mat.SetTranslationPart(p_bone.initial_position * -1.0f);
+ mat = transpose(p_bone.initial_rotation)*mat;
+ return mat;
+}
+
+int RiggedObject::GetTimeBetweenLastTwoAnimUpdates(){
+ return max_time_until_next_anim_update;
+ //return curr_anim_update_time - prev_anim_update_time;
+}
+
+int RiggedObject::GetTimeSinceLastAnimUpdate(){
+ return -curr_anim_update_time;
+}
+
+extern Timer game_timer;
+
+void RiggedObject::CalcRootBoneVelocity() {
+ vec3 pos(0.0f);
+ vec3 old_pos(0.0f);
+ pos = display_bone_matrices[0] * pos;
+ old_pos = old_root_bone * old_pos;
+
+ old_root_bone = display_bone_matrices[0];
+
+ velocity = (length(pos - old_pos)) / (game_timer.GetWallTime() - old_time);
+ old_time = game_timer.GetWallTime();
+}
+
+void RiggedObject::GetCubicSplineWeights(float interp, float *weights) {
+ float interp_squared = interp*interp;
+ float interp_cubed = interp_squared*interp;
+ weights[0] = 0.5f * (-interp_cubed + 2.0f * interp_squared - interp);
+ weights[1] = 0.5f * (3.0f * interp_cubed - 5.0f * interp_squared + 2.0f);
+ weights[2] = 0.5f * (-3.0f * interp_cubed + 4.0f * interp_squared + interp);
+ weights[3] = 0.5f * (interp_cubed - interp_squared);
+}
+
+const mat4 RiggedObject::InterpolateBetweenFourBonesQuadratic(const NetworkBone* bones, float inter_step) {
+ float weights[4];
+
+ GetCubicSplineWeights(inter_step, weights);
+
+ quaternion quaternions[4];
+
+ quaternions[0] = bones[0].rotation0;
+ quaternions[1] = bones[1].rotation0;
+ quaternions[2] = bones[2].rotation0;
+ quaternions[3] = bones[3].rotation0;
+
+ mat4 rotation0 = Mat4FromQuaternion(normalize(BlendFour(quaternions, weights)));
+
+ quaternions[0] = bones[0].rotation1;
+ quaternions[1] = bones[1].rotation1;
+ quaternions[2] = bones[2].rotation1;
+ quaternions[3] = bones[3].rotation1;
+
+ mat4 rotation1 = Mat4FromQuaternion(normalize(BlendFour(quaternions, weights)));
+
+ vec3 vectors[4];
+
+ vectors[0] = bones[0].translation0;
+ vectors[1] = bones[1].translation0;
+ vectors[2] = bones[2].translation0;
+ vectors[3] = bones[3].translation0;
+
+ vec3 translation0 = BlendFour(vectors, weights);
+
+ vectors[0] = bones[0].translation1;
+ vectors[1] = bones[1].translation1;
+ vectors[2] = bones[2].translation1;
+ vectors[3] = bones[3].translation1;
+
+ vec3 translation1 = BlendFour(vectors, weights);
+
+ float floats[4];
+
+ floats[0] = bones[0].scale;
+ floats[1] = bones[1].scale;
+ floats[2] = bones[2].scale;
+ floats[3] = bones[3].scale;
+
+ float scale = BlendFour(floats, weights);
+
+ floats[0] = bones[0].model_char_scale;
+ floats[1] = bones[1].model_char_scale;
+ floats[2] = bones[2].model_char_scale;
+ floats[3] = bones[3].model_char_scale;
+
+ float model_char_scale = BlendFour(floats, weights);
+
+ //'mat4 &mat = display_bone_matrices[index];
+ mat4 mat;
+ mat.LoadIdentity();
+ mat.SetTranslationPart(translation0);
+ mat = rotation0 * mat;
+ mat4 scale_mat;
+ scale_mat.SetScale(vec3(scale, scale, 1.0f) * model_char_scale);
+ mat = scale_mat * mat;
+ mat = rotation1 * mat;
+ mat.AddTranslation(translation1);
+ return mat;
+}
+
+const mat4 RiggedObject::InterpolateBetweenTwoBones(const NetworkBone& current, const NetworkBone& next, float interp_step) {
+
+ mat4 rotation0 = Mat4FromQuaternion(normalize(Slerp(current.rotation0, next.rotation0, interp_step)));
+
+ mat4 rotation1 = Mat4FromQuaternion(normalize(Slerp(current.rotation1, next.rotation1, interp_step)));
+
+ vec3 translation0 = mix(current.translation0, next.translation0, interp_step);
+ vec3 translation1 = mix(current.translation1, next.translation1, interp_step);
+
+ float scale = (1.0f - interp_step) * current.scale
+ + interp_step * next.scale;
+
+ float model_char_scale = (1.0f - interp_step) * current.model_char_scale
+ + interp_step * next.model_char_scale;
+
+ //'mat4 &mat = display_bone_matrices[index];
+ mat4 mat;
+ mat.LoadIdentity();
+ mat.SetTranslationPart(translation0);
+ mat = rotation0 * mat;
+ mat4 scale_mat;
+ scale_mat.SetScale(vec3(scale, scale, 1.0f) * model_char_scale);
+ mat = scale_mat * mat;
+ mat = rotation1 * mat;
+ mat.AddTranslation(translation1);
+ return mat;
+}
+
+void RiggedObject::PreDrawFrame(float curr_game_time) {
+ Online* online = Online::Instance();
+
+ if (g_debug_runtime_disable_rigged_object_pre_draw_frame) {
+ return;
+ }
+
+
+ CalcRootBoneVelocity();
+ if (!online->IsClient()) {
+ PROFILER_ZONE(g_profiler_ctx, "RiggedObject::PreDrawFrame");
+ {
+ PROFILER_ZONE(g_profiler_ctx, "needs_matrix_update");
+ float interp_weight;
+ if (animated) {
+ interp_weight = game_timer.GetInterpWeightX(GetTimeBetweenLastTwoAnimUpdates(), GetTimeSinceLastAnimUpdate());
+ }
+ else {
+ interp_weight = game_timer.GetInterpWeight();
+ }
+
+ int num_bones = skeleton_.physics_bones.size();
+ assert(num_bones < kMaxBones);
+ for (int j = 0; j < num_bones; ++j) {
+ const PhysicsBone& p_bone = skeleton_.physics_bones[j];
+ BulletObject* b_o = p_bone.bullet_object;
+ if (!b_o) {
+ continue;
+ }
+
+ mat4 &mat = display_bone_matrices[j];
+ mat.LoadIdentity();
+ mat.SetTranslationPart(p_bone.initial_position * -1.0f / model_char_scale);
+ mat = transpose(p_bone.initial_rotation)*mat;
+ mat = b_o->GetInterpWeightRotation(interp_weight) * mat;
+ mat.AddTranslation(b_o->GetInterpWeightPosition(interp_weight));
+ }
+ {
+ PROFILER_ZONE(g_profiler_ctx, "Angelscript DisplayMatrixUpdate");
+ as_context->CallScriptFunction(as_funcs.display_matrix_update);
+ }
+
+ //Apply scaling of character bones. Do this after the initial posing of the character to be able to retrieve
+ //relative connection points of armor and other attachables.
+ for (int j = 0; j < num_bones; ++j) {
+ const PhysicsBone& p_bone = skeleton_.physics_bones[j];
+ BulletObject* b_o = p_bone.bullet_object;
+ if (!b_o) {
+ continue;
+ }
+ mat4 &mat = display_bone_matrices[j];
+ //Store attachment point
+ display_bone_transforms[j] = mat;
+ //Store scale for network bone.
+ mat4 scale_mat;
+ scale_mat.SetScale(vec3(skeleton_.physics_bones[j].display_scale, skeleton_.physics_bones[j].display_scale, 1.0f) * model_char_scale);
+
+ //Shift back the bone to origo
+ mat.AddTranslation(-b_o->GetInterpWeightPosition(interp_weight));
+ //Rotate back to identity rotation.
+ mat = invert(b_o->GetInterpWeightRotation(interp_weight)) * mat;
+ //Apply scaling
+ mat = scale_mat * mat;
+ //Re-apply rotation
+ mat = b_o->GetInterpWeightRotation(interp_weight) * mat;
+ //Re-apply translation
+ mat.AddTranslation(b_o->GetInterpWeightPosition(interp_weight));
+ }
+ }
+ }
+
+ blood_surface.PreDrawFrame(
+ Textures::Instance()->getWidth(texture_ref) / 4,
+ Textures::Instance()->getHeight(texture_ref) / 4);
+
+ {
+ PROFILER_ZONE(g_profiler_ctx, "Update attached objects");
+ int num_attached_env_objects = children.size();
+ for (int i = 0; i < num_attached_env_objects; ++i) {
+ AttachedEnvObject& attached_env_object = children[i];
+ Object *obj = attached_env_object.direct_ptr;
+ if (obj) {
+ if (IsBeingMoved(scenegraph_->map_editor, obj) && obj->Selected()) {
+ }
+ else {
+ int total_connections = 0;
+ for (unsigned j = 0; j < kMaxBoneConnects; ++j) {
+ total_connections += attached_env_object.bone_connects[j].num_connections;
+ }
+
+ mat4 total_mat(0.0f);
+ for (unsigned j = 0; j < kMaxBoneConnects; ++j) {
+ const BoneConnect &bone_connect = attached_env_object.bone_connects[j];
+ if (bone_connect.num_connections) {
+ mat4 mat = display_bone_transforms[bone_connect.bone_id].GetMat4() * bone_connect.rel_mat;
+ total_mat += mat * ((float)bone_connect.num_connections / (float)total_connections);
+ }
+ }
+
+ if (!online->IsClient()) {
+ obj->SetTranslation(total_mat.GetTranslationPart());
+ obj->SetRotation(QuaternionFromMat4(total_mat.GetRotationPart()));
+ }
+ }
+ }
+ }
+ }
+}
+
+void RiggedObject::StoreNetworkBones() {
+ int num_bones = skeleton_.physics_bones.size();
+ assert(num_bones < kMaxBones);
+ network_bones.resize(num_bones);
+ network_bones_host_walltime = game_timer.GetWallTime();
+ for (int j = 0; j < num_bones; ++j) {
+ const PhysicsBone& p_bone = skeleton_.physics_bones[j];
+ BulletObject* b_o = p_bone.bullet_object;
+ if (!b_o) {
+ continue;
+ }
+
+ NetworkBone network_bone;
+ network_bone.translation0 = p_bone.initial_position * -1.0f / model_char_scale;
+ network_bone.model_char_scale = model_char_scale;
+ network_bone.rotation0 = QuaternionFromMat4(transpose(p_bone.initial_rotation));
+ network_bone.rotation1 = QuaternionFromMat4(b_o->transform.GetRotationPart());
+ network_bone.translation1 = b_o->transform.GetTranslationPart();//b_o->GetInterpWeightPosition(interp_weight);
+ network_bone.scale = skeleton_.physics_bones[j].display_scale;
+
+ network_bones[j] = (network_bone);
+ }
+}
+
+void RiggedObject::StoreNetworkMorphTargets() {
+ // first check if not present
+ if (morph_targets.size() != network_morphs.size()) {
+ for (uint32_t i = 0; i < morph_targets.size(); i++) {
+ bool present = false;
+
+ for (uint32_t j = 0; j < network_morphs.size(); j++) {
+ if (morph_targets[i].name == network_morphs[j].name) {
+ present = true;
+ break;
+ }
+
+ }
+
+ if (present == false) {
+ MorphTargetStateStorage missing_morph;
+ missing_morph.disp_weight = morph_targets[i].disp_weight;
+ missing_morph.name = morph_targets[i].name;
+ missing_morph.dirty = true;
+ missing_morph.mod_weight = morph_targets[i].mod_weight;
+ network_morphs.push_back(missing_morph);
+ }
+ }
+ }
+
+ for (uint32_t i = 0; i < morph_targets.size(); i++) {
+ for (uint32_t j = 0; j < network_morphs.size(); j++) {
+ if (morph_targets[i].name == network_morphs[j].name) {
+ float a = morph_targets[i].disp_weight;
+ float b = network_morphs[j].disp_weight;
+
+
+ float c = network_morphs[j].mod_weight;
+ float d = morph_targets[i].mod_weight;
+
+ if (std::abs(a - b) > 1e-4 ||
+ std::abs(c - d) > 1e-4) {
+
+ network_morphs[j].mod_weight = morph_targets[i].mod_weight;
+ network_morphs[j].disp_weight = morph_targets[i].disp_weight;
+ network_morphs[j].dirty = true;
+ }
+ }
+
+ }
+ }
+
+}
+
+void RiggedObject::PreDrawCamera(float curr_game_time) {
+ Online* online = Online::Instance();
+
+ if (g_debug_runtime_disable_rigged_object_pre_draw_camera) {
+ return;
+ }
+
+ PROFILER_ZONE(g_profiler_ctx, "RiggedObject::PreDraw");
+ for(int i=0; i<4; ++i){
+ need_vbo_update[i] = true;
+ }
+ Graphics::Instance()->setGLState(rigged_object_gl_state.gl_state);
+
+ shadow_group_id = -1;
+
+ lod_level = 0;
+ const vec3 &avg_pos = GetAvgPosition();
+ const vec3 &cam_pos = ActiveCameras::Get()->GetPos();
+ const float cam_zoom = 90.0f/ActiveCameras::Get()->GetFOV();
+ const float _lod_switch_threshold = 20.0f * cam_zoom * cam_zoom * char_scale * char_scale;
+ if (!online->IsActive()) {
+ if (distance_squared(avg_pos, cam_pos) > _lod_switch_threshold * 16.0f) {
+ lod_level = 3;
+ }
+ else if (distance_squared(avg_pos, cam_pos) > _lod_switch_threshold * 4.0f) {
+ lod_level = 2;
+ }
+ else if (distance_squared(avg_pos, cam_pos) > _lod_switch_threshold) {
+ lod_level = 1;
+ }
+ }
+
+ // Use base LOD if lods are not loaded
+ if(model_id[lod_level] == -1){
+ lod_level = 0;
+ }
+
+ {
+ PROFILER_ZONE(g_profiler_ctx, "ApplyBoneMatricesToModel");
+ ApplyBoneMatricesToModel(false, lod_level);
+ }
+}
+
+// Find the closest points between line segments ab and cd
+// Based on the theory described at: http://stackoverflow.com/questions/627563/calculating-the-shortest-distance-between-two-lines-line-segments-in-3d/702174#702174
+void ClosestPointsBetweenTwoLineSegments(const vec3 &a, const vec3 &b, const vec3 &c, const vec3 &d, vec3 *ab_closest, vec3 *cd_closest){
+ // Create two vectors perpendicular to AB
+ float ab_len = distance(a,b);
+ vec3 ab_dir = b-a;
+ if(ab_len != 0.0f){
+ ab_dir /= ab_len;
+ }
+ vec3 ab_perp[2];
+ ab_perp[0] = vec3(-ab_dir[1], ab_dir[0], 0.0f);
+ ab_perp[1] = cross(ab_dir, ab_perp[0]);
+ // Flatten start and end onto plane defined by AB
+ vec3 rel_c = c - a;
+ vec3 rel_d = d - a;
+ vec2 flat_c(dot(ab_perp[0],rel_c), dot(ab_perp[1], rel_c));
+ vec2 flat_d(dot(ab_perp[0],rel_d), dot(ab_perp[1], rel_d));
+ // Find vector perpendicular to flat CD
+ vec2 flat_cd_dir = normalize(flat_d - flat_c);
+ vec2 perp_dir(-flat_cd_dir[1], flat_cd_dir[0]);
+ // Move flat line so it intersects origin
+ vec2 shift = perp_dir * dot(flat_c, perp_dir);
+ flat_c -= shift;
+ flat_d -= shift;
+ // Find intersection point with origin in the space of flat CD
+ float t = 0.0f;
+ vec2 flat_dir = flat_d - flat_c;
+ if(flat_dir[0] != 0.0f){
+ t = -flat_c[0]/flat_dir[0];
+ } else if(flat_dir[1] != 0.0f){
+ t= -flat_c[1]/flat_dir[1];
+ }
+ // Clamp point to bounds of line segment CD
+ t = min(1.0f, max(0.0f, t));
+ // Find CD point in world space
+ *cd_closest = c + (d - c) * t;
+ // Find the point on AB nearest to the closest point on CD
+ float t2 = 0.0f;
+ if(ab_len != 0.0f){
+ t2 = dot(ab_dir, *cd_closest-a)/ab_len;
+ }
+ // Clamp point to bounds of line segment AB
+ t2 = min(1.0f, max(0.0f, t2));
+ // Find AB point in world space
+ *ab_closest = a + (b - a) * t2;
+}
+
+const int kOcclusionRadius = 2;
+
+void RiggedObject::ClientBeforeDraw() {
+ if(Online::Instance()->IsClient()) {
+ /*
+ std::list<RiggedObjectFrame>& bones = mp->bones[char_id];
+ while(bones.size() > 1) {
+ network_time_interpolator.timestamps.Clear();
+ for(const RiggedObjectFrame& bone : bones) {
+ network_time_interpolator.timestamps.PushValue(bone.host_walltime);
+ }
+
+ int interpolation_return = network_time_interpolator.Update();
+
+ if(interpolation_return == 1) {
+ continue;
+ } else if(interpolation_return == 2) {
+ delete[] bones.front().bones;
+ bones.pop_front();
+ continue;
+ } else if(interpolation_return == 3) {
+ break;
+ }
+
+ RiggedObjectFrame& current_bones = *bones.begin();
+ RiggedObjectFrame& next_bones = *std::next(bones.begin());
+
+ float prev_root_z = GetDisplayBonePosition(0).z();
+
+ for(uint32_t i = 0; i < skeleton_.physics_bones.size(); i++) {
+ InterpolateBetweenTwoBones(current_bones.bones[i], next_bones.bones[i], network_time_interpolator.interpolation_step, i);
+ }
+
+ break;
+ }
+ */
+
+ if(network_display_bone_matrices.size() == display_bone_matrices.size()) {
+ //Replace this with a better routine in the future, where we just use the network bone matrices on a client when rendering.
+ display_bone_matrices = network_display_bone_matrices;
+ }
+ }
+}
+
+void RiggedObject::Draw(const mat4& proj_view_matrix, Object::DrawType type) {
+ if (g_debug_runtime_disable_rigged_object_draw) {
+ return;
+ }
+
+ PROFILER_GPU_ZONE(g_profiler_ctx, "RiggedObject::Draw()");
+ if(type == Object::kFullDraw){
+ last_draw_time = game_timer.game_time;
+ }
+
+ const bool _draw_animation_bones_xray = false;
+ Graphics* graphics = Graphics::Instance();
+ Textures* textures = Textures::Instance();
+ Shaders* shaders = Shaders::Instance();
+ Camera* cam = ActiveCameras::Get();
+
+ CHECK_GL_ERROR();
+ if(_draw_char_collision_world && animated){
+ //skeleton.DrawLocalBulletWorld();
+ skeleton_.col_bullet_world->Draw(scenegraph_->sky->GetSpecularCubeMapTexture());
+ return;
+ }
+ /*if(!animated){
+ return;
+ }*/
+
+ if(model_id[0]==-1) {
+ return;
+ }
+
+ graphics->setGLState(rigged_object_gl_state.gl_state);
+ CHECK_GL_ERROR();
+
+ // Get active shader variation (based on shadow catching and alpha to coverage)
+
+ const int kShaderStrSize = 1024;
+ char buf[2][kShaderStrSize];
+ char* shader_str[2] = {buf[0], buf[1]};
+ FormatString(shader_str[0], kShaderStrSize, "%s %s", shader, global_shader_suffix);
+ if(GPU_skinning){
+ FormatString(shader_str[1], kShaderStrSize, "%s #GPU_SKINNING", shader_str[0]);
+ std::swap(shader_str[0], shader_str[1]);
+ }
+
+ int the_shader = shaders->returnProgram(shader_str[0]);
+
+ if(the_shader!=-1) {
+ shaders->setProgram(the_shader);
+
+ CHECK_GL_ERROR();
+
+ shaders->SetUniformMat4("mvp",proj_view_matrix);
+ shaders->SetUniformMat4("projection_view_mat", proj_view_matrix);
+ if(GPU_skinning){
+ shaders->SetUniformMat4Array("bone_mats", display_bone_matrices);
+ }
+
+ CHECK_GL_ERROR();
+ }
+ else {
+ shaders->noProgram();
+ }
+ CHECK_GL_ERROR();
+
+ if(model_id[0]!=-1){
+ //int LOD = 0;
+ Model* model = &Models::Instance()->GetModel(model_id[lod_level]);
+ graphics->DebugTracePrint(model->path.c_str());
+ textures->bindBlankTexture(7);
+ if(type == RiggedObject::kFullDraw){
+ PROFILER_GPU_ZONE(g_profiler_ctx, "Set up full draw state");
+ if(blood_surface.blood_tex_mipmap_dirty){
+ Textures::Instance()->GenerateMipmap(blood_surface.blood_tex);
+ blood_surface.blood_tex_mipmap_dirty = false;
+ }
+
+ std::vector<mat4> shadow_matrix;
+ shadow_matrix.resize(4);
+ for(int i=0; i<4; ++i){
+ shadow_matrix[i] = cam->biasMatrix * graphics->cascade_shadow_mat[i];
+ }
+ if(g_simple_shadows || !g_level_shadows){
+ shadow_matrix[3] = cam->biasMatrix * graphics->simple_shadow_mat;
+ }
+ shaders->SetUniformMat4Array("shadow_matrix",shadow_matrix);
+ shaders->SetUniformVec3("cam_pos",ActiveCameras::Get()->GetPos());
+ shaders->SetUniformVec3("ws_light",scenegraph_->primary_light.pos);
+ shaders->SetUniformVec4("primary_light_color",vec4(scenegraph_->primary_light.color, scenegraph_->primary_light.intensity));
+ shaders->SetUniformFloat("time",game_timer.GetRenderTime());
+ shaders->SetUniformVec3("blood_tint", vec3(-1,-1,-1)); // Presumably this is here to force refreshing this uniform?
+ shaders->SetUniformVec3("blood_tint", Graphics::Instance()->config_.blood_color());
+ std::vector<vec3> ambient_cube_color_vec;
+ for(int i=0; i<6; ++i){
+ ambient_cube_color_vec.push_back(ambient_cube_color[i]);
+ }
+ shaders->SetUniformVec3Array("ambient_cube_color", ambient_cube_color_vec);
+ shaders->SetUniformVec3Array("tint_palette", palette_colors_srgb);
+ if(scenegraph_->light_probe_collection.probe_lighting_enabled && scenegraph_->light_probe_collection.light_probe_buffer_object_id != -1){
+ shaders->SetUniformInt("num_tetrahedra", scenegraph_->light_probe_collection.ShaderNumTetrahedra());
+ shaders->SetUniformInt("num_light_probes", scenegraph_->light_probe_collection.ShaderNumLightProbes());
+ shaders->SetUniformVec3("grid_bounds_min", scenegraph_->light_probe_collection.grid_lookup.bounds[0]);
+ shaders->SetUniformVec3("grid_bounds_max", scenegraph_->light_probe_collection.grid_lookup.bounds[1]);
+ shaders->SetUniformInt("subdivisions_x", scenegraph_->light_probe_collection.grid_lookup.subdivisions[0]);
+ shaders->SetUniformInt("subdivisions_y", scenegraph_->light_probe_collection.grid_lookup.subdivisions[1]);
+ shaders->SetUniformInt("subdivisions_z", scenegraph_->light_probe_collection.grid_lookup.subdivisions[2]);
+ shaders->SetUniformInt(shaders->GetTexUniform(TEX_AMBIENT_COLOR_BUFFER), TEX_AMBIENT_COLOR_BUFFER);
+ shaders->SetUniformInt(shaders->GetTexUniform(TEX_AMBIENT_GRID_DATA), TEX_AMBIENT_GRID_DATA);
+ glBindBuffer(GL_TEXTURE_BUFFER, scenegraph_->light_probe_collection.light_probe_buffer_object_id);
+ }
+ shaders->SetUniformInt("reflection_capture_num", scenegraph_->ref_cap_matrix.size());
+ if(!scenegraph_->ref_cap_matrix.empty()){
+ assert(!scenegraph_->ref_cap_matrix_inverse.empty());
+ shaders->SetUniformMat4Array("reflection_capture_matrix", scenegraph_->ref_cap_matrix);
+ shaders->SetUniformMat4Array("reflection_capture_matrix_inverse", scenegraph_->ref_cap_matrix_inverse);
+ }
+ shaders->SetUniformFloat("haze_mult", scenegraph_->haze_mult);
+
+ std::vector<mat4> light_volume_matrix;
+ std::vector<mat4> light_volume_matrix_inverse;
+ for(int i=0, len=scenegraph_->light_volume_objects_.size(); i<len; ++i){
+ Object* obj = scenegraph_->light_volume_objects_[i];
+ const mat4 &mat = obj->GetTransform();
+ light_volume_matrix.push_back(mat);
+ light_volume_matrix_inverse.push_back(invert(mat));
+ }
+ shaders->SetUniformInt("light_volume_num", light_volume_matrix.size());
+ if(!light_volume_matrix.empty()){
+ assert(!light_volume_matrix_inverse.empty());
+ shaders->SetUniformMat4Array("light_volume_matrix", light_volume_matrix);
+ shaders->SetUniformMat4Array("light_volume_matrix_inverse", light_volume_matrix_inverse);
+ }
+
+ if( g_no_reflection_capture == false) {
+ textures->bindTexture(scenegraph_->cubemaps, 19);
+ }
+ if( g_character_decals_enabled ) {
+ scenegraph_->BindDecals(the_shader);
+ }
+ scenegraph_->BindLights(the_shader);
+
+ if(texture_ref.valid()){
+ textures->bindTexture(texture_ref, 0);
+ }
+ if(normal_texture_ref.valid()){
+ textures->bindTexture(normal_texture_ref, 1);
+ }
+ TextureAssetRef catcher_ref;
+ TextureAssetRef caster_ref;
+ if(!graphics->drawing_shadow){
+ if(g_simple_shadows || !g_level_shadows){
+ textures->bindTexture(graphics->static_shadow_depth_ref, 4);
+ } else {
+ textures->bindTexture(graphics->cascade_shadow_depth_ref, 4);
+ }
+ textures->bindBlankTexture(5);
+ } else {
+ textures->bindBlankTexture(4);
+ textures->bindBlankTexture(5);
+ }
+ if(cube_map.valid()){
+ textures->bindTexture(cube_map, 2);
+ } else {
+ textures->bindTexture(scenegraph_->sky->GetSpecularCubeMapTexture(), 2);
+ }
+ TextureRef blood_tex = blood_surface.blood_tex;
+ if(blood_tex.valid()){
+ textures->bindTexture(blood_tex, 6);
+ }
+ if(palette_texture_ref.valid()){
+ textures->bindTexture(palette_texture_ref, 8);
+ } else {
+ textures->bindBlankTexture(8);
+ }
+ if(scenegraph_->light_probe_collection.light_volume_enabled && scenegraph_->light_probe_collection.ambient_3d_tex.valid()){
+ textures->bindTexture(scenegraph_->light_probe_collection.ambient_3d_tex, 16);
+ }
+ textures->bindTexture(graphics->screen_color_tex, 17);
+ textures->bindTexture(graphics->screen_depth_tex, 18);
+ }
+
+ if(need_vbo_update[lod_level]) {
+ need_vbo_update[lod_level] = false;
+ PROFILER_GPU_ZONE(g_profiler_ctx, "Fill dynamic vbos");
+ int model_num_verts = model->vertices.size()/3;
+ int fur_model_num_verts = fur_model.vertices.size()/3;
+ if(GPU_skinning){
+ PROFILER_ZONE(g_profiler_ctx, "GPU_skinning");
+ if(!transform_vec_vbo0[lod_level]->valid()){
+ transform_vec_vbo0[lod_level]->SetHint(model_num_verts*4*sizeof(GLfloat),kVBOStatic);
+ transform_vec_vbo0[lod_level]->Fill(model_num_verts*4*sizeof(GLfloat), &model->bone_ids[0]);
+
+ transform_vec_vbo1[lod_level]->SetHint(model_num_verts*4*sizeof(GLfloat),kVBOStatic);
+ transform_vec_vbo1[lod_level]->Fill(model_num_verts*4*sizeof(GLfloat), &model->bone_weights[0]);
+ }
+ if(fur_model.faces.size() > 0 && !fur_transform_vec_vbo0.valid()){
+ fur_transform_vec_vbo0.SetHint(fur_model_num_verts*4*sizeof(GLfloat),kVBOStatic);
+ fur_transform_vec_vbo0.Fill(fur_model_num_verts*4*sizeof(GLfloat), &fur_model.bone_ids[0]);
+
+ fur_transform_vec_vbo1.SetHint(fur_model_num_verts*4*sizeof(GLfloat),kVBOStatic);
+ fur_transform_vec_vbo1.Fill(fur_model_num_verts*4*sizeof(GLfloat), &fur_model.bone_weights[0]);
+ }
+ transform_vec_vbo2[lod_level]->SetHint(model_num_verts*3*sizeof(GLfloat),kVBODynamic);
+ transform_vec_vbo2[lod_level]->Fill(model_num_verts*3*sizeof(GLfloat), &model->morph_transform_vec[0]);
+ } else {
+ {
+ PROFILER_ZONE(g_profiler_ctx, "transform_vec_vbos");
+ transform_vec_vbo0[lod_level]->SetHint(model_num_verts*4*sizeof(GLfloat),kVBODynamic);
+ transform_vec_vbo0[lod_level]->Fill(model_num_verts*4*sizeof(GLfloat), &model->transform_vec[0][0]);
+
+ transform_vec_vbo1[lod_level]->SetHint(model_num_verts*4*sizeof(GLfloat),kVBODynamic);
+ transform_vec_vbo1[lod_level]->Fill(model_num_verts*4*sizeof(GLfloat), &model->transform_vec[1][0]);
+
+ transform_vec_vbo2[lod_level]->SetHint(model_num_verts*4*sizeof(GLfloat),kVBODynamic);
+ transform_vec_vbo2[lod_level]->Fill(model_num_verts*4*sizeof(GLfloat), &model->transform_vec[2][0]);
+ }
+ }
+
+ static std::vector<float> vel;
+ vel.resize(model_num_verts * 3);
+
+ if(!animated){ // If ragdolled, propagate velocity to fixed bones as well
+ for(int j=0, len=skeleton_.physics_bones.size(); j<len; j++){
+ if(skeleton_.fixed_obj[j] && skeleton_.physics_bones[j].bullet_object){
+ int bone = j;
+ while(skeleton_.parents[bone] != -1 && skeleton_.fixed_obj[bone]){
+ bone = skeleton_.parents[bone];
+ }
+ skeleton_.physics_bones[j].bullet_object->SetLinearVelocity(skeleton_.physics_bones[bone].bullet_object->GetLinearVelocity());
+ }
+ }
+ }
+
+ if(graphics->config_.motion_blur_amount_ > 0.01f){
+ PROFILER_ZONE(g_profiler_ctx, "Calculate vertex velocities");
+ for(int j=0; j<model_num_verts; j++){
+ vec3 vert_vel = skeleton_.physics_bones[(unsigned int)model->bone_ids[j][0]].bullet_object->GetLinearVelocity() * model->bone_weights[j][0];
+ for(int k=1; k<4; k++){
+ if(model->bone_weights[j][k] > 0.0f) {
+ vert_vel += skeleton_.physics_bones[(unsigned int)model->bone_ids[j][k]].bullet_object->GetLinearVelocity() * model->bone_weights[j][k];
+ }
+ }
+ vel[j*3+0] = vert_vel[0];
+ vel[j*3+1] = vert_vel[1];
+ vel[j*3+2] = vert_vel[2];
+ }
+ vel_vbo.SetHint(model_num_verts*3*sizeof(GLfloat),kVBODynamic);
+ vel_vbo.Fill(model_num_verts*3*sizeof(GLfloat), &vel[0]);
+ } else {
+ PROFILER_ZONE(g_profiler_ctx, "Calculate null vertex velocities");
+ for(int i=0, len=vel.size(); i<len; ++i){
+ vel[i] = 0.0f;
+ }
+ vel_vbo.SetHint(model_num_verts*3*sizeof(GLfloat),kVBODynamic);
+ vel_vbo.Fill(model_num_verts*3*sizeof(GLfloat), &vel[0]);
+ }
+
+ if(lod_level == 0 || !tex_transform_vbo[lod_level]->valid()){
+ PROFILER_ZONE(g_profiler_ctx, "tex_transform_vbo");
+ if(lod_level == 0) {
+ tex_transform_vbo[lod_level]->SetHint(model_num_verts*2*sizeof(GLfloat),kVBODynamic);
+ tex_transform_vbo[lod_level]->Fill(model_num_verts*2*sizeof(GLfloat), &model->tex_transform_vec[0]);
+ } else {
+ //Was static
+ tex_transform_vbo[lod_level]->SetHint(model_num_verts*2*sizeof(GLfloat),kVBOStatic);
+ tex_transform_vbo[lod_level]->Fill(model_num_verts*2*sizeof(GLfloat), &model->tex_transform_vec[0]);
+ }
+ }
+ if(lod_level == 0 && fur_model.faces.size() != 0){
+ PROFILER_ZONE(g_profiler_ctx, "fur_transform_vec_vbo");
+ if(GPU_skinning){
+ fur_transform_vec_vbo2.SetHint(fur_model_num_verts*3*sizeof(GLfloat),kVBODynamic);
+ fur_transform_vec_vbo2.Fill(fur_model_num_verts*3*sizeof(GLfloat), &fur_model.morph_transform_vec[0]);
+ } else {
+ fur_transform_vec_vbo0.SetHint(fur_model_num_verts*4*sizeof(GLfloat),kVBODynamic);
+ fur_transform_vec_vbo0.Fill(fur_model_num_verts*4*sizeof(GLfloat), &fur_model.transform_vec[0][0]);
+
+ fur_transform_vec_vbo1.SetHint(fur_model_num_verts*4*sizeof(GLfloat),kVBODynamic);
+ fur_transform_vec_vbo1.Fill(fur_model_num_verts*4*sizeof(GLfloat), &fur_model.transform_vec[1][0]);
+
+ fur_transform_vec_vbo2.SetHint(fur_model_num_verts*4*sizeof(GLfloat),kVBODynamic);
+ fur_transform_vec_vbo2.Fill(fur_model_num_verts*4*sizeof(GLfloat), &fur_model.transform_vec[2][0]);
+ }
+ if(!fur_tex_transform_vbo.valid()){
+ fur_tex_transform_vbo.SetHint(fur_model_num_verts*2*sizeof(GLfloat),kVBOStatic);
+ fur_tex_transform_vbo.Fill(fur_model_num_verts*2*sizeof(GLfloat), &fur_model.tex_transform_vec[0]);
+ }
+ }
+ }
+
+ DrawModel(model, lod_level);
+ /*ApplyBoneMatricesToModel(true);
+ transform_vec_vbo0.FillDynamic(model->num_vertices*4*sizeof(GLfloat), &model->transform_vec[0][0]);
+ transform_vec_vbo1.FillDynamic(model->num_vertices*4*sizeof(GLfloat), &model->transform_vec[1][0]);
+ transform_vec_vbo2.FillDynamic(model->num_vertices*4*sizeof(GLfloat), &model->transform_vec[2][0]);
+ tex_transform_vbo.FillDynamic(model->num_vertices*2*sizeof(GLfloat), &model->tex_transform_vec[0]);
+ DrawModel(model);*/
+ }
+
+ if(_draw_animation_bones_xray){
+ float closest_dist;
+ int closest_bone = -1;
+ for(unsigned i=0; i<display_bone_matrices.size(); ++i){
+ vec3 start = display_bone_matrices[i] * skeleton_.points[skeleton_.bones[skeleton_.physics_bones[i].bone].points[0]];
+ vec3 end = display_bone_matrices[i] * skeleton_.points[skeleton_.bones[skeleton_.physics_bones[i].bone].points[1]];
+ vec3 closest_point, closest_point_mouseray;
+ ClosestPointsBetweenTwoLineSegments(
+ ActiveCameras::Get()->GetPos(),
+ ActiveCameras::Get()->GetPos()+ActiveCameras::Get()->GetMouseRay()*200.0f,
+ start,
+ end,
+ &closest_point_mouseray,
+ &closest_point);
+ float dist = distance_squared(closest_point, closest_point_mouseray);
+ if(closest_bone == -1 || dist < closest_dist){
+ closest_dist = dist;
+ closest_bone = i;
+ }
+ }
+ const float kDistThreshold = 0.01f;
+ for(unsigned i=0; i<display_bone_matrices.size(); ++i){
+ vec3 start = display_bone_matrices[i] * skeleton_.points[skeleton_.bones[skeleton_.physics_bones[i].bone].points[0]];
+ vec3 end = display_bone_matrices[i] * skeleton_.points[skeleton_.bones[skeleton_.physics_bones[i].bone].points[1]];
+ vec4 color(1.0f);
+ if(closest_bone == (int)i && closest_dist < kDistThreshold){
+ color = vec4(1.0f,0.0f,0.0f,1.0f);
+ }
+ DebugDraw::Instance()->AddLine(start, end, color, _delete_on_draw, _DD_XRAY);
+ }
+ }
+ CHECK_GL_ERROR();
+}
+
+bool RiggedObject::DrawBoneConnectUI(Object* objects[], int num_obj_ids, IMUIContext &imui_context, EditorTypes::Tool tool, int id) {
+ Online* online = Online::Instance();
+ bool did_something = false;
+ float closest_dist;
+ int closest_bone = -1;
+ const float kDistThreshold = 0.01f;
+ const std::vector<vec3> &points = skeleton_.skeleton_asset_ref->GetData().points;
+ for(unsigned i=0; i<display_bone_matrices.size(); ++i){
+ if(!skeleton_.has_verts_assigned[skeleton_.physics_bones[i].bone]){
+ continue;
+ }
+ vec3 start = display_bone_matrices[i] * points[skeleton_.bones[skeleton_.physics_bones[i].bone].points[0]];
+ vec3 end = display_bone_matrices[i] * points[skeleton_.bones[skeleton_.physics_bones[i].bone].points[1]];
+ vec3 closest_point, closest_point_mouseray;
+ ClosestPointsBetweenTwoLineSegments(
+ ActiveCameras::Get()->GetPos(),
+ ActiveCameras::Get()->GetPos()+ActiveCameras::Get()->GetMouseRay()*200.0f,
+ start,
+ end,
+ &closest_point_mouseray,
+ &closest_point);
+ float dist = distance_squared(closest_point, closest_point_mouseray);
+ if(dist < kDistThreshold && (closest_bone == -1 || dist < closest_dist)){
+ closest_dist = dist;
+ closest_bone = i;
+ }
+ }
+
+ for(unsigned i=0; i<display_bone_matrices.size(); ++i){
+ if(!skeleton_.has_verts_assigned[skeleton_.physics_bones[i].bone]){
+ continue;
+ }
+ IMUIContext::UIState ui_state;
+ if(imui_context.DoButtonMouseOver(i + id * 100, closest_bone == (int)i,ui_state)){
+ did_something = true;
+ if(tool == EditorTypes::CONNECT){
+ for(int obj_id_iter = 0; obj_id_iter < num_obj_ids; ++obj_id_iter){
+ Object* obj = objects[obj_id_iter];
+ // Check if we already have a connection between this object and this character
+ bool already_attached = false;
+ for(unsigned j=0; j<children.size(); ++j){
+ AttachedEnvObject& attached_env_object = children[j];
+ if(attached_env_object.direct_ptr == obj){
+ // If this object is already attached to the selected bone, increment the number of connections
+ attached_env_object.bone_connection_dirty = true;
+ already_attached = true;
+ bool already_attached_to_bone = false;
+ for(unsigned k=0; k<kMaxBoneConnects; ++k){
+ BoneConnect& bone_connect = attached_env_object.bone_connects[k];
+ if(bone_connect.bone_id == (int)i){
+ ++bone_connect.num_connections;
+ already_attached_to_bone = true;
+ break;
+ }
+ }
+ // If this object is not already attached to the bone, try to attach it with a spare slot
+ if(!already_attached_to_bone){
+ for(unsigned k=0; k<kMaxBoneConnects; ++k){
+ BoneConnect& bone_connect = attached_env_object.bone_connects[k];
+ if(bone_connect.num_connections == 0){
+ bone_connect.bone_id = i;
+ bone_connect.num_connections = 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ // If object is not attached to character, then we need a new attachment
+ if(!already_attached){
+ // sync_attach_1 här sker själa ändringen, detta bör syncas
+ // Vi vill översätta detta till obj id's och replikeras hos clienten
+ // set parent och skapa ett attachedenvobj
+ children.resize(children.size() + 1);
+ AttachedEnvObject &attached_env_object = children.back();
+ attached_env_object.bone_connection_dirty = true;
+ attached_env_object.direct_ptr = obj;
+ attached_env_object.bone_connects[0].bone_id = i;
+ attached_env_object.bone_connects[0].num_connections = 1;
+ Object* obj = attached_env_object.direct_ptr;
+ obj->SetParent(scenegraph_->GetObjectFromID(char_id));
+
+ if (Online::Instance()->IsActive()) {
+ online->Send<OnlineMessages::AttachToMessage>(char_id, obj->GetID(), i, true, false);
+ }
+ for(unsigned j=1; j<kMaxBoneConnects; ++j){
+ BoneConnect &bone_connect = attached_env_object.bone_connects[j];
+ bone_connect.bone_id = -1;
+ bone_connect.num_connections = 0;
+ }
+ }
+ }
+ } else if(tool == EditorTypes::DISCONNECT){
+ for(int obj_id_iter = 0; obj_id_iter < num_obj_ids; ++obj_id_iter){
+ Object* obj = objects[obj_id_iter];
+ for(int j=children.size()-1; j>=0; --j){
+ AttachedEnvObject& attached_obj = children[j];
+ if(attached_obj.direct_ptr == obj){
+ int total_connections = 0; //NOTE(David): Number of connections remaining between object and character
+ for(unsigned k=0; k<kMaxBoneConnects; ++k){
+ BoneConnect& bone_connect = attached_obj.bone_connects[k];
+ if(bone_connect.bone_id == (int)i && bone_connect.num_connections > 0){
+ --bone_connect.num_connections;
+ }
+ total_connections += bone_connect.num_connections;
+ }
+ if(total_connections == 0){
+ Object* obj = attached_obj.direct_ptr;
+ obj->SetParent(NULL);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ vec4 bone_color(1.0f);
+ if(ui_state == IMUIContext::kActive){
+ bone_color = vec4(1.0f,0.0f,0.0f,1.0f);
+ }
+ if(ui_state == IMUIContext::kHot){
+ bone_color = vec4(1.0f,0.5f,0.5f,1.0f);
+ }
+ vec3 start = display_bone_matrices[i] * points[skeleton_.bones[skeleton_.physics_bones[i].bone].points[0]];
+ vec3 end = display_bone_matrices[i] * points[skeleton_.bones[skeleton_.physics_bones[i].bone].points[1]];
+ DebugDraw::Instance()->AddLine(start, end, bone_color, _delete_on_draw);
+ bone_color[3] *= 0.3f;
+ DebugDraw::Instance()->AddLine(start, end, bone_color, _delete_on_draw, _DD_XRAY);
+ }
+
+ for(unsigned i=0; i<children.size(); ++i){
+ const AttachedEnvObject& attached_env_object = children[i];
+ Object* obj = children[i].direct_ptr;
+ if(obj && obj->Selected()){
+ for(unsigned j=0; j<kMaxBoneConnects; ++j){
+ const BoneConnect& bone_connect = attached_env_object.bone_connects[j];
+ if(bone_connect.num_connections > 0){
+ int bone_id = bone_connect.bone_id;
+ const Bone& bone = skeleton_.bones[skeleton_.physics_bones[bone_id].bone];
+ vec3 start = obj->GetTranslation();
+ vec3 a = display_bone_matrices[bone_id] * skeleton_.points[bone.points[0]];
+ vec3 b = display_bone_matrices[bone_id] * skeleton_.points[bone.points[1]];
+ vec3 a_b = normalize(b-a);
+ vec3 end = display_bone_matrices[bone_id] * ((points[bone.points[0]]+skeleton_.points[bone.points[1]])*0.5f);
+ for(int k=0; k<bone_connect.num_connections; ++k){
+ float offset = ((float)k-(float)bone_connect.num_connections/2.0f)*0.03f;
+ DebugDraw::Instance()->AddLine(start+a_b*offset, end+a_b*offset, vec4(0.0f,1.0f,0.0f,0.8f), _delete_on_draw);
+ DebugDraw::Instance()->AddLine(start+a_b*offset, end+a_b*offset, vec4(0.0f,1.0f,0.0f,0.3f), _delete_on_draw, _DD_XRAY);
+ }
+ }
+ }
+ }
+ }
+ return did_something;
+}
+
+bool RiggedObject::Initialize() {
+ return true;
+}
+
+vec3 RiggedObject::GetTransformedVertex(int vert_id){
+ Model *model = &Models::Instance()->GetModel(model_id[0]);
+ LOG_ASSERT_LT(vert_id, (int)model->vertices.size()/3);
+ vec3 vert = vec3(model->vertices[vert_id*3+0],
+ model->vertices[vert_id*3+1],
+ model->vertices[vert_id*3+2]);
+ vec3 transformed;
+ for(unsigned i=0; i<4; ++i){
+ if(model->bone_weights[vert_id][i] > 0.0f){
+ transformed += display_bone_matrices[(unsigned int)model->bone_ids[vert_id][i]] * vert *
+ model->bone_weights[vert_id][i];
+ }
+ }
+ return transformed;
+}
+
+void RiggedObject::GetTransformedTri(int id, vec3* points){
+ Model *model = &Models::Instance()->GetModel(model_id[0]);
+ points[0] = GetTransformedVertex(model->faces[id*3+0]);
+ points[1] = GetTransformedVertex(model->faces[id*3+1]);
+ points[2] = GetTransformedVertex(model->faces[id*3+2]);
+}
+
+int GetIKAttachBone( const Skeleton &skeleton, const std::string & ik_attach ) {
+ Skeleton::SimpleIKBoneMap::const_iterator iter =
+ skeleton.simple_ik_bones.find(ik_attach);
+ int bone_id = -1;
+ if(iter != skeleton.simple_ik_bones.end()){
+ bone_id = iter->second.bone_id;
+ } else {
+ if(ik_attach == "hip"){
+ iter = skeleton.simple_ik_bones.find("torso");
+ bone_id = iter->second.bone_id;
+ bone_id = skeleton.parents[bone_id];
+ bone_id = skeleton.parents[bone_id];
+ }
+ }
+ return bone_id;
+}
+
+int GetAttachmentBone( const Skeleton &skeleton, const std::string &label, int bone ) {
+ Skeleton::SimpleIKBoneMap::const_iterator iter =
+ skeleton.simple_ik_bones.find(label);
+ int bone_id = -1;
+ if(iter != skeleton.simple_ik_bones.end()){
+ bone_id = iter->second.bone_id;
+ }
+
+ for(int i=0; i<bone; ++i){
+ bone_id = skeleton.parents[bone_id];
+ }
+ return bone_id;
+}
+
+void UpdateAttachedItemRagdoll(AttachedItem &stuck_item, const Skeleton& skeleton, const std::map<int, mat4> &weapon_offset) {
+ ItemObjectScriptReader& item = stuck_item.item;
+ //AttachmentType type = _at_grip;
+ if(item->state() == ItemObject::kSheathed){
+ //type = _at_sheathe;
+ }
+ int bone_id = stuck_item.bone_id;
+ mat4 bone_mat = skeleton.physics_bones[bone_id].bullet_object->GetTransform();
+ mat4 the_weap_mat = bone_mat * weapon_offset.find(item->GetID())->second;
+ mat4 temp_weap_mat = the_weap_mat;
+ temp_weap_mat.SetColumn(2, the_weap_mat.GetColumn(1)*-1.0f);
+ temp_weap_mat.SetColumn(1, the_weap_mat.GetColumn(2));
+ item.SetPhysicsTransform(temp_weap_mat);
+ if(item.just_created){
+ item.SetPhysicsTransform(temp_weap_mat);
+ item.just_created = false;
+ }
+ item.SetInterpInfo(1,1);
+}
+
+void RiggedObject::Update(float timestep) {
+ PROFILER_ZONE(g_profiler_ctx, "RiggedObject Update()");
+
+ Model* model = &Models::Instance()->GetModel(model_id[0]);
+ //blood_surface.CreateDripInTri(RangedRandomInt(0, model->faces.size()/3-1), vec3(1.0f/3.0f), 1.0f, 0.0f, true, SurfaceWalker::WATER);
+ {
+ if(game_timer.game_time < blood_surface.sleep_time){
+ PROFILER_ZONE(g_profiler_ctx, "Update blood surface");
+ blood_surface.Update(scenegraph_, timestep);
+ }
+ }
+ {
+ PROFILER_ZONE(g_profiler_ctx, "Update anim client");
+ anim_client.Update(timestep);
+ }
+ {
+ PROFILER_ZONE(g_profiler_ctx, "HandleAnimationEvents");
+ HandleAnimationEvents(anim_client);
+ }
+ if(!animated) {
+ PROFILER_ZONE(g_profiler_ctx, "Update ragdoll");
+ if(!first_ragdoll_frame) {
+ skeleton_.UpdateTwistBones(true);
+ }
+ first_ragdoll_frame = false;
+ // Updated attached items
+ for(AttachedItemList::iterator iter = attached_items.items.begin(); iter != attached_items.items.end(); ++iter) {
+ UpdateAttachedItemRagdoll(*iter, skeleton_, weapon_offset);
+ }
+ // Make sure attached items are not asleep if skeleton is awake
+ for(AttachedItemList::iterator iter = stuck_items.items.begin(); iter != stuck_items.items.end(); ++iter) {
+ if(skeleton_.physics_bones[0].bullet_object->IsActive()){
+ iter->item->WakeUpPhysics();
+ }
+ }
+ }
+
+ --time_until_next_anim_update;
+ --curr_anim_update_time;
+ --prev_anim_update_time;
+
+ if(time_until_next_anim_update <= 0) {
+ for(unsigned i=0; i<morph_targets.size(); ++i){
+ morph_targets[i].UpdateForInterpolation();
+ }
+ if(!animated){
+ PROFILER_ZONE(g_profiler_ctx, "Update ragdoll animation");
+ // Get results from animation blend tree
+ AnimOutput anim_output;
+ anim_client.GetMatrices(anim_output, &skeleton_.parents);
+ if(!anim_output.matrices.empty()){
+ // Store animation frame
+ for(int i=0, len=animation_frame_bone_matrices.size(); i<len; i++){
+ anim_output.matrices[i].origin *= model_char_scale;
+ animation_frame_bone_matrices[i] = anim_output.matrices[i];
+ }
+ // Apply physics weights for active ragdoll
+ std::map<BulletObject*, int> boid;
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); ++i){
+ boid[skeleton_.physics_bones[i].bullet_object] = i;
+ }
+ if(anim_output.physics_weights.size() >= animation_frame_bone_matrices.size()){
+ for(unsigned i=0; i<skeleton_.physics_joints.size(); ++i){
+ PhysicsJoint &joint = skeleton_.physics_joints[i];
+ int id[2];
+ id[0] = boid[joint.bt_bone[0]];
+ id[1] = boid[joint.bt_bone[1]];
+ float weight = max(anim_output.physics_weights[id[0]],
+ anim_output.physics_weights[id[1]]);
+ skeleton_.SetGFStrength(joint, weight * ragdoll_strength);
+ }
+ } else {
+ for(unsigned i=0; i<skeleton_.physics_joints.size(); ++i){
+ PhysicsJoint &joint = skeleton_.physics_joints[i];
+ skeleton_.SetGFStrength(joint, 1.0f * ragdoll_strength);
+ }
+ }
+ skeleton_.RefreshFixedJoints(animation_frame_bone_matrices);
+ // Apply morph weights from animation output
+ std::map<std::string, MorphTarget*> morph_target_names;
+ for(unsigned i=0; i<morph_targets.size(); ++i){
+ morph_target_names[morph_targets[i].name] = &morph_targets[i];
+ }
+ for(unsigned i=0; i<anim_output.shape_keys.size(); ++i){
+ const ShapeKeyBlend& skb = anim_output.shape_keys[i];
+ const std::string &label = skb.label;
+ if(morph_target_names.find(label) != morph_target_names.end()) {
+ morph_target_names[label]->anim_weight =
+ mix(morph_target_names[label]->anim_weight, skb.weight*skb.weight_weight, ragdoll_strength);
+ }
+ }
+ }
+ } else if(animated){
+ PROFILER_ZONE(g_profiler_ctx, "Update animation");
+ HandleLipSyncMorphTargets(timestep);
+ AnimOutput anim_output;
+ // Get results from animation blend tree
+ bool drawn_recently = last_draw_time > game_timer.game_time - 1.0f;
+ if(cached_animation_frame_bone_matrices.empty() || drawn_recently || !static_char){
+ {
+ PROFILER_ZONE(g_profiler_ctx, "anim_client.GetMatrices()");
+ anim_client.GetMatrices(anim_output, &skeleton_.parents);
+ }
+ for(int i=0, len=anim_output.matrices.size(); i<len; ++i){
+ anim_output.matrices[i].origin *= model_char_scale;
+ }
+ for(int i=0, len=anim_output.ik_bones.size(); i<len; ++i){
+ anim_output.ik_bones[i].transform.origin *= model_char_scale;
+ }
+ weap_anim_info_map = anim_output.weap_anim_info_map;
+ // Copy bone transforms for further manipulation
+ std::vector<BoneTransform> transforms = anim_output.matrices;
+ // Get info for moving and rotating entire character based on animation
+ total_center_offset += vec3(anim_output.delta_offset[0],
+ anim_output.delta_offset[1],
+ anim_output.delta_offset[2]) * model_char_scale;
+ total_rotation += anim_output.delta_rotation;
+
+ unmodified_transforms = anim_output.unmodified_matrices;
+
+ // Apply full-character rotation to bone and weapon transforms
+ vec4 angle_axis(0.0f,1.0f,0.0f,total_rotation);
+ quaternion rot(angle_axis);
+ mat4 rotmat4 = Mat4FromQuaternion(rot);
+ for(unsigned i=0; i<transforms.size(); i++){
+ transforms[i] = rotmat4 * transforms[i];
+ }
+ for(int i=0, len=anim_output.ik_bones.size(); i<len; ++i){
+ anim_output.ik_bones[i].transform = rotmat4 * anim_output.ik_bones[i].transform;
+ }
+ for(WeapAnimInfoMap::iterator iter = weap_anim_info_map.begin(); iter != weap_anim_info_map.end(); ++iter) {
+ WeapAnimInfo &weap_anim_info = iter->second;
+ if(weap_anim_info.relative_id == -1){
+ weap_anim_info.bone_transform = rotmat4 * weap_anim_info.bone_transform;
+ }
+ }
+
+ // Apply full-character translation to bone and weapon transforms
+ for(unsigned i=0; i<transforms.size(); i++){
+ transforms[i].origin += total_center_offset;
+ animation_frame_bone_matrices[i] = transforms[i];
+ }
+ for(int i=0, len=anim_output.ik_bones.size(); i<len; ++i){
+ anim_output.ik_bones[i].transform.origin += total_center_offset;
+ }
+ for(std::map<int, WeapAnimInfo>::iterator iter = weap_anim_info_map.begin(); iter != weap_anim_info_map.end(); ++iter) {
+ WeapAnimInfo &weap_anim_info = iter->second;
+ if(weap_anim_info.relative_id == -1){
+ weap_anim_info.bone_transform.origin += total_center_offset;
+ } else {
+ weap_anim_info.matrix = weap_anim_info.bone_transform.GetMat4();
+ }
+ }
+
+ // Apply animation morphs
+ std::map<std::string, MorphTarget*> morph_target_names;
+ for(unsigned i=0; i<morph_targets.size(); ++i){
+ morph_target_names[morph_targets[i].name] = &morph_targets[i];
+ }
+ for(unsigned i=0; i<anim_output.shape_keys.size(); ++i){
+ const ShapeKeyBlend& skb = anim_output.shape_keys[i];
+ const std::string &label = skb.label;
+ if(morph_target_names.find(label) != morph_target_names.end()) {
+ morph_target_names[label]->anim_weight = skb.weight*skb.weight_weight;
+ }
+ }
+
+ // Apply status keys
+ status_keys.clear();
+ for(unsigned i=0; i<anim_output.status_keys.size(); ++i){
+ const StatusKeyBlend key = anim_output.status_keys[i];
+ status_keys[key.label] = key.weight*key.weight_weight;
+ }
+
+ blended_bone_paths = anim_output.ik_bones;
+ cached_animation_frame_bone_matrices = animation_frame_bone_matrices;
+ } else {
+ animation_frame_bone_matrices = cached_animation_frame_bone_matrices;
+ }
+
+ ASArglist args;
+ if(drawn_recently){
+ args.Add(anim_update_period);
+ } else {
+ args.Add(-anim_update_period);
+ }
+ {
+ PROFILER_ZONE(g_profiler_ctx, "Angelscript FinalAnimationMatrixUpdate()");
+ as_context->CallScriptFunction(as_funcs.final_animation_matrix_update, &args);
+ }
+ // Apply transform to physics objects
+ for(unsigned i=0; i<animation_frame_bone_matrices.size(); i++){
+ BulletObject* b_object = skeleton_.physics_bones[i].bullet_object;
+ if(!b_object){
+ continue;
+ }
+ b_object->SetRotationAndVel(animation_frame_bone_matrices[i].rotation, -curr_anim_update_time);
+ b_object->SetPositionAndVel(animation_frame_bone_matrices[i].origin, -curr_anim_update_time);
+ b_object->SetRotation(animation_frame_bone_matrices[i].rotation);
+ b_object->SetPosition(animation_frame_bone_matrices[i].origin);
+ if(animation_config.kDisablePhysicsInterpolation){
+ b_object->FixDiscontinuity();
+ b_object->SetPosition(animation_frame_bone_matrices[i].origin);
+ }
+ b_object->UpdateTransform();
+ }
+
+ UpdateAttachedItems();
+
+ {
+ PROFILER_ZONE(g_profiler_ctx, "Angelscript FinalAttachedItemUpdate()");
+ as_context->CallScriptFunction(as_funcs.final_attached_item_update, &args);
+ }
+ }
+ max_time_until_next_anim_update = anim_update_period;
+ time_until_next_anim_update = anim_update_period;
+ prev_anim_update_time = curr_anim_update_time;
+ curr_anim_update_time = 0;
+
+ UpdateCollisionObjects();
+
+ //Store the current animation frame bones for the next network character update.
+ StoreNetworkBones();
+ }
+
+ if(animated){
+ for(AttachedItemList::iterator iter = attached_items.items.begin(); iter != attached_items.items.end(); ++iter) {
+ ItemObjectScriptReader& item = iter->item;
+ item.SetInterpInfo(GetTimeSinceLastAnimUpdate(), GetTimeBetweenLastTwoAnimUpdates());
+ }
+ for(AttachedItemList::iterator iter=stuck_items.items.begin(); iter != stuck_items.items.end(); ++iter) {
+ iter->item.SetInterpInfo(GetTimeSinceLastAnimUpdate(), GetTimeBetweenLastTwoAnimUpdates());
+ }
+ }
+}
+
+
+namespace {
+ struct BoneInfo {
+ int id;
+ float weight;
+ };
+ bool BoneInfoIDSort (const BoneInfo &a, const BoneInfo &b){
+ return a.id < b.id;
+ }
+ bool BoneInfoWeightSort (const BoneInfo &a, const BoneInfo &b){
+ return a.weight > b.weight;
+ }
+ void ApplyNewEdgeCollapseToModel(const Model &base_model,
+ Model &model,
+ const WOLFIRE_SIMPLIFY::SimplifyModel &sm,
+ const WOLFIRE_SIMPLIFY::ParentRecordListVec &vert_parent,
+ const WOLFIRE_SIMPLIFY::ParentRecordListVec &tex_parent)
+ {
+ std::vector<float> sm_vertices(sm.vertices.size());
+ std::vector<float> sm_tex_coords(sm.tex_coords.size());
+ for(int i=0, len=vert_parent.size(); i<len; ++i){
+ WOLFIRE_SIMPLIFY::ParentRecordList parents = vert_parent[i];
+ float total[3];
+ for(int j=0; j<3; ++j){
+ total[j] = 0.0f;
+ }
+ for(WOLFIRE_SIMPLIFY::ParentRecordList::iterator iter = parents.begin(); iter != parents.end(); ++iter){
+ WOLFIRE_SIMPLIFY::ParentRecord pr = (*iter);
+ if(pr.weight > 0.0f){
+ int parent_vert = sm.old_vert_id[pr.id];
+ int parent_vert_index = parent_vert*3;
+ for(int j=0; j<3; ++j){
+ total[j] += base_model.vertices[parent_vert_index+j] * pr.weight;
+ }
+ }
+ }
+ for(WOLFIRE_SIMPLIFY::ParentRecordList::iterator iter = parents.begin(); iter != parents.end(); ++iter){
+ WOLFIRE_SIMPLIFY::ParentRecord pr = (*iter);
+ int parent_vert_index = pr.id*3;
+ for(int j=0; j<3; ++j){
+ sm_vertices[parent_vert_index+j] = total[j];
+ }
+ }
+ }
+ for(int i=0, len=tex_parent.size(); i<len; ++i){
+ WOLFIRE_SIMPLIFY::ParentRecordList parents = tex_parent[i];
+ float total[2];
+ for(int j=0; j<2; ++j){
+ total[j] = 0.0f;
+ }
+ for(WOLFIRE_SIMPLIFY::ParentRecordList::iterator iter = parents.begin(); iter != parents.end(); ++iter){
+ WOLFIRE_SIMPLIFY::ParentRecord pr = (*iter);
+ int parent_tex_index = sm.old_tex_id[pr.id]*2;
+ for(int j=0; j<2; ++j){
+ total[j] += base_model.tex_coords[parent_tex_index+j] * pr.weight;
+ }
+ }
+ for(WOLFIRE_SIMPLIFY::ParentRecordList::iterator iter = parents.begin(); iter != parents.end(); ++iter){
+ WOLFIRE_SIMPLIFY::ParentRecord pr = (*iter);
+ int parent_tex_index = pr.id*2;
+ for(int j=0; j<2; ++j){
+ sm_tex_coords[parent_tex_index+j] = total[j];
+ }
+ }
+ }
+ int num_indices = sm.vert_indices.size();
+ for(int i=0; i<num_indices; ++i){
+ int vert_index = sm.vert_indices[i]*3;
+ for(int j=0; j<3; ++j){
+ model.vertices.push_back(sm_vertices[vert_index+j]);
+ }
+ int tex_index = sm.tex_indices[i]*2;
+ for(int j=0; j<2; ++j){
+ model.tex_coords.push_back(sm_tex_coords[tex_index+j]);
+ }
+ }
+ }
+} // namespace ""
+
+void RiggedObject::Load(const std::string& character_path, vec3 pos, SceneGraph* scenegraph_ptr, OGPalette &palette) {
+ //CharacterRef char_ref = Characters::Instance()->ReturnRef(character_path);
+ CharacterRef char_ref = Engine::Instance()->GetAssetManager()->LoadSync<Character>(character_path);
+ scenegraph_ = scenegraph_ptr;
+
+ //ofc = ObjectFiles::Instance()->ReturnRef(char_ref->GetObjPath());
+ ofc = Engine::Instance()->GetAssetManager()->LoadSync<ObjectFile>(char_ref->GetObjPath());
+
+ Models* mi = Models::Instance();
+ int the_model_id = mi->loadModel(ofc->model_name, _MDL_CENTER);
+ model_id[0] = the_model_id;
+
+ Textures *ti = Textures::Instance();
+ texture_ref = Engine::Instance()->GetAssetManager()->LoadSync<TextureAsset>(ofc->color_map, PX_SRGB, 0x0);
+ normal_texture_ref = Engine::Instance()->GetAssetManager()->LoadSync<TextureAsset>(ofc->normal_map);
+
+ if(!ofc->palette_map_path.empty()) {
+ palette_texture_ref = Engine::Instance()->GetAssetManager()->LoadSync<TextureAsset>(ofc->palette_map_path);
+ for(int i=0; i<max_palette_elements; ++i){
+ if(!ofc->palette_label[i].empty()){
+ LabeledColor lc;
+ lc.label = ofc->palette_label[i];
+ lc.channel = i;
+ lc.color = vec3(1.0f);
+ palette.push_back(lc);
+ }
+ }
+ }
+
+ model_id[0] = Models::Instance()->CopyModel(the_model_id);
+ Model *model = &Models::Instance()->GetModel(model_id[0]);
+ floor_height = model->min_coords[1];
+
+ UpdateGPUSkinning();
+ SetTranslation(pos);
+
+ skeleton_.SetBulletWorld(scenegraph_->bullet_world_);
+
+ const std::string& skeleton_path = char_ref->GetSkeletonPath();
+
+ FZXAssetRef fzx_ref;
+ std::string fzx_path = ofc->model_name.substr(0, ofc->model_name.size()-3)+"fzx";
+ if(FileExists(fzx_path.c_str(), kDataPaths|kModPaths)){
+ //fzx_ref = FZXAssets::Instance()->ReturnRef(fzx_path);
+ fzx_ref = Engine::Instance()->GetAssetManager()->LoadSync<FZXAsset>(fzx_path);
+ }
+ skeleton_.Read(skeleton_path, model_char_scale, char_scale, fzx_ref);
+
+ int num_verts = model->vertices.size()/3;
+ model->transform_vec.resize(4);
+ model->transform_vec[0].resize(num_verts);
+ model->transform_vec[1].resize(num_verts);
+ model->transform_vec[2].resize(num_verts);
+ model->transform_vec[3].resize(num_verts);
+ model->tex_transform_vec.resize(num_verts);
+ model->morph_transform_vec.resize(num_verts);
+
+ //SkeletonAssetRef skeleton_asset_ref = SkeletonAssets::Instance()->ReturnRef(skeleton_path);
+ SkeletonAssetRef skeleton_asset_ref = Engine::Instance()->GetAssetManager()->LoadSync<SkeletonAsset>(skeleton_path);
+ const SkeletonFileData &data = skeleton_asset_ref->GetData();
+ model->bone_weights = data.model_bone_weights;
+ model->bone_ids = data.model_bone_ids;
+
+ skeleton_.SetGravity(true);
+ skeleton_.ReduceROM(0.5f);
+ skeleton_.UnlinkFromBulletWorld();
+
+ anim_client.SetBoneMasses(skeleton_.GetBoneMasses());
+ anim_client.SetSkeleton(&skeleton_);
+ anim_client.SetRetargeting(skeleton_path);
+
+ const mat4 &transform = GetTransform();
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); i++){
+ if(!skeleton_.physics_bones[i].bullet_object){
+ continue;
+ }
+ skeleton_.physics_bones[i].bullet_object->ApplyTransform(transform);
+ }
+
+ ik_offset.resize(skeleton_.physics_bones.size());
+ total_ik_offset = 0.0f;
+ ik_enabled = true;
+ ragdoll_strength = 1.0f;
+
+ lipsync_reader.SetVisemeMorphs(char_ref->GetVisemeMorphs());
+
+ if(skeleton_.simple_ik_bones.find("head")!=skeleton_.simple_ik_bones.end()){
+ const std::vector<int> &parents = skeleton_.parents;
+ const SimpleIKBone &head_sib = skeleton_.simple_ik_bones["head"];
+ int the_bone = head_sib.bone_id;
+ for(int i=0; i<head_sib.chain_length; ++i){
+ if(the_bone != -1){
+ head_chain.insert(skeleton_.physics_bones[the_bone].bullet_object);
+ the_bone = parents[the_bone];
+ }
+ }
+ }
+
+ const std::vector<MorphInfo> &morphs = char_ref->GetMorphs();
+ for(unsigned i=0; i<morphs.size(); ++i){
+ MorphTarget mt;
+ mt.Load(ofc->model_name, the_model_id, morphs[i].label, morphs[i].num_steps);
+ morph_targets.push_back(mt);
+ }
+
+
+ const std::string &morph_path = char_ref->GetPermanentMorphPath();
+ if(!morph_path.empty() && FileExists(morph_path.c_str(), kDataPaths|kModPaths)){
+ MorphTarget mt;
+ mt.Load(ofc->model_name, the_model_id, morph_path, 1, true);
+ mt.name = "permanent";
+ mt.script_weight_weight = 1.0f;
+ mt.script_weight = 1.0f;
+ morph_targets.push_back(mt);
+ }
+ if(!LoadSimplificationCache(char_ref->path_)){
+ const Model &base_model = Models::Instance()->GetModel(model_id[0]);
+ WOLFIRE_SIMPLIFY::SimplifyModelInput smi;
+ // Prepare model to pass to simplify algorithm
+ for(int i=0, len=base_model.faces.size(); i<len; ++i){
+ int vert_index = base_model.faces[i]*3;
+ for(int j=0; j<3; ++j){
+ smi.vertices.push_back(base_model.vertices[vert_index+j]);
+ }
+ smi.old_vert_id.push_back(base_model.faces[i]);
+ int tex_index = base_model.faces[i]*2;
+ for(int j=0; j<2; ++j){
+ smi.tex_coords.push_back(base_model.tex_coords[tex_index+j]);
+ }
+ smi.old_tex_id.push_back(base_model.faces[i]);
+ }
+
+ const int LOD_LEVELS = 5;
+ WOLFIRE_SIMPLIFY::MorphModel lod[LOD_LEVELS];
+ WOLFIRE_SIMPLIFY::ParentRecordListVec vert_parents[LOD_LEVELS];
+ WOLFIRE_SIMPLIFY::ParentRecordListVec tex_parents[LOD_LEVELS];
+ if(WOLFIRE_SIMPLIFY::SimplifyMorphLOD(smi, lod, vert_parents, tex_parents, LOD_LEVELS)){
+ std::vector<BoneInfo> bone_info_accumulate;
+ std::vector<vec4> bone_id;
+ std::vector<vec4> bone_weight;
+
+ for(int lod_index=1; lod_index<4; ++lod_index){
+ model_id[lod_index] = Models::Instance()->AddModel();
+ Model& model = Models::Instance()->GetModel(model_id[lod_index]);
+ WOLFIRE_SIMPLIFY::SimplifyModel sm = lod[lod_index].model;
+ WOLFIRE_SIMPLIFY::ParentRecordListVec vert_parent = vert_parents[lod_index-1];
+ WOLFIRE_SIMPLIFY::ParentRecordListVec tex_parent = tex_parents[lod_index-1];
+ bone_id.clear();
+ bone_weight.clear();
+ bone_id.resize(sm.vertices.size()/3, 0.0f);
+ bone_weight.resize(sm.vertices.size()/3, 0.0f);
+ for(int i=0, len=vert_parent.size(); i<len; ++i){
+ WOLFIRE_SIMPLIFY::ParentRecordList parents = vert_parent[i];
+ float total[3];
+ bone_info_accumulate.clear();
+ for(int j=0; j<3; ++j){
+ total[j] = 0.0f;
+ }
+ for(WOLFIRE_SIMPLIFY::ParentRecordList::iterator iter = parents.begin(); iter != parents.end(); ++iter){
+ WOLFIRE_SIMPLIFY::ParentRecord pr = (*iter);
+ if(pr.weight > 0.0f){
+ int parent_vert = sm.old_vert_id[pr.id];
+ int parent_vert_index = parent_vert*3;
+ for(int j=0; j<3; ++j){
+ total[j] += base_model.vertices[parent_vert_index+j] * pr.weight;
+ }
+ for(int j=0; j<4; ++j){
+ BoneInfo bi;
+ bi.id = (int)base_model.bone_ids[parent_vert][j];
+ bi.weight = base_model.bone_weights[parent_vert][j] * pr.weight;
+ if(bi.weight > 0.0f){
+ bone_info_accumulate.push_back(bi);
+ }
+ }
+ }
+ }
+ // Merge duplicates
+ std::sort(bone_info_accumulate.begin(), bone_info_accumulate.end(), BoneInfoIDSort);
+ if(!bone_info_accumulate.empty()){
+ int last_unique = 0;
+ for(int i=1,len=bone_info_accumulate.size(); i<len; ++i){
+ BoneInfo &bi = bone_info_accumulate[i];
+ BoneInfo &unique_bi = bone_info_accumulate[last_unique];
+ if(bi.id == unique_bi.id){
+ unique_bi.weight += bi.weight;
+ } else {
+ ++last_unique;
+ bone_info_accumulate[last_unique] = bi;
+ }
+ }
+ bone_info_accumulate.resize(last_unique+1);
+ }
+ // Only use most-weighted 4 bones
+ std::sort(bone_info_accumulate.begin(), bone_info_accumulate.end(), BoneInfoWeightSort);
+ if(bone_info_accumulate.size() > 4){
+ bone_info_accumulate.resize(4);
+ }
+ // Normalize weights to add up to 1.0
+ float total_weight = 0.0f;
+ for(int i=0,len=bone_info_accumulate.size(); i<len; ++i){
+ BoneInfo &bi = bone_info_accumulate[i];
+ total_weight += bi.weight;
+ }
+ if(total_weight != 0.0f){
+ for(int i=0,len=bone_info_accumulate.size(); i<len; ++i){
+ BoneInfo &bi = bone_info_accumulate[i];
+ bi.weight /= total_weight;
+ }
+ }
+ for(WOLFIRE_SIMPLIFY::ParentRecordList::iterator iter = parents.begin(); iter != parents.end(); ++iter){
+ WOLFIRE_SIMPLIFY::ParentRecord pr = (*iter);
+ int parent_vert_index = pr.id*3;
+ for(int j=0; j<3; ++j){
+ sm.vertices[parent_vert_index+j] = total[j];
+ }
+ for(int j=0,len=bone_info_accumulate.size(); j<len; ++j){
+ bone_id[pr.id][j] = (float)bone_info_accumulate[j].id;
+ bone_weight[pr.id][j] = bone_info_accumulate[j].weight;
+ }
+ }
+ }
+ for(int i=0, len=tex_parent.size(); i<len; ++i){
+ WOLFIRE_SIMPLIFY::ParentRecordList parents = tex_parent[i];
+ float total[2];
+ for(int j=0; j<2; ++j){
+ total[j] = 0.0f;
+ }
+ for(WOLFIRE_SIMPLIFY::ParentRecordList::iterator iter = parents.begin(); iter != parents.end(); ++iter){
+ WOLFIRE_SIMPLIFY::ParentRecord pr = (*iter);
+ int parent_tex_index = sm.old_tex_id[pr.id]*2;
+ for(int j=0; j<2; ++j){
+ total[j] += base_model.tex_coords[parent_tex_index+j] * pr.weight;
+ }
+ }
+ for(WOLFIRE_SIMPLIFY::ParentRecordList::iterator iter = parents.begin(); iter != parents.end(); ++iter){
+ WOLFIRE_SIMPLIFY::ParentRecord pr = (*iter);
+ int parent_tex_index = pr.id*2;
+ for(int j=0; j<2; ++j){
+ sm.tex_coords[parent_tex_index+j] = total[j];
+ }
+ }
+ }
+ int num_indices = sm.vert_indices.size();
+ for(int i=0; i<num_indices; ++i){
+ int vert_index = sm.vert_indices[i]*3;
+ for(int j=0; j<3; ++j){
+ model.vertices.push_back(sm.vertices[vert_index+j]);
+ }
+ model.bone_ids.push_back(bone_id[sm.vert_indices[i]]);
+ model.bone_weights.push_back(bone_weight[sm.vert_indices[i]]);
+ int tex_index = sm.tex_indices[i]*2;
+ for(int j=0; j<2; ++j){
+ model.tex_coords.push_back(sm.tex_coords[tex_index+j]);
+ }
+ model.faces.push_back(i);
+ }
+
+ model.RemoveDuplicatedVerts();
+ model.RemoveDegenerateTriangles();
+ model.OptimizeTriangleOrder();
+ model.OptimizeVertexOrder();
+
+ int model_num_verts = model.vertices.size()/3;
+ model.transform_vec.resize(4);
+ for(int j=0; j<4; ++j){
+ model.transform_vec[j].resize(model_num_verts);
+ }
+ model.tex_transform_vec.resize(model_num_verts);
+ model.morph_transform_vec.resize(model_num_verts);
+
+ std::queue<MorphTarget*> queue;
+ for(unsigned i=0; i<morph_targets.size(); ++i){
+ queue.push(&morph_targets[i]);
+ }
+ while(!queue.empty()){
+ MorphTarget& morph = (*queue.front());
+ queue.pop();
+ for(unsigned i=0; i<morph.morph_targets.size(); ++i){
+ queue.push(&morph.morph_targets[i]);
+ }
+ if(morph.model_id[0] != -1) {
+ morph.model_id[lod_index] = Models::Instance()->AddModel();
+ morph.model_copy[lod_index] = true;
+ Model& morph_model = Models::Instance()->GetModel(morph.model_id[lod_index]);
+ Model& base_morph_model = Models::Instance()->GetModel(morph.model_id[0]);
+ ApplyNewEdgeCollapseToModel(base_morph_model, morph_model, sm, vert_parent, tex_parent);
+ morph_model.CopyVertCollapse(model);
+ }
+ }
+ for(unsigned i=0; i<morph_targets.size(); ++i){
+ morph_targets[i].CalcVertsModified(model_id[lod_index],lod_index);
+ }
+ }
+ SaveSimplificationCache(char_ref->path_);
+ } else {
+ LOGI << "Simplification failed!" << std::endl;
+ }
+ }
+
+ Model *attach_model = &Models::Instance()->GetModel(model_id[0]);
+ blood_surface.AttachToModel(attach_model);
+ blood_surface.sleep_time = game_timer.game_time;
+
+ if (!blood_inited && IsMainThread()) {
+ blood_surface.PreDrawFrame(
+ Textures::Instance()->getWidth(texture_ref) / 4,
+ Textures::Instance()->getHeight(texture_ref) / 4);
+ blood_surface.Update(scenegraph_, 0.001f, true);
+ blood_inited = true;
+ }
+
+ const std::string &fur_path = char_ref->GetFurPath();
+ int fur_model_id = -1;
+ if(!fur_path.empty() && FileExists(fur_path.c_str(), kDataPaths|kModPaths)){
+ fur_model_id = mi->loadModel(fur_path.c_str(), 0);
+ CreateFurModel(model_id[0], fur_model_id);
+ }
+
+ Textures::Instance()->setWrap(GL_REPEAT, GL_CLAMP_TO_EDGE);
+ fur_tex = Engine::Instance()->GetAssetManager()->LoadSync<TextureAsset>("Data/Textures/fur_fin.tga");
+ Textures::Instance()->setWrap(GL_REPEAT, GL_REPEAT);
+
+ skeleton_.AlternateHull(ofc->model_name, Models::Instance()->GetModel(model_id[0]).old_center, model_char_scale);
+ display_bone_matrices.resize(skeleton_.physics_bones.size());
+ display_bone_transforms.resize(skeleton_.physics_bones.size());
+ animation_frame_bone_matrices.resize(skeleton_.physics_bones.size());
+
+ // Allocate aligned memory for SIMD matrix palette skinning
+ #ifdef USE_SSE
+ int bytes_needed = display_bone_matrices.size() * sizeof(simd_mat4);
+ #ifdef _WIN32
+ simd_bone_mats = (simd_mat4*)_aligned_malloc(bytes_needed, 16);
+ #else
+ #ifdef __APPLE__
+ simd_bone_mats = (simd_mat4*)malloc(bytes_needed);
+ #else
+ simd_bone_mats = (simd_mat4*)memalign(16, bytes_needed);
+ #endif
+ #endif
+ #endif
+
+ cached_skeleton_info_.bind_matrices.resize(skeleton_.physics_bones.size());
+ for(int i=0, len=skeleton_.physics_bones.size(); i<len; ++i){
+ cached_skeleton_info_.bind_matrices[i] = ASGetBindMatrix(&skeleton_, i);
+ }
+}
+
+bool RiggedObject::InHeadChain(BulletObject* obj) {
+ if(head_chain.find(obj) != head_chain.end()) {
+ return true;
+ }
+ return false;
+}
+
+void RiggedObject::InvalidatedItem(ItemObjectScriptReader *invalidated) {
+ int weap_id = -1;
+ for(AttachedItemList::iterator iter = attached_items.items.begin(); iter != attached_items.items.end(); ++iter) {
+ if(&(iter->item) == invalidated){
+ weap_id = (iter->item)->GetID();
+ break;
+ }
+ }
+ attached_items.InvalidatedItem(invalidated);
+ if(as_context){
+ ASArglist args;
+ args.Add(weap_id);
+ as_context->CallScriptFunction(as_funcs.notify_item_detach, &args);
+ }
+ char_anim.clear();
+}
+
+mat4 LoadAttachPose(const std::string &anim_path, const std::string &ik_attach, const Skeleton &skeleton, int* bone_id_ptr, bool mirror, bool blade_orient, int chain_depth){
+ int& bone_id = *bone_id_ptr;
+ //AnimationRef anim_ref = Animations::Instance()->ReturnRef(anim_path);
+ AnimationRef anim_ref = Engine::Instance()->GetAssetManager()->LoadSync<Animation>(anim_path);
+ if( anim_ref.valid() ) {
+ AnimOutput anim_output;
+ BlendMap blend_map;
+ AnimInput anim_input(blend_map, NULL);
+ anim_ref->GetMatrices(0.0f, anim_output, anim_input);
+
+ BoneTransform weapon_bt = anim_output.weapon_matrices[0];
+ weapon_bt.origin += anim_output.center_offset;
+
+ bone_id = GetIKAttachBone(skeleton, ik_attach);
+ for(int i=0; i<chain_depth; ++i){
+ bone_id = skeleton.parents[bone_id];
+ }
+ if(mirror){
+ bone_id = skeleton.skeleton_asset_ref->GetData().symmetry[bone_id];
+ anim_input.mirrored = mirror;
+ anim_ref->GetMatrices(0.0f, anim_output, anim_input);
+ }
+ BoneTransform hand_bt = anim_output.matrices[bone_id];
+ hand_bt.origin += anim_output.center_offset;
+ if(mirror){
+ weapon_bt.origin[0] *= -1.0f;
+ {
+ quaternion old_rotation = weapon_bt.rotation;
+ vec4 aa = Quat_2_AA(old_rotation);
+ aa[0] *= -1.0f;
+ aa[3] *= -1.0f;
+ weapon_bt.rotation = quaternion(aa);
+ if(blade_orient){
+ quaternion new_rotation = weapon_bt.rotation;
+ vec3 tip_dir;
+ vec3 blade_dir;
+ if(ik_attach == "hip"){
+ tip_dir = normalize(vec3(0.0f,-0.5f,1.0f));
+ blade_dir = vec3(0.0f,-1.0f,0.0f);
+ } else {
+ tip_dir = vec3(0.0f,0.0f,-1.0f);
+ blade_dir = normalize(vec3(-0.25f, -0.165f, 0.0f));
+ }
+ vec3 new_blade_dir = new_rotation * invert(old_rotation) * blade_dir;
+ vec3 targ_blade_dir;
+ if(ik_attach == "hip"){
+ targ_blade_dir = vec3(0.0f,-1.0f,0.0f);
+ } else {
+ targ_blade_dir = vec3(0.25f, -0.165f, 0.0f);
+ }
+ if(dot(new_blade_dir, targ_blade_dir) < 0.0f){
+ //quaternion quat(vec4(0.0f,0.0f,1.0f,3.1415f));
+ //weapon_bt.rotation = quat * weapon_bt.rotation;
+ vec3 axis_candidate[3];
+ axis_candidate[0] = new_rotation * vec3(1.0f,0.0f,0.0f);
+ axis_candidate[1] = new_rotation * vec3(0.0f,1.0f,0.0f);
+ axis_candidate[2] = new_rotation * vec3(0.0f,0.0f,1.0f);
+ int which = 0;
+ if(fabs(axis_candidate[1][2]) > fabs(axis_candidate[0][2]) &&
+ fabs(axis_candidate[1][2]) > fabs(axis_candidate[2][2]))
+ {
+ which = 1;
+ } else if(fabs(axis_candidate[2][2]) > fabs(axis_candidate[0][2]))
+ {
+ which = 2;
+ }
+ quaternion quat(vec4(axis_candidate[which],3.1415f));
+ weapon_bt.rotation = quat * weapon_bt.rotation;
+ }
+ }
+ }
+ }
+ mat4 hand_mat = hand_bt.GetMat4();
+ mat4 weapon_mat = weapon_bt.GetMat4();
+ return invert(hand_mat) * weapon_mat;
+ } else {
+ return mat4(1.0f);
+ }
+}
+
+void RiggedObject::SetWeaponOffset(AttachedItem &stuck_item, AttachmentType type, bool mirror){
+ ItemObjectScriptReader& item = stuck_item.item;
+ mat4& wo = weapon_offset[item->GetID()];
+ mat4& wor = weapon_offset_retarget[item->GetID()];
+
+ int bone_id;
+ const ItemRef& ir = item.GetAttached()->item_ref();
+ std::string ir_attachanimpath = ir->GetAttachAnimPath(type);
+ if( ir_attachanimpath.empty() == false ) {
+ wo = LoadAttachPose(ir_attachanimpath, ir->GetIKAttach(type), skeleton_, &bone_id, mirror, true, 0);
+ if(!ir->GetAttachAnimBasePath(type).empty()){
+ wor = LoadAttachPose(ir->GetAttachAnimBasePath(type), ir->GetIKAttach(type), skeleton_, &bone_id, mirror, true, 0);
+ wor = invert(wor) * wo;
+ } else {
+ wor.LoadIdentity();
+ }
+ } else {
+ LOGE << "No AttachAnimPath for type " << type << " in " << GetName() << std::endl;
+ }
+
+ stuck_item.rel_mat = wo;
+ stuck_item.bone_id = bone_id;
+}
+
+void RiggedObject::SetItemAttachment(AttachedItem &stuck_item, AttachmentRef attachment_ref, bool mirror){
+ ItemObjectScriptReader& item = stuck_item.item;
+ mat4& wo = weapon_offset[item->GetID()];
+ mat4& wor = weapon_offset_retarget[item->GetID()];
+
+ int bone_id;
+ wo = LoadAttachPose(attachment_ref->anim, attachment_ref->ik_chain_label, skeleton_, &bone_id, mirror, false, attachment_ref->ik_chain_bone);
+
+ /*AnimationRef anim_ref = Animations::Instance()->ReturnRef(attachment_ref->anim);
+ AnimOutput anim_output;
+ BlendMap blend_map;
+ AnimInput anim_input(blend_map, NULL);
+ anim_ref->GetMatrices(0.0f, anim_output, anim_input);
+
+ int bone_id = GetAttachmentBone(skeleton, attachment_ref->ik_chain_label, attachment_ref->ik_chain_bone);
+ mat4 bone_mat = anim_output.matrices[bone_id].GetMat4();
+ mat4 weapon_mat = anim_output.weapon_matrices[0].GetMat4();
+ wo = invert(bone_mat) * weapon_mat;*/
+
+ wor.LoadIdentity();
+ stuck_item.rel_mat = wo;
+ stuck_item.bone_id = bone_id;
+}
+
+void RiggedObject::SheatheItem(int id, bool on_right){
+ AttachedItem *item_ptr = NULL;
+ for(std::list<AttachedItem>::iterator iter = attached_items.items.begin();
+ iter != attached_items.items.end(); ++iter)
+ {
+ ItemObjectScriptReader& item = iter->item;
+ if(item->GetID() == id){
+ item_ptr = &(*iter);
+ }
+ }
+ if(!item_ptr){
+ return;
+ }
+ ItemObjectScriptReader &item = item_ptr->item;
+ item->SetState(ItemObject::kSheathed);
+ bool mirror = on_right;
+ SetWeaponOffset(*item_ptr, _at_sheathe, mirror);
+ char_anim.clear();
+ character_script_getter->ItemsChanged(GetWieldedItemRefs());
+}
+
+void RiggedObject::UnSheatheItem(int id, bool right_hand){
+ AttachedItem *item_ptr = NULL;
+ for(std::list<AttachedItem>::iterator iter = attached_items.items.begin();
+ iter != attached_items.items.end(); ++iter)
+ {
+ ItemObjectScriptReader& item = iter->item;
+ if(item->GetID() == id){
+ item_ptr = &(*iter);
+ }
+ }
+ if(!item_ptr){
+ return;
+ }
+ ItemObjectScriptReader &item = item_ptr->item;
+ item->SetState(ItemObject::kWielded);
+ bool mirror = !right_hand;
+ SetWeaponOffset(*item_ptr, _at_grip, mirror);
+ char_anim.clear();
+ character_script_getter->ItemsChanged(GetWieldedItemRefs());
+}
+
+void RiggedObject::DetachItem(ItemObject* item_object){
+ int item_id = item_object->GetID();
+ attached_items.Remove(item_id);
+ char_anim.clear();
+}
+
+void RiggedObject::DetachAllItems(){
+ attached_items.RemoveAll();
+ char_anim.clear();
+
+ for(std::list<AttachedItem>::iterator iter = stuck_items.items.begin();
+ iter != stuck_items.items.end(); ++iter)
+ {
+ iter->item->ActivatePhysics();
+ }
+ stuck_items.items.clear();
+}
+
+
+void RiggedObject::InvalidatedItemCallback(ItemObjectScriptReader *invalidated, void* this_ptr) {
+ ((RiggedObject*)this_ptr)->InvalidatedItem(invalidated);
+}
+
+vec3 CalcVertexOffset (const vec3 &world_pos, float wind_amount) {
+ const float time = game_timer.game_time*2.0f;
+ vec3 vertex_offset = vec3(0.0f);
+
+ float wind_shake_amount = 0.02f;
+ float wind_time_scale = 8.0f;
+ float wind_shake_detail = 60.0f;
+ float wind_shake_offset = (world_pos[0]+world_pos[1])*wind_shake_detail;
+ /*wind_shake_amount *= max(0.0f,sinf((world_pos[0]+world_pos[1])+time*0.3f));
+ wind_shake_amount *= sinf((world_pos[0]*0.1f+world_pos[2])*0.3f+time*0.6f)+1.0f;
+ wind_shake_amount = max(0.002f,wind_shake_amount);
+ wind_shake_amount *= wind_amount;*/
+
+ wind_shake_amount = 0.05f*wind_amount;
+
+ vertex_offset[0] += sinf(time*wind_time_scale+wind_shake_offset);
+ vertex_offset[2] += cosf(time*wind_time_scale*1.2f+wind_shake_offset);
+ vertex_offset[1] += cosf(time*wind_time_scale*1.4f+wind_shake_offset);
+
+ vertex_offset *= wind_shake_amount;
+
+ return vertex_offset;
+}
+
+bool RiggedObject::GetOnlineIncomingMorphTargetState(MorphTargetStateStorage& dest, const char* name) {
+ if (incoming_network_morphs.find(name) == incoming_network_morphs.end()) {
+ return false;
+ }
+
+ dest = incoming_network_morphs[name];
+ return true;
+}
+
+void RiggedObject::ApplyBoneMatricesToModel(bool old, int lod_level) {
+ Model* model = &Models::Instance()->GetModel(model_id[lod_level]);
+ Online* online = Online::Instance();
+
+ std::vector<MorphTarget*> active_morph_targets;
+ const bool kMorphTargetsEnabled = true;
+ if(kMorphTargetsEnabled){
+ // Determine which morphs are active (have effective weight > 0)
+ for(unsigned k=0; k<morph_targets.size(); ++k){
+ MorphTarget& m_t = morph_targets[k];
+ if(online->IsClient() || m_t.weight > 0.0f ||
+ (m_t.anim_weight > 0.0f && m_t.script_weight_weight < 1.0f) ||
+ (m_t.script_weight > 0.0f && m_t.script_weight_weight > 0.0f))
+ {
+ m_t.PreDrawCamera(GetTimeBetweenLastTwoAnimUpdates(), GetTimeSinceLastAnimUpdate(), lod_level, char_id);
+ if(online->IsClient() || m_t.weight > 0.0f ){
+ active_morph_targets.push_back(&m_t);
+ }
+ }
+ }
+
+ // Initialize per-vertex morph accumulators
+ for(int j=0, len=model->vertices.size()/3; j<len; j++){
+ model->tex_transform_vec[j] = 0.0f;
+ model->morph_transform_vec[j] = 0.0f;
+ }
+
+ // Accumulate the effect of all active morphs
+ Models *mi = Models::Instance();
+ int vert_id, vert_id_index;
+ for(unsigned k=0, len=active_morph_targets.size(); k<len; ++k){
+ MorphTarget* morph = active_morph_targets[k];
+
+ const Model *morph_model = &mi->GetModel(morph->model_id[lod_level]);
+ const std::vector<int> &modified_tc = morph->modified_tc[lod_level];
+ for(unsigned j=0, len2=modified_tc.size(); j<len2; ++j){
+ vert_id = modified_tc[j];
+ vert_id_index = vert_id*2;
+ model->tex_transform_vec[vert_id] +=
+ vec2(morph_model->tex_coords[vert_id_index+0],
+ morph_model->tex_coords[vert_id_index+1]) *
+ morph->disp_weight;
+ }
+ const std::vector<int> &modified_vert = morph->modified_verts[lod_level];
+
+ for(unsigned j=0,len2=modified_vert.size(); j<len2; ++j){
+ vert_id = modified_vert[j];
+ vert_id_index = vert_id*3;
+
+ if (online->IsClient()) {
+ MorphTargetStateStorage state;
+ if (GetOnlineIncomingMorphTargetState(state, morph->name.c_str())) {
+ morph->disp_weight = state.disp_weight;
+ }
+ }
+ model->morph_transform_vec[vert_id][0] += morph_model->vertices[vert_id_index+0] * morph->disp_weight;
+ model->morph_transform_vec[vert_id][1] += morph_model->vertices[vert_id_index+1] * morph->disp_weight;
+ model->morph_transform_vec[vert_id][2] += morph_model->vertices[vert_id_index+2] * morph->disp_weight;
+ }
+ }
+
+ if (online->IsHosting()) {
+ StoreNetworkMorphTargets();
+ }
+ }
+ if(lod_level == 0 && !fur_model.faces.empty()){
+ for(int j=0, len=fur_model.vertices.size()/3; j<len; j++){
+ fur_model.morph_transform_vec[j] = model->morph_transform_vec[fur_base_vertex_ids[j]];
+ }
+ }
+ if(GPU_skinning){
+ return;
+ }
+
+#ifndef USE_SSE
+ #pragma omp parallel for
+ for(int j=0; j<model->num_vertices; j++){
+ mat4 transform = display_bone_matrices[(unsigned int)model->bone_ids[j][0]] * model->bone_weights[j][0];
+ for(int k=1; k<4; k++){
+ if(model->bone_weights[j][k] > 0.0f) {
+ transform += display_bone_matrices[(unsigned int)model->bone_ids[j][k]] * model->bone_weights[j][k];
+ }
+ }
+ transform.SetTranslationPart(transform.GetTranslationPart() +
+ transform.GetRotatedvec3(model->morph_transform_vec[j]));
+ transform.ToVec4(&model->transform_vec[0][j],
+ &model->transform_vec[1][j],
+ &model->transform_vec[2][j],
+ &model->transform_vec[3][j]);
+ }
+#else
+#pragma omp parallel for
+ for(int j=0; j<(int)display_bone_matrices.size(); j++){
+ simd_bone_mats[j].assign(display_bone_matrices[j].entries);
+ }
+
+#pragma omp parallel for
+ for(int j=0,len=model->vertices.size()/3; j<len; j++){
+ simd_mat4 transform = simd_bone_mats[(unsigned int)model->bone_ids[j][0]] * model->bone_weights[j][0];
+ for(int k=1; k<4; k++){
+ if(model->bone_weights[j][k] > 0.0f) {
+ transform += simd_bone_mats[(unsigned int)model->bone_ids[j][k]] * model->bone_weights[j][k];
+ }
+ }
+ transform.AddRotatedVec3AndSave(model->morph_transform_vec[j].entries,
+ model->transform_vec[0][j].entries,
+ model->transform_vec[1][j].entries,
+ model->transform_vec[2][j].entries,
+ model->transform_vec[3][j].entries);
+ }
+
+ if(lod_level == 0 && !fur_model.faces.empty()){
+ #pragma omp parallel for
+ for(int j=0, len=fur_model.vertices.size()/3; j<len; j++){
+ simd_mat4 transform = simd_bone_mats[(unsigned int)fur_model.bone_ids[j][0]] * fur_model.bone_weights[j][0];
+ for(int k=1; k<4; k++){
+ if(fur_model.bone_weights[j][k] > 0.0f) {
+ transform += simd_bone_mats[(unsigned int)fur_model.bone_ids[j][k]] * fur_model.bone_weights[j][k];
+ }
+ }
+ transform.AddRotatedVec3AndSave(fur_model.morph_transform_vec[j].entries,
+ fur_model.transform_vec[0][j].entries,
+ fur_model.transform_vec[1][j].entries,
+ fur_model.transform_vec[2][j].entries,
+ fur_model.transform_vec[3][j].entries);
+ }
+ }
+#endif
+}
+
+void MorphTarget::SetScriptWeight(float weight, float weight_weight) {
+ script_weight = weight;
+ script_weight_weight = weight_weight;
+}
+
+void RiggedObject::SetMTTargetWeight( const std::string &target_name, float weight, float weight_weight ) {
+ for(unsigned i=0; i<morph_targets.size(); ++i){
+ if(morph_targets[i].name == target_name){
+ morph_targets[i].SetScriptWeight(weight, weight_weight);
+ return;
+ }
+ }
+}
+
+vec3 RiggedObject::FetchCenterOffset() {
+ vec3 offset = total_center_offset;
+ total_center_offset = vec3(0.0f);
+ return offset;
+}
+
+float RiggedObject::FetchRotation() {
+ float rot = total_rotation;
+ total_rotation = 0.0f;
+ return rot;
+}
+
+void RiggedObject::DrawModel( Model* model, int lod_level ) {
+ PROFILER_ZONE(g_profiler_ctx, "RiggedObject::DrawModel()");
+ Graphics* graphics = Graphics::Instance();
+ Shaders* shaders = Shaders::Instance();
+
+ if(!model->vbo_loaded){
+ PROFILER_ENTER(g_profiler_ctx, "Create VBO");
+ model->createVBO();
+ PROFILER_LEAVE(g_profiler_ctx);
+ }
+
+ PROFILER_ENTER(g_profiler_ctx, "Get shader attributes");
+ CHECK_GL_ERROR();
+ int shader = shaders->bound_program;
+ int vertex_attrib_id = shaders->returnShaderAttrib("vertex_attrib", shader);
+ int tex_coord_attrib_id = shaders->returnShaderAttrib("tex_coord_attrib", shader);
+ int morph_tex_offset_attrib_id = shaders->returnShaderAttrib("morph_tex_offset_attrib", shader);
+ int fur_tex_coord_attrib_id = shaders->returnShaderAttrib("fur_tex_coord_attrib", shader);
+ int bone_ids_id = shaders->returnShaderAttrib("bone_ids", shader);
+ int bone_weights_id = shaders->returnShaderAttrib("bone_weights", shader);
+ int morph_offsets_id = shaders->returnShaderAttrib("morph_offsets", shader);
+ int transform_mat_column_a_id = shaders->returnShaderAttrib("transform_mat_column_a", shader);
+ int transform_mat_column_b_id = shaders->returnShaderAttrib("transform_mat_column_b", shader);
+ int transform_mat_column_c_id = shaders->returnShaderAttrib("transform_mat_column_c", shader);
+ int vel_id = shaders->returnShaderAttrib("vel_attrib", shader);
+ PROFILER_LEAVE(g_profiler_ctx);
+
+ PROFILER_ENTER(g_profiler_ctx, "Enable shader attributes");
+ CHECK_GL_ERROR();
+ if(vertex_attrib_id != -1){
+ graphics->EnableVertexAttribArray(vertex_attrib_id);
+ model->VBO_vertices.Bind();
+ glVertexAttribPointer(vertex_attrib_id, 3, GL_FLOAT, false, 0, 0);
+ }
+ if(tex_coord_attrib_id != -1){
+ graphics->EnableVertexAttribArray(tex_coord_attrib_id);
+ model->VBO_tex_coords.Bind();
+ glVertexAttribPointer(tex_coord_attrib_id, 2, GL_FLOAT, false, 0, 0);
+ }
+
+ if (GPU_skinning) {
+ if(bone_ids_id != -1){
+ graphics->EnableVertexAttribArray(bone_ids_id);
+ transform_vec_vbo0[lod_level]->Bind();
+ glVertexAttribPointer(bone_ids_id, 4, GL_FLOAT, false, 0, (const void*)transform_vec_vbo0[lod_level]->offset());
+ }
+ if(bone_weights_id != -1){
+ graphics->EnableVertexAttribArray(bone_weights_id);
+ transform_vec_vbo1[lod_level]->Bind();
+ glVertexAttribPointer(bone_weights_id, 4, GL_FLOAT, false, 0, (const void*)transform_vec_vbo1[lod_level]->offset());
+ }
+ if(morph_offsets_id != -1){
+ graphics->EnableVertexAttribArray(morph_offsets_id);
+ transform_vec_vbo2[lod_level]->Bind();
+ glVertexAttribPointer(morph_offsets_id, 3, GL_FLOAT, false, 0, (const void*)transform_vec_vbo2[lod_level]->offset());
+ }
+ } else {
+ if(transform_mat_column_a_id != -1){
+ graphics->EnableVertexAttribArray(transform_mat_column_a_id);
+ transform_vec_vbo0[lod_level]->Bind();
+ glVertexAttribPointer(transform_mat_column_a_id, 4, GL_FLOAT, false, 0, (const void*)transform_vec_vbo0[lod_level]->offset());
+ }
+ if(transform_mat_column_b_id != -1){
+ graphics->EnableVertexAttribArray(transform_mat_column_b_id);
+ transform_vec_vbo1[lod_level]->Bind();
+ glVertexAttribPointer(transform_mat_column_b_id, 4, GL_FLOAT, false, 0, (const void*)transform_vec_vbo1[lod_level]->offset());
+ }
+ if(transform_mat_column_c_id != -1){
+ graphics->EnableVertexAttribArray(transform_mat_column_c_id);
+ transform_vec_vbo2[lod_level]->Bind();
+ glVertexAttribPointer(transform_mat_column_c_id, 4, GL_FLOAT, false, 0, (const void*)transform_vec_vbo2[lod_level]->offset());
+ }
+ }
+ if(morph_tex_offset_attrib_id != -1){
+ graphics->EnableVertexAttribArray(morph_tex_offset_attrib_id);
+ tex_transform_vbo[lod_level]->Bind();
+ glVertexAttribPointer(morph_tex_offset_attrib_id, 2, GL_FLOAT, false, 0, (const void*)tex_transform_vbo[lod_level]->offset());
+ }
+ if(fur_tex_coord_attrib_id != -1){
+ graphics->EnableVertexAttribArray(fur_tex_coord_attrib_id);
+ model->VBO_tex_coords.Bind();
+ glVertexAttribPointer(fur_tex_coord_attrib_id, 2, GL_FLOAT, false, 0, 0);
+ }
+ if(vel_id != -1){
+ graphics->EnableVertexAttribArray(vel_id);
+ vel_vbo.Bind();
+ glVertexAttribPointer(vel_id, 3, GL_FLOAT, false, 0, (const void*)vel_vbo.offset());
+ }
+ PROFILER_LEAVE(g_profiler_ctx);
+
+ PROFILER_ENTER(g_profiler_ctx, "Bind faces VBO");
+ model->VBO_faces.Bind();
+ PROFILER_LEAVE(g_profiler_ctx);
+ GL_PERF_START();
+ PROFILER_ENTER(g_profiler_ctx, "glDrawElements");
+ graphics->DrawElements(GL_TRIANGLES, model->faces.size(), GL_UNSIGNED_INT, 0);
+ PROFILER_LEAVE(g_profiler_ctx);
+ GL_PERF_END();
+ CHECK_GL_ERROR();
+
+ if(lod_level == 0 && !fur_model.faces.empty() && !graphics->drawing_shadow){
+ PROFILER_GPU_ZONE(g_profiler_ctx, "Draw fur");
+ CHECK_GL_ERROR();
+ Textures::Instance()->bindTexture(fur_tex, 7);
+ graphics->setCullFace(false);
+ graphics->SetBlendFunc(GL_ONE,GL_ZERO,GL_ONE,GL_ONE);
+ graphics->setBlend(true);
+
+ if(graphics->use_sample_alpha_to_coverage){
+ glEnable( GL_SAMPLE_ALPHA_TO_COVERAGE );
+ }
+ if(!fur_model.vbo_loaded){
+ fur_model.createVBO();
+ }
+
+ if(vertex_attrib_id != -1){
+ fur_model.VBO_vertices.Bind();
+ glVertexAttribPointer(vertex_attrib_id, 3, GL_FLOAT, false, 0, 0);
+ }
+ if(tex_coord_attrib_id != -1){
+ fur_model.VBO_tex_coords.Bind();
+ glVertexAttribPointer(tex_coord_attrib_id, 2, GL_FLOAT, false, 0, 0);
+ }
+ if (GPU_skinning) {
+ if(bone_ids_id != -1){
+ graphics->EnableVertexAttribArray(bone_ids_id);
+ fur_transform_vec_vbo0.Bind();
+ glVertexAttribPointer(bone_ids_id, 4, GL_FLOAT, false, 0, (const void*)fur_transform_vec_vbo0.offset());
+ }
+ if(bone_weights_id != -1){
+ graphics->EnableVertexAttribArray(bone_weights_id);
+ fur_transform_vec_vbo1.Bind();
+ glVertexAttribPointer(bone_weights_id, 4, GL_FLOAT, false, 0, (const void*)fur_transform_vec_vbo1.offset());
+ }
+ if(morph_offsets_id != -1){
+ graphics->EnableVertexAttribArray(morph_offsets_id);
+ fur_transform_vec_vbo2.Bind();
+ glVertexAttribPointer(morph_offsets_id, 3, GL_FLOAT, false, 0, (const void*)fur_transform_vec_vbo2.offset());
+ }
+ } else {
+ if(transform_mat_column_a_id != -1){
+ fur_transform_vec_vbo0.Bind();
+ glVertexAttribPointer(transform_mat_column_a_id, 4, GL_FLOAT, false, 0, (const void*)fur_transform_vec_vbo0.offset());
+ }
+ if(transform_mat_column_b_id != -1){
+ fur_transform_vec_vbo1.Bind();
+ glVertexAttribPointer(transform_mat_column_b_id, 4, GL_FLOAT, false, 0, (const void*)fur_transform_vec_vbo1.offset());
+ }
+ if(transform_mat_column_c_id != -1){
+ fur_transform_vec_vbo2.Bind();
+ glVertexAttribPointer(transform_mat_column_c_id, 4, GL_FLOAT, false, 0, (const void*)fur_transform_vec_vbo2.offset());
+ }
+ }
+ if(morph_tex_offset_attrib_id != -1){
+ fur_tex_transform_vbo.Bind();
+ glVertexAttribPointer(morph_tex_offset_attrib_id, 2, GL_FLOAT, false, 0, (const void*)fur_tex_transform_vbo.offset());
+ }
+ if(fur_tex_coord_attrib_id != -1){
+ graphics->EnableVertexAttribArray(fur_tex_coord_attrib_id);
+ fur_model.VBO_tex_coords2.Bind();
+ glVertexAttribPointer(fur_tex_coord_attrib_id, 2, GL_FLOAT, false, 0, 0);
+ }
+ fur_model.VBO_faces.Bind();
+ graphics->DrawElements(GL_TRIANGLES, fur_model.faces.size(), GL_UNSIGNED_INT, 0);
+ graphics->setCullFace(true);
+
+ if(graphics->use_sample_alpha_to_coverage){
+ glDisable( GL_SAMPLE_ALPHA_TO_COVERAGE );
+ }
+ }
+
+ PROFILER_ENTER(g_profiler_ctx, "Disable shader attributes");
+ CHECK_GL_ERROR();
+ graphics->ResetVertexAttribArrays();
+
+ CHECK_GL_ERROR();
+ PROFILER_LEAVE(g_profiler_ctx);
+}
+
+void RiggedObject::AddAnimation( std::string path, float weight_coord )
+{
+ //AnimationRef new_anim = Animations::Instance()->ReturnRef(path);
+ //(animation_group.AddAnimation(new_anim, weight_coord);
+}
+
+void RiggedObject::FixDiscontinuity() {
+ time_until_next_anim_update = 0;
+ Update(game_timer.timestep);
+ time_until_next_anim_update = 0;
+ Update(game_timer.timestep);
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); i++){
+ if(skeleton_.physics_bones[i].bullet_object){
+ skeleton_.physics_bones[i].bullet_object->FixDiscontinuity();
+ }
+ }
+}
+
+void RiggedObject::RefreshRagdoll()
+{
+ if(!animated){
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); i++){
+ BulletObject* b_object = skeleton_.physics_bones[i].bullet_object;
+ if(!b_object){
+ continue;
+ }
+ b_object->Activate();
+ }
+ }
+}
+
+
+void RiggedObject::Ragdoll(const vec3 &velocity) {
+ if(animated){
+ animated = false;
+ first_ragdoll_frame = true;
+
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); i++){
+ BulletObject* b_object = skeleton_.physics_bones[i].bullet_object;
+ if(!b_object){
+ continue;
+ }
+ b_object->Activate();
+ b_object->old_transform.SetTranslationPart(b_object->GetInterpPositionX(GetTimeBetweenLastTwoAnimUpdates(), GetTimeSinceLastAnimUpdate()+1));
+ b_object->old_transform.SetRotationPart(b_object->GetInterpRotationX(GetTimeBetweenLastTwoAnimUpdates(), GetTimeSinceLastAnimUpdate()+1));
+ }
+
+ skeleton_.LinkToBulletWorld();
+ for(unsigned i=0; i<skeleton_.physics_joints.size(); ++i){
+ skeleton_.SetGFStrength(skeleton_.physics_joints[i], 0.0f);
+ }
+
+ for(std::list<AttachedItem>::iterator iter = stuck_items.items.begin();
+ iter != stuck_items.items.end(); ++iter)
+ {
+ iter->item.ActivatePhysics();
+ iter->item.AddConstraint(skeleton_.physics_bones[iter->bone_id].bullet_object);
+ }
+ }
+}
+
+void RiggedObject::SetDamping(float amount){
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); i++){
+ BulletObject* b_object = skeleton_.physics_bones[i].bullet_object;
+ if(!b_object){
+ continue;
+ }
+ b_object->SetDamping(amount);
+ if(amount >= 1.0f){
+ b_object->Sleep();
+ }
+ }
+ if(amount >= 1.0f){
+ for(std::list<AttachedItem>::iterator iter = stuck_items.items.begin();
+ iter != stuck_items.items.end(); ++iter)
+ {
+ iter->item->SleepPhysics();
+ }
+ }
+}
+
+void RiggedObject::UnRagdoll() {
+ //mat4 inv_transform = invert(GetTransformationMatrix());
+ if(!animated){
+ for(std::list<AttachedItem>::iterator iter = stuck_items.items.begin();
+ iter != stuck_items.items.end(); ++iter)
+ {
+ iter->item.RemoveConstraint();
+ }
+
+ anim_client.Reset();
+
+ animated = true;
+
+ skeleton_.UnlinkFromBulletWorld();
+ skeleton_.UpdateTwistBones(true);
+
+ time_until_next_anim_update = rand()%anim_update_period-anim_update_period;
+ }
+}
+
+vec3 RiggedObject::GetAvgPosition()
+{
+ vec3 total(0.0f);
+ int num_total = 0;
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); i++){
+ if(!skeleton_.physics_bones[i].bullet_object){
+ continue;
+ }
+ num_total++;
+ total += skeleton_.physics_bones[i].bullet_object->GetPosition();
+ }
+
+ total /= (float)num_total;
+
+ return total;
+}
+
+vec3 RiggedObject::GetAvgVelocity()
+{
+ vec3 total(0.0f);
+ float divisor = 0.0f;
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); i++){
+ if(!skeleton_.physics_bones[i].bullet_object ||
+ !skeleton_.physics_bones[i].bullet_object->linked){
+ continue;
+ }
+ const float mass = skeleton_.physics_bones[i].bullet_object->GetMass();
+ divisor += mass;
+ total += skeleton_.physics_bones[i].bullet_object->GetLinearVelocity() * mass;
+ }
+
+ total /= divisor;
+
+ return total;
+}
+
+vec3 RiggedObject::GetAvgAngularVelocity() {
+ vec3 total(0.0f);
+ float divisor = 0.0f;
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); i++){
+ if(!skeleton_.physics_bones[i].bullet_object ||
+ !skeleton_.physics_bones[i].bullet_object->linked){
+ continue;
+ }
+ total += skeleton_.physics_bones[i].bullet_object->GetAngularVelocity() *
+ skeleton_.physics_bones[i].bullet_object->GetMass();
+ divisor += skeleton_.physics_bones[i].bullet_object->GetMass();
+ }
+
+ total /= divisor;
+
+ return total;
+}
+
+
+quaternion RiggedObject::GetAvgRotation()
+{
+ quaternion total;
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); i++){
+ if(!skeleton_.physics_bones[i].bullet_object){
+ continue;
+ }
+ const PhysicsBone& bone = skeleton_.physics_bones[i];
+ mat4 rotation = bone.bullet_object->GetRotation() *
+ invert(bone.initial_rotation);
+ total += QuaternionFromMat4(rotation) * bone.bullet_object->GetMass();
+ }
+
+ total = normalize(total);
+
+ return total;
+}
+
+vec3 RiggedObject::GetIKTargetPosition( const std::string &target_name )
+{
+ if(animation_frame_bone_matrices.empty()){
+ return vec3(0.0f);
+ }
+ Skeleton::SimpleIKBoneMap::iterator iter;
+ iter = skeleton_.simple_ik_bones.find(target_name);
+ if(iter != skeleton_.simple_ik_bones.end()){
+ return animation_frame_bone_matrices[iter->second.bone_id].origin;
+ }
+ return vec3(0.0f);
+}
+
+float RiggedObject::GetIKWeight( const std::string &target_name ) {
+ for(unsigned i=0; i<blended_bone_paths.size(); ++i){
+ if(blended_bone_paths[i].ik_bone.label == target_name){
+ return blended_bone_paths[i].weight;
+ }
+ }
+ return 0.0f;
+}
+
+BoneTransform GetIKTransform( RiggedObject* rigged_object, const std::string &target_name ) {
+ for(unsigned i=0; i<rigged_object->blended_bone_paths.size(); ++i){
+ if(rigged_object->blended_bone_paths[i].ik_bone.label == target_name){
+ return rigged_object->blended_bone_paths[i].transform;
+ }
+ }
+ BoneTransform identity;
+ return identity;
+}
+
+mat4 GetUnmodifiedIKTransform( RiggedObject* rigged_object, const std::string &target_name ) {
+ for(unsigned i=0; i<rigged_object->blended_bone_paths.size(); ++i){
+ if(rigged_object->blended_bone_paths[i].ik_bone.label == target_name){
+ return rigged_object->blended_bone_paths[i].unmodified_transform.GetMat4();
+ }
+ }
+ mat4 identity;
+ return identity;
+}
+
+vec3 RiggedObject::GetIKTargetAnimPosition( const std::string &target_name ) {
+ for(unsigned i=0; i<blended_bone_paths.size(); ++i){
+ if(blended_bone_paths[i].ik_bone.label == target_name){
+ int target_bone = blended_bone_paths[i].ik_bone.bone_path.back();
+ return unmodified_transforms[target_bone].origin;
+ }
+ }
+ return vec3(0.0f);
+}
+
+void RiggedObject::SetASContext( ASContext* _as_context ) {
+ as_context = _as_context;
+
+ as_funcs.handle_animation_event = as_context->RegisterExpectedFunction("void HandleAnimationEvent(string,vec3)", true);
+ as_funcs.display_matrix_update = as_context->RegisterExpectedFunction("void DisplayMatrixUpdate()", true);
+ as_funcs.final_animation_matrix_update = as_context->RegisterExpectedFunction("void FinalAnimationMatrixUpdate(int)", true);
+ as_funcs.final_attached_item_update = as_context->RegisterExpectedFunction("void FinalAttachedItemUpdate(int)", true);
+ as_funcs.notify_item_detach = as_context->RegisterExpectedFunction("void NotifyItemDetach(int)", true);
+
+ as_context->LoadExpectedFunctions();
+
+ anim_client.SetASContext(_as_context);
+}
+
+void MorphTarget::CalcVertsModified(int base_model_id, int lod_level) {
+ Model& model = Models::Instance()->GetModel(model_id[lod_level]);
+ int model_num_verts = model.vertices.size()/3;
+ if(morph_targets.empty()){
+ verts_modified[lod_level].resize(model_num_verts, false);
+ tc_modified[lod_level].resize(model_num_verts, false);
+ for(int i=0; i<model_num_verts; ++i){
+ if(fabsf(model.vertices[i*3+0]) > EPSILON ||
+ fabsf(model.vertices[i*3+1]) > EPSILON ||
+ fabsf(model.vertices[i*3+2]) > EPSILON)
+ {
+ modified_verts[lod_level].push_back(i);
+ verts_modified[lod_level][i] = true;
+ }
+ if(fabsf(model.tex_coords[i*2+0]) > EPSILON ||
+ fabsf(model.tex_coords[i*2+1]) > EPSILON)
+ {
+ modified_tc[lod_level].push_back(i);
+ tc_modified[lod_level][i] = true;
+ }
+ }
+ } else {
+ verts_modified[lod_level].resize(model_num_verts, false);
+ tc_modified[lod_level].resize(model_num_verts, false);
+ for(unsigned i=0; i<morph_targets.size(); ++i){
+ MorphTarget &mt = morph_targets[i];
+ mt.CalcVertsModified(base_model_id, lod_level);
+ for(int j=0; j<model_num_verts; ++j){
+ if(mt.verts_modified[lod_level][j]){
+ verts_modified[lod_level][j] = true;
+ }
+ if(mt.tc_modified[lod_level][j]){
+ tc_modified[lod_level][j] = true;
+ }
+ }
+ }
+ for(int i=0; i<model_num_verts; ++i){
+ if(verts_modified[lod_level][i]){
+ modified_verts[lod_level].push_back(i);
+ }
+ if(tc_modified[lod_level][i]){
+ modified_tc[lod_level].push_back(i);
+ }
+ }
+ }
+}
+
+void MorphTarget::Load(const std::string &base_model_name,
+ int base_model_id,
+ const std::string &_name,
+ int parts,
+ bool absolute_path)
+{
+ static const int kBufSize = 256;
+ char buf[kBufSize];
+ FormatString(buf, kBufSize, "Loading morph target: %s", _name.c_str());
+ PROFILER_ZONE_DYNAMIC_STRING(g_profiler_ctx, buf);
+
+ name = _name;
+ old_weight = 0.0f;
+ weight = 0.0f;
+ anim_weight = 0.0f;
+ script_weight = 0.0f;
+ script_weight_weight = 0.0f;
+ checksum = 0;
+
+ for(int i=0; i<4; ++i){
+ model_id[i] = -1;
+ }
+
+ if(parts == 1){
+ std::string path;
+ if(absolute_path){
+ path = _name;
+ } else {
+ path = GetMorphPath(base_model_name, _name);
+ }
+
+ model_id[0] = Models::Instance()->LoadModelAsMorph(path.c_str(), base_model_id, base_model_name);
+ char abs_path[kPathSize];
+ if(FindFilePath(path.c_str(), abs_path, kPathSize, kDataPaths | kModPaths) == -1){
+ FatalError("Error", "Could not find %s", path.c_str());
+ }
+ checksum = Checksum(abs_path);
+ model_copy[0] = false;
+ if(model_id[0] == -1){
+ return;
+ }
+ } else {
+ model_id[0] = Models::Instance()->CopyModel(base_model_id);
+ Model& model = Models::Instance()->GetModel(model_id[0]);
+ for(int i=0, len=(int)model.vertices.size(); i<len; ++i){
+ model.vertices[i] = 0.0f;
+ }
+ for(int i=0, len=(int)model.tex_coords.size(); i<len; ++i){
+ model.tex_coords[i] = 0.0f;
+ }
+ model_copy[0] = true;
+ for(int i=0; i<parts; ++i){
+ MorphTarget mt;
+ std::stringstream oss;
+ oss << _name << i;
+ mt.Load(base_model_name, base_model_id, oss.str());
+ checksum += mt.checksum;
+ morph_targets.push_back(mt);
+ }
+ }
+ CalcVertsModified(base_model_id, 0);
+}
+
+void MorphTarget::PreDrawCamera(int num, int progress, int lod_level, uint32_t char_id) {
+ if (g_debug_runtime_disable_morph_target_pre_draw_camera) {
+ return;
+ }
+
+ Online* online = Online::Instance();
+ MovementObject* mov = (MovementObject*)Engine::Instance()->GetSceneGraph()->GetObjectFromID(char_id);
+ RiggedObject* rigged_object = nullptr;
+
+ if(mov != nullptr) {
+ rigged_object = mov->rigged_object();
+ }
+
+ float interp_weight = mix(old_weight, weight, game_timer.GetInterpWeightX(num, progress));
+ if(!morph_targets.empty()){
+ Model& model = Models::Instance()->GetModel(model_id[lod_level]);
+ int num_parts = morph_targets.size();
+ mod_weight = max(0.0f,interp_weight*(float)(num_parts)-1.0f);
+ int start = max(0, min((int)mod_weight, num_parts-1));
+ int end = max(0, min(start+1, num_parts-1));
+ float new_weight = mod_weight - (float)start;
+
+ if (online->IsClient()) {
+ MorphTargetStateStorage state;
+ if(rigged_object != nullptr) {
+ if(rigged_object->GetOnlineIncomingMorphTargetState(state, name.c_str())) {
+ mod_weight = state.mod_weight;
+ start = max(0, min((int)mod_weight, num_parts - 1));
+ end = max(0, min(start + 1, num_parts - 1));
+ }
+ }
+ }
+
+ Model& morph_model_start = Models::Instance()->GetModel(morph_targets[start].model_id[lod_level]);
+ Model& morph_model_end = Models::Instance()->GetModel(morph_targets[end].model_id[lod_level]);
+
+ if(new_weight == 0.0f || new_weight == 1.0f){
+ Model *the_model = (new_weight == 0.0f)?&morph_model_start:&morph_model_end;
+ for(unsigned j=0; j<modified_verts[lod_level].size(); ++j){
+ const int index = modified_verts[lod_level][j]*3;
+ model.vertices[index+0] = the_model->vertices[index+0];
+ model.vertices[index+1] = the_model->vertices[index+1];
+ model.vertices[index+2] = the_model->vertices[index+2];
+ }
+ for(unsigned j=0; j<modified_tc[lod_level].size(); ++j){
+ const int tc_index = modified_tc[lod_level][j]*2;
+ model.tex_coords[tc_index+0] = the_model->tex_coords[tc_index+0];
+ model.tex_coords[tc_index+1] = the_model->tex_coords[tc_index+1];
+ }
+ } else {
+ for(unsigned j=0; j<modified_verts[lod_level].size(); ++j){
+ const int index = modified_verts[lod_level][j]*3;
+ model.vertices[index+0] = morph_model_start.vertices[index+0]*(1.0f-new_weight);
+ model.vertices[index+1] = morph_model_start.vertices[index+1]*(1.0f-new_weight);
+ model.vertices[index+2] = morph_model_start.vertices[index+2]*(1.0f-new_weight);
+ model.vertices[index+0] += morph_model_end.vertices[index+0]*new_weight;
+ model.vertices[index+1] += morph_model_end.vertices[index+1]*new_weight;
+ model.vertices[index+2] += morph_model_end.vertices[index+2]*new_weight;
+ }
+ for(unsigned j=0; j<modified_tc[lod_level].size(); ++j){
+ const int tc_index = modified_tc[lod_level][j]*2;
+ model.tex_coords[tc_index+0] = morph_model_start.tex_coords[tc_index+0]*(1.0f-new_weight);
+ model.tex_coords[tc_index+1] = morph_model_start.tex_coords[tc_index+1]*(1.0f-new_weight);
+ model.tex_coords[tc_index+0] += morph_model_end.tex_coords[tc_index+0]*new_weight;
+ model.tex_coords[tc_index+1] += morph_model_end.tex_coords[tc_index+1]*new_weight;
+ }
+ }
+ disp_weight = max(0.0f, min(1.0f,interp_weight*(float)(num_parts)));
+ } else {
+ disp_weight = max(0.0f, min(1.0f,interp_weight));
+ }
+}
+
+float MorphTarget::GetScriptWeight()
+{
+ return script_weight;
+}
+
+float MorphTarget::GetWeight() {
+ return mix(anim_weight, script_weight, script_weight_weight);
+}
+
+void MorphTarget::UpdateForInterpolation()
+{
+ old_weight = weight;
+ weight = GetWeight();
+ weight = min(1.0f, max(0.0f, weight));
+}
+
+void MorphTarget::Dispose() {
+ for(int i=0; i<4; ++i){
+ if(model_copy[i]){
+ Models::Instance()->DeleteModel(model_id[i]);
+ }
+ }
+ for(unsigned i=0; i<morph_targets.size(); ++i){
+ morph_targets[i].Dispose();
+ }
+}
+
+float RiggedObject::GetStatusKeyValue( const std::string &label ) {
+ if(status_keys.find(label) != status_keys.end()){
+ return status_keys[label];
+ } else {
+ return 0.0f;
+ }
+}
+
+void RiggedObject::ApplyForceToRagdoll( const vec3 &force, const vec3 &position ) {
+ std::vector<float> dist(skeleton_.physics_bones.size());
+ float total = 0.0f;
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); ++i){
+ BulletObject* bo = skeleton_.physics_bones[i].bullet_object;
+ if(!bo || skeleton_.fixed_obj[i]){
+ continue;
+ }
+ dist[i] = 1.0f / (distance_squared(position, bo->GetPosition())+0.01f);
+ dist[i] *= bo->GetMass();
+ total += dist[i];
+ }
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); ++i){
+ BulletObject* bo = skeleton_.physics_bones[i].bullet_object;
+ if(!bo || skeleton_.fixed_obj[i]){
+ continue;
+ }
+ bo->ApplyForceAtWorldPoint(bo->GetPosition(), force*dist[i] / total);
+ }
+}
+
+void RiggedObject::ApplyForceToBone( const vec3 &force, const int &bone ) {
+ BulletObject* bo = skeleton_.physics_bones[bone].bullet_object;
+ bo->ApplyForceAtWorldPoint(bo->GetPosition(), force);
+}
+
+void RiggedObject::ApplyForceLineToRagdoll( const vec3 &force, const vec3 &position, const vec3 &line_dir ) {
+ std::vector<float> dist(skeleton_.physics_bones.size());
+ float total = 0.0f;
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); ++i){
+ BulletObject* bo = skeleton_.physics_bones[i].bullet_object;
+ if(!bo || skeleton_.fixed_obj[i]){
+ continue;
+ }
+ vec3 temp_pos = bo->GetPosition() - position;
+ temp_pos -= line_dir * dot(line_dir, temp_pos);
+ dist[i] = 1.0f / (length_squared(temp_pos)+0.01f);
+ dist[i] *= bo->GetMass();
+ total += dist[i];
+ }
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); ++i){
+ BulletObject* bo = skeleton_.physics_bones[i].bullet_object;
+ if(!bo || skeleton_.fixed_obj[i]){
+ continue;
+ }
+ bo->ApplyForceAtWorldPoint(bo->GetPosition(), force*dist[i] / total);
+ }
+}
+
+vec3 RiggedObject::GetDisplayBonePosition(int bone) {
+ return display_bone_matrices[bone].GetTranslationPart();
+}
+
+vec3 RiggedObject::GetBonePosition( int bone ) {
+ return skeleton_.physics_bones[bone].bullet_object->GetPosition();
+}
+
+vec3 RiggedObject::GetBoneLinearVel( int bone ) {
+ return skeleton_.physics_bones[bone].bullet_object->GetLinearVelocity();
+}
+
+mat4 RiggedObject::GetBoneRotation( int bone ) {
+ return skeleton_.physics_bones[bone].bullet_object->GetRotation();
+}
+
+void RiggedObject::SetSkeletonOwner( Object* owner ) {
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); ++i){
+ if(!skeleton_.physics_bones[i].bullet_object){
+ continue;
+ }
+ skeleton_.physics_bones[i].bullet_object->owner_object = owner;
+ }
+}
+
+void RiggedObject::SetAnimUpdatePeriod( int _anim_update_period ) {
+ anim_update_period = _anim_update_period;
+}
+
+mat4 RiggedObject::GetIKTargetTransform( const std::string &target_name ) {
+ int bone = skeleton_.simple_ik_bones[target_name].bone_id;
+ return skeleton_.physics_bones[bone].bullet_object->GetTransform();
+}
+
+void RiggedObject::HandleAnimationEvents( AnimationClient &_anim_client ) {
+ // Loop through animation events and send them to Angelscript to handle
+ std::queue<AnimationEvent> active_events = _anim_client.GetActiveEvents();
+ while(!active_events.empty()){
+ AnimationEvent& the_event = active_events.front();
+ vec3 position = skeleton_.physics_bones[the_event.which_bone].
+ bullet_object->GetPosition();
+ ASArglist args;
+ args.AddObject(&the_event.event_string);
+ args.AddObject(&position);
+ as_context->CallScriptFunction(as_funcs.handle_animation_event, &args);
+ active_events.pop();
+ }
+}
+
+void RiggedObject::SetRagdollStrength( float amount ) {
+ ragdoll_strength = amount;
+}
+
+mat4 RiggedObject::GetAvgIKChainTransform( const std::string &target_name ) {
+ mat4 total(0.0f);
+ float total_num = 0.0f;
+ SimpleIKBone &sib = skeleton_.simple_ik_bones[target_name];
+ int bone = sib.bone_id;
+ for(int i=0; i<sib.chain_length; ++i){
+ total += skeleton_.physics_bones[bone].bullet_object->GetTransform();
+ total_num += 1.0f;
+ bone = skeleton_.parents[bone];
+ if(bone == -1){
+ break;
+ }
+ }
+ return total / total_num;
+}
+
+vec3 RiggedObject::GetAvgIKChainPos( const std::string &target_name ) {
+ vec3 total;
+ float total_num = 0.0f;
+ SimpleIKBone &sib = skeleton_.simple_ik_bones[target_name];
+ int bone = sib.bone_id;
+ for(int i=0; i<sib.chain_length; ++i){
+ total += skeleton_.physics_bones[bone].bullet_object->GetPosition();
+ total_num += 1.0f;
+ bone = skeleton_.parents[bone];
+ if(bone == -1){
+ break;
+ }
+ }
+ return total / total_num;
+}
+
+
+mat4 RiggedObject::GetIKChainTransform( const std::string &target_name, int which ) {
+ SimpleIKBone &sib = skeleton_.simple_ik_bones[target_name];
+ int bone = sib.bone_id;
+ for(int i=0; i<which; ++i){
+ bone = skeleton_.parents[bone];
+ if(bone == -1){
+ break;
+ }
+ }
+ return skeleton_.physics_bones[bone].bullet_object->GetTransform();
+}
+
+vec3 RiggedObject::GetIKChainPos( const std::string &target_name, int which )
+{
+ SimpleIKBone &sib = skeleton_.simple_ik_bones[target_name];
+ int bone = sib.bone_id;
+ for(int i=0; i<which; ++i){
+ bone = skeleton_.parents[bone];
+ if(bone == -1){
+ break;
+ }
+ }
+ return skeleton_.physics_bones[bone].bullet_object->GetPosition();
+}
+
+void RiggedObject::EnableSleep()
+{
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); ++i){
+ if(skeleton_.physics_bones[i].bullet_object){
+ skeleton_.physics_bones[i].bullet_object->CanSleep();
+ }
+ }
+}
+
+void RiggedObject::DisableSleep()
+{
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); ++i){
+ if(skeleton_.physics_bones[i].bullet_object){
+ skeleton_.physics_bones[i].bullet_object->NoSleep();
+ }
+ }
+}
+
+void RiggedObject::CheckForNAN()
+{
+ skeleton_.CheckForNAN();
+}
+
+void RiggedObject::HandleLipSyncMorphTargets(float timestep) {
+ if(lipsync_reader.valid()){
+ lipsync_reader.UpdateWeights();
+ const std::map<std::string, float> &mw = lipsync_reader.GetMorphWeights();
+ std::map<std::string, float>::const_iterator iter;
+ for(iter = mw.begin(); iter != mw.end(); ++iter){
+ for(unsigned i=0; i<morph_targets.size(); ++i){
+ if(morph_targets[i].name == iter->first){
+ morph_targets[i].script_weight =
+ mix(morph_targets[i].script_weight, iter->second, 0.5f);
+ morph_targets[i].script_weight_weight = 1.0f;
+ break;
+ }
+ }
+ }
+ lipsync_reader.Update(timestep);
+ if(!lipsync_reader.valid()){
+ for(iter = mw.begin(); iter != mw.end(); ++iter){
+ for(unsigned i=0; i<morph_targets.size(); ++i){
+ if(morph_targets[i].name == iter->first){
+ morph_targets[i].script_weight_weight = 0.0f;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void RiggedObject::CreateBloodDrip( const std::string &ik_name, int ik_link, const vec3 &direction )
+{
+ Skeleton::SimpleIKBoneMap::iterator iter =
+ skeleton_.simple_ik_bones.find(ik_name);
+ if(iter == skeleton_.simple_ik_bones.end()){
+ return;
+ }
+ SimpleIKBone &sib = iter->second;
+ int look_link = ik_link;
+ int bone_id = sib.bone_id;
+ while(look_link > 0){
+ bone_id = skeleton_.parents[bone_id];
+ --look_link;
+ }
+ vec3 bone_pos = skeleton_.physics_bones[bone_id].initial_position;
+
+ blood_surface.CreateBloodDripNearestPointDirection(bone_pos, direction, 1.0f, true);
+ blood_surface.sleep_time = game_timer.game_time + 5.0f;
+}
+
+void RiggedObject::CreateBloodDripAtBone( int bone_id, int ik_link, const vec3 &direction )
+{
+ BulletObject* bo = skeleton_.physics_bones[bone_id].bullet_object;
+ if(skeleton_.fixed_obj[bone_id]){
+ bo = skeleton_.physics_bones[skeleton_.parents[bone_id]].bullet_object;
+ }
+ if(skeleton_.fixed_obj[skeleton_.parents[bone_id]]){
+ bo = skeleton_.physics_bones[skeleton_.parents[skeleton_.parents[bone_id]]].bullet_object;
+ }
+ vec3 curr_pos = bo->GetPosition();
+ blood_surface.CreateBloodDripNearestPointDirection(curr_pos, direction, 1.0f, true);
+ blood_surface.sleep_time = game_timer.game_time + 5.0f;
+}
+
+void RiggedObject::CleanBlood() {
+ blood_surface.CleanBlood();
+}
+
+void RiggedObject::SetCharAnim( const std::string &path, char flags, const std::string &override ) {
+ if(char_anim == path && override == char_anim_override){
+ return;
+ }
+ char_anim_override = override;
+ char_anim = path;
+ char_anim_flags = flags;
+ CheckItemAnimBlends(char_anim, flags);
+}
+
+const float _item_blend_fade_speed = 5.0f;
+void RiggedObject::CheckItemAnimBlends(const std::string &path, char flags) {
+ anim_client.ClearBlendAnimation();
+ for(std::list<AttachedItem>::iterator iter = attached_items.items.begin();
+ iter != attached_items.items.end(); ++iter)
+ {
+ ItemObjectScriptReader& item = iter->item;
+ if(item->item_ref()->HasAnimBlend(path) && item->state() == ItemObject::kWielded){
+ const std::string &a_path = item->item_ref()->GetAnimBlend(path);
+ anim_client.SetBlendAnimation(a_path);
+ }
+ }
+}
+
+void RiggedObject::MPCutPlane(const vec3 & normal, const vec3 & pos, const vec3 & dir, int type, int depth, std::vector<int> mp_hit_list, vec3 mp_points[3])
+{
+ if (Graphics::Instance()->config_.blood() == BloodLevel::kNone) {
+ return;
+ }
+
+
+
+ Model* model = &Models::Instance()->GetModel(model_id[0]);
+ vec3 points[3];
+
+ for (int i = 0; i < 3; i++)
+ points[i] = mp_points[i];
+
+ int num_pos_side = 0;
+ int num_neg_side = 0;
+ int num_hit = 0;
+ std::vector<int> hit_list;
+
+ hit_list = mp_hit_list;
+ num_hit = mp_hit_list.size();
+
+
+
+ if (num_hit < 10 && depth < 4) {
+ if (num_neg_side > num_pos_side) {
+ MPCutPlane(normal, pos - normal * 0.1f, dir, type, depth + 1, mp_hit_list, mp_points);
+ }
+ else {
+ MPCutPlane(normal, pos + normal * 0.1f, dir, type, depth + 1, mp_hit_list, mp_points);
+ }
+ }
+ else {
+ //blood_surface.AddBloodToTriangles(hit_list);
+ for (unsigned i = 0; i < hit_list.size(); ++i) {
+ GetTransformedTri(hit_list[i], mp_points);
+ if (dot(cross(mp_points[1] - mp_points[0], mp_points[2] - mp_points[0]), dir) > 0.0f) {
+ continue;
+ }
+ float time[3];
+ for (unsigned j = 0; j < 3; ++j) {
+ const vec3 d = mp_points[(j + 1) % 3] - mp_points[j];
+ const vec3 o = mp_points[j] - pos;
+ const vec3 &n = normal;
+ time[j] = (-o[0] * n[0] - o[1] * n[1] - o[2] * n[2]) /
+ (d[0] * n[0] + d[1] * n[1] + d[2] * n[2]);
+ }
+ vec2 tex_points[3];
+ for (unsigned j = 0; j < 3; ++j) {
+ tex_points[j][0] = model->tex_coords[model->faces[hit_list[i] * 3 + j] * 2 + 0];
+ tex_points[j][1] = model->tex_coords[model->faces[hit_list[i] * 3 + j] * 2 + 1];
+
+
+ }
+ vec2 tex_intersect_points[2];
+ vec3 intersect_points[2];
+ int which_point = 0;
+ for (unsigned j = 0; j < 3; ++j) {
+ if (time[j] >= 0.0f && time[j] <= 1.0f) {
+ tex_intersect_points[which_point] =
+ mix(tex_points[j], tex_points[(j + 1) % 3], time[j]);
+ intersect_points[which_point] =
+ mix(points[j], mp_points[(j + 1) % 3], time[j]);
+ ++which_point;
+ if (which_point > 1) {
+ break;
+ }
+ }
+ }
+ //DebugDraw::Instance()->AddLine(intersect_points[0], intersect_points[1], vec4(1.0f), _persistent);
+ blood_surface.AddBloodLine(tex_intersect_points[0], tex_intersect_points[1]);
+
+
+ if (type == _light || RangedRandomFloat(0.0f, 1.0f) < 0.6f) {
+ vec2 bleed_point = mix(tex_intersect_points[0], tex_intersect_points[1], 0.5f);
+ vec3 coords = bary_coords(bleed_point, tex_points[0], tex_points[1], tex_points[2]);
+ blood_surface.CreateDripInTri(hit_list[i], coords, RangedRandomFloat(0.1f, 1.0f), RangedRandomFloat(0.0f, 1.0f), true, SurfaceWalker::BLOOD);
+ }
+
+ if (type == _heavy || RangedRandomFloat(0.0f, 1.0f) < 0.2f) {
+ vec2 bleed_point = mix(tex_intersect_points[0], tex_intersect_points[1], 0.5f);
+ vec3 coords = bary_coords(bleed_point, tex_points[0], tex_points[1], tex_points[2]);
+ blood_surface.CreateDripInTri(hit_list[i], coords, RangedRandomFloat(0.1f, 0.5f), RangedRandomFloat(0.0f, 10.0f), true, SurfaceWalker::BLOOD);
+ }
+
+ //dot(offset+dir*time, normal) = 0.0f
+ // ox*nx + dx*t*nx + oy*ny + dy*t*ny + oz*nz + dz*t*nz = 0
+ // dx*t*nx + dy*t*ny + dz*t*nz = -ox*nx-oy*ny-oz*nz
+ // t = (-ox*nx-oy*ny-oz*nz) / (dx*nx + dy*ny + dz*nz)
+
+ bool add_blood_cloud = (rand() % 10 == 0);
+ bool add_blood_splat = (type == _light && rand() % 40 == 0);
+ if (add_blood_cloud || add_blood_splat) {
+ vec3 pos = (points[0] + points[1] + points[2]) / 3.0f;
+ if (add_blood_cloud) {
+ scenegraph_->particle_system->MakeParticle(
+ scenegraph_, "Data/Particles/bloodcloud.xml", pos,
+ vec3(RangedRandomFloat(-1.0f, 1.0f), RangedRandomFloat(-1.0f, 1.0f), RangedRandomFloat(-1.0f, 1.0f)),
+ Graphics::Instance()->config_.blood_color());
+ }
+ if (add_blood_splat) {
+ scenegraph_->particle_system->MakeParticle(
+ scenegraph_, "Data/Particles/bloodsplat.xml", pos,
+ vec3(RangedRandomFloat(-2.0f, 2.0f), RangedRandomFloat(-2.0f, 2.0f), RangedRandomFloat(-2.0f, 2.0f)),
+ Graphics::Instance()->config_.blood_color());
+ }
+ }
+ }
+ }
+ blood_surface.sleep_time = game_timer.game_time + 5.0f;
+}
+
+void UpdateAttachedItem(AttachedItem& attached_item, const Skeleton &skeleton, float char_scale, const std::map<int, mat4> weapon_offset, const WeapAnimInfoMap &weap_anim_info_map, const std::map<int, mat4> &weapon_offset_retarget) {
+ ItemObjectScriptReader& item = attached_item.item;
+ int bone = attached_item.bone_id;
+ mat4 bone_mat = skeleton.physics_bones[bone].bullet_object->GetTransform();
+ mat4 weap_mat = weapon_offset.find(item->GetID())->second;
+ weap_mat.SetTranslationPart(weap_mat.GetTranslationPart() * char_scale);
+ mat4 the_weap_mat = bone_mat * weap_mat;
+ if(item->state() == ItemObject::kWielded){
+ WeapAnimInfoMap::const_iterator iter = weap_anim_info_map.find(item->GetID());
+ if(iter != weap_anim_info_map.end()){
+ const WeapAnimInfo &weap_anim_info = iter->second;
+ mat4 anim_mat = weap_anim_info.matrix;
+ if(weap_anim_info.relative_id != -1){
+ anim_mat = skeleton.physics_bones[weap_anim_info.relative_id].bullet_object->GetTransform() * anim_mat;
+ }
+ anim_mat = anim_mat * weapon_offset_retarget.find(item->GetID())->second;
+ the_weap_mat = mix(the_weap_mat, anim_mat, weap_anim_info.weight);
+ }
+ }
+ mat4 temp_weap_mat = the_weap_mat;
+ temp_weap_mat.SetColumn(2, the_weap_mat.GetColumn(1)*-1.0f);
+ temp_weap_mat.SetColumn(1, the_weap_mat.GetColumn(2));
+ if(!item.just_created){
+ item.SetPhysicsTransform(temp_weap_mat);
+ }
+ if(item.just_created){
+ item.SetPhysicsTransform(temp_weap_mat);
+ item.SetPhysicsTransform(temp_weap_mat);
+ item.just_created = false;
+ }
+}
+
+void UpdateStuckItem(AttachedItem& stuck_item, const Skeleton &skeleton, bool animated) {
+ int bone = stuck_item.bone_id;
+ mat4 bone_mat = skeleton.physics_bones[bone].bullet_object->GetTransform();
+ mat4 the_weap_mat = bone_mat * stuck_item.rel_mat;
+ mat4 temp_weap_mat = the_weap_mat;
+ ItemObjectScriptReader &item = stuck_item.item;
+ item.SetPhysicsTransform(temp_weap_mat);
+ if(item.just_created){
+ item.SetPhysicsTransform(temp_weap_mat);
+ item.just_created = false;
+ }
+ if(!animated){
+ item.SetInterpInfo(1,1);
+ }
+}
+
+void RiggedObject::UpdateAttachedItems() {
+ for(AttachedItemList::iterator iter = attached_items.items.begin(); iter != attached_items.items.end(); ++iter){
+ UpdateAttachedItem(*iter, skeleton_, model_char_scale, weapon_offset, weap_anim_info_map, weapon_offset_retarget);
+ }
+ for(AttachedItemList::iterator iter = stuck_items.items.begin(); iter != stuck_items.items.end(); ++iter) {
+ UpdateStuckItem(*iter, skeleton_, animated);
+ }
+}
+
+void RiggedObject::CutPlane( const vec3 &normal, const vec3 &pos, const vec3 &dir, int type, int depth){
+ Online* online = Online::Instance();
+
+ if(Graphics::Instance()->config_.blood() == BloodLevel::kNone){
+ return;
+ }
+
+ Model* model = &Models::Instance()->GetModel(model_id[0]);
+ vec3 points[3];
+ bool pos_side;
+ bool neg_side;
+ int num_pos_side = 0;
+ int num_neg_side = 0;
+ int num_hit = 0;
+ std::vector<int> hit_list;
+ if (!online->IsClient()) {
+ for (int i = 0, len = model->faces.size() / 3; i < len; ++i) {
+ GetTransformedTri(i, points);
+ pos_side = false;
+ neg_side = false;
+ for (unsigned j = 0; j < 3; ++j) {
+ if (dot(points[j] - pos, normal) < 0.0f) {
+ neg_side = true;
+ }
+ else {
+ pos_side = true;
+ }
+ }
+ if (neg_side && pos_side) {
+ hit_list.push_back(i);
+ ++num_hit;
+ }
+ else if (neg_side) {
+ ++num_neg_side;
+ }
+ else {
+ ++num_pos_side;
+ }
+ }
+ }
+
+ if(num_hit < 10 && depth < 4){
+ if(num_neg_side > num_pos_side){
+ CutPlane(normal, pos-normal*0.1f, dir, type, depth+1);
+ } else {
+ CutPlane(normal, pos+normal*0.1f, dir, type, depth+1);
+ }
+ } else {
+ //blood_surface.AddBloodToTriangles(hit_list);
+ for(unsigned i=0; i<hit_list.size(); ++i){
+ GetTransformedTri(hit_list[i], points);
+ if(dot(cross(points[1] - points[0], points[2] - points[0]), dir) > 0.0f){
+ continue;
+ }
+ float time[3];
+ for(unsigned j=0; j<3; ++j){
+ const vec3 d = points[(j+1)%3]-points[j];
+ const vec3 o = points[j]-pos;
+ const vec3 &n = normal;
+ time[j] = (-o[0]*n[0] - o[1]*n[1] - o[2]*n[2])/
+ (d[0]*n[0] + d[1]*n[1] + d[2]*n[2]);
+ }
+ vec2 tex_points[3];
+ for(unsigned j=0; j<3; ++j){
+ tex_points[j][0] = model->tex_coords[model->faces[hit_list[i]*3+j]*2+0];
+ tex_points[j][1] = model->tex_coords[model->faces[hit_list[i]*3+j]*2+1];
+ }
+ vec2 tex_intersect_points[2];
+ vec3 intersect_points[2];
+ int which_point = 0;
+ for(unsigned j=0; j<3; ++j){
+ if(time[j] >= 0.0f && time[j] <= 1.0f){
+ tex_intersect_points[which_point] =
+ mix(tex_points[j], tex_points[(j+1)%3], time[j]);
+ intersect_points[which_point] =
+ mix(points[j], points[(j+1)%3], time[j]);
+ ++which_point;
+ if(which_point>1){
+ break;
+ }
+ }
+ }
+ //DebugDraw::Instance()->AddLine(intersect_points[0], intersect_points[1], vec4(1.0f), _persistent);
+ blood_surface.AddBloodLine(tex_intersect_points[0], tex_intersect_points[1]);
+
+ if(type == _light || RangedRandomFloat(0.0f,1.0f)<0.6f){
+ vec2 bleed_point = mix(tex_intersect_points[0], tex_intersect_points[1], 0.5f);
+ vec3 coords = bary_coords(bleed_point, tex_points[0], tex_points[1], tex_points[2]);
+
+ //LOGI << "BLEED POINT " << bleed_point << " coords " << coords << std::endl;
+ blood_surface.CreateDripInTri(hit_list[i], coords, RangedRandomFloat(0.1f,1.0f), RangedRandomFloat(0.0f,1.0f), true, SurfaceWalker::BLOOD);
+ }
+
+ if(type == _heavy || RangedRandomFloat(0.0f,1.0f)<0.2f){
+ vec2 bleed_point = mix(tex_intersect_points[0], tex_intersect_points[1], 0.5f);
+ vec3 coords = bary_coords(bleed_point, tex_points[0], tex_points[1], tex_points[2]);
+
+ //LOGI << "BLEED POINT " << bleed_point << " coords " << coords << std::endl;
+ blood_surface.CreateDripInTri(hit_list[i], coords, RangedRandomFloat(0.1f,0.5f), RangedRandomFloat(0.0f,10.0f), true, SurfaceWalker::BLOOD);
+ }
+
+ //dot(offset+dir*time, normal) = 0.0f
+ // ox*nx + dx*t*nx + oy*ny + dy*t*ny + oz*nz + dz*t*nz = 0
+ // dx*t*nx + dy*t*ny + dz*t*nz = -ox*nx-oy*ny-oz*nz
+ // t = (-ox*nx-oy*ny-oz*nz) / (dx*nx + dy*ny + dz*nz)
+
+ bool add_blood_cloud = (rand()%10 == 0);
+ bool add_blood_splat = (type == _light && rand()%40 == 0);
+ if(add_blood_cloud || add_blood_splat){
+ vec3 pos = (points[0] + points[1] + points[2]) / 3.0f;
+ if(add_blood_cloud){
+ scenegraph_->particle_system->MakeParticle(
+ scenegraph_, "Data/Particles/bloodcloud.xml", pos,
+ vec3(RangedRandomFloat(-1.0f,1.0f),RangedRandomFloat(-1.0f,1.0f),RangedRandomFloat(-1.0f,1.0f)),
+ Graphics::Instance()->config_.blood_color());
+ }
+ if(add_blood_splat){
+ scenegraph_->particle_system->MakeParticle(
+ scenegraph_, "Data/Particles/bloodsplat.xml",pos,
+ vec3(RangedRandomFloat(-2.0f,2.0f),RangedRandomFloat(-2.0f,2.0f),RangedRandomFloat(-2.0f,2.0f)),
+ Graphics::Instance()->config_.blood_color());
+ }
+ }
+ }
+ }
+
+ if (online->IsHosting()) {
+ online->Send<OnlineMessages::CutLine>(
+ char_id,
+ points,
+ pos,
+ normal,
+ dir,
+ type,
+ depth,
+ num_hit,
+ hit_list);
+ }
+ blood_surface.sleep_time = game_timer.game_time + 5.0f;
+}
+
+void RiggedObject::Stab( const vec3 &pos, const vec3 &dir, int type, int depth ) {
+ const float _stab_threshold = pow(0.001f,1.0f/(float)(depth*0.3f+1));
+ Model* model = &Models::Instance()->GetModel(model_id[0]);
+ vec3 points[3];
+ bool close_enough;
+ std::vector<int> hit_list;
+ for(int i=0, len=model->faces.size()/3; i<len; ++i){
+ close_enough = false;
+ GetTransformedTri(i, points);
+ for(unsigned j=0; j<3; ++j){
+ points[j] -= dir * dot(dir, points[j] - pos);
+ if(distance_squared(points[j], pos) < _stab_threshold){
+ close_enough = true;
+ }
+ }
+ if(close_enough){
+ hit_list.push_back(i);
+ }
+ }
+ if(hit_list.size() < 5 && depth < 12){
+ Stab(pos, dir, type, depth+1);
+ return;
+ }
+ if(Graphics::Instance()->config_.blood() == BloodLevel::kNone){
+ return;
+ }
+ blood_surface.AddDecalToTriangles(hit_list,
+ pos, dir, (depth + 1) * 0.1f,
+ stab_texture);
+ vec3 start = pos - dir * 1000.0f;
+ vec3 end = pos + dir * 1000.0f;
+ vec3 intersect;
+ for(unsigned i=0; i<hit_list.size(); ++i){
+ GetTransformedTri(hit_list[i], points);
+ if(LineFacet(start, end, points[0], points[1], points[2], &intersect)){
+ for(unsigned j=0; j<10; ++j){
+ blood_surface.CreateDripInTri(hit_list[i], vec3(0.333f,0.333f,0.333f), RangedRandomFloat(0.1f,1.0f), RangedRandomFloat(0.0f,10.0f), true, SurfaceWalker::BLOOD);
+ }
+ }
+ }
+
+ for(unsigned i=0; i<hit_list.size(); ++i){
+ GetTransformedTri(hit_list[i], points);
+ vec3 pos = (points[0] + points[1] + points[2]) / 3.0f;
+ if(rand()%(hit_list.size()) == 0){
+ scenegraph_->particle_system->MakeParticle(
+ scenegraph_, "Data/Particles/bloodcloud.xml",pos,
+ vec3(RangedRandomFloat(-1.0f,1.0f),RangedRandomFloat(-1.0f,1.0f),RangedRandomFloat(-1.0f,1.0f))+dir,
+ Graphics::Instance()->config_.blood_color());
+ scenegraph_->particle_system->MakeParticle(
+ scenegraph_, "Data/Particles/bloodcloud.xml",pos,
+ vec3(RangedRandomFloat(-1.0f,1.0f),RangedRandomFloat(-1.0f,1.0f),RangedRandomFloat(-1.0f,1.0f))-dir,
+ Graphics::Instance()->config_.blood_color());
+ }
+ if(type == _light && rand()%40 == 0){
+ scenegraph_->particle_system->MakeParticle(
+ scenegraph_, "Data/Particles/bloodsplat.xml",pos,
+ vec3(RangedRandomFloat(-2.0f,2.0f),RangedRandomFloat(-2.0f,2.0f),RangedRandomFloat(-2.0f,2.0f)),
+ Graphics::Instance()->config_.blood_color());
+ }
+ }
+ blood_surface.sleep_time = game_timer.game_time + 5.0f;
+}
+
+std::vector<int> water_logged;
+
+void RiggedObject::AddWaterCube( const mat4 &transform ) {
+ if(blood_surface.blood_tex.valid()){
+ PROFILER_GPU_ZONE(g_profiler_ctx, "RiggedObject::AddWaterCube");
+
+ std::swap(blood_surface.blood_tex, blood_surface.blood_work_tex);
+ blood_surface.StartDrawingToBloodTexture();
+ Graphics::Instance()->setBlend(false);
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ Graphics::Instance()->Clear(true);
+ Shaders* shaders = Shaders::Instance();
+ int the_shader = shaders->returnProgram(water_cube);
+
+ if(the_shader!=-1) {
+ shaders->setProgram(the_shader);
+
+ std::vector<mat4> fire_decal_matrix;
+ std::vector<mat4> water_decal_matrix;
+ for(int i=0, len=scenegraph_->decal_objects_.size(); i<len; ++i){
+ DecalObject* obj = (DecalObject*)scenegraph_->decal_objects_[i];
+ if(obj->decal_file_ref->special_type == 3){
+ fire_decal_matrix.push_back(obj->GetTransform());
+ }
+ if(obj->decal_file_ref->special_type == 5){
+ water_decal_matrix.push_back(obj->GetTransform());
+ }
+ }
+ shaders->SetUniformInt("fire_decal_num", fire_decal_matrix.size());
+ if(!fire_decal_matrix.empty()){
+ shaders->SetUniformMat4Array("fire_decal_matrix", fire_decal_matrix);
+ }
+ shaders->SetUniformInt("water_decal_num", water_decal_matrix.size());
+ if(!water_decal_matrix.empty()){
+ shaders->SetUniformMat4Array("water_decal_matrix", water_decal_matrix);
+ }
+
+ shaders->SetUniformMat4("transform", transform);
+ shaders->SetUniformFloat("time", game_timer.GetRenderTime());
+ Textures::Instance()->bindTexture(blood_surface.blood_work_tex);
+
+ Model* model;
+ model =&Models::Instance()->GetModel(model_id[lod_level]);
+ DrawModel(model, lod_level);
+ }
+ blood_surface.EndDrawingToBloodTexture();
+
+ // Expand blood texels by one pixel to fill in seams
+ std::swap(blood_surface.blood_tex, blood_surface.blood_work_tex);
+ blood_surface.StartDrawingToBloodTexture();
+ Graphics::Instance()->setBlend(false);
+ Graphics::Instance()->SetBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+ int shader_id = Shaders::Instance()->returnProgram(water_cube_expand);
+ Shaders::Instance()->setProgram(shader_id);
+ Textures::Instance()->bindTexture(blood_surface.blood_work_tex);
+ Shaders::Instance()->SetUniformInt("tex_width", Textures::Instance()->getWidth(blood_surface.blood_work_tex));
+ Shaders::Instance()->SetUniformInt("tex_height", Textures::Instance()->getHeight(blood_surface.blood_work_tex));
+
+ static const GLfloat data[] = {0,0, 1,0, 1,1, 0,1};
+ static const GLuint index[] = {0,1,2, 0,2,3};
+ static VBOContainer square_vbo;
+ static VBOContainer index_vbo;
+ if(!square_vbo.valid()){
+ square_vbo.Fill(kVBOFloat | kVBOStatic, sizeof(data), (void*)data);
+ index_vbo.Fill(kVBOElement | kVBOStatic, sizeof(index), (void*)index);
+ }
+ square_vbo.Bind();
+ index_vbo.Bind();
+
+ int vert_attrib_id = Shaders::Instance()->returnShaderAttrib("vert_coord", shader_id);
+ Graphics::Instance()->EnableVertexAttribArray(vert_attrib_id);
+ glVertexAttribPointer(vert_attrib_id, 2, GL_FLOAT, false, 2*sizeof(GLfloat), 0);
+ Graphics::Instance()->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
+ Graphics::Instance()->ResetVertexAttribArrays();
+
+ blood_surface.EndDrawingToBloodTexture();
+ Textures::Instance()->GenerateMipmap(blood_surface.blood_tex);
+ }
+}
+
+void RiggedObject::UpdateCollisionObjects() {
+ for(unsigned i=0; i<skeleton_.physics_bones.size(); ++i){
+ BulletObject* cbo = skeleton_.physics_bones[i].col_bullet_object;
+ if(!cbo){
+ continue;
+ }
+ cbo->SetTransform(skeleton_.physics_bones[i].bullet_object->GetTransform());
+ cbo->UpdateTransform();
+ }
+ skeleton_.UpdateCollisionWorldAABB();
+}
+
+void RiggedObject::AddBloodAtPoint( const vec3 & point ) {
+ blood_surface.CreateBloodDripNearestPointTransformed(point, 0.1f, false);
+ blood_surface.sleep_time = game_timer.game_time + 5.0f;
+}
+
+enum LogReadStage { _deleted_faces, _first_vert, _second_vert, _offset_x, _offset_y, _offset_z, _modified_faces };
+
+static void ASSetBoneInflate(Skeleton* skeleton, int which, float val){
+ skeleton->physics_bones[which].display_scale = val;
+}
+
+static int ASGetParent(Skeleton* skeleton, int which){
+ return skeleton->parents[which];
+}
+
+static bool ASIKBoneExists(Skeleton* skeleton, const std::string& name){
+ return skeleton->simple_ik_bones.find(name) != skeleton->simple_ik_bones.end();
+}
+
+int ASIKBoneStart(Skeleton* skeleton, const std::string& name){
+ return skeleton->simple_ik_bones[name].bone_id;
+}
+
+int ASIKBoneLength(Skeleton* skeleton, const std::string& name){
+ return skeleton->simple_ik_bones[name].chain_length;
+}
+
+static int ASNumBones(Skeleton* skeleton){
+ return skeleton->physics_bones.size();
+}
+
+static vec3 ASGetBoneLinearVelocity(Skeleton* skeleton, int which){
+ if(!skeleton->physics_bones[which].bullet_object){
+ return vec3(0.0f);
+ } else {
+ return skeleton->physics_bones[which].bullet_object->GetLinearVelocity();
+ }
+}
+
+static mat4 ASGetBoneTransform(Skeleton* skeleton, int which){
+ mat4 return_mat = skeleton->physics_bones[which].bullet_object->GetRotation();
+ return_mat.SetTranslationPart(skeleton->physics_bones[which].bullet_object->GetPosition());
+ return return_mat;
+}
+
+static void ASSetBoneTransform(Skeleton* skeleton, int which, const mat4 &transform){
+ skeleton->physics_bones[which].bullet_object->SetTransform(transform);
+}
+
+static BoneTransform ASGetFrameMatrix(RiggedObject* rigged_object, int which){
+ return rigged_object->animation_frame_bone_matrices[which];
+}
+
+static void ASSetFrameMatrix(RiggedObject* rigged_object, int which, const BoneTransform &transform){
+ rigged_object->animation_frame_bone_matrices[which] = transform;
+}
+
+static vec3 ASGetPointPos(Skeleton* skeleton, int which){
+ return skeleton->points[which];
+}
+
+static int ASGetBonePoint(Skeleton* skeleton, int which_bone, int which_point){
+ return skeleton->bones[which_bone].points[which_point];
+}
+
+static void ASAddVelocity(Skeleton* skeleton, const vec3& vel){
+ for(int bone_index=0, num_bones=skeleton->physics_bones.size(); bone_index<num_bones; ++bone_index){
+ BulletObject* bullet_object = skeleton->physics_bones[bone_index].bullet_object;
+ if(bullet_object){
+ bullet_object->AddLinearVelocity(vel);
+ }
+ }
+}
+
+void RotateBoneToMatchVec(RiggedObject* rigged_object, const vec3 &a, const vec3 &b, int bone){
+ BoneTransform &mat = rigged_object->animation_frame_bone_matrices[bone];
+ mat.origin = (a+b)*0.5f;
+ vec3 dir = mat.rotation * vec3(0,0,1);
+ quaternion rot;
+ GetRotationBetweenVectors(dir, b - a, rot);
+ mat.rotation = rot * mat.rotation;
+}
+
+void RotateBonesToMatchVec(RiggedObject* rigged_object, const vec3& a, const vec3& c, int bone, int bone2, float weight) {
+ vec3 b = mix(a,c,1.0f-weight);
+
+ BoneTransform &mat = rigged_object->animation_frame_bone_matrices[bone];
+ mat.origin = (a+b)*0.5f;
+ vec3 dir = mat.rotation * vec3(0,0,1);
+ quaternion rot;
+ GetRotationBetweenVectors(dir, b - a, rot);
+ mat.rotation = rot * mat.rotation;
+
+ BoneTransform &mat2 = rigged_object->animation_frame_bone_matrices[bone2];
+ mat2.origin = (b+c)*0.5f;
+ mat2.rotation = rot * mat2.rotation;
+}
+
+static void TransformAllFrameMats(RiggedObject* rigged_object, const BoneTransform &transform) {
+ std::vector<BoneTransform> &mats = rigged_object->animation_frame_bone_matrices;
+ for(int i=0, len=mats.size(); i<len; ++i){
+ mats[i] = transform * mats[i];
+ }
+}
+
+static vec3 GetFrameCenterOfMass(RiggedObject* rigged_object) {
+ Skeleton& skeleton = rigged_object->skeleton_;
+ int num_bones = skeleton.physics_bones.size();
+ vec3 com;
+ float total_mass = 0.0f;
+ for(int i=0; i<num_bones; ++i){
+ BoneTransform &transform = rigged_object->animation_frame_bone_matrices[i];
+ float bone_mass = skeleton.bones[i].mass;
+ com += transform.origin * bone_mass;
+ total_mass += bone_mass;
+ }
+ if(total_mass != 0.0f){
+ com /= total_mass;
+ }
+ return com;
+}
+
+vec3 GetTransformedBonePoint(RiggedObject* rigged_object, int bone, int point){
+ Skeleton& skeleton = rigged_object->skeleton_;
+ BoneTransform transform = rigged_object->animation_frame_bone_matrices[bone] *
+ rigged_object->cached_skeleton_info_.bind_matrices[bone];
+ const vec3 &bone_point = skeleton.points[skeleton.bones[bone].points[point]];
+ return transform * bone_point;
+}
+
+static float ASGetBoneMass(Skeleton* skeleton, int which_bone){
+ return skeleton->bones[which_bone].mass;
+}
+
+vec3 ASGetModelCenter(RiggedObject* rigged_object){
+ const Model& model = Models::Instance()->GetModel(rigged_object->model_id[0]);
+ return model.old_center;
+}
+
+static mat4 GetDisplayBoneMatrix(RiggedObject* rigged_object, int bone){
+ return rigged_object->display_bone_matrices[bone];
+}
+
+static void SetDisplayBoneMatrix(RiggedObject* rigged_object, int bone, const mat4 &matrix){
+ rigged_object->display_bone_matrices[bone] = matrix;
+}
+
+static bool ASHasPhysics(Skeleton* skeleton, int which){
+ return (skeleton->physics_bones[which].bullet_object != NULL);
+}
+
+static CScriptArray *ASGetConvexHullPoints(Skeleton* skeleton, int bone){
+ std::vector<float> points;
+ BulletObject* bullet_obj = skeleton->physics_bones[bone].col_bullet_object;
+ if(bullet_obj){
+ btCollisionShape* shape = bullet_obj->shape.get();
+ if(shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE){
+ btConvexHullShape* convex_hull_shape = (btConvexHullShape*)shape;
+ int num_verts = convex_hull_shape->getNumVertices();
+ points.reserve(num_verts*3);
+ for(int i=0; i<num_verts; ++i){
+ btVector3 vert;
+ convex_hull_shape->getVertex(i, vert);
+ points.push_back(vert[0]);
+ points.push_back(vert[1]);
+ points.push_back(vert[2]);
+ }
+ }
+ }
+
+ asIScriptContext *ctx = asGetActiveContext();
+ asIScriptEngine *engine = ctx->GetEngine();
+ asITypeInfo *arrayType = engine->GetTypeInfoById(engine->GetTypeIdByDecl("array<float>"));
+ CScriptArray *array = CScriptArray::Create(arrayType, (asUINT)0);
+ array->Reserve(points.size());
+
+ for(int i=0, len=points.size(); i<len; ++i) {
+ array->InsertLast(&points[i]);
+ }
+ return array;
+}
+
+static BoneTransform ASApplyParentRotations(Skeleton* skeleton, const CScriptArray *matrices, int id) {
+ int parent_id = skeleton->parents[id];
+ if(parent_id == -1){
+ return *(BoneTransform*)(matrices->At(id));
+ } else {
+ return ASApplyParentRotations(skeleton, matrices, parent_id) * *(BoneTransform*)(matrices->At(id));
+ }
+}
+
+static void ASRemoveAllLayers(AnimationClient* animation_client){
+ animation_client->RemoveAllLayers();
+}
+
+static void ASSetFire(RiggedObject* rigged_object, float fire){
+ rigged_object->blood_surface.SetFire(fire);
+}
+
+static void ASIgnite(RiggedObject* rigged_object){
+ rigged_object->blood_surface.Ignite();
+ rigged_object->blood_surface.sleep_time = game_timer.game_time + 5.0f;
+}
+
+static void ASExtinguish(RiggedObject* rigged_object){
+ rigged_object->blood_surface.Extinguish();
+}
+
+static void ASSetWet(RiggedObject* rigged_object, float wet){
+ rigged_object->blood_surface.SetWet(wet);
+}
+
+void DefineRiggedObjectTypePublic(ASContext* ctx) {
+ if(ctx->TypeExists("AnimationClient")){
+ return;
+ }
+ ctx->RegisterObjectType("AnimationClient", 0, asOBJ_REF | asOBJ_NOCOUNT);
+ ctx->RegisterObjectMethod("AnimationClient","float GetAnimationEventTime(const string &in event)",asMETHOD(AnimationClient, GetAnimationEventTime), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("AnimationClient","float GetTimeUntilEvent(const string &in event)",asMETHOD(AnimationClient, GetTimeUntilEvent), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("AnimationClient","void SetAnimationCallback(const string &in function)",asMETHOD(AnimationClient, SetCallbackString), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("AnimationClient","void AddAnimationOffset(const vec3 &in)",asMETHOD(AnimationClient, AddAnimationOffset), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("AnimationClient","void AddAnimationRotOffset(float radians)",asMETHOD(AnimationClient, AddAnimationRotOffset), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("AnimationClient","int AddLayer(const string &in anim_path, float transition_speed, int8 flags)",asMETHOD(AnimationClient, AddLayer), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("AnimationClient","int RemoveLayer(int id, float transition_speed)",asMETHOD(AnimationClient, RemoveLayer), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("AnimationClient","void SetBlendCoord(const string &in coord_label, float coord_value)",asMETHOD(AnimationClient, SetBlendCoord), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("AnimationClient","void SetLayerOpacity(int id, float opacity)",asMETHOD(AnimationClient, SetLayerOpacity), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("AnimationClient","void SetSpeedMult(float)",asMETHOD(AnimationClient, SetSpeedMult), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("AnimationClient","void SetAnimatedItemID(int index, int id)",asMETHOD(AnimationClient, SetAnimatedItemID), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("AnimationClient","void SetLayerItemID(int layer, int index, int id)",asMETHOD(AnimationClient, SetLayerItemID), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("AnimationClient","const string& GetCurrAnim()",asMETHOD(AnimationClient, GetCurrAnim), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("AnimationClient","float GetNormalizedAnimTime()",asMETHOD(AnimationClient, GetNormalizedAnimTime), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("AnimationClient","float GetAnimationSpeed()",asMETHOD(AnimationClient, GetAnimationSpeed), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("AnimationClient","void RemoveAllLayers()",asFUNCTION(ASRemoveAllLayers), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("AnimationClient","void Reset()",asMETHOD(AnimationClient, Reset), asCALL_THISCALL);
+ ctx->DocsCloseBrace();
+
+ ctx->RegisterObjectType("Skeleton", 0, asOBJ_REF | asOBJ_NOCOUNT);
+ ctx->RegisterObjectMethod("Skeleton","vec3 GetCenterOfMass()",asMETHOD(Skeleton, GetCenterOfMass), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("Skeleton","void SetBoneInflate(int bone_id, float amount)",asFUNCTION(ASSetBoneInflate), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","int GetParent(int bone_id)",asFUNCTION(ASGetParent), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","bool IKBoneExists(const string& in)",asFUNCTION(ASIKBoneExists), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","float GetBoneMass(int bone_id)",asFUNCTION(ASGetBoneMass), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","int IKBoneStart(const string& in IK_label)",asFUNCTION(ASIKBoneStart), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","int IKBoneLength(const string& in IK_label)",asFUNCTION(ASIKBoneLength), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","int NumBones()",asFUNCTION(ASNumBones), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","vec3 GetBoneLinearVelocity(int which)",asFUNCTION(ASGetBoneLinearVelocity), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","mat4 GetBoneTransform(int bone_id)",asFUNCTION(ASGetBoneTransform), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","void SetBoneTransform(int bone_id, const mat4 &in transform)",asFUNCTION(ASSetBoneTransform), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","vec3 GetPointPos(int bone_id)",asFUNCTION(ASGetPointPos), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","int GetBonePoint(int bone_id, int which_point)",asFUNCTION(ASGetBonePoint), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","void AddVelocity(const vec3 &in vel)",asFUNCTION(ASAddVelocity), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","mat4 GetBindMatrix(int bone_id)",asFUNCTION(ASGetBindMatrix), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","bool HasPhysics(int bone_id)",asFUNCTION(ASHasPhysics), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","array<float> @GetConvexHullPoints(int bone_id)",asFUNCTION(ASGetConvexHullPoints), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("Skeleton","BoneTransform ApplyParentRotations(const array<BoneTransform> &in matrices, int id)",asFUNCTION(ASApplyParentRotations), asCALL_CDECL_OBJFIRST);
+ ctx->DocsCloseBrace();
+
+ ctx->RegisterObjectType("RiggedObject", 0, asOBJ_REF | asOBJ_NOCOUNT);
+ ctx->RegisterObjectProperty("RiggedObject", "bool ik_enabled", asOFFSET(RiggedObject,ik_enabled));
+ ctx->RegisterObjectProperty("RiggedObject", "int anim_update_period", asOFFSET(RiggedObject,anim_update_period));
+ ctx->RegisterObjectProperty("RiggedObject", "int curr_anim_update_time", asOFFSET(RiggedObject,curr_anim_update_time));
+ ctx->RegisterObjectMethod("RiggedObject","vec3 GetBonePosition(int bone)",asMETHOD(RiggedObject, GetBonePosition), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void AddWaterCube(const mat4 &in transform)",asMETHOD(RiggedObject, AddWaterCube), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void Ignite()",asFUNCTION(ASIgnite), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","void Extinguish()",asFUNCTION(ASExtinguish), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","void SetFire(float fire)",asFUNCTION(ASSetFire), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","void SetWet(float wet)",asFUNCTION(ASSetWet), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","BoneTransform GetFrameMatrix(int which)",asFUNCTION(ASGetFrameMatrix), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","void SetFrameMatrix(int which, const BoneTransform &in transform)",asFUNCTION(ASSetFrameMatrix), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","float GetCharScale()", asMETHOD(RiggedObject,GetCharScale), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","float GetRelativeCharScale()", asMETHOD(RiggedObject,GetRelativeCharScale), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void Draw()", asMETHOD(RiggedObject,Draw), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void CutPlane(const vec3 &in normal, const vec3 &in position, const vec3 &in direction, int type, int depth)",asMETHOD(RiggedObject, CutPlane), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void Stab(const vec3 &in position, const vec3 &in direction, int type, int depth)",asMETHOD(RiggedObject, Stab), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void SetRagdollStrength(float)",asMETHOD(RiggedObject, SetRagdollStrength), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void RefreshRagdoll()",asMETHOD(RiggedObject, RefreshRagdoll), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void SetRagdollDamping(float)",asMETHOD(RiggedObject, SetDamping), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void ApplyForceToRagdoll(const vec3 &in force, const vec3 &in position)",asMETHOD(RiggedObject, ApplyForceToRagdoll), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void ApplyForceToBone(const vec3 &in force, const int &in bone)",asMETHOD(RiggedObject, ApplyForceToBone), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void ApplyForceLineToRagdoll(const vec3 &in force, const vec3 &in position, const vec3 &in line_direction)",asMETHOD(RiggedObject, ApplyForceLineToRagdoll), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void MoveRagdollPart(const string &in, const vec3 &in, float)",asMETHOD(RiggedObject, MoveRagdollPart), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void FixedRagdollPart(int boneID, const vec3 &in)",asMETHOD(RiggedObject, FixedRagdollPart), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void SpikeRagdollPart(int boneID, const vec3 &start, const vec3 &end, const vec3 &pos)",asMETHOD(RiggedObject, SpikeRagdollPart), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void ClearBoneConstraints()",asMETHOD(RiggedObject, ClearBoneConstraints), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","vec3 GetAvgPosition()",asMETHOD(RiggedObject, GetAvgPosition), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","vec3 GetAvgVelocity()",asMETHOD(RiggedObject, GetAvgVelocity), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void CleanBlood()",asMETHOD(RiggedObject, CleanBlood), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void CreateBloodDrip(const string &in IK_label, int IK_link, const vec3 &in raycast_direction)",asMETHOD(RiggedObject, CreateBloodDrip), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void CreateBloodDripAtBone(int bone_id, int IK_link, const vec3 &in raycast_direction)",asMETHOD(RiggedObject, CreateBloodDripAtBone), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","vec3 GetAvgAngularVelocity()",asMETHOD(RiggedObject, GetAvgAngularVelocity), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","vec3 GetIKTargetPosition(const string &in IK_label)",asMETHOD(RiggedObject, GetIKTargetPosition), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","float GetIKWeight(const string &in IK_label)",asMETHOD(RiggedObject, GetIKWeight), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","BoneTransform GetIKTransform(const string &in IK_label)",asFUNCTION(GetIKTransform), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","mat4 GetUnmodifiedIKTransform(const string &in IK_label)",asFUNCTION(GetUnmodifiedIKTransform), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","mat4 GetIKTargetTransform(const string &in IK_label)",asMETHOD(RiggedObject, GetIKTargetTransform), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void SetMorphTargetWeight(const string &in morph_label, float weight, float weight_weight)",asMETHOD(RiggedObject, SetMTTargetWeight), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","float GetStatusKeyValue(const string &in status_key_label)",asMETHOD(RiggedObject, GetStatusKeyValue), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","vec3 GetIKTargetAnimPosition(const string &in IK_label)",asMETHOD(RiggedObject, GetIKTargetAnimPosition), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","mat4 GetAvgIKChainTransform(const string &in IK_label)",asMETHOD(RiggedObject, GetAvgIKChainTransform), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","mat4 GetIKChainTransform(const string &in IK_label, int IK_link)",asMETHOD(RiggedObject, GetIKChainTransform), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","vec3 GetIKChainPos(const string &in IK_label, int IK_link)",asMETHOD(RiggedObject, GetIKChainPos), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void DisableSleep()",asMETHOD(RiggedObject, DisableSleep), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void EnableSleep()",asMETHOD(RiggedObject, EnableSleep), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","vec3 GetAvgIKChainPos(const string &in IK_label)",asMETHOD(RiggedObject, GetAvgIKChainPos), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void SetAnimUpdatePeriod(int steps)",asMETHOD(RiggedObject, SetAnimUpdatePeriod), asCALL_THISCALL, "Animation is updated every N engine time steps");
+ ctx->RegisterObjectMethod("RiggedObject","void SheatheItem(int item_id, bool on_right)",asMETHOD(RiggedObject, SheatheItem), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void UnSheatheItem(int item_id, bool on_right)",asMETHOD(RiggedObject, UnSheatheItem), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject", "void UnStickItem(int item_id)", asMETHOD(RiggedObject,UnStickItem), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","void SetPrimaryWeaponID(int item_id)",asMETHOD(RiggedObject, SetPrimaryWeaponID), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","vec3 GetModelCenter()",asFunctionPtr(ASGetModelCenter), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","void RotateBoneToMatchVec(const vec3 &in a, const vec3 &in b, int bone)",asFunctionPtr(RotateBoneToMatchVec), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","void RotateBonesToMatchVec(const vec3 &in a, const vec3 &in c, int bone, int bone2, float weight)",asFunctionPtr(RotateBonesToMatchVec), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","void TransformAllFrameMats(const BoneTransform &in transform)",asFunctionPtr(TransformAllFrameMats), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","vec3 GetFrameCenterOfMass()",asFunctionPtr(GetFrameCenterOfMass), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","vec3 GetTransformedBonePoint(int bone, int point)",asFunctionPtr(GetTransformedBonePoint), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","mat4 GetDisplayBoneMatrix(int bone)",asFunctionPtr(GetDisplayBoneMatrix), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","void SetDisplayBoneMatrix(int bone, const mat4 &in transform)",asFunctionPtr(SetDisplayBoneMatrix), asCALL_CDECL_OBJFIRST);
+ ctx->RegisterObjectMethod("RiggedObject","Skeleton &skeleton()",asMETHOD(RiggedObject, skeleton), asCALL_THISCALL);
+ ctx->RegisterObjectMethod("RiggedObject","AnimationClient &anim_client()",asMETHOD(RiggedObject, GetAnimClient), asCALL_THISCALL);
+ ctx->DocsCloseBrace();
+}
+
+AnimationClient &RiggedObject::GetAnimClient() {
+ return anim_client;
+}
+
+static const int simp_cache_vers = 11;
+
+void RiggedObject::SaveSimplificationCache(const std::string &path) {
+ FILE *file;
+ Path found_path = FindFilePath(path.c_str(), kAnyPath, false);
+ if(found_path.isValid())
+ file = my_fopen((GetWritePath(found_path.GetModsource()).c_str()+path+".lod_cache").c_str(), "wb");
+ else {
+ LOGW << "Couldn't find path to \"" << path << "\" when saving simplification cache. This might result in a mod writing to the wrong directory" << std::endl;
+ file = my_fopen((GetWritePath(CoreGameModID).c_str()+path+".lod_cache").c_str(), "wb");
+ }
+ unsigned short checksum = skeleton_.skeleton_asset_ref->checksum()+
+ Models::Instance()->GetModel(model_id[0]).checksum;
+ for(int i=0, len=morph_targets.size(); i<len; ++i){
+ checksum += morph_targets[i].checksum;
+ }
+ fwrite(&simp_cache_vers, sizeof(int), 1, file);
+ fwrite(&checksum, sizeof(unsigned short), 1, file);
+ for(unsigned i=1; i<4; ++i){
+ const Model& model = Models::Instance()->GetModel(model_id[i]);
+ int model_num_verts = model.vertices.size()/3;
+ fwrite(&model_num_verts, sizeof(int), 1, file);
+ fwrite(&model.vertices[0], sizeof(GLfloat), model_num_verts*3, file);
+ fwrite(&model.bone_weights[0], sizeof(vec4), model_num_verts, file);
+ fwrite(&model.bone_ids[0], sizeof(vec4), model_num_verts, file);
+ fwrite(&model.tex_coords[0], sizeof(GLfloat), model_num_verts*2, file);
+ int num_faces = model.faces.size()/3;
+ fwrite(&num_faces, sizeof(int), 1, file);
+ fwrite(&model.faces[0], sizeof(GLuint), model.faces.size(), file);
+ }
+
+ {
+ int num_morphs = morph_targets.size();
+ fwrite(&num_morphs, sizeof(int), 1, file);
+ }
+
+ std::queue<MorphTarget*> queue;
+ for(unsigned i=0; i<morph_targets.size(); ++i){
+ queue.push(&morph_targets[i]);
+ }
+ while(!queue.empty()){
+ MorphTarget& morph = (*queue.front());
+ queue.pop();
+ for(unsigned i=0; i<morph.morph_targets.size(); ++i){
+ queue.push(&morph.morph_targets[i]);
+ }
+ for(unsigned i=1; i<4; ++i){
+ const Model& model = Models::Instance()->GetModel(morph.model_id[i]);
+ int model_num_verts = model.vertices.size()/3;
+ fwrite(&model.vertices[0], sizeof(GLfloat), model_num_verts*3, file);
+ fwrite(&model.tex_coords[0], sizeof(GLfloat), model_num_verts*2, file);
+ std::vector<char> &vm = morph.verts_modified[i];
+ fwrite(&vm[0],sizeof(char), model_num_verts, file);
+ int mv_size = morph.modified_verts[i].size();
+ fwrite(&mv_size, sizeof(int), 1, file);
+ if(mv_size){
+ std::vector<int> &mv = morph.modified_verts[i];
+ fwrite(&mv[0],sizeof(int), mv_size, file);
+ }
+
+ std::vector<char> &tcm = morph.tc_modified[i];
+ fwrite(&tcm[0],sizeof(char), model_num_verts, file);
+ int mtc_size = morph.modified_tc[i].size();
+ fwrite(&mtc_size, sizeof(int), 1, file);
+ if(mtc_size){
+ std::vector<int> &mtc = morph.modified_tc[i];
+ fwrite(&mtc[0],sizeof(int), mtc_size, file);
+ }
+ }
+ }
+ fclose(file);
+}
+
+bool RiggedObject::LoadSimplificationCache(const std::string& path) {
+ char lod_cache_path[kPathSize];
+ if(FindFilePath((path+".lod_cache").c_str(), lod_cache_path, kPathSize, kAnyPath, false) == -1){
+ return false;
+ }
+ FILE *file = my_fopen(lod_cache_path, "rb");
+ if(!file){
+ return false;
+ }
+ int vers;
+ fread(&vers, sizeof(int), 1, file);
+ if(vers != simp_cache_vers){
+ return false;
+ }
+ unsigned short checksum = skeleton_.skeleton_asset_ref->checksum()+
+ Models::Instance()->GetModel(model_id[0]).checksum;
+ for(int i=0, len=morph_targets.size(); i<len; ++i){
+ checksum += morph_targets[i].checksum;
+ }
+ unsigned short file_checksum;
+ fread(&file_checksum, sizeof(unsigned short), 1, file);
+ if(file_checksum != checksum){
+ return false;
+ }
+ for(unsigned i=1; i<4; ++i){
+ model_id[i] = Models::Instance()->AddModel();
+ Model& model = Models::Instance()->GetModel(model_id[i]);
+ int model_num_verts;
+ fread(&model_num_verts, sizeof(int), 1, file);
+ model.vertices.resize(model_num_verts*3);
+ model.bone_weights.resize(model_num_verts);
+ model.bone_ids.resize(model_num_verts);
+ model.tex_coords.resize(model_num_verts*2);
+ model.transform_vec.resize(4);
+ model.transform_vec[0].resize(model_num_verts);
+ model.transform_vec[1].resize(model_num_verts);
+ model.transform_vec[2].resize(model_num_verts);
+ model.transform_vec[3].resize(model_num_verts);
+ model.tex_transform_vec.resize(model_num_verts);
+ model.morph_transform_vec.resize(model_num_verts);
+ fread(&model.vertices[0], sizeof(GLfloat), model_num_verts*3, file);
+ fread(&model.bone_weights[0], sizeof(vec4), model_num_verts, file);
+ fread(&model.bone_ids[0], sizeof(vec4), model_num_verts, file);
+ fread(&model.tex_coords[0], sizeof(GLfloat), model_num_verts*2, file);
+ int num_faces;
+ fread(&num_faces, sizeof(int), 1, file);
+ model.faces.resize(num_faces*3);
+ fread(&model.faces[0], sizeof(GLuint), num_faces*3, file);
+ }
+
+ {
+ int num_morphs;
+ fread(&num_morphs, sizeof(int), 1, file);
+ if(num_morphs != (int)morph_targets.size()){
+ return false;
+ }
+ }
+
+ std::queue<MorphTarget*> queue;
+ for(unsigned i=0; i<morph_targets.size(); ++i){
+ queue.push(&morph_targets[i]);
+ }
+ while(!queue.empty()){
+ MorphTarget& morph = (*queue.front());
+ queue.pop();
+ for(unsigned i=0; i<morph.morph_targets.size(); ++i){
+ queue.push(&morph.morph_targets[i]);
+ }
+ for(unsigned i=1; i<4; ++i){
+ morph.model_id[i] = Models::Instance()->AddModel();
+ morph.model_copy[i] = true;
+ const Model& base_model = Models::Instance()->GetModel(model_id[i]);
+ Model& model = Models::Instance()->GetModel(morph.model_id[i]);
+ int model_num_verts = base_model.vertices.size()/3;
+ int base_model_num_verts = base_model.vertices.size()/3;
+ model.vertices.resize(base_model_num_verts*3);
+ model.tex_coords.resize(base_model_num_verts*2);
+ morph.verts_modified[i].resize(base_model_num_verts);
+ morph.tc_modified[i].resize(base_model_num_verts);
+ fread(&model.vertices[0], sizeof(GLfloat), model_num_verts*3, file);
+ fread(&model.tex_coords[0], sizeof(GLfloat), model_num_verts*2, file);
+
+ std::vector<char> &vm = morph.verts_modified[i];
+ fread(&vm[0],sizeof(char), model_num_verts, file);
+ int mv_size;
+ fread(&mv_size, sizeof(int), 1, file);
+ morph.modified_verts[i].resize(mv_size);
+ std::vector<int> &mv = morph.modified_verts[i];
+ if(mv_size){
+ fread(&mv[0],sizeof(int), mv_size, file);
+ }
+
+ std::vector<char> &tcm = morph.tc_modified[i];
+ fread(&tcm[0],sizeof(char), model_num_verts, file);
+ int mtc_size;
+ fread(&mtc_size, sizeof(int), 1, file);
+ morph.modified_tc[i].resize(mtc_size);
+ std::vector<int> &mtc = morph.modified_tc[i];
+ if(mtc_size){
+ fread(&mtc[0],sizeof(int), mtc_size, file);
+ }
+ }
+ }
+ fclose(file);
+ return true;
+}
+
+void RiggedObject::CreateFurModel( int model_id, int fur_model_id ) {
+ const Model &base_model = Models::Instance()->GetModel(model_id);
+ const Model &fur_ref_model = Models::Instance()->GetModel(fur_model_id);
+
+ // Create map to look up duplicate vertex positions
+ std::map<vec3, std::vector<int> > base_model_points;
+ for(unsigned i=0; i<base_model.vertices.size(); i+=3){
+ base_model_points[vec3(base_model.vertices[i+0],
+ base_model.vertices[i+1],
+ base_model.vertices[i+2])].push_back(i/3);
+ }
+
+ {
+ // Create vector of fur vertices offset by base model center
+ std::vector<vec3> fur_verts;
+ fur_verts.reserve(fur_ref_model.vertices.size()/3);
+ for(unsigned i=0; i<fur_ref_model.vertices.size(); i+=3){
+ fur_verts.push_back(vec3(fur_ref_model.vertices[i+0],
+ fur_ref_model.vertices[i+1],
+ fur_ref_model.vertices[i+2]));
+ fur_verts.back() -= base_model.old_center;
+ }
+
+ // Create fur model and populate it with tris that have at least one vertex
+ // that is not in the base model
+ int new_tris = 0;
+ const std::vector<GLuint> &faces = fur_ref_model.faces;
+ for(unsigned i=0; i<faces.size(); i+=3){
+ if(base_model_points.find(fur_verts[faces[i+0]]) == base_model_points.end() ||
+ base_model_points.find(fur_verts[faces[i+1]]) == base_model_points.end() ||
+ base_model_points.find(fur_verts[faces[i+2]]) == base_model_points.end())
+ {
+ ++new_tris;
+ fur_model.faces.push_back(faces[i+0]);
+ fur_model.faces.push_back(faces[i+1]);
+ fur_model.faces.push_back(faces[i+2]);
+ }
+ }
+ }
+
+ // Populate fur model with vertices used by the fur tris, and remove unused
+ // verts
+ {
+ std::vector<bool> included(fur_ref_model.vertices.size()/3, false);
+ for(unsigned i=0; i<fur_model.faces.size(); ++i){
+ included[fur_model.faces[i]] = true;
+ }
+
+ int remap_index = 0;
+ int vert_index;
+ std::vector<int> remapped(fur_ref_model.vertices.size()/3);
+ for(int i=0, len=fur_ref_model.vertices.size()/3; i<len; ++i){
+ if(included[i]){
+ remapped[i] = remap_index++;
+ vert_index = i*3;
+ fur_model.vertices.push_back(fur_ref_model.vertices[vert_index+0] - base_model.old_center[0]);
+ fur_model.vertices.push_back(fur_ref_model.vertices[vert_index+1] - base_model.old_center[1]);
+ fur_model.vertices.push_back(fur_ref_model.vertices[vert_index+2] - base_model.old_center[2]);
+ }
+ }
+ for(unsigned i=0; i<fur_model.faces.size(); ++i){
+ fur_model.faces[i] = remapped[fur_model.faces[i]];
+ }
+ }
+
+ // Get vec3 vector of fur vertices
+ std::vector<vec3> fur_verts;
+ fur_verts.reserve(fur_model.vertices.size()/3);
+ for(unsigned i=0; i<fur_model.vertices.size(); i+=3){
+ fur_verts.push_back(vec3(fur_model.vertices[i+0],
+ fur_model.vertices[i+1],
+ fur_model.vertices[i+2]));
+ }
+
+ // Collapse duplicate vertices
+ {
+ std::vector<int> vert_dup(fur_model.vertices.size()/3,-1);
+ std::map<vec3, std::vector<int> > fur_model_points;
+ for(unsigned i=0; i<fur_model.vertices.size(); i+=3){
+ fur_model_points[vec3(fur_model.vertices[i+0],
+ fur_model.vertices[i+1],
+ fur_model.vertices[i+2])].push_back(i/3);
+ }
+ for(int i=0, len=fur_model.vertices.size()/3; i<len; ++i){
+ vert_dup[i] = fur_model_points[fur_verts[i]].front();
+ }
+ for(unsigned i=0; i<fur_model.faces.size(); ++i){
+ fur_model.faces[i] = vert_dup[fur_model.faces[i]];
+ }
+ }
+
+ // Get a vector of the base vertices at each fur vertex
+ std::vector<std::vector<int>* > attachments(fur_model.vertices.size()/3);
+ for(unsigned i=0; i<fur_verts.size(); ++i){
+ std::map<vec3, std::vector<int> >::iterator iter =
+ base_model_points.find(fur_verts[i]);
+ if(iter != base_model_points.end()){
+ attachments[i] = &(iter->second);
+ }
+ }
+
+ // Get a set of the fur vertices connected to each fur vertex
+ std::vector<std::set<int> > connections(fur_model.vertices.size()/3);
+ for(unsigned i=0; i<fur_model.faces.size(); i+=3){
+ for(unsigned j=0; j<3; ++j){
+ connections[fur_model.faces[i+j]].insert(fur_model.faces[i+(1+j)%3]);
+ connections[fur_model.faces[i+j]].insert(fur_model.faces[i+(2+j)%3]);
+ }
+ }
+
+ // Get a set of the base vertices connected to each base vertex
+ std::vector<std::set<int> > base_connections(base_model.vertices.size()/3);
+ for(unsigned i=0; i<base_model.faces.size(); i+=3){
+ for(unsigned j=0; j<3; ++j){
+ base_connections[base_model.faces[i+j]].insert(base_model.faces[i+(1+j)%3]);
+ base_connections[base_model.faces[i+j]].insert(base_model.faces[i+(2+j)%3]);
+ }
+ }
+
+ // Map fur edges to their equivalent base edges
+ typedef std::map< std::pair<int, int>, std::pair<int, int> > PairMap;
+ PairMap base_pairs;
+ {
+ bool found_edge;
+ const std::vector<int> *attach_ptr[2];
+ std::vector<int>::const_iterator attach_iter[2];
+ std::set<int>::const_iterator connect_iter;
+ std::pair<int, int> edge;
+ for(int i=0, len=fur_model.faces.size()/3; i<len; ++i){
+ edge.first = -1;
+ edge.second = -1;
+ for(unsigned j=0; j<3; ++j){
+ if(attachments[fur_model.faces[i*3+j]]){
+ if(edge.first == -1){
+ edge.first = fur_model.faces[i*3+j];
+ } else {
+ edge.second = fur_model.faces[i*3+j];
+ }
+ }
+ }
+ if(edge.first == -1 || edge.second == -1){
+ continue;
+ }
+ if(edge.first > edge.second){
+ std::swap(edge.first, edge.second);
+ }
+ found_edge = false;
+ attach_ptr[0] = attachments[edge.first];
+ attach_ptr[1] = attachments[edge.second];
+ for(attach_iter[0] = attach_ptr[0]->begin();
+ attach_iter[0] != attach_ptr[0]->end(); ++attach_iter[0])
+ {
+ const std::set<int>* connect_ptr =
+ &base_connections[*attach_iter[0]];
+ for(connect_iter = connect_ptr->begin();
+ connect_iter != connect_ptr->end(); ++connect_iter)
+ {
+ for(attach_iter[1] = attach_ptr[1]->begin();
+ attach_iter[1] != attach_ptr[1]->end(); ++attach_iter[1])
+ {
+ if((*connect_iter) == (*attach_iter[1])){
+ base_pairs[edge] = std::pair<int,int>((*attach_iter[0]), (*attach_iter[1]));
+ found_edge = true;
+ break;
+ }
+ }
+ if(found_edge){
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ std::vector<int> face_attach(fur_model.faces.size());
+ {
+ // Get vector specifying which fur verts are attached to base mesh
+ std::vector<bool> in_base_pair(fur_model.vertices.size()/3, false);
+ {
+ PairMap::const_iterator iter;
+ for(iter = base_pairs.begin(); iter != base_pairs.end(); ++iter){
+ in_base_pair[iter->first.first] = true;
+ in_base_pair[iter->first.second] = true;
+ }
+ }
+
+ // Get map of the neighbors for each triangle
+ // Get map of the faces that share each edge
+ std::map< int, std::set<int> > face_neighbors;
+ std::map< std::pair<int, int>, std::set<int> > edge_tris;
+ {
+ std::pair<int, int> edge;
+ for(int i=0, len=fur_model.faces.size()/3; i<len; ++i){
+ for(unsigned j=0; j<3; ++j){
+ edge.first = fur_model.faces[i*3+j];
+ edge.second = fur_model.faces[i*3+(j+1)%3];
+ if(edge.first > edge.second){
+ std::swap(edge.first, edge.second);
+ }
+ edge_tris[edge].insert(i);
+ }
+ }
+
+ std::map< std::pair<int, int>, std::set<int> >::const_iterator edge_iter;
+ std::set<int>::const_iterator set_iter;
+ edge_iter = edge_tris.begin();
+ for(;edge_iter != edge_tris.end(); ++edge_iter){
+ set_iter = edge_iter->second.begin();
+ for(;set_iter != edge_iter->second.end(); ++set_iter){
+ face_neighbors[(*set_iter)].insert(edge_iter->second.begin(), edge_iter->second.end());
+ }
+ }
+
+ for(int i=0, len=fur_model.faces.size()/3; i<len; ++i){
+ face_neighbors[i].erase(i);
+ }
+ }
+
+ // Get map of extruded edges and their base-attached equivalents
+ PairMap edge_pairs;
+ {
+ int base_tri;
+ float closest_dist;
+ float dist;
+ std::pair<int, int> edge;
+ std::set<int>::const_iterator set_iter;
+ PairMap::const_iterator base_pair_iter = base_pairs.begin();
+ for(;base_pair_iter != base_pairs.end(); ++base_pair_iter){
+ closest_dist = -1.0f;
+ base_tri = *(edge_tris[base_pair_iter->first].begin());
+ const std::set<int>& neighbors = face_neighbors[base_tri];
+ for(set_iter = neighbors.begin();
+ set_iter != neighbors.end(); ++set_iter)
+ {
+ for(unsigned j=0; j<3; ++j){
+ edge.first = fur_model.faces[(*set_iter)*3+j];
+ edge.second = fur_model.faces[(*set_iter)*3+(j+1)%3];
+ if(in_base_pair[edge.first] || in_base_pair[edge.second]){
+ continue;
+ }
+ if(edge.first > edge.second){
+ std::swap(edge.first, edge.second);
+ }
+ dist = distance_squared(
+ fur_verts[edge.first] + fur_verts[edge.second],
+ fur_verts[base_pair_iter->first.first] +
+ fur_verts[base_pair_iter->first.second]);
+ if(closest_dist == -1.0f || dist < closest_dist){
+ closest_dist = dist;
+ } else {
+ continue;
+ }
+ edge_pairs[base_pair_iter->first] = edge;
+ }
+ }
+ }
+ }
+
+ {
+ PairMap::const_iterator edge_pair_iter = edge_pairs.begin();
+ std::map<int, int> vert_attach;
+ std::map<int, int> edge_vert_attach;
+ for(;edge_pair_iter != edge_pairs.end(); ++edge_pair_iter){
+ vert_attach.clear();
+ const std::pair<int,int>& edge_pair = edge_pair_iter->second;
+ const std::pair<int,int>& base_pair = edge_pair_iter->first;
+ vert_attach[base_pair.first] = base_pairs[base_pair].first;
+ vert_attach[base_pair.second] = base_pairs[base_pair].second;
+ int edge_tri = *(edge_tris[edge_pair].begin());
+ int base_tri = *(edge_tris[base_pair].begin());
+ int edge_vert_in_base = 0;
+ int base_vert_in_edge = 0;
+ for(unsigned i=0; i<3; ++i){
+ if(in_base_pair[fur_model.faces[edge_tri*3+i]]){
+ edge_vert_in_base = fur_model.faces[edge_tri*3+i];
+ }
+ if(!in_base_pair[fur_model.faces[base_tri*3+i]]){
+ base_vert_in_edge = fur_model.faces[base_tri*3+i];
+ }
+ }
+ if((edge_vert_in_base == base_pair.first &&
+ base_vert_in_edge == edge_pair.first) ||
+ (edge_vert_in_base == base_pair.second &&
+ base_vert_in_edge == edge_pair.second))
+ {
+ vert_attach[edge_pair.first] = vert_attach[base_pair.second];
+ vert_attach[edge_pair.second] = vert_attach[base_pair.first];
+ edge_vert_attach[edge_pair.first] = base_pair.second;
+ edge_vert_attach[edge_pair.second] = base_pair.first;
+ } else {
+ vert_attach[edge_pair.first] = vert_attach[base_pair.first];
+ vert_attach[edge_pair.second] = vert_attach[base_pair.second];
+ edge_vert_attach[edge_pair.first] = base_pair.first;
+ edge_vert_attach[edge_pair.second] = base_pair.second;
+ }
+ for(unsigned i=0; i<3; ++i){
+ face_attach[edge_tri*3+i] = vert_attach[fur_model.faces[edge_tri*3+i]];
+ face_attach[base_tri*3+i] = vert_attach[fur_model.faces[base_tri*3+i]];
+ }
+ }
+
+ fur_model.tex_coords2.resize(fur_model.vertices.size()/3*2,0.0f);
+ {
+ PairMap::const_iterator edge_pair_iter = edge_pairs.begin();
+ for(;edge_pair_iter != edge_pairs.end(); ++edge_pair_iter){
+ const std::pair<int,int>& edge_pair = edge_pair_iter->second;
+ fur_model.tex_coords2[edge_pair.first*2+1] = 1.0f;
+ fur_model.tex_coords2[edge_pair.second*2+1] = 1.0f;
+ }
+ }
+ {
+ float tex_val;
+ static const float tex_scale = 20.0f;
+ int num_base_connections;
+ int starting_point, last_point, point, next_point;
+ std::set<int>::const_iterator set_iter;
+ std::vector<bool> fur_tex_set(fur_model.vertices.size()/3, false);
+ for(int i=0, len=fur_model.vertices.size()/3; i<len; ++i){
+ if(!in_base_pair[i] || fur_tex_set[i]){
+ continue;
+ }
+ starting_point = i;
+ point = starting_point;
+ next_point = starting_point;
+ num_base_connections = 2;
+ do {
+ last_point = point;
+ point = next_point;
+ num_base_connections = 0;
+ set_iter = connections[point].begin();
+ for(;set_iter != connections[point].end(); ++set_iter){
+ if(in_base_pair[(*set_iter)]){
+ if((*set_iter) != last_point){
+ next_point = (*set_iter);
+ }
+ ++num_base_connections;
+ }
+ }
+ } while(starting_point != next_point &&
+ num_base_connections == 2);
+
+ tex_val = 0.0f;
+ next_point = point;
+ do {
+ tex_val += distance(fur_verts[point], fur_verts[next_point]);
+ point = next_point;
+ fur_model.tex_coords2[point*2+0] = tex_val * tex_scale;
+ fur_tex_set[point] = true;
+ set_iter = connections[point].begin();
+ for(;set_iter != connections[point].end(); ++set_iter){
+ if(in_base_pair[(*set_iter)] &&
+ !fur_tex_set[(*set_iter)])
+ {
+ next_point = (*set_iter);
+ }
+ }
+ } while(fur_tex_set[next_point] == false);
+ }
+ }
+ {
+ std::map<int, int>::iterator iter = edge_vert_attach.begin();
+ for(;iter != edge_vert_attach.end(); ++iter){
+ fur_model.tex_coords2[iter->first*2+0] =
+ fur_model.tex_coords2[iter->second*2+0];
+ }
+ }
+ }
+ }
+
+
+ int next_vert = fur_model.vertices.size()/3;
+ std::vector< std::map<int, int> > vert_attach(fur_model.vertices.size()/3);
+ std::vector< int > attach_base(fur_model.vertices.size()/3);
+ std::pair<int, int> edge;
+ int attach_id, v_id;
+ for(int i=0, len=fur_model.faces.size(); i<len; i+=3){
+ if(face_attach[i+0] == 0 &&
+ face_attach[i+1] == 0 &&
+ face_attach[i+2] == 0)
+ {
+ fur_model.faces[i+0] = 0;
+ fur_model.faces[i+1] = 0;
+ fur_model.faces[i+2] = 0;
+ continue;
+ }
+ for(unsigned j=0; j<3; ++j){
+ v_id = fur_model.faces[i+j];
+ attach_id = face_attach[i+j];
+ if(vert_attach[v_id].find(attach_id) == vert_attach[v_id].end()){
+ if(vert_attach[v_id].empty()){
+ vert_attach[v_id][attach_id] = v_id;
+ attach_base[v_id] = attach_id;
+ } else {;
+ attach_base.push_back(attach_id);
+ fur_model.vertices.push_back(fur_model.vertices[v_id*3+0]);
+ fur_model.vertices.push_back(fur_model.vertices[v_id*3+1]);
+ fur_model.vertices.push_back(fur_model.vertices[v_id*3+2]);
+ fur_model.tex_coords2.push_back(fur_model.tex_coords2[v_id*2+0]);
+ fur_model.tex_coords2.push_back(fur_model.tex_coords2[v_id*2+1]);
+ fur_model.faces[i+j] = next_vert;
+ vert_attach[v_id][attach_id] = next_vert++;
+ }
+ } else {
+ fur_model.faces[i+j] = vert_attach[v_id][attach_id];
+ }
+ }
+ }
+
+ int fur_model_num_vertices = attach_base.size();
+ fur_model.tex_coords.resize(fur_model_num_vertices*2);
+ fur_model.bone_weights.resize(fur_model_num_vertices);
+ fur_model.bone_ids.resize(fur_model_num_vertices);
+ fur_base_vertex_ids.resize(fur_model_num_vertices);
+ for(int i=0; i<fur_model_num_vertices; ++i){
+ const int &base_id = attach_base[i];
+ fur_base_vertex_ids[i] = base_id;
+ fur_model.tex_coords[i*2+0] = base_model.tex_coords[base_id*2+0];
+ fur_model.tex_coords[i*2+1] = base_model.tex_coords[base_id*2+1];
+ fur_model.bone_weights[i] = base_model.bone_weights[base_id];
+ fur_model.bone_ids[i] = base_model.bone_ids[base_id];
+ }
+
+ fur_model.transform_vec.resize(4);
+ fur_model.transform_vec[0].resize(fur_model_num_vertices);
+ fur_model.transform_vec[1].resize(fur_model_num_vertices);
+ fur_model.transform_vec[2].resize(fur_model_num_vertices);
+ fur_model.transform_vec[3].resize(fur_model_num_vertices);
+ fur_model.tex_transform_vec.resize(fur_model_num_vertices, vec2(0.0f));
+ fur_model.morph_transform_vec.resize(fur_model_num_vertices, vec3(0.0f));
+}
+
+void RiggedObject::MoveRagdollPart( const std::string &label, const vec3 &position, float strength_mult ) const {
+ Skeleton::SimpleIKBoneMap::const_iterator iter =
+ skeleton_.simple_ik_bones.find(label);
+ if( iter == skeleton_.simple_ik_bones.end())
+ {
+ return;
+ }
+ const SimpleIKBone &sib = iter->second;
+ BulletObject* bo = skeleton_.physics_bones[sib.bone_id].bullet_object;
+ vec3 curr_pos = bo->GetPosition();
+ if(skeleton_.fixed_obj[sib.bone_id]){
+ bo = skeleton_.physics_bones[skeleton_.parents[sib.bone_id]].bullet_object;
+ }
+ if(skeleton_.fixed_obj[skeleton_.parents[sib.bone_id]]){
+ bo = skeleton_.physics_bones[skeleton_.parents[skeleton_.parents[sib.bone_id]]].bullet_object;
+ }
+ scenegraph_->bullet_world_->CreateTempDragConstraint(bo, curr_pos, position);
+}
+
+void RiggedObject::FixedRagdollPart( int boneID, const vec3 &position) const {
+ BulletObject* bo = skeleton_.physics_bones[boneID].bullet_object;
+ if(skeleton_.fixed_obj[boneID]){
+ bo = skeleton_.physics_bones[skeleton_.parents[boneID]].bullet_object;
+ }
+ if(skeleton_.fixed_obj[skeleton_.parents[boneID]]){
+ bo = skeleton_.physics_bones[skeleton_.parents[skeleton_.parents[boneID]]].bullet_object;
+ }
+ vec3 curr_pos = bo->GetPosition();
+ scenegraph_->bullet_world_->CreateBoneConstraint(bo, curr_pos, position);
+}
+
+void RiggedObject::SpikeRagdollPart( int boneID, const vec3 &start, const vec3 &end, const vec3 &pos) const {
+ BulletObject* bo = skeleton_.physics_bones[boneID].bullet_object;
+ if(skeleton_.fixed_obj[boneID]){
+ bo = skeleton_.physics_bones[skeleton_.parents[boneID]].bullet_object;
+ }
+ if(skeleton_.parents[boneID] != -1 && skeleton_.fixed_obj[skeleton_.parents[boneID]]){
+ bo = skeleton_.physics_bones[skeleton_.parents[skeleton_.parents[boneID]]].bullet_object;
+ }
+ scenegraph_->bullet_world_->CreateSpikeConstraint(bo, start, end, pos);
+}
+
+void RiggedObject::ClearBoneConstraints(){
+ scenegraph_->bullet_world_->ClearBoneConstraints();
+}
+
+void RiggedObject::ApplyPalette( const OGPalette& _palette ) {
+ unsigned num_iters = min(palette_colors.size(), _palette.size());
+ for(unsigned i=0; i<num_iters; ++i){
+ palette_colors_srgb[_palette[i].channel] = _palette[i].color;
+ palette_colors[_palette[i].channel] = _palette[i].color;
+ palette_colors[_palette[i].channel][0] = pow(palette_colors[_palette[i].channel][0], 1.0f/2.2f);
+ palette_colors[_palette[i].channel][1] = pow(palette_colors[_palette[i].channel][1], 1.0f/2.2f);
+ palette_colors[_palette[i].channel][2] = pow(palette_colors[_palette[i].channel][2], 1.0f/2.2f);
+ }
+}
+
+std::vector<vec3> * RiggedObject::GetPaletteColors() {
+ return &palette_colors_srgb;
+}
+
+void RiggedObject::StickItem( int id, const vec3 & start, const vec3 & end, const mat4 &transform ) {
+ int bone = skeleton_.CheckRayCollisionBone(start, end);
+ if(bone != -1){
+ Object* obj = scenegraph_->GetObjectFromID(id);
+ if(!obj || obj->GetType() != _item_object){
+ FatalError("Error", "There is no item object %d", id);
+ }
+ ItemObject* io = (ItemObject*)obj;
+ vec3 old_vel = io->GetLinearVelocity();
+ io->SetPhysicsTransform(transform);
+ io->SetLinearVelocity(old_vel);
+ mat4 rel_mat = invert(skeleton_.physics_bones[bone].bullet_object->GetTransform()) * transform;
+ stuck_items.Add(io, bone, rel_mat, char_id, scenegraph_->bullet_world_, skeleton_);
+ if(!animated){
+ stuck_items.items.back().item.ActivatePhysics();
+ stuck_items.items.back().item.AddConstraint(skeleton_.physics_bones[bone].bullet_object);
+ }
+ }
+}
+
+extern std::stack<ASContext*> active_context_stack;
+
+void RiggedObject::UnStickItem( int id ) {
+ stuck_items.Remove(id);
+}
+
+std::vector<ItemRef> RiggedObject::GetWieldedItemRefs() const {
+ int num_wielded = 0;
+ for(std::list<AttachedItem>::const_iterator iter = attached_items.items.begin();
+ iter != attached_items.items.end(); ++iter)
+ {
+ const ItemObjectScriptReader& item = iter->item;
+ if(item->GetID() == primary_weapon_id){
+ ++num_wielded;
+ }
+ }
+ std::vector<ItemRef> wielded_item_refs(num_wielded);
+ num_wielded = 0;
+ for(std::list<AttachedItem>::const_iterator iter = attached_items.items.begin();
+ iter != attached_items.items.end(); ++iter)
+ {
+ const ItemObjectScriptReader& item = iter->item;
+ if(item->GetID() == primary_weapon_id){
+ wielded_item_refs[num_wielded++] = item->item_ref();
+ }
+ }
+ return wielded_item_refs;
+}
+
+void RiggedObject::SetCharacterScriptGetter( CharacterScriptGetter &csg ) {
+ character_script_getter = &csg;
+}
+
+void RiggedObject::SetPrimaryWeaponID( int id ) {
+ primary_weapon_id = id;
+ character_script_getter->ItemsChanged(GetWieldedItemRefs());
+}
+
+void DrawAvailableAttachmentSlot(const ItemRef& item_ref, AttachmentType type, const Skeleton &skeleton, bool mirrored, std::list<AttachmentSlot> *list){
+ int bone_id;
+ mat4 rel_mat = LoadAttachPose(item_ref->GetAttachAnimPath(type), item_ref->GetIKAttach(type), skeleton, &bone_id, mirrored, true, 0);
+ mat4 bone_mat = skeleton.physics_bones[bone_id].bullet_object->GetTransform();
+ mat4 world_mat = bone_mat * rel_mat;
+
+ list->resize(list->size()+1);
+ AttachmentSlot &slot = list->back();
+ slot.pos = world_mat.GetTranslationPart();
+ slot.type = type;
+ slot.mirrored = mirrored;
+}
+
+void DrawAvailableAttachment(const AttachmentRef attachment_ref, const Skeleton &skeleton, bool mirrored, AttachmentSlotList *list){
+ int bone_id;
+ mat4 rel_mat = LoadAttachPose(attachment_ref->anim, attachment_ref->ik_chain_label, skeleton, &bone_id, mirrored, false, attachment_ref->ik_chain_bone);
+ mat4 bone_mat = skeleton.physics_bones[bone_id].bullet_object->GetTransform();
+ mat4 world_mat = bone_mat * rel_mat;
+
+ list->resize(list->size()+1);
+ AttachmentSlot &slot = list->back();
+ slot.pos = world_mat.GetTranslationPart();
+ slot.type = _at_attachment;
+ slot.attachment_ref = attachment_ref;
+ slot.mirrored = mirrored;
+}
+
+void DrawAvailableAttachmentSlots(const ItemRef& item_ref, AttachmentType type, const Skeleton &skeleton, AttachmentSlotList *list){
+ DrawAvailableAttachmentSlot(item_ref, type, skeleton, false, list);
+ DrawAvailableAttachmentSlot(item_ref, type, skeleton, true, list);
+}
+
+void RiggedObject::AvailableItemSlots( const ItemRef& item_ref, AttachmentSlotList *list ) const {
+ if(item_ref->HasAttachment(_at_grip)){
+ DrawAvailableAttachmentSlots(item_ref, _at_grip, skeleton_, list);
+ }
+ if(item_ref->HasAttachment(_at_sheathe)){
+ DrawAvailableAttachmentSlots(item_ref, _at_sheathe, skeleton_, list);
+ }
+ const std::list<std::string> &attachment_list = item_ref->GetAttachmentList();
+ for(std::list<std::string>::const_iterator iter = attachment_list.begin();
+ iter != attachment_list.end(); ++iter)
+ {
+ //AttachmentRef attachment_ref = Attachments::Instance()->ReturnRef((*iter));
+ AttachmentRef attachment_ref = Engine::Instance()->GetAssetManager()->LoadSync<Attachment>((*iter));
+ DrawAvailableAttachment(attachment_ref, skeleton_, false, list);
+ if(attachment_ref->mirror_allowed){
+ DrawAvailableAttachment(attachment_ref, skeleton_, true, list);
+ }
+ }
+}
+
+void RiggedObject::AttachItemToSlot( ItemObject* item_object, AttachmentType type, bool mirrored, const AttachmentRef* attachment_ref ) {
+ // Detach item if it is already attached
+ DetachItem(item_object);
+
+ // Set up item attachment
+ attached_items.items.resize(attached_items.items.size()+1);
+ AttachedItem &stuck_item = attached_items.items.back();
+ ItemObjectScriptReader &item = stuck_item.item;
+ item.char_id = char_id;
+ item.holding = true;
+ if(attachment_ref){
+ item.attachment_ref = *attachment_ref;
+ }
+ item.SetInvalidateCallback(&RiggedObject::InvalidatedItemCallback, this);
+ item.AttachToItemObject(item_object);
+ if(type == _at_grip){
+ item->SetState(ItemObject::kWielded);
+ } else if(type == _at_sheathe){
+ item->SetState(ItemObject::kSheathed);
+ }
+ if(attachment_ref && attachment_ref->valid()){
+ SetItemAttachment(stuck_item, *attachment_ref, mirrored);
+ } else {
+ SetWeaponOffset(stuck_item, type, mirrored);
+ }
+
+ if(animated){
+ UpdateAttachedItem(stuck_item, skeleton_, model_char_scale, weapon_offset, weap_anim_info_map, weapon_offset_retarget);
+ } else {
+ UpdateAttachedItemRagdoll(stuck_item, skeleton_, weapon_offset);
+ }
+}
+
+Skeleton & RiggedObject::skeleton() {
+ return skeleton_;
+}
+
+void RiggedObject::SetCharScale(float val) {
+ char_scale = val * character_script_getter->GetDefaultScale();
+ model_char_scale = char_scale / character_script_getter->GetModelScale();
+}
+
+float RiggedObject::GetCharScale() {
+ return char_scale;
+}
+
+float RiggedObject::GetRelativeCharScale() {
+ return char_scale / character_script_getter->GetDefaultScale();
+}
+
+void RiggedObject::UpdateGPUSkinning() {
+ GPU_skinning = Graphics::Instance()->config_.gpu_skinning();
+ for(int i=0; i<kLodLevels; ++i){
+ transform_vec_vbo0[i]->Dispose();
+ transform_vec_vbo1[i]->Dispose();
+ }
+ fur_transform_vec_vbo0.Dispose();
+ fur_transform_vec_vbo1.Dispose();
+}
+
+void RiggedObject::GetShaderNames(std::map<std::string, int>& shaders) {
+ const int kShaderStrSize = 1024;
+ char buf[2][kShaderStrSize];
+ char* shader_str[2] = {buf[0], buf[1]};
+ strcpy(shader_str[0], shader);
+ if(GPU_skinning){
+ FormatString(shader_str[1], kShaderStrSize, "%s #GPU_SKINNING", shader_str[0]);
+ std::swap(shader_str[0], shader_str[1]);
+ }
+ shaders[shader_str[0]] = SceneGraph::kPreloadTypeAll;
+ shaders[water_cube] = 0;
+ shaders[water_cube_expand] = 0;
+
+ blood_surface.GetShaderNames(shaders);
+}
+
+void AttachedItems::Add( ItemObject* io, int bone, mat4 rel_mat, int char_id, BulletWorld *bw, Skeleton& skeleton ) {
+ items.resize(items.size()+1);
+ AttachedItem& item = items.back();
+ item.bone_id = bone;
+ item.rel_mat = rel_mat;
+
+ ItemObjectScriptReader& iosr = item.item;
+ iosr.stuck = true;
+ iosr.char_id = char_id;
+ iosr.SetInvalidateCallback(&AttachedItems::InvalidatedItemCallback, this);
+ iosr.AttachToItemObject(io);
+}
+
+void AttachedItems::InvalidatedItem(ItemObjectScriptReader *invalidated) {
+ for(std::list<AttachedItem>::iterator iter = items.begin(); iter != items.end();) {
+ if(&(iter->item) == invalidated){
+ iter->item->ActivatePhysics();
+ iter = items.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+}
+
+void AttachedItems::InvalidatedItemCallback(ItemObjectScriptReader *invalidated, void* this_ptr) {
+ ((AttachedItems*)this_ptr)->InvalidatedItem(invalidated);
+}
+
+void AttachedItems::Remove( int id ) {
+ for(std::list<AttachedItem>::iterator iter = items.begin(); iter != items.end();) {
+ if(iter->item->GetID() == id){
+ iter->item->ActivatePhysics();
+ iter = items.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+}
+
+void AttachedItems::RemoveAll() {
+ for(std::list<AttachedItem>::iterator iter = items.begin(); iter != items.end(); ++iter) {
+ iter->item->ActivatePhysics();
+ }
+ items.clear();
+}
+
+vec3 RiggedTransformedVertexGetter::GetTransformedVertex(int val) {
+ return rigged_object->GetTransformedVertex(val);
+}