/* by korenkonder GitHub/GitLab: korenkonder */ #include "rob.hpp" #include "../../KKdLib/key_val.hpp" #include "../../KKdLib/str_utils.hpp" #include "../app_system_detail.hpp" #include "../data.hpp" #include "../gl_state.hpp" #include "../sprite.hpp" #include "skin_param.hpp" struct ExpFuncUnaryTbl { const char* name; float_t(*func)(float_t v1); }; struct ExpFuncBinaryTbl { const char* name; float_t(*func)(float_t v1, float_t v2); }; struct ExpFuncTernaryTbl { const char* name; float_t(*func)(float_t v1, float_t v2, float_t v3); }; static const ExpFuncUnaryTbl* ExpFuncUnaryTblFindFunc(std::string& name, const ExpFuncUnaryTbl* array); static const ExpFuncBinaryTbl* ExpFuncBinaryTblFindFunc(std::string& name, const ExpFuncBinaryTbl* array); static const ExpFuncTernaryTbl* ExpFuncTernaryTblFindFunc(std::string& name, const ExpFuncTernaryTbl* array); static float_t exp_abs(float_t v1); static float_t exp_acos(float_t v1); static float_t exp_add(float_t v1, float_t v2); static float_t exp_and(float_t v1, float_t v2); static float_t exp_asin(float_t v1); static float_t exp_atan(float_t v1); static float_t exp_av(float_t v1); static float_t exp_ceil(float_t v1); static float_t exp_cond(float_t v1, float_t v2, float_t v3); static float_t exp_cos(float_t v1); static float_t exp_div(float_t v1, float_t v2); static float_t exp_eq(float_t v1, float_t v2); static float_t exp_exp(float_t v1); static float_t exp_floor(float_t v1); static float_t exp_fmod(float_t v1, float_t v2); static float_t exp_ge(float_t v1, float_t v2); static float_t exp_gt(float_t v1, float_t v2); static float_t exp_le(float_t v1, float_t v2); static float_t exp_log(float_t v1); static float_t exp_lt(float_t v1, float_t v2); static float_t exp_max(float_t v1, float_t v2); static float_t exp_min(float_t v1, float_t v2); static float_t exp_mul(float_t v1, float_t v2); static float_t exp_ne(float_t v1, float_t v2); static float_t exp_neg(float_t v1); static float_t exp_or(float_t v1, float_t v2); static float_t exp_pow(float_t v1, float_t v2); static float_t exp_rand(float_t v1, float_t v2, float_t v3); static float_t exp_rand_0_1(float_t v1); static float_t exp_round(float_t v1); static float_t exp_sin(float_t v1); static float_t exp_sqrt(float_t v1); static float_t exp_sub(float_t v1, float_t v2); static float_t exp_tan(float_t v1); static void apply_gravity(vec3& vec, const vec3& p0, const vec3& p1, const float_t gravity, const float_t weight); static float_t calc_tangent_basis(const vec3& v1, const vec3& v2, const vec3& pos_c, const vec2& uv0, const vec2& uv1, const vec2& uv2, vec3& tangent, vec3& binormal, vec3& normal); static float_t calc_tangent_basis(const vec3& v1, const vec3& v2, const float_t t1, const float_t t2, vec3& tangent, vec3& binormal, vec3& normal); static void calc_tangent_basis_sub(CLOTH_VERTEX* vtx, const CLOTH_VERTEX* vtxL, const CLOTH_VERTEX* vtxR, const CLOTH_VERTEX* vtxU, const CLOTH_VERTEX* vtxD); static void closest_pt_segment_segment(vec3& vec, const vec3& p0, const vec3& q0, const OsageCollision::Work* cls); static void length_limit(vec3& p, const vec3& r, const float_t rr); static bool make_axis_matrix(mat4& mat, const vec3& axis, const skin_param_hinge* hinge, vec3* rot, const ROTTYPE& rottype); static void make_direction_matrix(mat4& mat, const vec3& v0, const vec3& v1); static void modify_cloth_object(obj_mesh* mesh, obj_mesh_vertex_buffer* vb, CLOTH_VERTEX* vtxarg, float_t sgn, int32_t num_idx, uint16_t* idxtbl, bool do_ura); static const ExpFuncUnaryTbl exp_func_unary_tbl[] = { { "neg" , exp_neg }, { "sin" , exp_sin }, { "cos" , exp_cos }, { "tan" , exp_tan }, { "abs" , exp_abs }, { "sqrt" , exp_sqrt }, { "av" , exp_av }, { "floor" , exp_floor }, { "ceil" , exp_ceil }, { "round" , exp_round }, { "asin" , exp_asin }, { "acos" , exp_acos }, { "atan" , exp_atan }, { "log" , exp_log }, { "exp" , exp_exp }, { "rand_0_1", exp_rand_0_1 }, { 0 , 0 }, }; static const ExpFuncBinaryTbl exp_func_binary_tbl[] = { { "+" , exp_add }, { "-" , exp_sub }, { "*" , exp_mul }, { "/" , exp_div }, { "%" , exp_fmod }, { "==" , exp_eq }, { ">" , exp_gt }, { ">=" , exp_ge }, { "<" , exp_lt }, { "<=" , exp_le }, { "!=" , exp_ne }, { "&&" , exp_and }, { "||" , exp_or }, { "min" , exp_min }, { "max" , exp_max }, { "fmod", exp_fmod }, { "pow" , exp_pow }, { 0 , 0 }, }; static const ExpFuncTernaryTbl exp_func_ternary_tbl[] = { { "rand", exp_rand }, { "cond", exp_cond }, { 0 , 0 }, }; static const char* expression_component_names[] = { "posx", "posy", "posz", "rotx", "roty", "rotz", "scalex", "scaley", "scalez", }; static bool rob_osage_enable_distance = true; bool calc_normal_flag = false; size_t cloth_init_cnt = 0; int32_t cloth_modify_flag = 0x03; int32_t dbg_init_flag = 0; int32_t dbg_osage_line_disp_flag = 0; #if OPD_PLAY_GEN extern std::atomic_bool opd_play_gen_run; #endif ExNodeBlock::ExNodeBlock() : dst_node(), type(), name(), parent(), parent_name(), parent_node(), skin_disp(), is_parent(), done(), has_children_node() { } ExNodeBlock::~ExNodeBlock() { } void ExNodeBlock::CtrlBegin() { done = false; } void ExNodeBlock::dest() { dst_node = 0; } void ExNodeBlock::CtrlEnd() { done = false; } // 0x1405EEBF0 void ExNodeBlock::init_members() { dst_node = 0; type = EX_NODE_TYPE_NULL; name = 0; parent = 0; skin_disp = 0; is_parent = false; } // 0x1405EEB80 void ExNodeBlock::set_data(RobNode* node, ExNodeType type, const char* name, const RobSkinDisp* skin_disp) { init_members(); dst_node = node; this->type = type; this->name = name ? name : "(null)"; parent = 0; this->skin_disp = skin_disp; } // 0x1405F92B0 void ExNodeBlock::set_name(const char* name) { this->name = name; } ExNullBlock::ExNullBlock() { init(); } ExNullBlock::~ExNullBlock() { } void ExNullBlock::init() { dst_node = 0; type = EX_NODE_TYPE_NULL; name = 0; parent = 0; skin_disp = 0; data = 0; } void ExNullBlock::CtrlBegin() { done = false; } void ExNullBlock::CtrlStep(int32_t stage, bool disable_ex_force) { } void ExNullBlock::ctrl() { if (!dst_node) return; mat4 dsp_mat; mat4 mat = *parent->no_scale_mat; dst_node->transform.CalcMatrixHS(parent->transform.hsc, mat, dsp_mat); *dst_node->no_scale_mat = mat; *dst_node->mat_ptr = dsp_mat; } void ExNullBlock::CtrlOsagePlayData() { ctrl(); } void ExNullBlock::disp(const mat4& mat, render_context* rctx) { } void ExNullBlock::disp_debug() { } void ExNullBlock::pos_init() { ctrl(); } void ExNullBlock::pos_init_cont() { } void ExNullBlock::set_data(const RobSkinDisp* skin_disp, const obj_skin_ex_node_constraint* data, const char* name, const bone_database* bone_data) { RobNode* node = (RobNode*)skin_disp->get_node(name, bone_data); type = EX_NODE_TYPE_NULL; dst_node = node; this->data = data; this->name = node->name; this->skin_disp = skin_disp; } RobJointNodeDataNormalRef::RobJointNodeDataNormalRef() : set(), n(), u(), d(), l(), r() { } bool RobJointNodeDataNormalRef::Check() { set = false; if (!n) return set; if (u && !memcmp(&u->mat, &mat4_null, sizeof(mat4))) u = 0; if (d && !memcmp(&d->mat, &mat4_null, sizeof(mat4))) d = 0; if (l && !memcmp(&l->mat, &mat4_null, sizeof(mat4))) l = 0; if (r && !memcmp(&r->mat, &mat4_null, sizeof(mat4))) r = 0; if ((u || d) && (l || r)) { if (!u) u = n; if (!d) d = n; if (!l) l = n; if (!r) r = n; set = true; } return set; } // 0x14053CE30 void RobJointNodeDataNormalRef::GetMat(mat4* mat) { if (!set) return; vec3 n_trans; vec3 u_trans; vec3 d_trans; vec3 l_trans; vec3 r_trans; mat4_get_translation(n->dst_node->no_scale_mat, &n_trans); mat4_get_translation(u->dst_node->no_scale_mat, &u_trans); mat4_get_translation(d->dst_node->no_scale_mat, &d_trans); mat4_get_translation(l->dst_node->no_scale_mat, &l_trans); mat4_get_translation(r->dst_node->no_scale_mat, &r_trans); vec3 z_axis; vec3 y_axis; vec3 x_axis; if (RobJointNodeDataNormalRef::GetAxes( l_trans, r_trans, u_trans, d_trans, z_axis, y_axis, x_axis)) { mat4 temp = mat4( x_axis.x, x_axis.y, x_axis.z, 0.0f, y_axis.x, y_axis.y, y_axis.z, 0.0f, z_axis.x, z_axis.y, z_axis.z, 0.0f, n_trans.x, n_trans.y, n_trans.z, 1.0f); mat4_mul(&this->mat, &temp, mat); } } // 0x14053CAC0 void RobJointNodeDataNormalRef::Load() { if (!Check()) return; vec3 n_trans; vec3 u_trans; vec3 d_trans; vec3 l_trans; vec3 r_trans; mat4_get_translation(&n->mat, &n_trans); mat4_get_translation(&u->mat, &u_trans); mat4_get_translation(&d->mat, &d_trans); mat4_get_translation(&l->mat, &l_trans); mat4_get_translation(&r->mat, &r_trans); vec3 z_axis; vec3 y_axis; vec3 x_axis; if (RobJointNodeDataNormalRef::GetAxes( l_trans, r_trans, u_trans, d_trans, z_axis, y_axis, x_axis)) { mat4 temp = mat4( x_axis.x, y_axis.x, z_axis.x, 0.0f, x_axis.y, y_axis.y, z_axis.y, 0.0f, x_axis.z, y_axis.z, z_axis.z, 0.0f, -vec3::dot(n_trans, x_axis), -vec3::dot(n_trans, y_axis), -vec3::dot(n_trans, z_axis), 1.0f); mat4_mul(&n->mat, &temp, &mat); } } // 0x14053D1B0 bool RobJointNodeDataNormalRef::GetAxes(const vec3& l_trans, const vec3& r_trans, const vec3& u_trans, const vec3& d_trans, vec3& z_axis, vec3& y_axis, vec3& x_axis) { z_axis = d_trans - u_trans; if (fabsf(vec3::length_squared(z_axis)) <= 0.000001f) return false; z_axis = vec3::normalize(z_axis); y_axis = vec3::cross(r_trans - l_trans, z_axis); if (fabsf(vec3::length_squared(y_axis)) <= 0.000001f) return false; y_axis = vec3::normalize(y_axis); x_axis = vec3::normalize(vec3::cross(z_axis, y_axis)); return true; } inline bool skin_param_hinge::clamp(float_t& y, float_t& z) const { bool clamped = false; if (y > ymax) { y = ymax; clamped = true; } if (y < ymin) { y = ymin; clamped = true; } if (z > zmax) { z = zmax; clamped = true; } if (z < zmin) { z = zmin; clamped = true; } return clamped; } void skin_param_hinge::limit() { ymin = max_def(ymin, -179.0f) * DEG_TO_RAD_FLOAT; ymax = min_def(ymax, 179.0f) * DEG_TO_RAD_FLOAT; zmin = max_def(zmin, -179.0f) * DEG_TO_RAD_FLOAT; zmax = min_def(zmax, 179.0f) * DEG_TO_RAD_FLOAT; } skin_param_osage_node::skin_param_osage_node() : coli_r(), inertial_cancel() { weight = 1.0f; hinge.limit(); } RobJointNodeResetData::RobJointNodeResetData() : length() { } RobJointNodeData::RobJointNodeData() : force(), normal_ref() { } RobJointNodeData::~RobJointNodeData() { } void RobJointNodeData::SetForce(const skin_param_osage_root& skp_root, skin_param_osage_node* skp_osg_node, size_t index) { this->skp_osg_node = *skp_osg_node; this->skp_osg_node.hinge.limit(); float_t force = skp_root.force; float_t force_gain = skp_root.force_gain; for (; index >= 4; index -= 4) force = force * force_gain * force_gain * force_gain * force_gain; for (; index; index--) force *= force_gain; this->force = force; } // 0x140482D00 void RobJointNodeData::reset() { force = 0.0f; boc.clear(); normal_ref.set = false; normal_ref.n = 0; normal_ref.u = 0; normal_ref.d = 0; normal_ref.l = 0; normal_ref.r = 0; normal_ref.mat = mat4_identity; skp_osg_node.weight = 1.0f; skp_osg_node.inertial_cancel = 0.0f; skp_osg_node.coli_r = 0.0f; skp_osg_node.hinge = skin_param_hinge(); skp_osg_node.hinge.limit(); } opd_node_data::opd_node_data() : length() { } opd_node_data::opd_node_data(float_t length, vec3 rotation) { this->length = length; this->rotation = rotation; } // 0x140482DF0 void opd_node_data::lerp(opd_node_data& dst, const opd_node_data& src0, const opd_node_data& src1, float_t blend) { dst.length = lerp_def(src0.length, src1.length, blend); dst.rotation = vec3::lerp(src0.rotation, src1.rotation, blend); } opd_node_data_pair::opd_node_data_pair() { } // 0x140482100 void opd_node_data_pair::set_data(const opd_blend_data* blend_data, const opd_node_data& node_data) { if (!blend_data->use_blend) curr = node_data; else if (blend_data->type == MOTION_BLEND_FREEZE) { if (blend_data->blend == 0.0f) prev = curr; opd_node_data::lerp(curr, prev, node_data, blend_data->blend); } else if (blend_data->type == MOTION_BLEND_CROSS) opd_node_data::lerp(curr, curr, node_data, blend_data->blend); } RobJointNode::RobJointNode() : length_back(), length_next(), dst_node(), dst_node_mat(), distance(), length_dist(), hit() { friction = 1.0f; force = 1.0f; data_ptr = &data; opd_data.resize(3); } RobJointNode::~RobJointNode() { } // 0x140482180 void RobJointNode::CheckFloorCollision(const float_t& floor_height) { float_t pos_y = data_ptr->skp_osg_node.coli_r + floor_height; if (pos_y <= pos.y) return; pos.y = pos_y; pos = GetPrevNode().pos + vec3::normalize(pos - GetPrevNode().pos) * length_back; vec = 0.0f; hit += 1.0f; } // 0x140482490 void RobJointNode::CheckNodeDistance(const float_t& step, const float_t& hsc) { if (step != 1.0f) { vec3 d = pos - old_pos; float_t dist = vec3::length(d); if (dist != 0.0f) d *= 1.0f / dist; pos = old_pos + d * (step * dist); } length_limit(pos, GetPrevNode().pos, length_back * hsc); if (rob_osage_enable_distance) { if (distance) length_limit(pos, distance->pos, length_dist); } } // 0x140482B00 void RobJointNode::reset() { length_back = 0.0f; pos = 0.0f; old_pos = 0.0f; vec = 0.0f; vel = 0.0f; length_next = 0.0f; dst_node = 0; dst_node_mat = 0; distance = 0; length_dist = 0.0f; rel_pos = 0.0f; reset_data.pos = 0.0f; reset_data.vec = 0.0f; reset_data.rotation = 0.0f; reset_data.length = 0.0f; hit = 0.0f; friction = 1.0f; ex_force = 0.0f; force = 1.0f; data.reset(); data_ptr = &data; opd_data.clear(); opd_data.resize(3); mat = mat4_null; } OsageCollision::Work::Work() : type(), radius(), pos(), vec_center(), vec_center_length(), vec_center_length_squared(), friction() { } // 0x140485450 void OsageCollision::Work::update_cls_work(OsageCollision::Work* work, const SkinParam::CollisionParam* cls, const mat4* motmat) { work->type = SkinParam::CollisionTypeEnd; if (!cls || !motmat) return; for (; cls->type; work++, cls++) { work->type = cls->type; work->radius = cls->radius; mat4_transform_point(&motmat[cls->node_idx[0]], &cls->pos[0], &work->pos[0]); switch (work->type) { case SkinParam::CollisionTypePlane: mat4_transform_vector(&motmat[cls->node_idx[0]], &cls->pos[1], &work->pos[1]); break; case SkinParam::CollisionTypeCapsule: case SkinParam::CollisionTypeEllipse: mat4_transform_point(&motmat[cls->node_idx[1]], &cls->pos[1], &work->pos[1]); work->vec_center = work->pos[1] - work->pos[0]; work->vec_center_length_squared = vec3::length_squared(work->vec_center); work->vec_center_length = sqrtf(work->vec_center_length_squared); if (work->vec_center_length < 0.01f) work->type = SkinParam::CollisionTypeBall; break; } } work->type = SkinParam::CollisionTypeEnd; } // 0x140485410 void OsageCollision::Work::update_cls_work(OsageCollision::Work* work, const std::vector& cls_list, const mat4* motmat) { if (!cls_list.size()) { work->type = SkinParam::CollisionTypeEnd; work->radius = 0.0f; work->pos[0] = 0.0f; work->pos[1] = 0.0f; work->vec_center = 0.0f; work->vec_center_length = 0.0f; work->vec_center_length_squared = 0.0f; work->friction = 1.0f; } else update_cls_work(work, cls_list.data(), motmat); } OsageCollision::OsageCollision() { } OsageCollision::~OsageCollision() { } // 0x140483B30 int32_t OsageCollision::cls_aabb_oidashi(vec3& vec, const vec3& p, const OsageCollision::Work* cls, const float_t r) { vec3 v29; vec3 v30; for (int32_t i = 0; i < 3; i++) { float_t v13 = ((float_t*)&p)[i]; float_t v14 = ((float_t*)&cls->pos[0])[i] - v13; ((float_t*)&v30)[i] = v14; if (v14 > r) return 0; float_t v15 = v13 - (((float_t*)&cls->pos[0])[i] + ((float_t*)&cls->pos[1])[i]); ((float_t*)&v29)[i] = v15; if (v15 > r) return 0; } vec3 v28 = 0.0f; vec3 v32 = 0.0f; vec3 v27 = 1.0f; float_t v16 = 0.0f; for (int32_t i = 0; i < 3; i++) { float_t v18 = ((float_t*)&v30)[i]; float_t v19 = ((float_t*)&v29)[i]; if (fabsf(v18) >= fabsf(v19)) { ((float_t*)&v28)[i] = v19; ((float_t*)&v27)[i] = 1.0f; v18 = v19; } else { ((float_t*)&v28)[i] = v18; ((float_t*)&v27)[i] = -1.0f; } if (v18 > 0.0f) { float_t v20 = v18 * v18; ((float_t*)&v32)[i] = v20; v16 += v20; } } if (v16 > r * r) return 0; if (v16 > 0.0f) vec = v27 * v32 * (1.0f / v16) * (r - sqrtf(v16)); else { float_t v25 = ((float_t*)&v28)[0]; int32_t v26 = 0; for (int32_t i = 0; i < 3; i++) { if (v25 < ((float_t*)&v28)[i]) { v25 = ((float_t*)&v28)[i]; v26 = i; } } ((float_t*)&vec)[v26] -= ((float_t*)&v27)[v26] * (v25 - r); } return 1; } // 0x140483DE0 int32_t OsageCollision::cls_ball_oidashi(vec3& vec, const vec3& p, const vec3& center, const float_t r) { const float_t length = vec3::distance_squared(p, center); if (length > 0.000001f && length < r * r) { vec = (p - center) * (r / sqrtf(length) - 1.0f); return 1; } return 0; } // 0x140484540 int32_t OsageCollision::cls_capsule_oidashi(vec3& vec, const vec3& p, const OsageCollision::Work* cls, const float_t r) { const vec3 v11 = p - cls->pos[0]; const float_t v17 = vec3::dot(v11, cls->vec_center); if (v17 < 0.0f) return OsageCollision::cls_ball_oidashi(vec, p, cls->pos[0], r); const float_t v19 = cls->vec_center_length_squared; if (fabsf(v19) <= 0.000001f) return OsageCollision::cls_ball_oidashi(vec, p, cls->pos[0], r); else if (v17 > v19) return OsageCollision::cls_ball_oidashi(vec, p, cls->pos[1], r); const float_t v20 = vec3::length_squared(v11); const float_t v21 = v17 / v19; const float_t v22 = fabsf(v20 - v21 * v17); if (v22 > 0.000001f && v22 < r * r) { vec = (v11 - cls->vec_center * v21) * (r / sqrtf(v22) - 1.0f); return 1; } return 0; } // 0x140483EA0 int32_t OsageCollision::cls_ellipse_oidashi(vec3& vec, const vec3& p, const OsageCollision::Work* cls, const float_t r) { const vec3& p1 = cls->pos[0]; const vec3& q1 = cls->pos[1]; if (fabsf(cls->vec_center_length) <= 0.000001f) return OsageCollision::cls_ball_oidashi(vec, p, p1, r); const float_t v61 = cls->vec_center_length * 0.5f; const float_t v13 = sqrtf(r * r + v61 * v61); vec3 d0 = p - p1; const float_t d0_len = vec3::length(d0); vec3 d1 = p - q1; const float_t d1_len = vec3::length(d1); if (fabsf(d0_len) <= 0.000001 || fabsf(d1_len) <= 0.000001f) return 0; const float_t d_len = (d0_len + d1_len) * 0.5f; if (d_len >= v13) return 0; vec3 v63 = p - (p1 + q1) * 0.5f; const float_t v58 = vec3::length(v63); if (v58 != 0.0f) v63 *= 1.0f / v58; const float_t v36 = vec3::dot(v63, cls->vec_center) * (1.0f / cls->vec_center_length); const float_t v38 = d_len * d_len - v61 * v61; if (v38 < 0.0f) return 0; const float_t v39 = sqrtf(v38); if (fabsf(v39) <= 0.000001f) return 0; const float_t v40 = sqrtf(1.0f - min_def(v36 * v36, 1.0f)); const float_t v41 = (v13 / d_len - 1.0f) * (v36 * v58); const float_t v42 = (r / v39 - 1.0f) * (v40 * v58); const float_t v43 = sqrtf(v42 * v42 + v41 * v41); if (d0_len != 0.0f) d0 *= 1.0f / d0_len; if (d1_len != 0.0f) d1 *= 1.0f / d1_len; vec = vec3::normalize(d0 + d1) * v43; return 1; } // 0x140484450 int32_t OsageCollision::cls_line2ball_oidashi(vec3& vec, const vec3& p0, const vec3& p1, const vec3& q, const float_t r) { vec3 _vec; OsageCollision::get_nearest_line2point(_vec, p0, p1, q); return OsageCollision::cls_ball_oidashi(vec, _vec, q, r); } // 0x1404844A0 int32_t OsageCollision::cls_line2capsule_oidashi(vec3& vec, const vec3& p0, const vec3& p1, const OsageCollision::Work* cls, const float_t r) { vec3 _vec; closest_pt_segment_segment(_vec, p0, p1, cls); return OsageCollision::cls_capsule_oidashi(vec, _vec, cls, r); } // 0x1404844F0 int32_t OsageCollision::cls_line2ellipse_oidashi(vec3& vec, const vec3& p0, const vec3& p1, const OsageCollision::Work* cls, const float_t r) { vec3 _vec; closest_pt_segment_segment(_vec, p0, p1, cls); return OsageCollision::cls_ellipse_oidashi(vec, _vec, cls, r); } // 0x140484780 int32_t OsageCollision::cls_plane_oidashi(vec3& vec, const vec3& p, const vec3& p1, const vec3& p2, const float_t r) { const float_t d = vec3::dot(p, p2) - vec3::dot(p1, p2) - r; if (d < 0.0f) { vec = p2 * -d; return 1; } return 0; } // 0x140484E10 void OsageCollision::get_nearest_line2point(vec3& nearest, const vec3& p0, const vec3& p1, const vec3& q) { const vec3 p0p1 = p1 - p0; const vec3 p0q = q - p0; const float_t t = vec3::dot(p0q, p0p1); if (t < 0.0f) { nearest = p0; return; } const float_t p0p1_len = vec3::length_squared(p0p1); if (p0p1_len <= 0.000001f) nearest = p0; else if (t <= p0p1_len) nearest = p0 + p0p1 * (t / p0p1_len); else nearest = p1; } // 0x140484FE0 int32_t OsageCollision::osage_capsule_cls(const OsageCollision::Work* cls, vec3& p0, vec3& p1, const float_t& cls_r) { return OsageCollision::osage_capsule_cls(p0, p1, cls_r, cls); } // 0x140485000 int32_t OsageCollision::osage_capsule_cls(vec3& p0, vec3& p1, const float_t& cls_r, const OsageCollision::Work* cls) { if (!cls || cls->type == SkinParam::CollisionTypeEnd) return 0; int32_t hit = 0; while (cls->type != SkinParam::CollisionTypeEnd) { vec3 vec = 0.0f; switch (cls->type) { case SkinParam::CollisionTypeBall: hit += OsageCollision::cls_line2ball_oidashi(vec, p0, p1, cls->pos[0], cls_r + cls->radius); break; case SkinParam::CollisionTypeCapsule: hit += OsageCollision::cls_line2capsule_oidashi(vec, p0, p1, cls, cls_r + cls->radius); break; case SkinParam::CollisionTypeEllipse: hit += OsageCollision::cls_line2ellipse_oidashi(vec, p0, p1, cls, cls_r + cls->radius); break; } if (hit > 0) { p0 += vec; p1 += vec; } cls++; } return hit; } // 0x140485180 int32_t OsageCollision::osage_cls(const OsageCollision::Work* cls, vec3& p, const float_t& cls_r) { return OsageCollision::osage_cls(p, cls_r, cls, 0); } // 0x140485220 int32_t OsageCollision::osage_cls(vec3& p, const float_t& cls_r, const OsageCollision::Work* cls, float_t* fric) { if (!cls || cls->type == SkinParam::CollisionTypeEnd) return 0; int32_t hit = 0; while (cls->type != SkinParam::CollisionTypeEnd) { int32_t _hit = 0; vec3 vec = 0.0f; switch (cls->type) { case SkinParam::CollisionTypeBall: _hit = OsageCollision::cls_ball_oidashi(vec, p, cls->pos[0], cls->radius + cls_r); break; case SkinParam::CollisionTypeCapsule: _hit = OsageCollision::cls_capsule_oidashi(vec, p, cls, cls->radius + cls_r); break; case SkinParam::CollisionTypePlane: _hit = OsageCollision::cls_plane_oidashi(vec, p, cls->pos[0], cls->pos[1], cls_r); break; case SkinParam::CollisionTypeEllipse: _hit = OsageCollision::cls_ellipse_oidashi(vec, p, cls, cls->radius + cls_r); break; case SkinParam::CollisionTypeAABB: _hit = OsageCollision::cls_aabb_oidashi(vec, p, cls, cls_r); break; } if (fric && _hit > 0) *fric = max_def(*fric, cls->friction); hit += _hit; p += vec; cls++; } return hit; } // 0x1404851C0 int32_t OsageCollision::osage_cls_work_list(vec3& p, const float_t& cls_r, const OsageCollision& coli, float_t* fric) { if (coli.work_list.size() && coli.work_list.back().type == SkinParam::CollisionTypeEnd) return OsageCollision::osage_cls(p, cls_r, coli.work_list.data(), fric); return 0; } osage_ring_data::osage_ring_data() : rect_x(), rect_y(), rect_width(), rect_height(), ring_height(), out_height(), init() { reset(); } osage_ring_data::~osage_ring_data() { } void osage_ring_data::reset() { rect_x = 0.0f; rect_y = 0.0f; rect_width = 0.0f; rect_height = 0.0f; ring_height = -1000.0f; out_height = -1000.0f; init = false; coli_object.work_list.clear(); skp_root_coli.clear(); } inline float_t osage_ring_data::get_floor_height(const vec3& pos, const float_t coli_r) const { const float_t x = rect_x - coli_r; const float_t y = rect_y - coli_r; if (pos.x < x || pos.z < y || pos.x > x + rect_width + coli_r || pos.z > y + rect_height + coli_r) return out_height + coli_r; else return ring_height + coli_r; } void osage_ring_data::parse(const std::string& path, osage_ring_data& ring) { ring.reset(); data_struct* aft_data = &data_list[DATA_AFT]; bone_database* aft_bone_data = &aft_data->data_ft.bone_data; key_val kv; if (!aft_data->load_file(&kv, path.c_str(), key_val::load_file)) return; int32_t count; if (kv.read("object", "length", count)) { for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; int32_t type; if (!kv.read("type", type)) break; OsageCollision::Work cls; cls.type = (SkinParam::CollisionType)type; float_t radius; if (!kv.read("radius", radius)) break; cls.radius = radius; if (kv.open_scope("pos")) { for (int32_t j = 0; j < 2; j++) { if (!kv.open_scope_fmt(j)) continue; float_t x; if (kv.read("x", x)) cls.pos[j].x = x; float_t y; if (kv.read("y", y)) cls.pos[j].y = y; float_t z; if (kv.read("z", z)) cls.pos[j].z = z; kv.close_scope(); } kv.close_scope(); } float_t friction; if (kv.read("friction", friction)) cls.friction = friction; int32_t node_idx0 = -1; int32_t node_idx1 = -1; const char* bone0_name; const char* bone1_name; if (kv.read("bone.0.name", bone0_name)) { node_idx0 = aft_bone_data->get_bone_index(BONE_KIND_CMN, bone0_name); if (node_idx0 >= 0 && kv.read("bone.1.name", bone1_name)) node_idx1 = aft_bone_data->get_bone_index(BONE_KIND_CMN, bone1_name); } if (node_idx0 >= 0) { SkinParam::CollisionParam cls_param; cls_param.type = cls.type; cls_param.node_idx[0] = node_idx0; cls_param.node_idx[1] = node_idx1; cls_param.radius = cls.radius; cls_param.pos[0] = cls.pos[0]; cls_param.pos[1] = cls.pos[1]; ring.skp_root_coli.push_back(cls_param); } else { if (cls.type == SkinParam::CollisionTypeCapsule || cls.type == SkinParam::CollisionTypeEllipse) { cls.vec_center = cls.pos[1] - cls.pos[0]; cls.vec_center_length_squared = vec3::length_squared(cls.vec_center); cls.vec_center_length = sqrtf(cls.vec_center_length_squared); } ring.coli_object.work_list.push_back(cls); } kv.close_scope(); } kv.close_scope(); } ring.coli_object.work_list.push_back({}); ring.skp_root_coli.push_back({}); kv.open_scope("ring"); float_t rect_x; float_t rect_y; float_t rect_width; float_t rect_height; float_t ring_height; float_t out_height; if (kv.read("rect.x", rect_x) && kv.read("rect.y", rect_y) && kv.read("rect.width", rect_width) && kv.read("rect.height", rect_height) && kv.read("ring_height", ring_height) && kv.read("out_height", out_height)) { ring.init = true; ring.rect_x = rect_x; ring.rect_y = rect_y; ring.rect_width = rect_width; ring.rect_height = rect_height; ring.ring_height = ring_height; ring.out_height = out_height; } kv.close_scope(); } osage_ring_data& osage_ring_data::operator=(const osage_ring_data& ring) { rect_x = ring.rect_x; rect_y = ring.rect_y; rect_width = ring.rect_width; rect_height = ring.rect_height; ring_height = ring.ring_height; out_height = ring.out_height; init = ring.init; coli_object.work_list.assign(ring.coli_object.work_list.begin(), ring.coli_object.work_list.end()); skp_root_coli.assign(ring.skp_root_coli.begin(), ring.skp_root_coli.end()); return *this; } CLOTH_VERTEX::CLOTH_VERTEX() : flag(), m(), length_up(), length_down(), length_left(), length_right(), reset_data() { } CLOTH_VERTEX::~CLOTH_VERTEX() { } CLOTH::CLOTH() : flag(), width(), height(), wind_dir(), wet(), use_ex_force(), ex_force(), skin_param_ptr() { init(); } CLOTH::~CLOTH() { } void CLOTH::set_spring() { spring.clear(); if (height <= 1) return; CLOTH_SPRING line = {}; const size_t width = this->width; for (size_t i = 1; i < height; i++) { for (size_t j = 0; j < width; j++) { size_t top = (i - 1) * width + j; size_t center = i * width + j; line.index0 = top; line.index1 = center; line.length = vec3::distance(vtxarg.data()[top].pos, vtxarg.data()[center].pos); spring.push_back(line); if (j < width - 1) { size_t left = i * width + j + 1; line.index0 = center; line.index1= left; line.length = vec3::distance(vtxarg.data()[center].pos, vtxarg.data()[left].pos); spring.push_back(line); } } if (flag.ring) { line.index0 = i * width; line.index1 = (i + 1) * width - 1; spring.push_back(line); } } } void CLOTH::set_coli_r(float_t value) { skin_param_ptr->coli_r = value; } void CLOTH::set_friction(float_t value) { skin_param_ptr->friction = value; } void CLOTH::set_wind_affect(float_t value) { skin_param_ptr->wind_afc = value; } void CLOTH::set_wind_dir(const vec3& value) { wind_dir = value; } void CLOTH::set_wetness(float_t value) { wet = value; } void CLOTH::set_angle_limit(float_t y, float_t z) { skin_param_hinge& hinge = skin_param_ptr->hinge; hinge.ymax = y; hinge.zmax = z; hinge.ymin = -y; hinge.zmin = -z; hinge.limit(); } CLOTH_VERTEX* CLOTH::get_vertex() { return vtxarg.data(); } void CLOTH::init() { flag.exec = 0; width = 0; height = 0; vtxarg.clear(); wind_dir = 0.0f; wet = 0.0f; use_ex_force = false; ex_force = 0.0f; skin_param.reset(); skin_param_ptr = &skin_param; } void CLOTH::disp_debug() { if (!dbg_osage_line_disp_flag) return; const color4u8 color_spring = color_red; const CLOTH_VERTEX* vtx = vtxarg.data(); for (const auto& i : spring) spr::put_sprite_3d_line(vtx[i.index0].pos, vtx[i.index1].pos, color_spring); } void CLOTH::dest() { init(); } RobCloth::RobCloth() : local_mat(), skin_disp(), root_data(), data(), move_cancel(), osage_reset(), mesh(), submesh(), vb(), ib(), reset_data_list() { dest(); } RobCloth::~RobCloth() { dest(); } void RobCloth::disp_debug() { CLOTH::disp_debug(); if (!dbg_osage_line_disp_flag) return; const color4u8 color_spring = color_red; const color4u8 color_tangent = color_green; const color4u8 color_binormal = color_cyan; const color4u8 color_normal = color_white; const CLOTH_VERTEX* vtx = vtxarg.data(); for (const auto& i : spring) spr::put_sprite_3d_line(vtx[i.index0].pos, vtx[i.index1].pos, color_spring); vtx += width; for (size_t i = 1; i < height; i++) { for (size_t j = 0; j < width; j++, vtx++) { spr::put_sprite_3d_line(vtx->pos, vtx->tangent * 0.05f + vtx->pos, color_tangent); spr::put_sprite_3d_line(vtx->pos, vtx->binormal * 0.05f + vtx->pos, color_binormal); spr::put_sprite_3d_line(vtx->pos, vtx->normal * 0.05f + vtx->pos, color_normal); } } } void RobCloth::dest() { vb[0].unload(); vb[1].unload(); init(); local_mat = 0; skin_disp = 0; root_data = 0; data = 0; move_cancel = 0.0f; osage_reset = false; mesh[0] = {}; mesh[1] = {}; ib[0] = {}; ib[1] = {}; motion_reset_data.clear(); reset_data_list = 0; } void RobCloth::AddMotionResetData(uint32_t motnum, float_t frame) { int32_t frame_int = (int32_t)prj::roundf(frame * 1000.0f); auto elem = motion_reset_data.find({ motnum, frame_int }); if (elem != motion_reset_data.end()) motion_reset_data.erase(elem); std::list reset_data_list; CLOTH_VERTEX* i_begin = vtxarg.data() + width; CLOTH_VERTEX* i_end = vtxarg.data() + vtxarg.size(); for (CLOTH_VERTEX* i = i_begin; i != i_end; i++) reset_data_list.push_back(i->reset_data); motion_reset_data.insert({ { motnum, frame_int }, reset_data_list }); } // 0x1402196D0 void RobCloth::ApplyResetData() { const size_t width = this->width; const size_t height = this->height; if (reset_data_list) { auto reset_data = this->reset_data_list->begin(); CLOTH_VERTEX* i_begin = vtxarg.data() + width; CLOTH_VERTEX* i_end = vtxarg.data() + vtxarg.size(); for (CLOTH_VERTEX* i = i_begin; i != i_end; i++) i->reset_data = *reset_data++; reset_data_list = 0; } CLOTH_WEIGHTED_ROOT* root = this->root.data(); for (size_t i = 0; i < width; i++, root++) { CLOTH_VERTEX* vtx = &vtxarg.data()[i + width]; for (size_t j = 1; j < height; j++, vtx += width) { mat4_transform_point(&root->mat_pos, &vtx->reset_data.pos, &vtx->pos); mat4_transform_vector(&root->mat_pos, &vtx->reset_data.vec, &vtx->vec); } } } // 0x140218E40 void RobCloth::CtrlOsagePlayData(const std::vector& opd_blend_data) { calc_root(); const ::opd_blend_data* i_begin = opd_blend_data.data() + opd_blend_data.size(); const ::opd_blend_data* i_end = opd_blend_data.data(); for (const ::opd_blend_data* i = i_begin; i != i_end; ) { i--; float_t frame = i->frame; #if OPD_PLAY_GEN if (frame >= i->frame_max) frame = i->no_loop ? i->frame_max - 1.0f : 0.0f; #else if (frame >= i->frame_max) frame = 0.0f; #endif int32_t curr_key = (int32_t)(int64_t)prj::floorf(frame); int32_t next_key = curr_key + 1; #if OPD_PLAY_GEN if ((float_t)next_key >= i->frame_max) next_key = i->no_loop ? (int32_t)i->frame_max - 1 : 0; #else if ((float_t)next_key >= i->frame_max) next_key = 0; #endif float_t blend = frame - (float_t)(int64_t)frame; float_t inv_blend = 1.0f - blend; for (size_t j = 0; j < width; j++) { CLOTH_VERTEX& root_node = vtxarg.data()[j]; vec3 parent_trans = root_node.pos; mat4 mat = root.data()[j].mat; mat4_mul_translate(&mat, &root_node.org_pos, &mat); CLOTH_VERTEX* v29 = &vtxarg.data()[j + width]; vec3 localvec; mat4_transform_vector(&mat, &v29->localvec, &localvec); ROTTYPE rottype = ROTTYPE_YZ; make_axis_matrix(mat, localvec, 0, 0, rottype); const mat4& mat_pos = root.data()[j].mat_pos; for (size_t k = 1; k < height; k++, v29 += width) { opd_vec3_data* opd = &v29->opd_data[i - i_end]; const float_t* opd_x = opd->x; const float_t* opd_y = opd->y; const float_t* opd_z = opd->z; vec3 curr_trans; curr_trans.x = opd_x[curr_key]; curr_trans.y = opd_y[curr_key]; curr_trans.z = opd_z[curr_key]; vec3 next_trans; next_trans.x = opd_x[next_key]; next_trans.y = opd_y[next_key]; next_trans.z = opd_z[next_key]; mat4_transform_point(&mat_pos, &curr_trans, &curr_trans); mat4_transform_point(&mat_pos, &next_trans, &next_trans); vec3 _trans = curr_trans * inv_blend + next_trans * blend; vec3 localvec; mat4_inverse_transform_point(&mat, &_trans, &localvec); vec3 rotation = 0.0f; ROTTYPE rottype = ROTTYPE_YZ; make_axis_matrix(mat, localvec, 0, &rotation, rottype); mat4_mul_translate(&mat, vec3::distance(_trans, parent_trans), 0.0f, 0.0f, &mat); v29->opd_node_data.set_data(i, { v29->length_up, rotation }); parent_trans = _trans; } } } for (size_t i = 0; i < width; i++) { CLOTH_VERTEX& root_node = vtxarg.data()[i]; mat4 mat = root.data()[i].mat; mat4_mul_translate(&mat, &root_node.org_pos, &mat); CLOTH_VERTEX* v50 = &vtxarg.data()[i + width]; vec3 localvec; mat4_transform_vector(&mat, &v50->localvec, &localvec); ROTTYPE rottype = ROTTYPE_YZ; make_axis_matrix(mat, localvec, 0, 0, rottype); for (size_t j = 1; j < height; j++, v50 += width) { mat4_mul_rotate_z(&mat, v50->opd_node_data.curr.rotation.z, &mat); mat4_mul_rotate_y(&mat, v50->opd_node_data.curr.rotation.y, &mat); mat4_mul_translate(&mat, v50->opd_node_data.curr.length, 0.0f, 0.0f, &mat); mat4_get_translation(&mat, &v50->pos); } } } const float_t* RobCloth::LoadOpdData(size_t node_index, const float_t* opd_data, size_t opd_count) { CLOTH_VERTEX* i_begin = vtxarg.data() + width; CLOTH_VERTEX* i_end = vtxarg.data() + vtxarg.size(); for (CLOTH_VERTEX* i = i_begin; i != i_end; i++) { if (node_index >= i->opd_data.size()) i->opd_data.resize(node_index + 1); opd_vec3_data* opd = &i->opd_data[node_index]; opd->x = opd_data; opd_data += opd_count; opd->y = opd_data; opd_data += opd_count; opd->z = opd_data; opd_data += opd_count; } return opd_data; } void RobCloth::LoadSkinParam(void* kv, const char* name, const bone_database* bone_data) { key_val* _kv = (key_val*)kv; skin_param_osage_root root; skin_param_osage_root_parse(_kv, name, root, bone_data); SetSkinParamOsageRoot(root); } void RobCloth::SetMotionResetData(uint32_t motnum, float_t frame) { osage_reset = true; auto elem = motion_reset_data.find({ motnum, (int32_t)prj::roundf(frame * 1000.0f) }); if (elem != motion_reset_data.end() && elem->second.size() + width == vtxarg.size()) { this->reset_data_list = &elem->second; auto reset_data_list = this->reset_data_list->begin(); CLOTH_VERTEX* i_begin = vtxarg.data() + width; CLOTH_VERTEX* i_end = vtxarg.data() + vtxarg.size(); for (CLOTH_VERTEX* i = i_begin; i != i_end; i++) i->reset_data = *reset_data_list++; } } const float_t* RobCloth::SetOsagePlayDataInit(const float_t* opdi_data) { CLOTH_VERTEX* i_begin = vtxarg.data() + width; CLOTH_VERTEX* i_end = vtxarg.data() + vtxarg.size(); for (CLOTH_VERTEX* i = i_begin; i != i_end; i++) { i->pos.x = *opdi_data++; i->pos.y = *opdi_data++; i->pos.z = *opdi_data++; i->vec.x = *opdi_data++; i->vec.y = *opdi_data++; i->vec.z = *opdi_data++; i->old_pos = i->pos; } return opdi_data; } void RobCloth::SetOsageReset() { osage_reset = true; } void RobCloth::SetRing(const osage_ring_data& ring) { this->ring = ring; } void RobCloth::SetSkinParam(skin_param_file_data* skp) { skin_param_ptr = &skp->skin_param; } void RobCloth::SetSkinParamOsageRoot(const skin_param_osage_root& skp_root) { set_param(skp_root.force, skp_root.force_gain, skp_root.air_res); set_friction(skp_root.friction); set_wind_affect(skp_root.wind_afc); set_coli_r(skp_root.coli_r); set_angle_limit(skp_root.hinge_y, skp_root.hinge_z); skin_param_ptr->coli.assign(skp_root.coli.begin(), skp_root.coli.end()); } // 0x14021DC60 void RobCloth::calc(const float_t dt) { if (dt <= 0.0f) return; calc_root(); calc_force(dt, true); calc_stretch(); calc_velocity(dt, true); } // 0x1402187D0 void RobCloth::calc_force(float_t time, bool init_flag) { float_t fric = (1.0f - wet) * (1.0f - skin_param_ptr->air_res); float_t osage_gravity = get_gravity(); vec3 ex_force = wind_dir * skin_param_ptr->wind_afc; if (use_ex_force) { ex_force += this->ex_force; osage_gravity = 0.0f; } const size_t width = this->width; const size_t height = this->height; float_t force = skin_param_ptr->force; CLOTH_VERTEX* vtx = &vtxarg.data()[width]; for (size_t i = 1; i < height; i++) { CLOTH_WEIGHTED_ROOT* root = this->root.data(); for (size_t j = 0; j < width; j++, root++, vtx++) { mat4 mat = root->mat; vec3 localvec; mat4_transform_vector(&mat, &vtx->localvec, &localvec); const float_t _fric = dbg_init_flag || init_flag ? (vtx->vec.y >= 0.0f ? 1.0f : 0.0f) : fric; vec3 vel = localvec * force - vtx->vec * _fric + ex_force; vel.y -= osage_gravity; vtx->vec += vel; vtx->old_pos = vtx->pos; vtx->pos += vtx->vec; } force *= skin_param_ptr->force_gain; } } // 0x140218B40 void RobCloth::calc_normal() { const ssize_t width = this->width; CLOTH_VERTEX* vtx = &vtxarg.data()[width]; if (calc_normal_flag) { for (size_t i = 1; i < height - 1; i++) { calc_tangent_basis_sub(vtx, vtx, vtx + 1, &vtx[-width], &vtx[width]); vtx++; for (ssize_t j = 1; j < width - 1; j++, vtx++) calc_tangent_basis_sub(vtx, vtx - 1, vtx + 1, &vtx[-width], &vtx[width]); calc_tangent_basis_sub(vtx, vtx - 1, vtx, &vtx[-width], &vtx[width]); vtx++; } calc_tangent_basis_sub(vtx, vtx, vtx + 1, &vtx[-width], vtx); vtx++; for (ssize_t i = 1; i < width - 1; i++, vtx++) calc_tangent_basis_sub(vtx, vtx - 1, vtx + 1, &vtx[-width], vtx); calc_tangent_basis_sub(vtx, vtx - 1, vtx, &vtx[-width], vtx); } else for (size_t i = 1; i < height; i++, vtx++) { for (ssize_t j = 0; j < width - 1; j++, vtx++) { vtx[0].m = calc_tangent_basis( vtx[0].pos, vtx[-width].pos, vtx[1].pos, vtx[0].uv, vtx[-width].uv, vtx[1].uv, vtx[0].tangent, vtx[0].binormal, vtx[0].normal); } vtx[0].normal = vtx[-1].normal; vtx[0].tangent = vtx[-1].tangent; vtx[0].binormal = vtx[-1].binormal; vtx[0].m = vtx[-1].m; } } // 0x140219940 void RobCloth::calc_root() { for (size_t i = 0; i < width; i++) { CLOTH_WEIGHTED_ROOT& root = this->root.data()[i]; CLOTH_VERTEX& root_node = vtxarg.data()[i]; mat4 m = mat4_null; for (int32_t j = 0; j < 4; j++) { if (!root.bone_mat[j] || !root.node_mat[j]) continue; mat4 mat; mat4_mul(root.bone_mat[j], root.node_mat[j], &mat); float_t weight = root.weight[j]; mat4_mul_scale(&mat, weight, weight, weight, weight, &mat); mat4_add(&m, &mat, &m); } root.mat = m; mat4_transform_point(&m, &root_node.org_pos, &root_node.pos); mat4_transform_vector(&m, &root.normal, &root_node.normal); mat4_transform_vector(&m, (vec3*)&root.tangent, &root_node.tangent); root_node.m = root.tangent.w; root_node.old_pos = root_node.pos; mat4_mul_translate(&m, &root_node.org_pos, &m); root.mat_pos = m; mat4_invert(&m, &m); root.inv_mat_pos = m; } } // 0x140219D10 void RobCloth::calc_stretch() { CLOTH_VERTEX* vtx = vtxarg.data(); const ssize_t width = this->width; const size_t height = this->height; for (size_t i = height - 2; i; i--) { CLOTH_VERTEX* v7 = &vtx[width * i]; for (ssize_t j = 0; j < width; j++, v7++) length_limit(v7->pos, v7[width].pos, v7->length_down); } if (height <= 1) return; vec3* v11 = (vec3*)operator new(sizeof(vec3) * width); vec3* v12 = (vec3*)operator new(sizeof(vec3) * width); memset(v11, 0, sizeof(vec3) * width); memset(v12, 0, sizeof(vec3) * width); vec3* v13 = &v11[width - 2]; vec3* v14 = v12 + 1; CLOTH_VERTEX* v15 = &vtx[width]; CLOTH_VERTEX* v16 = &vtx[2 * width - 1]; for (size_t i = height - 1; i; i--) { if (width) { vec3* v17 = v12; vec3* v17a = v11; CLOTH_VERTEX* v18 = v15; for (ssize_t j = width; j > 0; j--, v17++, v17a++, v18++) { *v17 = v18->pos; *v17a = v18->pos; } } if (width - 2 >= 0) { vec3* v20 = v13; CLOTH_VERTEX* v22 = v16 - 1; for (ssize_t j = width - 1; j > 0; j--, v20--, v22--) length_limit(v20[0], v20[1], v22->length_right); v14 = v12 + 1; } if (flag.ring) length_limit(v11[width - 1], v11[0], v16->length_left); if (width > 1) { vec3* v23 = v14; CLOTH_VERTEX* v24 = v15 + 1; for (ssize_t j = width - 1; j > 0; j--, v23++, v24++) length_limit(v23[0], v23[-1], v24->length_left); v14 = v12 + 1; } if (flag.ring) length_limit(v12[0], v12[width - 1], v15->length_right); if (width > 0) { vec3* v27 = v11; vec3* v28 = v12; CLOTH_VERTEX* v36 = v15; for (ssize_t j = width; j > 0; j--, v27++, v28++, v36++) v36->pos = (*v27 + *v28) * 0.5f; } v16 += width; v15 += width; } operator delete(v11); operator delete(v12); } // 0x14021AA60 void RobCloth::calc_velocity(const float_t dt, bool a3) { const ssize_t width = this->width; const size_t height = this->height; const float_t inv_dt = 1.0f / dt; const CLOTH_WEIGHTED_ROOT* root = this->root.data(); CLOTH_VERTEX* vtx = &vtxarg.data()[width]; const float_t floor_height = ring.get_floor_height(vtx->pos, skin_param_ptr->coli_r); for (size_t i = 1; i < height; i++) { for (ssize_t j = 0; j < width; j++, vtx++) { float_t fric = (1.0f - wet) * skin_param_ptr->friction; if (dt != 1.0f) { vec3 vec = vtx->pos - vtx->old_pos; float_t trans_length = vec3::length(vec); if (trans_length * dt > 0.0f && trans_length != 0.0f) vec *= 1.0f / trans_length; vtx->pos = vtx->old_pos + vec * (trans_length * dt); } length_limit(vtx[0].pos, vtx[-width].pos, vtx[0].length_up); int32_t hit = OsageCollision::osage_cls_work_list(vtx->pos, skin_param_ptr->coli_r, ring.coli_object, &fric); hit += OsageCollision::osage_cls(coli_ring, vtx->pos, skin_param_ptr->coli_r); hit += OsageCollision::osage_cls(coli_chara, vtx->pos, skin_param_ptr->coli_r); if (floor_height > vtx->pos.y && floor_height < 1001.0f) { vtx->pos.y = floor_height; vtx->vec = 0.0f; } mat4 mat = root->mat; mat4_set_translation(&mat, &vtx[-width].pos); ROTTYPE rottype = ROTTYPE_ZY; make_axis_matrix(mat, vtx->localvec, 0, 0, rottype); vec3 localvec; mat4_inverse_transform_point(&mat, &vtx->pos, &localvec); make_axis_matrix(mat, localvec, &skin_param_ptr->hinge, &vtx->reset_data.rotation, rottype); mat4_mul_translate(&mat, vtx->length_up, 0.0f, 0.0f, &mat); mat4_get_translation(&mat, &vtx->pos); if (hit) vtx->vec *= fric; vtx->vec = (vtx->pos - vtx->old_pos) * inv_dt; if (!a3) { const mat4& inv_mat_pos = this->root.data()[j].inv_mat_pos; mat4_transform_point(&inv_mat_pos, &vtx->pos, &vtx->reset_data.pos); mat4_transform_vector(&inv_mat_pos, &vtx->vec, &vtx->reset_data.vec); } } } } // 0x140218560 void RobCloth::ctrl(const float_t dt, bool init_flag) { calc_root(); if (osage_reset) { ApplyResetData(); osage_reset = false; } if (move_cancel > 0.0f) { const size_t width = this->width; const size_t height = this->height; float_t move_cancel = this->move_cancel; for (size_t i = 0; i < width; i++) { const mat4& mat_pos = root.data()[i].mat_pos; CLOTH_VERTEX* vtx = &vtxarg.data()[i + width]; for (size_t j = 1; j < height; j++, vtx += width) { vec3 pos; mat4_transform_point(&mat_pos, &vtx->reset_data.pos, &pos); vtx->pos += (pos - vtx->pos) * move_cancel; } } } if (dt > 0.0f && !get_pause()) { calc_force(dt, init_flag); calc_stretch(); calc_velocity(dt, false); if (!init_flag) { use_ex_force = false; ex_force = 0.0f; } } } // 0x14021B930 void RobCloth::disp(const mat4& mat, render_context* rctx) { obj* obj = objset_info_storage_get_obj(skin_disp->obj_uid); if (!obj) return; std::vector* textures = objset_info_storage_get_obj_set_gentex(skin_disp->obj_uid.set_id); vec3 center = (vtxarg.data()[0].pos + vtxarg.data()[width * height - 1].pos) * 0.5f; ::obj o = *obj; o.num_mesh = ib[1].buffer != 0 ? 2 : 1; o.mesh_array = mesh; o.bounding_sphere.center = center; o.bounding_sphere.radius *= 2.0f; for (int32_t i = 0; i < o.num_mesh; i++) { obj_mesh& mesh = this->mesh[i]; mesh.bounding_sphere.center = center; for (int32_t j = 0; j < mesh.num_submesh; j++) { obj_sub_mesh& submesh = mesh.submesh_array[j]; submesh.bounding_sphere.center = center; } } rctx->disp_manager->set_texture_pattern((int32_t)skin_disp->texchg_vec.size(), skin_disp->texchg_vec.data()); rctx->disp_manager->entry_obj_by_obj(mat, &o, textures, vb, ib, 0, skin_disp->alpha); rctx->disp_manager->set_texture_pattern(); } // 0x14021D330 void RobCloth::modify_obj() { if (pv_osage_manager_array_get_disp()) return; if (cloth_modify_flag & 0x01) calc_normal(); if (cloth_modify_flag & 0x02) { obj_mesh* omote_mesh = objset_info_storage_get_obj_mesh(skin_disp->obj_uid, data->omote_name); obj_mesh* ura_mesh = objset_info_storage_get_obj_mesh(skin_disp->obj_uid, data->ura_name); if (data->ura_name) { modify_cloth_object(omote_mesh, &vb[0], vtxarg.data(), 1.0f, data->num_omote_index, data->omote_index_array, false); modify_cloth_object(ura_mesh, &vb[1], vtxarg.data(), -1.0f, data->num_ura_index, data->ura_index_array, false); } else modify_cloth_object(omote_mesh, &vb[0], vtxarg.data(), 1.0, data->num_omote_index, data->omote_index_array, true); } } // 0x14021E460 void RobCloth::set_data(size_t w, size_t h, const obj_skin_ex_node_cloth_root * rt_data, const obj_skin_ex_node_cloth_point* move, const mat4* lcl_mat, uint32_t ring_flag, const RobSkinDisp* skin, const bone_database* bone_data) { skin_disp = skin; obj* obj = objset_info_storage_get_obj(skin_disp->obj_uid); if (!obj) return; obj_mesh_index_buffer* ib = objset_info_storage_get_obj_mesh_index_buffer(skin_disp->obj_uid); uint32_t omotea_mesh_index = obj->get_obj_mesh_index(data->omote_name); uint32_t ura_mesh_index = obj->get_obj_mesh_index(data->ura_name); mesh[0] = obj->mesh_array[omotea_mesh_index]; if (data->ura_name && ura_mesh_index != -1) mesh[1] = obj->mesh_array[ura_mesh_index]; else mesh[1] = {}; for (int32_t i = 0; i < 2; i++) { for (int32_t j = 0; j < mesh[i].num_submesh; j++) { obj_sub_mesh& cloth_mesh = submesh[i][j]; cloth_mesh = mesh[i].submesh_array[j]; cloth_mesh.attrib.m.cloth = 0; cloth_mesh.bounding_sphere.radius = 1000.0f; cloth_mesh.axis_aligned_bounding_box.center = 0.0f; cloth_mesh.axis_aligned_bounding_box.size = { 1000.0f, 1000.0f, 1000.0f }; mesh[i].submesh_array[j].attrib.m.cloth = 1; } mesh[i].submesh_array = submesh[i]; mesh[i].bounding_sphere.radius = 1000.0f; } this->ib[0] = ib[omotea_mesh_index]; if (!vb[0].load(mesh[0], GL::BUFFER_USAGE_STREAM)) return; this->ib[1] = {}; if (data->ura_name && ura_mesh_index != -1) this->ib[1] = ib[ura_mesh_index]; if (data->ura_name && !vb[1].load(mesh[1], GL::BUFFER_USAGE_STREAM)) return; flag.exec = 1; flag.disp = 1; flag.ring = ring_flag; this->width = w; this->height = h; this->root_data = rt_data; this->local_mat = lcl_mat; this->root.clear(); this->root.reserve(w); for (size_t i = 0; i < w; i++) { this->root.push_back({}); CLOTH_WEIGHTED_ROOT& root = this->root.back(); root.pos = rt_data[i].pos; root.normal = rt_data[i].normal; for (int32_t j = 0; j < 4; j++) { const obj_skin_ex_node_cloth_weight& weight = rt_data[i].weight[j]; root.node[j] = 0; root.node_mat[j] = 0; root.bone_mat[j] = &lcl_mat[weight.mat_idx]; if (weight.bone_name) { root.node[j] = skin->get_node(weight.bone_name, bone_data); if (root.node[j]) root.node_mat[j] = root.node[j]->mat_ptr; } root.weight[j] = weight.weight; } } this->vtxarg.clear(); this->vtxarg.reserve(w); for (size_t i = 0; i < w; i++) { this->vtxarg.push_back({}); CLOTH_VERTEX& vtx = this->vtxarg.back(); vtx.pos = root[i].pos; vtx.org_pos = root[i].pos; } std::vector index_array(w * h, 0); uint16_t* omote_index_array = data->omote_index_array; for (size_t i = 0; i < data->num_omote_index; i++) index_array.data()[omote_index_array[i]] = i; obj_mesh* mesh = objset_info_storage_get_obj_mesh(skin_disp->obj_uid, data->omote_name); obj_vertex_format vertex_format = (obj_vertex_format)0; obj_vertex_data* vertex_array = 0; if (mesh) { vertex_format = mesh->vertex_format; vertex_array = mesh->vertex_array; } if (mesh && vertex_format & (OBJ_VERTEX_TEXCOORD0 | OBJ_VERTEX_TANGENT | OBJ_VERTEX_NORMAL | OBJ_VERTEX_POSITION)) { for (size_t i = 0; i < w; ++i) this->root.data()[i].tangent = vertex_array[index_array.data()[i]].tangent; } for (size_t i = 1; i < h; i++) for (size_t j = 0; j < w; j++) { this->vtxarg.push_back({}); const obj_skin_ex_node_cloth_point& mv = move[(i - 1) * w + j]; CLOTH_VERTEX& vtx = this->vtxarg.back(); vtx.pos = mv.pos; vtx.org_pos = mv.pos; vtx.normal = { 0.0f, 0.0f, 1.0f }; vtx.tangent = { 1.0f, 0.0f, 0.0f }; vtx.vec = mv.vec; vtx.localvec = vec3::normalize(mv.vec); vtx.length_up = mv.length[0]; vtx.length_down = mv.length[1]; vtx.length_left = mv.length[2]; vtx.length_right = mv.length[3]; vtx.reset_data = {}; uint32_t flag = 0x00; if (i) flag |= 0x01; if (i != h - 1) flag |= 0x02; if (j) flag |= 0x04; if (j != w - 1) flag |= 0x08; vtx.flag = flag; if (mesh && vertex_format & OBJ_VERTEX_TEXCOORD0) vtx.uv = vertex_array[index_array.data()[i * w + j]].texcoord0; } set_spring(); } // 0x14021EEB0 void RobCloth::set_data(const obj_skin_ex_node_cloth* cldata, const RobSkinDisp* skin, const bone_database* bone_data) { dest(); data = cldata; set_data(cldata->width, cldata->height, cldata->fix_point, cldata->move_point, cldata->mat_array, cldata->ring_flag, skin, bone_data); obj* obj = objset_info_storage_get_obj(skin->obj_uid); if (!obj) return; for (int32_t i = 0; i < 2; i++) { if (!mesh[i].num_vertex || !mesh[i].vertex_array || !ib[i].buffer) continue; obj_mesh_vertex_buffer* obj_vert_buf = &vb[i]; obj_mesh_index_buffer* obj_index_buf = &ib[i]; obj_mesh* mesh = &this->mesh[i]; for (int32_t j = 0; j < mesh->num_submesh; j++) { obj_material_data* material = &obj->material_array[mesh->submesh_array[j].material_index]; for (int32_t k = 0; k < (mesh->attrib.m.double_buffer ? 2 : 1); k++) { extern render_context* rctx_ptr; rctx_ptr->disp_manager->add_vertex_array(mesh, &mesh->submesh_array[j], material, obj_vert_buf->get_buffer(), obj_vert_buf->get_offset(), obj_index_buf->buffer, 0, 0); obj_vert_buf->cycle_index(); } } } } // 0x14021EF20 void RobCloth::set_ex_force(const vec3& f) { use_ex_force = true; ex_force = f; } // 0x14021EF50 void RobCloth::set_move_cancel(const float_t& mv_ccl) { move_cancel = mv_ccl; } // 0x14021FD00 void RobCloth::make_osage_coli(const mat4* motmat) { if (skin_param_ptr->coli.size()) OsageCollision::Work::update_cls_work(coli_chara, skin_param_ptr->coli.data(), motmat); OsageCollision::Work::update_cls_work(coli_ring, ring.skp_root_coli, motmat); } // 0x14021D480 void RobCloth::pos_init() { calc_root(); const float_t gravity = get_gravity(); const ssize_t width = this->width; const size_t height = this->height; CLOTH_VERTEX* vtx = &vtxarg.data()[width]; const float_t floor_height = ring.get_floor_height(vtx->pos, skin_param_ptr->coli_r); for (size_t i = 1; i < height; i++) { CLOTH_WEIGHTED_ROOT* root = this->root.data(); for (ssize_t j = 0; j < width; j++, root++, vtx++) { mat4 mat = root->mat; vec3 v38; mat4_transform_vector(&mat, &vtx->localvec, &v38); v38.y -= gravity; vtx[0].pos = vtx[-width].pos + vec3::normalize(v38) * vtx->length_up; OsageCollision::osage_cls_work_list(vtx->pos, skin_param_ptr->coli_r, ring.coli_object); OsageCollision::osage_cls(coli_ring, vtx->pos, skin_param_ptr->coli_r); OsageCollision::osage_cls(coli_chara, vtx->pos, skin_param_ptr->coli_r); if (floor_height > vtx->pos.y && floor_height < 1001.0f) vtx->pos.y = floor_height; vtx->vec = 0.0f; vtx->old_pos = vtx->pos; } } for (size_t i = 0; i < cloth_init_cnt; ++i) calc(1.0f); vtx = &vtxarg.data()[width]; for (size_t i = 1; i < height; i++) for (ssize_t j = 0; j < width; j++, vtx++) vtx->vec = 0.0f; } // 0x14021D840 void RobCloth::pos_init_cont() { ctrl(1.0f, true); } void RobCloth::reset_ex_force() { use_ex_force = false; ex_force = 0.0f; } void RobCloth::set_param(float_t force, float_t force_gain, float_t air_res) { skin_param_ptr->force = force; skin_param_ptr->force_gain = force_gain; skin_param_ptr->air_res = air_res; } ExClothBlock::ExClothBlock() { init(); } ExClothBlock::~ExClothBlock() { } void ExClothBlock::init() { cloth_work.init(); data = 0; motion_matrix = 0; block_idx = 0; } void ExClothBlock::CtrlBegin() { done = false; } void ExClothBlock::CtrlStep(int32_t stage, bool disable_ex_force) { } void ExClothBlock::ctrl() { set_param(); const RobDisp* rob_disp = skin_disp->rob_disp; float_t delta_frame = get_delta_frame(); #if OPD_PLAY_GEN if (opd_play_gen_run) delta_frame = 1.0f; #endif float_t step = delta_frame * rob_disp->osage_step; if (rob_disp->opd_blend_data.size() && rob_disp->opd_blend_data.front().use_blend) step = 1.0f; cloth_work.ctrl(step, false); } void ExClothBlock::CtrlOsagePlayData() { cloth_work.CtrlOsagePlayData(skin_disp->rob_disp->opd_blend_data); } void ExClothBlock::disp(const mat4& mat, render_context* rctx) { cloth_work.modify_obj(); cloth_work.disp(mat, rctx); } void ExClothBlock::dest() { cloth_work.dest(); dst_node = 0; } void ExClothBlock::disp_debug() { } void ExClothBlock::pos_init() { set_param(); cloth_work.pos_init(); } void ExClothBlock::pos_init_cont() { set_param(); cloth_work.pos_init_cont(); } void ExClothBlock::AddMotionResetData(const uint32_t& motnum, const float_t& frame) { cloth_work.AddMotionResetData(motnum, frame); } const float_t* ExClothBlock::LoadOpdData(size_t node_index, const float_t* opd_data, size_t opd_count) { return cloth_work.LoadOpdData(node_index, opd_data, opd_count); } void ExClothBlock::SetMotionResetData(const uint32_t& motnum, const float_t& frame) { cloth_work.SetMotionResetData(motnum, frame); } const float_t* ExClothBlock::SetOsagePlayDataInit(const float_t* opdi_data) { return cloth_work.SetOsagePlayDataInit(opdi_data); } void ExClothBlock::SetOsageReset() { cloth_work.SetOsageReset(); } void ExClothBlock::SetRing(const osage_ring_data& ring) { cloth_work.SetRing(ring); } void ExClothBlock::SetSkinParam(skin_param_file_data* skp) { cloth_work.SetSkinParam(skp); } void ExClothBlock::SetSkinParamOsageRoot(const skin_param_osage_root* skp_root) { if (!skp_root) return; cloth_work.SetSkinParamOsageRoot(*skp_root); vec3 wind_dir = task_wind->stage_wind.wind->val * skin_disp->rob_disp->wind_strength; cloth_work.set_wind_dir(wind_dir); } // 0x1405F5220 void ExClothBlock::reset_ex_force() { cloth_work.reset_ex_force(); } void ExClothBlock::set_data(const RobSkinDisp* skin_disp, const obj_skin_ex_node_cloth* data, const skin_param_osage_root* skp_root, const bone_database* bone_data) { type = EX_NODE_TYPE_CLOTH; dst_node = 0; this->data = data; name = skp_root ? skp_root->name : 0; this->skin_disp = skin_disp; motion_matrix = skin_disp->get_node(0)->mat_ptr; cloth_work.dest(); cloth_work.set_data(data, skin_disp, bone_data); SetSkinParamOsageRoot(skp_root); } // 0x1405F7EF0 void ExClothBlock::set_ex_force(const vec3& f) { cloth_work.set_ex_force(f); } // 0x1405F9280 void ExClothBlock::set_move_cancel(const float_t& mv_ccl) { cloth_work.set_move_cancel(mv_ccl); } void ExClothBlock::set_param() { cloth_work.make_osage_coli(motion_matrix); cloth_work.set_wind_dir(task_wind->stage_wind.wind->val); } RobOsage::RobOsage() : skin_param_ptr(), apply_physics(), field_2A1(), field_2A4(), wet(), rottype(), root_matrix_ptr(), move_cancel(), move_cancelled(), osage_reset(), osage_reset_done(), disable_collision(), use_ex_force(), reset_data_list() { } RobOsage::~RobOsage() { } void RobOsage::AddMotionResetData(const uint32_t& motnum, const float_t& frame) { int32_t frame_int = (int32_t)prj::roundf(frame * 1000.0f); auto elem = motion_reset_data.find({ motnum, frame_int }); if (elem != motion_reset_data.end()) motion_reset_data.erase(elem); std::list reset_data_list; RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) reset_data_list.push_back(i->reset_data); motion_reset_data.insert({ { motnum, frame_int }, reset_data_list }); } // 0x14047D620 void RobOsage::ApplyBocRootColi(const float_t step) { if (get_pause() || step <= 0.0f || disable_collision) return; const vec3 pos = joint_node_vec.data()[0].pos; const OsageCollision::Work* coli_chara = this->coli_chara; const OsageCollision::Work* coli_ring = this->coli_ring; const SkinParam::RootCollisionType coli_type = skin_param_ptr->coli_type; RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) { skin_param_osage_node* skp_osg_node = &i->data_ptr->skp_osg_node; for (RobJointNode*& j : i->data_ptr->boc) { float_t hit = (float_t)( OsageCollision::osage_capsule_cls(coli_ring, i->pos, j->pos, skp_osg_node->coli_r) + OsageCollision::osage_capsule_cls(coli_chara, i->pos, j->pos, skp_osg_node->coli_r)); i->hit += hit; j->hit += hit; } if (coli_type != SkinParam::RootCollisionTypeEnd && (coli_type != SkinParam::RootCollisionTypeCapsule || i != i_begin)) { float_t hit = (float_t)( OsageCollision::osage_capsule_cls(coli_ring, i->pos, i->GetPrevNode().pos, skp_osg_node->coli_r) + OsageCollision::osage_capsule_cls(coli_chara, i->pos, i->GetPrevNode().pos, skp_osg_node->coli_r)); i->hit += hit; i->GetPrevNode().hit += hit; } } joint_node_vec.data()[0].pos = pos; } // 0x14047C800 void RobOsage::ApplyPhysics(const mat4& root_matrix, const vec3& hsc, const float_t step, bool disable_ex_force, bool ring_coli, bool has_children_node) { if (!joint_node_vec.size()) return; const float_t gravity = get_gravity(); BeginCalc(root_matrix, hsc, false); RobJointNode* node = &joint_node_vec.data()[0]; node->old_pos = node->pos; const vec3 v113 = transform.pos * hsc; mat4 v130 = root_matrix; mat4_transform_point(&v130, &v113, &node->pos); node->vec = node->pos - node->old_pos; RotateMat(v130, hsc); *joint_node_vec.data()[0].dst_node_mat = v130; *joint_node_vec.data()[0].dst_node->no_scale_mat = v130; vec3 direction = { 1.0f, 0.0f, 0.0f }; mat4_transform_vector(&v130, &direction, &direction); if (!osage_reset && (get_pause() || step <= 0.0f)) return; const bool stiffness = skin_param_ptr->stiffness > 0.0f; RobJointNode* v26_begin = joint_node_vec.data() + 1; RobJointNode* v26_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* v26 = v26_begin; v26 != v26_end; v26++) { float_t weight = v26->data_ptr->skp_osg_node.weight; vec3 force; if (!use_ex_force) { apply_gravity(force, v26->pos, v26->GetPrevNode().pos, gravity, weight); if (v26 != v26_end - 1) { vec3 _force; apply_gravity(_force, v26->pos, v26->GetNextNode().pos, gravity, weight); force = (force + _force) * 0.5f; } } else force = ex_force * (1.0f / weight); const vec3 _direction = direction * (v26->data_ptr->force * v26->force); const float_t fric = (1.0f - wet) * (1.0f - skin_param_ptr->air_res); vec3 vel = force + _direction - v26->vec * fric + v26->ex_force * weight; if (!disable_ex_force) vel += wind_dir * skin_param_ptr->wind_afc; if (stiffness) vel -= (v26->vec - v26->GetPrevNode().vec) * (1.0f - skin_param_ptr->air_res); v26->vel = vel * (1.0f / (weight - (weight - 1.0f) * v26->data_ptr->skp_osg_node.inertial_cancel)); } if (stiffness) { mat4 v131 = v130; vec3 v111; mat4_get_translation(&v131, &v111); RobJointNode* v55_begin = joint_node_vec.data() + 1; RobJointNode* v55_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* v55 = v55_begin; v55 != v55_end; v55++) { vec3 v128; mat4_transform_point(&v131, &v55->rel_pos, &v128); const vec3 vec = v55->vec + v55->vel; vec3 v126 = v55->pos + vec; length_limit(v126, v111, v55->length_back * hsc.x); vec3 v117 = (v128 - v126) * skin_param_ptr->stiffness; const float_t weight = v55->data_ptr->skp_osg_node.weight; v117 *= 1.0f / (weight - (weight - 1.0f) * v55->data_ptr->skp_osg_node.inertial_cancel); v55->vel += v117; v126 = v55->pos + vec + v117; vec3 direction; mat4_inverse_transform_point(&v131, &v126, &direction); make_axis_matrix(v131, direction, 0, 0, rottype); mat4_mul_translate(&v131, vec3::distance(v111, v126), 0.0f, 0.0f, &v131); v111 = v126; } } RobJointNode* v82_begin = joint_node_vec.data() + 1; RobJointNode* v82_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* v82 = v82_begin; v82 != v82_end; v82++) { v82->old_pos = v82->pos; v82->vec += v82->vel; v82->pos += v82->vec; } if (joint_node_vec.size() > 1) { RobJointNode* v90_begin = joint_node_vec.data() + joint_node_vec.size() - 2; RobJointNode* v90_end = joint_node_vec.data(); for (RobJointNode* v90 = v90_begin; v90 != v90_end; v90--) length_limit(v90[0].pos, v90[1].pos, v90->length_next * hsc.x); } if (ring_coli) { RobJointNode* node = &joint_node_vec.data()[0]; const float_t floor_height = ring.get_floor_height( node->pos, node->data_ptr->skp_osg_node.coli_r); RobJointNode* v98_begin = joint_node_vec.data() + 1; RobJointNode* v98_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* v98 = v98_begin; v98 != v98_end; v98++) { v98->CheckNodeDistance(step, hsc.x); v98->CheckFloorCollision(floor_height); } } if (has_children_node) { RobJointNode* v99_begin = joint_node_vec.data() + 1; RobJointNode* v99_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* v99 = v99_begin; v99 != v99_end; v99++) { vec3 direction; mat4_inverse_transform_point(&v130, &v99->pos, &direction); bool rot_clamped = make_axis_matrix(v130, direction, &v99->data_ptr->skp_osg_node.hinge, &v99->reset_data.rotation, rottype); *v99->dst_node->no_scale_mat = v130; v99->TranslateMat(v130, rot_clamped, hsc.x); } if (joint_node_vec.size() && effector.dst_node_mat) { mat4 mat = *joint_node_vec.back().dst_node->no_scale_mat; mat4_mul_translate_x(&mat, effector.length_back* hsc.x, &mat); *effector.dst_node->no_scale_mat = mat; } } apply_physics = false; } // 0x14047EE90 void RobOsage::ApplyResetData(const mat4& mat) { if (reset_data_list) { auto reset_data = reset_data_list->begin(); RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) i->reset_data = *reset_data++; reset_data_list = 0; } RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) { mat4_transform_vector(&mat, &i->reset_data.vec, &i->vec); mat4_transform_point(&mat, &i->reset_data.pos, &i->pos); } root_matrix_prev = *root_matrix_ptr; } // 0x1404803B0 void RobOsage::BeginCalc(const mat4& root_matrix, const vec3& hsc, bool has_children_node) { mat4 mat = root_matrix; const vec3 pos = transform.pos * hsc; mat4_transform_point(&mat, &pos, &joint_node_vec.data()[0].pos); if (osage_reset && !osage_reset_done) { osage_reset_done = true; ApplyResetData(root_matrix); } if (!move_cancelled) { move_cancelled = true; float_t move_cancel = skin_param_ptr->move_cancel; if (move_cancel == 1.0f || move_cancel < 0.0f) move_cancel = this->move_cancel; if (move_cancel > 0.0f) { RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) { vec3 pos; mat4_inverse_transform_point(&root_matrix_prev, &i->pos, &pos); mat4_transform_point(root_matrix_ptr, &pos, &pos); i->pos += (pos - i->pos) * move_cancel; } } } if (!has_children_node) return; RotateMat(mat, hsc); *joint_node_vec.data()[0].dst_node_mat = mat; RobJointNode* v30_begin = joint_node_vec.data() + 1; RobJointNode* v30_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* v30 = v30_begin; v30 != v30_end; v30++) { vec3 direction; mat4_inverse_transform_point(&mat, &v30->pos, &direction); bool rot_clamped = make_axis_matrix(mat, direction, &v30->data_ptr->skp_osg_node.hinge, &v30->reset_data.rotation, rottype); *v30->dst_node->no_scale_mat = mat; v30->TranslateMat(mat, rot_clamped, hsc.x); } if (joint_node_vec.size() && effector.dst_node_mat) { mat4 mat = *joint_node_vec.back().dst_node->no_scale_mat; mat4_mul_translate_x(&mat, effector.length_back * hsc.x, &mat); *effector.dst_node->no_scale_mat = mat; } } bool RobOsage::CheckPartsBits(const rob_osage_parts_bit& parts_bits) { if (osage_setting.parts != ROB_OSAGE_PARTS_NONE) return !!(parts_bits & (1 << osage_setting.parts)); return false; } // 0x14047ECA0 void RobOsage::CollideNodes(const float_t step) { if (get_pause() || step <= 0.0f) return; const OsageCollision::Work* coli_chara = this->coli_chara; const OsageCollision::Work* coli_ring = this->coli_ring; const OsageCollision& coli_object = ring.coli_object; RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) { skin_param_osage_node* skp_osg_node = &i->data_ptr->skp_osg_node; i->hit += (float_t)OsageCollision::osage_cls_work_list(i->pos, skp_osg_node->coli_r, coli_object, &i->friction); if (disable_collision) continue; for (RobJointNode*& j : i->data_ptr->boc) { j->hit += (float_t)OsageCollision::osage_cls(coli_ring, j->pos, skp_osg_node->coli_r); j->hit += (float_t)OsageCollision::osage_cls(coli_chara, j->pos, skp_osg_node->coli_r); } i->hit += (float_t)OsageCollision::osage_cls(coli_ring, i->pos, skp_osg_node->coli_r); i->hit += (float_t)OsageCollision::osage_cls(coli_chara, i->pos, skp_osg_node->coli_r); } } // 0x14047D8C0 void RobOsage::CollideNodesTargetOsage(const mat4& root_matrix, const vec3& hsc, const float_t step, bool collide_nodes) { if (!osage_reset && (get_pause() || step <= 0.0f)) return; const OsageCollision::Work* coli_chara = this->coli_chara; const OsageCollision::Work* coli_ring = this->coli_ring; const OsageCollision& coli_object = ring.coli_object; const float_t inv_step = step > 0.0f ? 1.0f / step : 0.0f; mat4 v64; if (collide_nodes) v64 = *joint_node_vec.data()[0].dst_node_mat; else { v64 = root_matrix; const vec3 pos = transform.pos * hsc; mat4_transform_point(&v64, &pos, &joint_node_vec.data()[0].pos); RotateMat(v64, hsc); } float_t floor_height = -1000.0f; if (collide_nodes) { RobJointNode* node = &joint_node_vec.data()[0]; floor_height = ring.get_floor_height( node->pos, node->data_ptr->skp_osg_node.coli_r); } const float_t v23 = step < 1.0f ? 0.2f / (2.0f - step) : 0.2f; if (skin_param_ptr->colli_tgt_osg) { std::vector* colli_tgt_osg = skin_param_ptr->colli_tgt_osg; RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) { vec3 v62 = 0.0f; RobJointNode* j_begin = colli_tgt_osg->data() + 1; RobJointNode* j_end = colli_tgt_osg->data() + colli_tgt_osg->size(); for (RobJointNode* j = j_begin; j != j_end; j++) OsageCollision::cls_ball_oidashi(v62, i->pos, j->pos, i->data_ptr->skp_osg_node.coli_r + j->data_ptr->skp_osg_node.coli_r); i->pos += v62; } } RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) { const float_t fric = (1.0f - wet) * skin_param_ptr->friction; if (collide_nodes) { i->CheckNodeDistance(step, hsc.x); i->hit += (float_t)OsageCollision::osage_cls_work_list(i->pos, i->data_ptr->skp_osg_node.coli_r, coli_object, &i->friction); if (!disable_collision) { i->hit += (float_t)OsageCollision::osage_cls(coli_ring, i->pos, i->data_ptr->skp_osg_node.coli_r); i->hit += (float_t)OsageCollision::osage_cls(coli_chara, i->pos, i->data_ptr->skp_osg_node.coli_r); } i->CheckFloorCollision(floor_height); } else length_limit(i->pos, i->GetPrevNode().pos, i->length_back * hsc.x); vec3 direction; mat4_inverse_transform_point(&v64, &i->pos, &direction); bool rot_clamped = make_axis_matrix(v64, direction, &i->data_ptr->skp_osg_node.hinge, &i->reset_data.rotation, rottype); i->dst_node->transform.hsc = hsc; *i->dst_node->no_scale_mat = v64; if (i->dst_node_mat) mat4_scale_rot(&v64, &hsc, i->dst_node_mat); i->reset_data.length = i->TranslateMat(v64, rot_clamped, hsc.x); i->vec = (i->pos - i->old_pos) * inv_step; if (i->hit > 0.0f) i->vec *= min_def(fric, i->friction); const float_t v55 = vec3::length_squared(i->vec); if (v55 > v23 * v23) i->vec *= v23 / sqrtf(v55); mat4_inverse_transform_point(&root_matrix, &i->pos, &i->reset_data.pos); mat4_inverse_transform_vector(&root_matrix, &i->vec, &i->reset_data.vec); } if (joint_node_vec.size() && effector.dst_node_mat) { mat4 mat = *joint_node_vec.back().dst_node->no_scale_mat; mat4_mul_translate_x(&mat, effector.length_back * hsc.x, &mat); *effector.dst_node->no_scale_mat = mat; mat4_scale_rot(&mat, &hsc, &mat); *effector.dst_node_mat = mat; effector.dst_node->transform.hsc = hsc; } } // 0x14047E1C0 void RobOsage::CtrlEnd(const vec3& hsc) { RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) if (i->data_ptr->normal_ref.set) { i->data_ptr->normal_ref.GetMat(i->dst_node_mat); mat4_scale_rot(i->dst_node_mat, &hsc, i->dst_node_mat); } } // 0x14047E240 void RobOsage::CtrlOsagePlayData(const mat4& root_matrix, const vec3& hsc, const std::vector& opd_blend_data) { if (!opd_blend_data.size()) return; const vec3 v63 = transform.pos * hsc; mat4 v85 = root_matrix; mat4_transform_point(&v85, &v63, &joint_node_vec.data()[0].pos); RotateMat(v85, hsc); *joint_node_vec.data()[0].dst_node_mat = v85; *joint_node_vec.data()[0].dst_node->no_scale_mat = v85; const ::opd_blend_data* i_begin = opd_blend_data.data() + opd_blend_data.size(); const ::opd_blend_data* i_end = opd_blend_data.data(); for (const ::opd_blend_data* i = i_begin; i != i_end; ) { i--; float_t frame = i->frame; #if OPD_PLAY_GEN if (frame >= i->frame_max) frame = i->no_loop ? i->frame_max - 1.0f : 0.0f; #else if (frame >= i->frame_max) frame = 0.0f; #endif int32_t curr_key = (int32_t)(int64_t)prj::floorf(frame); int32_t next_key = curr_key + 1; #if OPD_PLAY_GEN if ((float_t)next_key >= i->frame_max) next_key = i->no_loop ? (int32_t)i->frame_max - 1 : 0; #else if ((float_t)next_key >= i->frame_max) next_key = 0; #endif float_t blend = frame - (float_t)(int64_t)frame; float_t inv_blend = 1.0f - blend; mat4 v87 = v85; vec3 parent_curr_trans = joint_node_vec.data()[0].pos; vec3 parent_next_trans = joint_node_vec.data()[0].pos; RobJointNode* j_begin = joint_node_vec.data() + 1; RobJointNode* j_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* j = j_begin; j != j_end; j++) { opd_vec3_data* opd = &j->opd_data[i - i_end]; const float_t* opd_x = opd->x; const float_t* opd_y = opd->y; const float_t* opd_z = opd->z; vec3 curr_trans; curr_trans.x = opd_x[curr_key]; curr_trans.y = opd_y[curr_key]; curr_trans.z = opd_z[curr_key]; vec3 next_trans; next_trans.x = opd_x[next_key]; next_trans.y = opd_y[next_key]; next_trans.z = opd_z[next_key]; mat4_transform_point(&root_matrix, &curr_trans, &curr_trans); mat4_transform_point(&root_matrix, &next_trans, &next_trans); vec3 _trans = curr_trans * inv_blend + next_trans * blend; vec3 direction; mat4_inverse_transform_point(&v87, &_trans, &direction); vec3 rotation = 0.0f; make_axis_matrix(v87, direction, 0, &rotation, rottype); mat4_set_translation(&v87, &_trans); float_t length = vec3::distance(curr_trans, parent_curr_trans) * inv_blend + vec3::distance(next_trans, parent_next_trans) * blend; j->opd_node_data.set_data(i, { length, rotation }); parent_curr_trans = curr_trans; parent_next_trans = next_trans; } } RobJointNode* j_begin = joint_node_vec.data() + 1; RobJointNode* j_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* j = j_begin; j != j_end; j++) { float_t rot_y = j->opd_node_data.curr.rotation.y; float_t rot_z = j->opd_node_data.curr.rotation.z; rot_y = clamp_def(rot_y, (float_t)-M_PI, (float_t)M_PI); rot_z = clamp_def(rot_z, (float_t)-M_PI, (float_t)M_PI); mat4_mul_rotate_z(&v85, rot_z, &v85); mat4_mul_rotate_y(&v85, rot_y, &v85); j->dst_node->transform.hsc = hsc; *j->dst_node->no_scale_mat = v85; if (j->dst_node_mat) mat4_scale_rot(&v85, &hsc, j->dst_node_mat); mat4_mul_translate(&v85, j->opd_node_data.curr.length, 0.0f, 0.0f, &v85); j->old_pos = j->pos; mat4_get_translation(&v85, &j->pos); } if (joint_node_vec.size() && effector.dst_node_mat) { mat4 mat = *joint_node_vec.back().dst_node->no_scale_mat; mat4_mul_translate_x(&mat, effector.length_back * hsc.x, &mat); *effector.dst_node->no_scale_mat = mat; mat4_scale_rot(&mat, &hsc, &mat); *effector.dst_node_mat = mat; effector.dst_node->transform.hsc = hsc; } } // 0x140480260 void RobOsage::EndCalc(const mat4& root_matrix, const vec3& hsc, const float_t step, bool disable_ex_force) { if (!disable_ex_force) { SetNodesExternalForce(0, 1.0f); SetNodesForce(1.0f); use_ex_force = false; ex_force = 0.0f; } apply_physics = true; field_2A1 = false; field_2A4 = -1.0f; move_cancelled = false; osage_reset = false; osage_reset_done = false; for (RobJointNode& i : joint_node_vec) { i.hit = 0.0f; i.friction = 1.0f; } root_matrix_prev = *root_matrix_ptr; } const float_t* RobOsage::LoadOpdData(size_t node_index, const float_t* opd_data, size_t opd_count) { RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) { if (node_index >= i->opd_data.size()) i->opd_data.resize(node_index + 1); opd_vec3_data* opd = &i->opd_data[node_index]; opd->x = opd_data; opd_data += opd_count; opd->y = opd_data; opd_data += opd_count; opd->z = opd_data; opd_data += opd_count; } return opd_data; } void RobOsage::LoadSkinParam(void* kv, const char* name, skin_param_osage_root& skp_root, const object_info& obj_info, const bone_database* bone_data) { key_val* _kv = (key_val*)kv; skin_param_ptr = &skin_param; for (RobJointNode& i : joint_node_vec) i.data_ptr = &i.data; skin_param_osage_root_parse(_kv, name, skp_root, bone_data); SetSkinParamOsageRoot(skp_root); const osage_setting_osg_cat* osage_setting = osage_setting_data_get_cat_value(obj_info, name); if (osage_setting) this->osage_setting = *osage_setting; std::vector vec; vec.resize(joint_node_vec.size() - 1); skin_param_osage_node_parse(_kv, name, &vec, skp_root); SetSkpOsgNodes(&vec); } // 0x14047F110 void RobOsage::RotateMat(mat4& mat, const vec3& hsc, bool init_rot) { const vec3 position = transform.pos * hsc; mat4_mul_translate(&mat, &position, &mat); mat4_mul_rotate_zyx(&mat, &transform.rot, &mat); const vec3 rot = init_rot ? skin_param_ptr->init_rot + skin_param_ptr->rot : skin_param_ptr->rot; mat4_mul_rotate_zyx(&mat, &rot, &mat); } // 0x140481090 void RobOsage::SetDisableCollision(const bool& value) { disable_collision = value; } void RobOsage::SetMotionResetData(const uint32_t& motnum, const float_t& frame) { osage_reset = true; auto elem = motion_reset_data.find({ motnum, (int32_t)prj::roundf(frame * 1000.0f) }); if (elem != motion_reset_data.end() && elem->second.size() + 1 == joint_node_vec.size()) reset_data_list = &elem->second; } // 0x140480F40 void RobOsage::SetNodesExternalForce(const vec3* ex_force, const float_t& gain) { if (!ex_force) { RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) i->ex_force = 0.0f; return; } vec3 _ex_force = *ex_force; for (size_t i = osage_setting.exf; i; i--) _ex_force *= gain; RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) { i->ex_force = _ex_force; _ex_force *= gain; } } // 0x140481540 void RobOsage::SetNodesForce(const float_t& force) { RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) i->force = force; } const float_t* RobOsage::SetOsagePlayDataInit(const float_t* opdi_data) { RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) { i->pos.x = *opdi_data++; i->pos.y = *opdi_data++; i->pos.z = *opdi_data++; i->vec.x = *opdi_data++; i->vec.y = *opdi_data++; i->vec.z = *opdi_data++; i->old_pos = i->pos; } return opdi_data; } // 0x1405F9A90, but it's combined with ExOsageBlock::SetOsageReset void RobOsage::SetOsageReset() { osage_reset = true; } void RobOsage::SetRing(const osage_ring_data& ring) { this->ring = ring; } void RobOsage::SetSkinParam(skin_param_file_data* skp) { if (skp->nodes_data.size() == joint_node_vec.size() - 1) { skin_param_ptr = &skp->skin_param; size_t node_index = 0; RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) i->data_ptr = &skp->nodes_data[node_index++]; } else { skin_param_ptr = &skin_param; for (RobJointNode& i : joint_node_vec) i.data_ptr = &i.data; } } void RobOsage::SetSkinParamOsageRoot(const skin_param_osage_root& skp_root) { skin_param_ptr->reset(); set_force(skp_root.force, skp_root.force_gain); set_air_res(skp_root.air_res); set_angle(skp_root.rot_y, skp_root.rot_z); set_init_angle(skp_root.init_rot_y, skp_root.init_rot_z); set_rot_type(skp_root.rottype); skin_param_ptr->friction = skp_root.friction; skin_param_ptr->wind_afc = skp_root.wind_afc; set_coli_r(skp_root.coli_r); set_limit_angle(skp_root.hinge_y, skp_root.hinge_z); skin_param_ptr->coli.assign(skp_root.coli.begin(), skp_root.coli.end()); skin_param_ptr->coli_type = skp_root.coli_type; skin_param_ptr->stiffness = skp_root.stiffness; skin_param_ptr->move_cancel = skp_root.move_cancel; } void RobOsage::SetSkpOsgNodes(const std::vector* skp_osg_nodes) { if (!skp_osg_nodes) return; if (joint_node_vec.size() - 1 != skp_osg_nodes->size()) return; RobJointNode* i = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); const skin_param_osage_node* j = skp_osg_nodes->data(); const skin_param_osage_node* j_end = skp_osg_nodes->data() + skp_osg_nodes->size(); while (i != i_end && j != j_end) { i->data_ptr->skp_osg_node = *j++; i->data_ptr->skp_osg_node.hinge.limit(); i++; } } void RobOsage::SetWindDirection(const vec3* value) { if (value) wind_dir = *value; else wind_dir = 0.0f; } // 0x14047C750 void RobOsage::ctrl(const mat4& root_matrix, const vec3& hsc, const float_t step) { pos_init_cont(root_matrix, hsc, step, false); } // 0x14047F200 void RobOsage::dest() { joint_node_vec.clear(); effector.reset(); wind_dir = 0.0f; wet = 0.0f; rottype = ROTTYPE_YZ; apply_physics = true; motion_reset_data.clear(); move_cancel = 0.0f; move_cancelled = false; osage_reset = false; osage_reset_done = false; ring = osage_ring_data(); disable_collision = false; skin_param.reset(); skin_param_ptr = &skin_param; osage_setting = osage_setting_osg_cat(); reset_data_list = 0; field_2A4 = 0.0f; field_2A1 = false; use_ex_force = false; ex_force = 0.0f; root_matrix_ptr = 0; root_matrix_prev = mat4_null; } // 0x1404808C0 void RobOsage::dest_boc() { RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) i->data_ptr->boc.clear(); } // 0x140482AA0 RobJointNode* RobOsage::get_joint_node(size_t index) { if (index < joint_node_vec.size()) return &joint_node_vec.data()[index]; else return &effector; } // 0x14047F370 void RobOsage::init(const obj_skin_ex_node_osage* osg_data, const obj_skin_osage_joint* joint, RobNode* ex_node, const obj_skin* skin) { dest(); transform.init(osg_data->transform.position, osg_data->transform.rotation); joint_node_vec.clear(); joint_node_vec.resize(osg_data->nb_joint + 1ULL); RobJointNode* node = joint_node_vec.data(); node->reset(); node->length_next = joint->length; RobNode* root_node = &ex_node[osg_data->root_idx & 0x7FFF]; node->dst_node = root_node; node->dst_node_mat = root_node->mat_ptr; *root_node->no_scale_mat = mat4_identity; node->distance = 0; RobNode* parent_node_ptr = node->dst_node; for (uint32_t i = 0; i < osg_data->nb_joint; i++) { const obj_skin_osage_joint* osg_node = &joint[i]; RobJointNode* node = &joint_node_vec.data()[i + 1LL]; node->reset(); node->length_back = osg_node[0].length; if (i + 1 < osg_data->nb_joint) node->length_next = osg_node[1].length; RobNode* node_ptr = &ex_node[osg_node->nid & 0x7FFF]; node->dst_node = node_ptr; node->dst_node_mat = node_ptr->mat_ptr; node_ptr->parent = parent_node_ptr; parent_node_ptr = node->dst_node; } effector.reset(); effector.length_back = joint[osg_data->nb_joint - 1].length; RobNode* effector_node = &ex_node[osg_data->efc_idx & 0x7FFF]; effector.dst_node = effector_node; effector.dst_node_mat = effector_node->mat_ptr; effector_node->parent = parent_node_ptr; *effector.dst_node->no_scale_mat = mat4_identity; if (osg_data->nb_joint) { obj_skin_osage_joint_rotation* joint_rotation = osg_data->joint_rotation_array; RobJointNode* node = &joint_node_vec.data()[1]; if (joint_rotation) for (uint32_t i = 0; i < osg_data->nb_joint; i++, joint_rotation++, node++) { mat3 mat; mat3_rotate_zyx(&joint_rotation->rotation, &mat); const vec3 rel_pos = { node->length_back, 0.0f, 0.0f }; mat3_transform_vector(&mat, &rel_pos, &node->rel_pos); } else for (uint32_t i = 0; i < osg_data->nb_joint; i++, node++) node->rel_pos = { node->length_back, 0.0f, 0.0f }; } if (osg_data->nb_joint) { RobJointNode* node = &joint_node_vec.data()[1]; size_t v33 = 0; for (uint32_t i = 0; i < osg_data->nb_joint; i++, node++) { node->mat = mat4_null; obj_skin_bone* bone = skin->bone_array; for (int32_t j = 0; j < skin->num_bone; j++, bone++) if (bone->id == joint->nid) { mat4_invert_fast(&bone->inv_bind_pose_mat, &node->mat); break; } joint++; } } RobJointNode* last_node = &joint_node_vec.back(); if (memcmp(&last_node->mat, &mat4_null, sizeof(mat4))) mat4_mul_translate(&last_node->mat, effector.length_back, 0.0f, 0.0f, &effector.mat); } // 0x1404816B0 void RobOsage::make_osage_coli(const mat4* motmat) { if (skin_param_ptr->coli.size()) OsageCollision::Work::update_cls_work(coli_chara, skin_param_ptr->coli.data(), motmat); OsageCollision::Work::update_cls_work(coli_ring, ring.skp_root_coli, motmat); } // 0x14047F990 void RobOsage::pos_init(const mat4& root_matrix, const vec3& hsc, bool dist_flag) { if (!joint_node_vec.size()) return; vec3 root_pos = transform.pos * hsc; mat4_transform_point(&root_matrix, &root_pos, &root_pos); RobJointNode* v12 = &joint_node_vec.data()[0]; v12->pos = root_pos; v12->old_pos = root_pos; v12->vec = 0.0f; RobJointNode* node = &joint_node_vec.data()[0]; const float_t floor_height = ring.get_floor_height( node->pos, node->data_ptr->skp_osg_node.coli_r); mat4 v78 = root_matrix; RotateMat(v78, hsc, true); vec3 v60 = { 1.0f, 0.0f, 0.0f }; mat4_transform_vector(&v78, &v60, &v60); RobJointNode* i_begin = this->joint_node_vec.data() + 1; RobJointNode* i_end = this->joint_node_vec.data() + this->joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) { vec3 v74 = i->GetPrevNode().pos + vec3::normalize(v60) * (i->length_back * hsc.x); if (dist_flag && i->distance) length_limit(v74, i->distance->pos, i->length_dist); skin_param_osage_node* v38 = &i->data_ptr->skp_osg_node; OsageCollision::osage_cls(coli_ring, v74, v38->coli_r); OsageCollision::osage_cls(coli_chara, v74, v38->coli_r); const float_t v39 = floor_height + v38->coli_r; if (v74.y < v39 && v39 < 1001.0f) { v74.y = v39; v74 = vec3::normalize(v74 - i->GetPrevNode().pos) * (i->length_back * hsc.x) + i->GetPrevNode().pos; } i->pos = v74; i->vec = 0.0f; vec3 direction; mat4_inverse_transform_point(&v78, &i->pos, &direction); make_axis_matrix(v78, direction, &i->data_ptr->skp_osg_node.hinge, &i->reset_data.rotation, rottype); i->dst_node->transform.hsc = hsc; *i->dst_node->no_scale_mat = v78; if (i->dst_node_mat) mat4_scale_rot(&v78, &hsc, i->dst_node_mat); float_t v55 = vec3::distance(i->pos, i->GetPrevNode().pos); float_t v56 = i->length_back * hsc.x; if (v55 >= fabsf(v56)) v56 = v55; mat4_mul_translate(&v78, v56, 0.0f, 0.0f, &v78); mat4_get_translation(&v78, &i->pos); v60 = i->pos - i->GetPrevNode().pos; } if (joint_node_vec.size() && effector.dst_node_mat) { mat4 mat = *joint_node_vec.back().dst_node->no_scale_mat; mat4_mul_translate_x(&mat, effector.length_back * hsc.x, &mat); *effector.dst_node->no_scale_mat = mat; mat4_scale_rot(&mat, &hsc, &mat); *effector.dst_node_mat = mat; effector.dst_node->transform.hsc = hsc; } } // 0x14047C770 void RobOsage::pos_init_cont(const mat4& root_matrix, const vec3& hsc, const float_t step, bool disable_ex_force) { ApplyPhysics(root_matrix, hsc, step, disable_ex_force, false, false); CollideNodesTargetOsage(root_matrix, hsc, step, true); EndCalc(root_matrix, hsc, step, disable_ex_force); } // 0x1404808F0 void RobOsage::reset_ex_force() { use_ex_force = false; ex_force = 0.0f; } // 0x140480C60 void RobOsage::set_air_res(float_t air) { skin_param_ptr->air_res = air; } // 0x140480C70 void RobOsage::set_angle(float_t angle_y, float_t angle_z) { skin_param_ptr->rot.y = angle_y * DEG_TO_RAD_FLOAT; skin_param_ptr->rot.z = angle_z * DEG_TO_RAD_FLOAT; } // 0x140480D30 void RobOsage::set_coli_r(float_t coli_r) { RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) i->data_ptr->skp_osg_node.coli_r = coli_r; } // 0x140480F10 void RobOsage::set_ex_force(const vec3& f) { use_ex_force = true; ex_force = f; } // 0x140481040 void RobOsage::set_force(float_t force, float_t gain) { skin_param_ptr->force = force; skin_param_ptr->force_gain = gain; RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) { i->data_ptr->force = force; force = force * gain; } } // 0x1404810A0 void RobOsage::set_init_angle(float_t angle_y, float_t angle_z) { skin_param_ptr->init_rot.y = angle_y * DEG_TO_RAD_FLOAT; skin_param_ptr->init_rot.z = angle_z * DEG_TO_RAD_FLOAT; } // 0x140480CA0 void RobOsage::set_limit_angle(float_t angle_y, float_t angle_z) { angle_y = min_def(angle_y, 179.0f) * DEG_TO_RAD_FLOAT; angle_z = min_def(angle_z, 179.0f) * DEG_TO_RAD_FLOAT; RobJointNode* i_begin = joint_node_vec.data() + 1; RobJointNode* i_end = joint_node_vec.data() + joint_node_vec.size(); for (RobJointNode* i = i_begin; i != i_end; i++) { skin_param_osage_node* skp_osg_node = &i->data_ptr->skp_osg_node; skp_osg_node->hinge.ymin = -angle_y; skp_osg_node->hinge.ymax = angle_y; skp_osg_node->hinge.zmin = -angle_z; skp_osg_node->hinge.zmax = angle_z; } } // 0x1404810D0 void RobOsage::set_move_cancel(const float_t& mv_ccl) { move_cancel = mv_ccl; } // 0x1404814C0 void RobOsage::set_rot_type(ROTTYPE rot_type) { rottype = rot_type; } ExOsageBlock::ExOsageBlock() : motion_matrix(), flag() { init_coma_step = 1.0f; dest(); } ExOsageBlock::~ExOsageBlock() { dest(); } void ExOsageBlock::init() { dest(); } void ExOsageBlock::CtrlBegin() { done = false; } void ExOsageBlock::CtrlStep(int32_t stage, bool disable_ex_force) { const RobDisp* rob_disp = skin_disp->rob_disp; float_t delta_frame = get_delta_frame(); #if OPD_PLAY_GEN if (opd_play_gen_run) delta_frame = 1.0f; #endif float_t step = delta_frame * rob_disp->osage_step; if (rob_disp->opd_blend_data.size() && rob_disp->opd_blend_data.front().use_blend) step = 1.0f; const mat4& root_matrix = *parent->no_scale_mat; const vec3 hsc = parent->transform.hsc; switch (stage) { case 0: osage_work.BeginCalc(root_matrix, hsc, has_children_node); break; case 1: case 2: if ((stage == 1 && is_parent) || (stage == 2 && osage_work.apply_physics)) { SetWindDirection(); osage_work.ApplyPhysics(root_matrix, hsc, step, disable_ex_force, true, has_children_node); } break; case 3: osage_work.make_osage_coli(motion_matrix); osage_work.CollideNodes(step); break; case 4: osage_work.ApplyBocRootColi(step); break; case 5: { osage_work.CollideNodesTargetOsage(root_matrix, hsc, step, false); osage_work.EndCalc(root_matrix, hsc, step, disable_ex_force); done = true; } break; } } void ExOsageBlock::ctrl() { flag.pos_init = 0; if (done) { done = false; return; } const RobDisp* rob_disp = skin_disp->rob_disp; float_t delta_frame = get_delta_frame(); #if OPD_PLAY_GEN if (opd_play_gen_run) delta_frame = 1.0f; #endif float_t step = delta_frame * rob_disp->osage_step; if (rob_disp->opd_blend_data.size() && rob_disp->opd_blend_data.front().use_blend) step = 1.0f; const vec3 hsc = parent->transform.hsc; vec3 scale = parent->transform.scale; mat4 root_matrix = *parent->no_scale_mat; if (scale.x != 1.0f || scale.y != 1.0f || scale.z != 1.0f) { scale = 1.0f / scale; mat4_scale_rot(&root_matrix, &scale, &root_matrix); } SetWindDirection(); osage_work.make_osage_coli(motion_matrix); osage_work.ctrl(root_matrix, hsc, step); } void ExOsageBlock::CtrlOsagePlayData() { osage_work.CtrlOsagePlayData(*parent->no_scale_mat, parent->transform.hsc, skin_disp->rob_disp->opd_blend_data); } void ExOsageBlock::disp(const mat4& mat, render_context* rctx) { } void ExOsageBlock::dest() { block_idx = 0; osage_work.dest(); flag.alive = 0; flag.pos_init = 0; motion_matrix = 0; init_coma_step = 1.0f; ExNodeBlock::dest(); } void ExOsageBlock::disp_debug() { } void ExOsageBlock::pos_init() { init_coma_step = 4.0f; SetWindDirection(); osage_work.make_osage_coli(motion_matrix); const vec3 hsc = parent->transform.hsc; osage_work.pos_init(*parent->no_scale_mat, hsc, false); flag.pos_init = 0; } void ExOsageBlock::pos_init_cont() { if (done) { done = false; return; } SetWindDirection(); const vec3 hsc = parent->transform.hsc; osage_work.make_osage_coli(motion_matrix); osage_work.pos_init_cont(*parent->no_scale_mat, hsc, init_coma_step, true); init_coma_step = max_def(init_coma_step * 0.5f, 1.0f); } void ExOsageBlock::CtrlEnd() { const vec3 hsc = parent->transform.hsc; osage_work.CtrlEnd(hsc); done = false; } void ExOsageBlock::AddMotionResetData(const uint32_t& motnum, const float_t& frame) { osage_work.AddMotionResetData(motnum, frame); } const float_t* ExOsageBlock::LoadOpdData(size_t node_index, const float_t* opd_data, size_t opd_count) { return osage_work.LoadOpdData(node_index, opd_data, opd_count); } // 0x1405F9510 void ExOsageBlock::SetDisableCollision(const bool& value) { osage_work.SetDisableCollision(value); } void ExOsageBlock::SetMotionResetData(const uint32_t& motnum, const float_t& frame) { osage_work.SetMotionResetData(motnum, frame); } const float_t* ExOsageBlock::SetOsagePlayDataInit(const float_t* opdi_data) { return osage_work.SetOsagePlayDataInit(opdi_data); } // 0x1405F9A90, but it's combined with RobOsage::SetOsageReset void ExOsageBlock::SetOsageReset() { osage_work.SetOsageReset(); } void ExOsageBlock::SetRing(const osage_ring_data& ring) { osage_work.SetRing(ring); } void ExOsageBlock::SetSkinParam(skin_param_file_data* skp) { osage_work.SetSkinParam(skp); } void ExOsageBlock::SetWindDirection() { vec3 wind_dir = task_wind->stage_wind.wind->val * skin_disp->rob_disp->wind_strength; osage_work.SetWindDirection(&wind_dir); } // 0x1405F3E10 void ExOsageBlock::make_joint_map(const obj_skin_ex_node_osage* root, const obj_skin_osage_joint* joint, prj::vector_pair& joint_map, std::map& node_name_map) { RobJointNode* root_node = osage_work.get_joint_node(0); joint_map.push_back(root->root_idx, root_node); for (uint32_t i = 0; i < root->nb_joint; i++) { RobJointNode* node = osage_work.get_joint_node(i + 1ULL); joint_map.push_back(joint[i].nid, node); if (node->dst_node && node->dst_node->name) node_name_map.insert({ node->dst_node->name, this }); } RobJointNode* effector_node = osage_work.get_joint_node(root->nb_joint + 1ULL); joint_map.push_back(root->efc_idx, effector_node); if (effector_node->dst_node && effector_node->dst_node->name) node_name_map.insert({ effector_node->dst_node->name, this }); } void ExOsageBlock::reset_ex_force() { osage_work.reset_ex_force(); } // 0x1405F7E10 void ExOsageBlock::set_data(const RobSkinDisp* skp, const obj_skin_ex_node_osage* root, const obj_skin_osage_joint* joint, const RobNode* mot_node, RobNode* ex_node, const obj_skin* skin) { ExNodeBlock::set_data(&ex_node[root->root_idx & 0x7FFF], EX_NODE_TYPE_OSAGE, 0, skp); osage_work.init(root, joint, ex_node, skin); flag.pos_init = 0; motion_matrix = mot_node->mat_ptr; } void ExOsageBlock::set_ex_force(const vec3& f) { osage_work.set_ex_force(f); } // 0x1405F9290 void ExOsageBlock::set_move_cancel(const float_t& mv_ccl) { osage_work.set_move_cancel(mv_ccl); } ExConstraintBlock::ExConstraintBlock() : cns_type(), src_node(), upvector_node(), data(), dst_node_id(), src_node_id() { init(); } ExConstraintBlock::~ExConstraintBlock() { } void ExConstraintBlock::init() { cns_type = OBJ_SKIN_EX_NODE_CONSTRAINT_NONE; src_node = 0; upvector_node = 0; data = 0; dst_node_id = 0; src_node_id = 0; } void ExConstraintBlock::CtrlBegin() { if (dst_node) { RobTransform& transform = dst_node->transform; transform.pos = data->transform.position; transform.rot = data->transform.rotation; transform.scale = data->transform.scale; } done = false; } void ExConstraintBlock::CtrlStep(int32_t stage, bool disable_ex_force) { if (done) return; switch (stage) { case 0: if (is_parent) ctrl(); break; case 2: if (has_children_node) CalcMatrixHS(); break; case 5: ctrl(); break; } } void ExConstraintBlock::ctrl() { if (!parent) return; if (done) { done = false; return; } Calc(); CalcMatrixHS(); done = true; } void ExConstraintBlock::CtrlOsagePlayData() { ctrl(); } void ExConstraintBlock::disp(const mat4& mat, render_context* rctx) { } void ExConstraintBlock::disp_debug() { } void ExConstraintBlock::pos_init() { ctrl(); } void ExConstraintBlock::pos_init_cont() { ctrl(); } void ExConstraintBlock::Calc() { RobNode* node = dst_node; if (!node) return; vec3 pos = parent->transform.hsc * node->transform.pos; mat4 mat; mat4_mul_translate(parent->no_scale_mat, &pos, &mat); switch (cns_type) { case OBJ_SKIN_EX_NODE_CONSTRAINT_ORIENTATION: CalcConstraintOrientation(mat); break; case OBJ_SKIN_EX_NODE_CONSTRAINT_DIRECTION: CalcConstraintDirection(mat); break; case OBJ_SKIN_EX_NODE_CONSTRAINT_POSITION: CalcConstraintPosition(mat); break; case OBJ_SKIN_EX_NODE_CONSTRAINT_DISTANCE: CalcConstraintDistance(mat); break; default: *node->mat_ptr = mat; break; } } inline void ExConstraintBlock::CalcConstraintDirection(mat4 mat) { obj_skin_ex_node_constraint_direction* direction = data->direction; vec3 align_axis = direction->align_axis; vec3 target_offset = direction->target_offset; vec3 world_offset; mat4_transform_point(src_node->mat_ptr, &target_offset, &world_offset); vec3 v51; mat4_inverse_transform_point(&mat, &world_offset, &v51); float_t v51_length = vec3::length_squared(v51); if (v51_length <= 0.000001f) return; mat4 rot_mat; make_direction_matrix(rot_mat, align_axis, v51); if (upvector_node) { vec3 affected_axis = direction->up_vector.affected_axis; mat4 dir_mat; mat4_mul(&rot_mat, &mat, &dir_mat); vec3 dir_axis; mat4_transform_vector(&dir_mat, &affected_axis, &dir_axis); mat4* up_mat = upvector_node->mat_ptr; vec3 up_vector_trans; vec3 post_dir_trans; mat4_get_translation(up_mat, &up_vector_trans); mat4_get_translation(&dir_mat, &post_dir_trans); vec3 up_vector_off = up_vector_trans - post_dir_trans; vec3 v50; mat4_transform_vector(&mat, &v51, &v50); vec3 v25 = vec3::normalize(vec3::cross(v50, dir_axis)); vec3 v29 = vec3::normalize(vec3::cross(v50, up_vector_off)); vec3 v35 = vec3::cross(v25, v29); float_t v39 = vec3::dot(v25, v29); float_t v36 = vec3::dot(v35, v50); float_t v40 = vec3::length(v35); if (v36 >= 0.0f) v40 = -v40; mat4 up_corr_mat; mat4_set(&v51, -v40, v39, &up_corr_mat); mat4_mul(&rot_mat, &up_corr_mat, &rot_mat); } mat4_mul(&rot_mat, &mat, dst_node->mat_ptr); } void ExConstraintBlock::CalcConstraintDistance(mat4 mat) { *dst_node->mat_ptr = mat; } void ExConstraintBlock::CalcConstraintOrientation(mat4 mat) { obj_skin_ex_node_constraint_orientation* orientation = data->orientation; mat3 rot; mat4_to_mat3(src_node->mat_ptr, &rot); mat3_normalize_rotation(&rot, &rot); mat3_mul_rotate_zyx(&rot, &orientation->offset, &rot); mat4_replace_rotation(&mat, &rot, dst_node->mat_ptr); } void ExConstraintBlock::CalcConstraintPosition(mat4 mat) { obj_skin_ex_node_constraint_position* position = data->position; vec3 constraining_offset = position->constraining_object.offset; vec3 constrained_offset = position->constrained_object.offset; if (position->constraining_object.affected_by_orientation) mat4_transform_vector(src_node->mat_ptr, &constraining_offset, &constraining_offset); vec3 source_node_trans; mat4_get_translation(src_node->mat_ptr, &source_node_trans); source_node_trans = constraining_offset + source_node_trans; mat4_set_translation(&mat, &source_node_trans); if (upvector_node) { vec3 up_vector_trans; mat4_get_translation(upvector_node->mat_ptr, &up_vector_trans); mat4_inverse_transform_point(&mat, &up_vector_trans, &up_vector_trans); mat4 rot_mat; make_direction_matrix(rot_mat, position->up_vector.affected_axis, up_vector_trans); mat4_mul(&rot_mat, &mat, &mat); } if (position->constrained_object.affected_by_orientation) mat4_transform_vector(&mat, &constrained_offset, &constrained_offset); mat4 constrained_offset_mat; mat4_translate(&constrained_offset, &constrained_offset_mat); mat4_mul(&mat, &constrained_offset_mat, dst_node->mat_ptr); } void ExConstraintBlock::CalcMatrixHS() { if (!dst_node) return; RobTransform& transform = dst_node->transform; const vec3 hsc = parent->transform.hsc; mat4 mat; mat4_invert_fast(parent->no_scale_mat, &mat); mat4_mul(dst_node->mat_ptr, &mat, &mat); mat4_get_rotation_zyx(&mat, &transform.rot); mat4_get_translation(&mat, &transform.pos); if (fabsf(hsc.x) > 0.000001f) transform.pos.x /= hsc.x; if (fabsf(hsc.y) > 0.000001f) transform.pos.y /= hsc.z; if (fabsf(hsc.z) > 0.000001f) transform.pos.z /= hsc.z; *dst_node->no_scale_mat = *dst_node->mat_ptr; mat4_scale_rot(dst_node->mat_ptr, &hsc, dst_node->mat_ptr); transform.hsc = transform.scale * hsc; } void ExConstraintBlock::set_data(const RobSkinDisp* skin_disp, const obj_skin_ex_node_constraint* data, const char* name, const bone_database* bone_data) { RobNode* node = (RobNode*)skin_disp->get_node(name, bone_data); type = EX_NODE_TYPE_CONSTRAINT; dst_node = node; this->data = data; this->name = node->name; this->skin_disp = skin_disp; src_node = skin_disp->get_node(data->src_name, bone_data); obj_skin_ex_node_constraint_type type = data->type; const char* up_vector_name; if (type == OBJ_SKIN_EX_NODE_CONSTRAINT_DIRECTION) { cns_type = OBJ_SKIN_EX_NODE_CONSTRAINT_DIRECTION; up_vector_name = data->direction->up_vector.name; } else if (type == OBJ_SKIN_EX_NODE_CONSTRAINT_POSITION) { cns_type = OBJ_SKIN_EX_NODE_CONSTRAINT_POSITION; up_vector_name = data->position->up_vector.name; } else if (type == OBJ_SKIN_EX_NODE_CONSTRAINT_DISTANCE) { cns_type = OBJ_SKIN_EX_NODE_CONSTRAINT_DISTANCE; up_vector_name = data->distance->up_vector.name; } else if (type == OBJ_SKIN_EX_NODE_CONSTRAINT_ORIENTATION) { cns_type = OBJ_SKIN_EX_NODE_CONSTRAINT_ORIENTATION; return; } else { cns_type = OBJ_SKIN_EX_NODE_CONSTRAINT_NONE; return; } if (up_vector_name) upvector_node = skin_disp->get_node(up_vector_name, bone_data); } float_t Expr_node::eval() { float_t value = 0.0f; switch (type) { case Expr_constant: value = data.constant; break; case Expr_variable: value = *data.variable; break; case Expr_variable_rad: value = *data.variable * RAD_TO_DEG_FLOAT; break; case Expr_func1: { float_t v1 = operand[0]->eval(); if (data.func1) value = data.func1(v1); } break; case Expr_func2: { float_t v1 = operand[0]->eval(); float_t v2 = operand[1]->eval(); if (data.func1) value = data.func2(v1, v2); } break; case Expr_func3: { float_t v1 = operand[0]->eval(); float_t v2 = operand[1]->eval(); float_t v3 = operand[2]->eval(); if (data.func3) value = data.func3(v1, v2, v3); } break; } return value; } ExExpressionBlock::ExExpressionBlock() : result(), result_type(), expr_node(), expr_work(), data(), field_3D20(), hard_coded_func(), timer(), step(true) { } ExExpressionBlock::~ExExpressionBlock() { } void ExExpressionBlock::init() { timer = 0.0f; } void ExExpressionBlock::CtrlBegin() { RobTransform& transform = dst_node->transform; transform.pos = data->transform.position; transform.rot = data->transform.rotation; transform.scale = data->transform.scale; done = false; } void ExExpressionBlock::CtrlStep(int32_t stage, bool disable_ex_force) { if (done) return; switch (stage) { case 0: if (is_parent) ctrl(); break; case 2: if (has_children_node) CalcMatrixHS(); break; case 5: ctrl(); break; } } void ExExpressionBlock::ctrl() { if (!parent) return; if (done) { done = false; return; } Calc(); CalcMatrixHS(); done = true; } void ExExpressionBlock::CtrlOsagePlayData() { ctrl(); } void ExExpressionBlock::disp(const mat4& mat, render_context* rctx) { } void ExExpressionBlock::disp_debug() { } void ExExpressionBlock::pos_init() { ctrl(); } void ExExpressionBlock::pos_init_cont() { ctrl(); } void ExExpressionBlock::Calc() { float_t delta_frame = get_delta_frame(); #if OPD_PLAY_GEN if (opd_play_gen_run) delta_frame = 1.0f; #endif if (step) delta_frame *= skin_disp->rob_disp->osage_step; float_t timer = this->timer + delta_frame; this->timer = timer >= 65535.0f ? timer - 65535.0f : timer; if (hard_coded_func) { hard_coded_func(&dst_node->transform); return; } for (int32_t i = 0; i < 9; i++) { if (!expr_node[i]) break; float_t value = expr_node[i]->eval(); if (result_type[i] == Expr_variable_rad) value *= DEG_TO_RAD_FLOAT; *result[i] = value; } } void ExExpressionBlock::CalcMatrixHS() { RobTransform* data = &dst_node->transform; const vec3 hsc = parent->transform.hsc; mat4 mat = *parent->no_scale_mat; mat4 dsp_mat = mat4_identity; data->CalcMatrixHS(hsc, mat, dsp_mat); *dst_node->mat_ptr = dsp_mat; *dst_node->no_scale_mat = mat; } void ExExpressionBlock::set_data(const RobSkinDisp* skin_disp, const obj_skin_ex_node_expression* data, const char* node_name, object_info objuid, size_t index, const bone_database* bone_data) { Expr_node* stack_buf[28]; Expr_node** stack_buf_val = stack_buf; RobNode* node = (RobNode*)skin_disp->get_node(node_name, bone_data); type = EX_NODE_TYPE_EXPRESSION; dst_node = node; this->data = data; this->name = node->name; this->skin_disp = skin_disp; node->transform.pos = data->transform.position; node->transform.rot = data->transform.rotation; node->transform.scale = data->transform.scale; hard_coded_func = 0; Expr_node* expr = expr_work; for (int32_t i = 0; i < 9; i++) { result[i] = 0; expr_node[i] = 0; } for (int32_t i = 0; i < 9; i++) { const char* script = data->script[i]; if (!script || str_utils_compare_length(script, utf8_length(script), "= ", 2)) break; script += 2; int32_t index = 0; script = str_utils_get_next_int32_t(script, index, ' '); result[i] = dst_node->get_transform_component(index, result_type[i]); while (script) { std::string value_type; script = str_utils_get_next_string(script, value_type, ' '); if (!value_type.size() || !memcmp(value_type.c_str(), "error", min_def(value_type.size(), 5)) && value_type.size() == 5) { printf_debug_info("Expression error: %s.", this->name); if (index >= 0 && index < 9) printf_debug_info("%s\n", expression_component_names[index]); else printf_debug_info("\n"); break; } if (value_type[0] == 'n') { expr->type = Expr_constant; script = str_utils_get_next_float_t(script, expr->data.constant, ' '); *stack_buf_val++ = expr; } else if (value_type[0] == 'v') { std::string func_str; script = str_utils_get_next_string(script, func_str, ' '); expr->type = Expr_variable; int32_t index = func_str[0] - '0'; if (index >= 0 && index < 9) { const RobNode* node = skin_disp->get_node(func_str.c_str() + 2, bone_data); if (node) expr->data.variable = node->get_transform_component(index, expr->type); else { expr->type = Expr_constant; expr->data.constant = 0.0f; } } else expr->data.variable = &timer; *stack_buf_val++ = expr; } else if (value_type[0] == 'f') { std::string func_str; script = str_utils_get_next_string(script, func_str, ' '); expr->type = Expr_func1; expr->operand[0] = stack_buf_val[-1]; expr->data.func1 = ExpFuncUnaryTblFindFunc(func_str, exp_func_unary_tbl)->func; stack_buf_val[-1] = expr; } else if (value_type[0] == 'g') { std::string func_str; script = str_utils_get_next_string(script, func_str, ' '); expr->type = Expr_func2; expr->operand[0] = stack_buf_val[-2]; expr->operand[1] = stack_buf_val[-1]; expr->data.func2 = ExpFuncBinaryTblFindFunc(func_str, exp_func_binary_tbl)->func; stack_buf_val[-2] = expr; stack_buf_val--; } else if (value_type[0] == 'h') { std::string func_str; script = str_utils_get_next_string(script, func_str, ' '); expr->type = Expr_func3; expr->operand[0] = stack_buf_val[-3]; expr->operand[1] = stack_buf_val[-2]; expr->operand[2] = stack_buf_val[-1]; expr->data.func3 = ExpFuncTernaryTblFindFunc(func_str, exp_func_ternary_tbl)->func; stack_buf_val[-3] = expr; stack_buf_val -= 2; } expr_node[i] = stack_buf_val[-1]; expr++; } } step = !(index == RPK_TE_R || index == RPK_TE_L); } static const ExpFuncUnaryTbl* ExpFuncUnaryTblFindFunc(std::string& name, const ExpFuncUnaryTbl* array) { const char* name_str = name.c_str(); while (array->name) { if (!str_utils_compare(name_str, array->name)) return array; array++; } return 0; } static const ExpFuncBinaryTbl* ExpFuncBinaryTblFindFunc(std::string& name, const ExpFuncBinaryTbl* array) { const char* name_str = name.c_str(); while (array->name) { if (!str_utils_compare(name_str, array->name)) return array; array++; } return 0; } static const ExpFuncTernaryTbl* ExpFuncTernaryTblFindFunc(std::string& name, const ExpFuncTernaryTbl* array) { const char* name_str = name.c_str(); while (array->name) { if (!str_utils_compare(name_str, array->name)) return array; array++; } return 0; } static float_t exp_abs(float_t v1) { return fabsf(v1); } static float_t exp_acos(float_t v1) { return acosf(v1) * RAD_TO_DEG_FLOAT; } static float_t exp_add(float_t v1, float_t v2) { return v1 + v2; } static float_t exp_and(float_t v1, float_t v2) { return v1 != 0.0f && v2 != 0.0f ? 1.0f : 0.0f; } static float_t exp_asin(float_t v1) { return asinf(v1) * RAD_TO_DEG_FLOAT; } static float_t exp_atan(float_t v1) { return atanf(v1) * RAD_TO_DEG_FLOAT; } static float_t exp_av(float_t v1) { return v1 * 0.1f; } static float_t exp_ceil(float_t v1) { return ceilf(v1); } static float_t exp_cond(float_t v1, float_t v2, float_t v3) { return v1 != 0.0f ? v2 : v3; } static float_t exp_cos(float_t v1) { return cosf(fmodf(v1, 360.0f) * DEG_TO_RAD_FLOAT); } static float_t exp_div(float_t v1, float_t v2) { return v1 / v2; } static float_t exp_eq(float_t v1, float_t v2) { return v1 == v2 ? 1.0f : 0.0f; } static float_t exp_exp(float_t v1) { return expf(v1); } static float_t exp_floor(float_t v1) { return floorf(v1); } static float_t exp_fmod(float_t v1, float_t v2) { return fmodf(v1, v2); } static float_t exp_ge(float_t v1, float_t v2) { return v1 >= v2 ? 1.0f : 0.0f; } static float_t exp_gt(float_t v1, float_t v2) { return v1 > v2 ? 1.0f : 0.0f; } static float_t exp_le(float_t v1, float_t v2) { return v1 <= v2 ? 1.0f : 0.0f; } static float_t exp_log(float_t v1) { return logf(v1); } static float_t exp_lt(float_t v1, float_t v2) { return v1 < v2 ? 1.0f : 0.0f; } static float_t exp_max(float_t v1, float_t v2) { return max_def(v1, v2); } static float_t exp_min(float_t v1, float_t v2) { return min_def(v1, v2); } static float_t exp_mul(float_t v1, float_t v2) { return v1 * v2; } static float_t exp_ne(float_t v1, float_t v2) { return v1 != v2 ? 1.0f : 0.0f; } static float_t exp_neg(float_t v1) { return -v1; } static float_t exp_or(float_t v1, float_t v2) { return v1 != 0.0f || v2 != 0.0f ? 1.0f : 0.0f; } static float_t exp_pow(float_t v1, float_t v2) { return powf(v1, v2); } static float_t exp_rand(float_t v1, float_t v2, float_t v3) { return v1 != 0.0f ? v2 : v3; } static float_t exp_rand_0_1(float_t v1) { return 0.0f; } static float_t exp_round(float_t v1) { return prj::roundf(v1); } static float_t exp_sin(float_t v1) { return sinf(fmodf(v1, 360.0f) * DEG_TO_RAD_FLOAT); } static float_t exp_sqrt(float_t v1) { return sqrtf(v1); } static float_t exp_sub(float_t v1, float_t v2) { return v1 - v2; } static float_t exp_tan(float_t v1) { return tanf(fmodf(v1, 360.0f) * DEG_TO_RAD_FLOAT); } // 0x140482300 static void apply_gravity(vec3& vec, const vec3& p0, const vec3& p1, const float_t gravity, const float_t weight) { vec3 diff = p0 - p1; float_t dist = vec3::length(diff); if (dist <= fabsf(gravity)) { diff.y = gravity; dist = vec3::length(diff); } diff *= 1.0f / dist; vec = vec3(0.0f, -(gravity * weight), 0.0f) - (diff * diff.y * (gravity * weight)); } // 0x14021A290 static float_t calc_tangent_basis(const vec3& p0, const vec3& p1, const vec3& p2, const vec2& uv0, const vec2& uv1, const vec2& uv2, vec3& tangent, vec3& binormal, vec3& normal) { vec3 v1 = p1 - p0; vec3 v2 = p2 - p0; normal = vec3::cross(v1, v2); float_t normal_length = vec3::length(normal); if (normal_length <= 0.000001f) return 1.0f; normal *= 1.0f / normal_length; vec2 t1 = uv1 - uv0; vec2 t2 = uv2 - uv0; float_t r = 1.0f / (t2.y * t1.x - t1.y * t2.x); vec3 _tangent = vec3::normalize((v1 * t2.y - v2 * t1.y) * r); binormal = vec3::cross(_tangent, normal); tangent = vec3::cross(normal, binormal); mat3 mat = mat3(tangent, binormal, normal); mat3_transpose(&mat, &mat); return mat3_determinant(&mat); } // 0x14021A5E0 static float_t calc_tangent_basis(const vec3& v1, const vec3& v2, const float_t t1, const float_t t2, vec3& tangent, vec3& binormal, vec3& normal) { normal = vec3::normalize(vec3::cross(v1, v2)); vec3 _tangent = vec3::normalize(v1 * t2 - v2 * t1); binormal = vec3::cross(_tangent, normal); tangent = vec3::cross(normal, binormal); mat3 mat = mat3(tangent, binormal, normal); mat3_transpose(&mat, &mat); return mat3_determinant(&mat); } // 0x14021A890 static void calc_tangent_basis_sub(CLOTH_VERTEX* vtx, const CLOTH_VERTEX* vtxL, const CLOTH_VERTEX* vtxR, const CLOTH_VERTEX* vtxU, const CLOTH_VERTEX* vtxD) { const vec3 v1 = vtxU->pos - vtxD->pos; const vec3 v2 = vtxR->pos - vtxL->pos; const float_t v2_length = vec3::length(v1); const float_t v1_length = vec3::length(v2); if (v2_length <= 0.000001f || v1_length <= 0.000001f) return; const vec2 uv_a = vtxU->uv - vtxD->uv; const vec2 uv_b = vtxR->uv - vtxL->uv; float_t r = uv_b.y * uv_a.x - uv_a.y * uv_b.x; if (fabsf(r) > 0.000001f) { r = 1.0f / r; vtx->m = calc_tangent_basis(v1, v2, uv_a.y * r, uv_b.y * r, vtx->tangent, vtx->binormal, vtx->normal); } } // 0x140484850 static void closest_pt_segment_segment(vec3& vec, const vec3& p0, const vec3& q0, const OsageCollision::Work* cls) { const vec3& p1 = cls->pos[0]; const vec3& q1 = cls->pos[1]; vec3 d0 = q0 - p0; vec3 d1 = cls->vec_center; if (vec3::length_squared(vec3::cross(d0, d1)) <= 0.000001f) { vec3 p0_proj; vec3 q0_proj; vec3 p1_proj; vec3 q1_proj; OsageCollision::get_nearest_line2point(p0_proj, p1, q1, p0); OsageCollision::get_nearest_line2point(q0_proj, p1, q1, q0); OsageCollision::get_nearest_line2point(p1_proj, p0, q0, p1); OsageCollision::get_nearest_line2point(q1_proj, p0, q0, q1); const float_t p0_dist = vec3::distance_squared(p0, p0_proj); const float_t q0_dist = vec3::distance_squared(q0, q0_proj); const float_t p1_dist = vec3::distance_squared(p1, p1_proj); const float_t q1_dist = vec3::distance_squared(q1, q1_proj); float_t dist = p0_dist; vec = p0; if (dist > q0_dist) { vec = q0; dist = q0_dist; } if (dist > p1_dist) { vec = p1_proj; dist = p1_dist; } if (dist > q1_dist) { vec = q1_proj; dist = q1_dist; } } else { const float_t d0_len = vec3::length(d0); if (d0_len != 0.0f) d0 *= 1.0f / d0_len; const float_t d1_len = cls->vec_center_length; if (d1_len != 0.0f) d1 *= 1.0f / d1_len; const float_t b = vec3::dot(d1, d0); const float_t t = vec3::dot(d1 * b - d0, p1 - p0) / (b * b - 1.0f); if (t < 0.0f) vec = p0; else if (t <= d0_len) vec = p0 + d0 * t; else vec = q0; } } // 0x140482F30 static void length_limit(vec3& p, const vec3& r, float_t rr) { const vec3 d = p - r; const float_t dist = vec3::length_squared(d); if (dist > rr * rr) p = r + d * (rr / sqrtf(dist)); } // 0x140482FF0 static bool make_axis_matrix(mat4& mat, const vec3& axis, const skin_param_hinge* hinge, vec3* rot, const ROTTYPE& rottype) { bool rot_clamped = false; float_t z_rot; float_t y_rot; if (rottype == ROTTYPE_ZY) { y_rot = atan2f(-axis.z, axis.x); z_rot = atan2f(axis.y, sqrtf(axis.x * axis.x + axis.z * axis.z)); if (hinge) rot_clamped = hinge->clamp(y_rot, z_rot); mat4_mul_rotate_y(&mat, y_rot, &mat); mat4_mul_rotate_z(&mat, z_rot, &mat); } else { z_rot = atan2f(axis.y, axis.x); y_rot = atan2f(-axis.z, sqrtf(axis.x * axis.x + axis.y * axis.y)); if (hinge) rot_clamped = hinge->clamp(y_rot, z_rot); mat4_mul_rotate_z(&mat, z_rot, &mat); mat4_mul_rotate_y(&mat, y_rot, &mat); } if (rot) { rot->y = y_rot; rot->z = z_rot; } return rot_clamped; } // 0x1401EB410 static void make_direction_matrix(mat4& mat, const vec3& v0, const vec3& v1) { vec3 v0_norm = vec3::normalize(v0); vec3 v1_norm = vec3::normalize(v1); vec3 axis = vec3::cross(v1_norm, v0_norm); float_t c = clamp_def(vec3::dot(v1_norm, v0_norm), -1.0f, 1.0f); float_t s = sqrtf(clamp_def(1.0f - c * c, 0.0f, 1.0f)); mat4_set(&axis, -s, c, &mat); } static void modify_cloth_object(obj_mesh* mesh, obj_mesh_vertex_buffer* vb, CLOTH_VERTEX* vtxarg, float_t sgn, int32_t num_idx, uint16_t* idxtbl, bool do_ura) { if (!mesh || !vb || (mesh->vertex_format & (OBJ_VERTEX_NORMAL | OBJ_VERTEX_POSITION)) != (OBJ_VERTEX_NORMAL | OBJ_VERTEX_POSITION)) return; vb->cycle_index(); GL::ArrayBuffer buffer = vb->get_buffer(); size_t data = (size_t)buffer.MapMemory(gl_state); if (!data) return; if (do_ura) num_idx /= 2; bool tangent = !!(mesh->vertex_format & OBJ_VERTEX_TANGENT); switch (mesh->attrib.m.compression) { case 0: default: if (tangent) for (int32_t i = 2; i; i--, sgn = -1.0f) { for (int32_t j = num_idx; j; j--, idxtbl++) { CLOTH_VERTEX* vtx = &vtxarg[*idxtbl]; *(vec3*)data = vtx->pos; *(vec3*)(data + 0x0C) = vtx->normal * sgn; *(vec3*)(data + 0x18) = vtx->tangent; *(float_t*)(data + 0x24) = vtx->m; data += mesh->size_vertex; } if (!do_ura) break; } else for (int32_t i = 2; i; i--, sgn = -1.0f) { for (int32_t j = num_idx; j; j--, idxtbl++) { CLOTH_VERTEX* vtx = &vtxarg[*idxtbl]; *(vec3*)data = vtx->pos; *(vec3*)(data + 0x0C) = vtx->normal * sgn; data += mesh->size_vertex; } if (!do_ura) break; } break; case 1: if (tangent) for (int32_t i = 2; i; i--, sgn = -1.0f) { for (int32_t j = num_idx; j; j--, idxtbl++) { CLOTH_VERTEX* vtx = &vtxarg[*idxtbl]; *(vec3*)data = vtx->pos; vec3_to_vec3i16(vtx->normal * (32767.0f * sgn), *(vec3i16*)(data + 0x0C)); *(int16_t*)(data + 0x12) = 0; vec4 tangent; *(vec3*)&tangent = vtx->tangent; tangent.w = vtx->m; vec4_to_vec4i16(tangent * 32767.0f, *(vec4i16*)(data + 0x14)); data += mesh->size_vertex; } if (!do_ura) break; } else for (int32_t i = 2; i; i--, sgn = -1.0f) { for (int32_t j = num_idx; j; j--, idxtbl++) { CLOTH_VERTEX* vtx = &vtxarg[*idxtbl]; *(vec3*)data = vtx->pos; vec3_to_vec3i16(vtx->normal * (32767.0f * sgn), *(vec3i16*)(data + 0x0C)); *(int16_t*)(data + 0x12) = 0; data += mesh->size_vertex; } if (!do_ura) break; } case 2: if (tangent) for (int32_t i = 2; i; i--, sgn = -1.0f) { for (int32_t j = num_idx; j; j--, idxtbl++) { CLOTH_VERTEX* vtx = &vtxarg[*idxtbl]; *(vec3*)data = vtx->pos; vec3i16 normal_int; vec3_to_vec3i16(vtx->normal * 511.0f, normal_int); *(uint32_t*)(data + 0x0C) = (((uint32_t)0 & 0x03) << 30) | (((uint32_t)normal_int.z & 0x3FF) << 20) | (((uint32_t)normal_int.y & 0x3FF) << 10) | ((uint32_t)normal_int.x & 0x3FF); vec4 tangent; *(vec3*)&tangent = vtx->tangent; tangent.w = vtx->m; vec4i16 tangent_int; vec4_to_vec4i16(tangent * 511.0f, tangent_int); *(uint32_t*)(data + 0x10) = (((uint32_t)clamp_def(tangent_int.w, -1, 1) & 0x03) << 30) | (((uint32_t)tangent_int.z & 0x3FF) << 20) | (((uint32_t)tangent_int.y & 0x3FF) << 10) | ((uint32_t)tangent_int.x & 0x3FF); data += mesh->size_vertex; } if (!do_ura) break; } else for (int32_t i = 2; i; i--, sgn = -1.0f) { for (int32_t j = num_idx; j; j--, idxtbl++) { CLOTH_VERTEX* vtx = &vtxarg[*idxtbl]; *(vec3*)data = vtx->pos; vec3i16 normal_int; vec3_to_vec3i16(vtx->normal * 511.0f, normal_int); *(uint32_t*)(data + 0x0C) = (((uint32_t)0 & 0x03) << 30) | (((uint32_t)normal_int.z & 0x3FF) << 20) | (((uint32_t)normal_int.y & 0x3FF) << 10) | ((uint32_t)normal_int.x & 0x3FF); data += mesh->size_vertex; } if (!do_ura) break; } break; } buffer.UnmapMemory(gl_state); }