/* =========================================================================== Copyright (C) 2015 the OpenMoHAA team This file is part of OpenMoHAA source code. OpenMoHAA source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. OpenMoHAA source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenMoHAA source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // bspline.cpp: Uniform non-rational bspline class. // #include "g_local.h" #include "bspline.h" #include "game.h" #include "debuglines.h" #include "g_phys.h" #include "scriptexception.h" void BSpline::Set(Vector *control_points_, int num_control_points_, splinetype_t type) { int i; SetType(type); has_orientation = false; if (control_points) { delete[] control_points; control_points = NULL; } num_control_points = num_control_points_; if (num_control_points) { control_points = new BSplineControlPoint[num_control_points]; assert(control_points); for (i = 0; i < num_control_points; i++) { control_points[i].Set(control_points_[i]); } } } void BSpline::Set( Vector *control_points_, Vector *control_orients_, float *control_speeds_, int num_control_points_, splinetype_t type ) { int i; SetType(type); has_orientation = true; if (control_points) { delete[] control_points; control_points = NULL; } num_control_points = num_control_points_; if (num_control_points) { control_points = new BSplineControlPoint[num_control_points]; assert(control_points); for (i = 0; i < num_control_points; i++) { control_points[i].Set(control_points_[i], control_orients_[i], control_speeds_[i]); } } } void BSpline::Clear(void) { if (control_points) { delete[] control_points; control_points = NULL; } num_control_points = 0; has_orientation = false; } inline float BSpline::EvalNormal(float u, Vector& pos, Vector& orient) { int segment_id; float B[4]; float tmp; float u_2; float u_3; Vector ang; float roll; float speed; segment_id = (int)u; if (segment_id < 0) { segment_id = 0; } if (segment_id > num_control_points - 4) { segment_id = num_control_points - 4; } u -= (float)segment_id; u_2 = u * u; u_3 = u * u_2; tmp = 1 - u; B[0] = (tmp * tmp * tmp) * (1.0f / 6.0f); B[1] = (3.0f * u_3 - 6.0f * u_2 + 4.0f) * (1.0f / 6.0f); B[2] = (-3.0f * u_3 + 3.0f * u_2 + 3.0f * u + 1) * (1.0f / 6.0f); B[3] = u_3 * (1.0f / 6.0f); pos = *control_points[0 + segment_id].GetPosition() * B[0] + *control_points[1 + segment_id].GetPosition() * B[1] + *control_points[2 + segment_id].GetPosition() * B[2] + *control_points[3 + segment_id].GetPosition() * B[3]; ang = *control_points[0 + segment_id].GetOrientation() * B[0] + *control_points[1 + segment_id].GetOrientation() * B[1] + *control_points[2 + segment_id].GetOrientation() * B[2] + *control_points[3 + segment_id].GetOrientation() * B[3]; roll = *control_points[0 + segment_id].GetRoll() * B[0] + *control_points[1 + segment_id].GetRoll() * B[1] + *control_points[2 + segment_id].GetRoll() * B[2] + *control_points[3 + segment_id].GetRoll() * B[3]; speed = *control_points[0 + segment_id].GetSpeed() * B[0] + *control_points[1 + segment_id].GetSpeed() * B[1] + *control_points[2 + segment_id].GetSpeed() * B[2] + *control_points[3 + segment_id].GetSpeed() * B[3]; orient = ang.toAngles(); orient[ROLL] = roll; return speed; } inline float BSpline::EvalLoop(float t, Vector& pos, Vector& orient) { Vector retval; Vector ang; float speed; float roll; int segment_id; int next_id; float B[4]; float tmp; float u; float u_2; float u_3; int i; int j; segment_id = (int)floor(t); u = t - floor(t); segment_id %= num_control_points; if (segment_id < 0) { segment_id += num_control_points; } u_2 = u * u; u_3 = u * u_2; tmp = 1 - u; B[0] = (tmp * tmp * tmp) * (1.0f / 6.0f); B[1] = (3.0f * u_3 - 6.0f * u_2 + 4.0f) * (1.0f / 6.0f); B[2] = (-3.0f * u_3 + 3.0f * u_2 + 3.0f * u + 1) * (1.0f / 6.0f); B[3] = u_3 * (1.0f / 6.0f); speed = 0; roll = 0; for (i = 0, j = segment_id; i < 4; i++, j++) { if (j >= num_control_points) { j -= (num_control_points - loop_control_point); } retval += *control_points[j].GetPosition() * B[i]; ang += *control_points[j].GetOrientation() * B[i]; speed += *control_points[j].GetSpeed() * B[i]; roll += *control_points[j].GetRoll() * B[i]; } pos = retval; next_id = segment_id + 1; if (next_id >= num_control_points) { next_id -= (num_control_points - loop_control_point); } orient = ang.toAngles(); orient[ROLL] = roll; return speed; } inline float BSpline::EvalClamp(float t, Vector& pos, Vector& orient) { Vector retval; Vector ang; int segment_id; int next_id; float B[4]; float tmp; float u; float u_2; float u_3; int i; int j; float speed; float roll; segment_id = (int)floor(t); u = t - floor(t); u_2 = u * u; u_3 = u * u_2; tmp = 1 - u; B[0] = (tmp * tmp * tmp) * (1.0f / 6.0f); B[1] = (3.0f * u_3 - 6.0f * u_2 + 4.0f) * (1.0f / 6.0f); B[2] = (-3.0f * u_3 + 3.0f * u_2 + 3.0f * u + 1) * (1.0f / 6.0f); B[3] = u_3 * (1.0f / 6.0f); speed = 0; roll = 0; for (i = 0; i < 4; i++, segment_id++) { j = segment_id; if (j < 0) { j = 0; } else if (j >= num_control_points) { j = num_control_points - 1; } retval += *control_points[j].GetPosition() * B[i]; ang += *control_points[j].GetOrientation() * B[i]; speed += *control_points[j].GetSpeed() * B[i]; roll += *control_points[j].GetRoll() * B[i]; } pos = retval; next_id = segment_id + 1; if (segment_id < 0) { segment_id = 0; } if (segment_id >= num_control_points) { segment_id = num_control_points - 1; } if (next_id < 0) { next_id = 0; } if (next_id >= num_control_points) { next_id = num_control_points - 1; } orient = ang.toAngles(); orient[ROLL] = roll; return speed; } Vector BSpline::Eval(float u) { Vector pos; Vector orient; switch (curvetype) { default: case SPLINE_NORMAL: EvalNormal(u, pos, orient); break; case SPLINE_CLAMP: EvalClamp(u, pos, orient); break; case SPLINE_LOOP: if (u < 0) { EvalClamp(u, pos, orient); } else { EvalLoop(u, pos, orient); } break; } return pos; } float BSpline::Eval(float u, Vector& pos, Vector& orient) { switch (curvetype) { default: case SPLINE_NORMAL: return EvalNormal(u, pos, orient); break; case SPLINE_CLAMP: return EvalClamp(u, pos, orient); break; case SPLINE_LOOP: if (u < 0) { return EvalClamp(u, pos, orient); } else { return EvalLoop(u, pos, orient); } break; } } void BSpline::DrawControlSegments(void) { int i; G_BeginLine(); for (i = 0; i < num_control_points; i++) { G_Vertex(*control_points[i].GetPosition()); } G_EndLine(); } void BSpline::DrawCurve(int num_subdivisions) { float u; float du; if (!num_control_points) { return; } du = 1.0f / (float)num_subdivisions; G_BeginLine(); for (u = -2.0f; u <= (float)num_control_points; u += du) { G_Vertex((Vector)Eval(u)); } G_EndLine(); } void BSpline::DrawCurve(Vector offset, int num_subdivisions) { float u; float du; du = 1.0f / (float)num_subdivisions; G_BeginLine(); for (u = -2.0f; u <= (float)num_control_points; u += du) { G_Vertex(offset + (Vector)Eval(u)); } G_EndLine(); } void BSpline::AppendControlPoint(const Vector& new_control_point) { BSplineControlPoint *old_control_points; int i; old_control_points = control_points; num_control_points++; control_points = new BSplineControlPoint[num_control_points]; assert(control_points); if (old_control_points) { for (i = 0; i < num_control_points - 1; i++) { control_points[i] = old_control_points[i]; } delete[] old_control_points; } control_points[num_control_points - 1].Set(new_control_point); } void BSpline::AppendControlPoint(const Vector& new_control_point, const float& speed) { BSplineControlPoint *old_control_points; int i; old_control_points = control_points; num_control_points++; control_points = new BSplineControlPoint[num_control_points]; assert(control_points); if (old_control_points) { for (i = 0; i < num_control_points - 1; i++) { control_points[i] = old_control_points[i]; } delete[] old_control_points; } control_points[num_control_points - 1].Set(new_control_point, speed); } void BSpline::AppendControlPoint( const Vector& new_control_point, const Vector& new_control_orient, const float& new_control_speed ) { BSplineControlPoint *old_control_points; int i; has_orientation = true; old_control_points = control_points; num_control_points++; control_points = new BSplineControlPoint[num_control_points]; assert(control_points); if (old_control_points) { for (i = 0; i < num_control_points - 1; i++) { control_points[i] = old_control_points[i]; } delete[] old_control_points; } control_points[num_control_points - 1].Set(new_control_point, new_control_orient, new_control_speed); } void BSpline::SetLoopPoint(const Vector& pos) { int i; for (i = 0; i < num_control_points; i++) { if (pos == *control_points[i].GetPosition()) { loop_control_point = i; break; } } } int BSpline::PickControlPoint(const Vector& window_point, float pick_size) { int i; float closest_dist_2; int closest_index; float dist_2; Vector delta; closest_index = -1; closest_dist_2 = 1000000.0f; for (i = 0; i < num_control_points; i++) { delta = window_point - *control_points[i].GetPosition(); dist_2 = delta * delta; if (dist_2 < closest_dist_2) { closest_dist_2 = dist_2; closest_index = i; } } if (pick_size * pick_size >= closest_dist_2) { return closest_index; } else { return -1; } } Event EV_SplinePath_Create ( "SplinePath_create", EV_DEFAULT, NULL, NULL, "Creates the spline path from the target list.", EV_NORMAL ); Event EV_SplinePath_Loop ( "loop", EV_CONSOLE, "s", "loop_name", "Sets the loop name.", EV_NORMAL ); Event EV_SplinePath_Speed ( "speed", EV_DEFAULT, "f", "speed", "Sets the path speed.", EV_NORMAL ); Event EV_SplinePath_SetTriggerTarget ( "triggertarget", EV_DEFAULT, "s", "target", "Sets the trigger target.", EV_NORMAL ); Event EV_SplinePath_SetWatch ( "watch", EV_CONSOLE, "s", "watchEntity", "Sets the entity to watch at this node.", EV_NORMAL ); Event EV_SplinePath_SetFov ( "fov", EV_CONSOLE, "f", "cameraFOV", "Sets the fov at this node.", EV_NORMAL ); Event EV_SplinePath_SetFadeTime ( "fadetime", EV_DEFAULT, "f", "fadeTime", "Sets the fadetime at this node.", EV_NORMAL ); CLASS_DECLARATION(Entity, SplinePath, "info_splinepath") { {&EV_SplinePath_Create, &SplinePath::CreatePath }, {&EV_SplinePath_Loop, &SplinePath::SetLoop }, {&EV_SplinePath_Speed, &SplinePath::SetSpeed }, {&EV_SplinePath_SetTriggerTarget, &SplinePath::SetTriggerTarget}, {&EV_SplinePath_SetWatch, &SplinePath::SetWatch }, {&EV_SplinePath_SetFov, &SplinePath::SetFov }, {&EV_SplinePath_SetFadeTime, &SplinePath::SetFadeTime }, {NULL, NULL } }; SplinePath::SplinePath() { entflags |= ECF_SPLINEPATH; AddWaitTill(STRING_REACH); owner = this; next = NULL; loop = NULL; speed = 1; doWatch = false; watchEnt = ""; fov = 0; fadeTime = -1; setMoveType(MOVETYPE_NONE); setSolidType(SOLID_NOT); hideModel(); if (!LoadingSavegame) { PostEvent(EV_SplinePath_Create, FRAMETIME); } } SplinePath::~SplinePath() { // disconnect from the chain if (owner != this) { owner->SetNext(next); } else if (next) { next->SetPrev(NULL); next = NULL; } assert(owner == this); assert(next == NULL); entflags &= ~ECF_SPLINEPATH; } void SplinePath::SetLoop(Event *ev) { loop_name = ev->GetString(1); } void SplinePath::SetSpeed(Event *ev) { speed = ev->GetFloat(1); } void SplinePath::SetTriggerTarget(Event *ev) { SetTriggerTarget(ev->GetString(1)); } void SplinePath::CreatePath(Event *ev) { const char *target; Entity *ent; // Make the path from the targetlist. target = Target(); if (target[0]) { ent = (Entity *)G_FindTarget(NULL, target); if (ent && ent->IsSubclassOfSplinePath()) { next = (SplinePath *)ent; if (next->owner != next) { // Fixed in OPM // If the target already has an owner, make sure to properly remove the owner next->owner->SetNext(NULL); } next->owner = this; } else if (ent) { ScriptError("SplinePath::CreatePath: target '%s' for '%s' not found (cannot connect to class '%s')\n", target, targetname.c_str(), ent->getClassname()); } else { ScriptError("SplinePath::CreatePath: target %s not found\n", target); } } if (loop_name.length()) { ent = (Entity *)G_FindTarget(NULL, loop_name.c_str()); if (ent) { loop = (SplinePath *)ent; } } } SplinePath *SplinePath::GetNext(void) { return next; } SplinePath *SplinePath::GetPrev(void) { if (owner == this) { return NULL; } return owner; } void SplinePath::SetNext(SplinePath *node) { if (next) { // remove ourselves from the chain next->owner = next; } next = node; if (next) { // disconnect next from it's previous node if (next->owner != next) { next->owner->next = NULL; } next->owner = this; } } void SplinePath::SetPrev(SplinePath *node) { if (owner != this) { owner->next = NULL; } if (node && (node != this)) { // safely remove the node from its chain if (node->next) { node->next->owner = node->next; } node->next = this; owner = node; } else { owner = this; } } SplinePath *SplinePath::GetLoop(void) { return loop; } void SplinePath::SetWatch(const char *name) { if (watchEnt != name) { watchEnt = name; if (watchEnt.length()) { doWatch = true; } else { doWatch = false; } } } void SplinePath::SetWatch(Event *ev) { SetWatch(ev->GetString(1)); } void SplinePath::NoWatch(void) { doWatch = true; watchEnt = "none"; } str SplinePath::GetWatch(void) { return watchEnt; } void SplinePath::SetFov(float newFov) { fov = newFov; } void SplinePath::SetFov(Event *ev) { fov = ev->GetFloat(1); } float SplinePath::GetFov(void) { return fov; } void SplinePath::SetFadeTime(float newFadeTime) { fadeTime = newFadeTime; } void SplinePath::SetFadeTime(Event *ev) { fadeTime = ev->GetFloat(1); } float SplinePath::GetFadeTime(void) { return fadeTime; }