diff --git a/engine/src/cmd/drawable.cpp b/engine/src/cmd/drawable.cpp index b19d1e960f..a0ae4bd89d 100644 --- a/engine/src/cmd/drawable.cpp +++ b/engine/src/cmd/drawable.cpp @@ -29,9 +29,24 @@ #include "gfx/mesh.h" #include "gfx/quaternion.h" #include "unit_generic.h" +#include "gfx/point_to_cam.h" +#include "gfx/halo_system.h" +#include "options.h" +#include "unit.h" +#include "weapon_info.h" +#include "beam.h" #include +// Dupe to same function in unit.cpp +// TODO: remove duplication +inline static float perspectiveFactor( float d ) +{ + if (d > 0) + return g_game.x_resolution*GFXGetZPerspective( d ); + else + return 1.0f; +} Drawable::Drawable() : animatedMesh(true), @@ -266,3 +281,220 @@ bool Drawable::animationRuns() const { return !done; } + +Matrix* GetCumulativeTransformationMatrix(Unit *unit, const Matrix &parentMatrix, Matrix invview) { + Matrix *ctm = &unit->cumulative_transformation_matrix; + + if (unit->graphicOptions.FaceCamera == 1) { + Vector p, q, r; + QVector pos( ctm->p ); + float wid, hei; + float magr = parentMatrix.getR().Magnitude(); + float magp = parentMatrix.getP().Magnitude(); + float magq = parentMatrix.getQ().Magnitude(); + CalculateOrientation( pos, p, q, r, wid, hei, 0, false, ctm ); + VectorAndPositionToMatrix( invview, p*magp, q*magq, r*magr, ctm->p ); + ctm = &invview; + } + + return ctm; +} + + +/** + * @brief Drawable::Sparkle caused damaged units to emit sparks + */ +void Drawable::Sparkle(bool on_screen, Matrix *ctm) { + Unit *unit = static_cast(this); + const Vector velocity = unit->GetVelocity(); + + // Docked units don't sparkle + // Move to a separate isDocked() function + if(unit->docked&(unit->DOCKED|unit->DOCKED_INSIDE)) { + return; + } + + // Units not shown don't sparkle + if(!on_screen) { + return; + } + + // Obviously, don't sparkle if the option isn't set + if(unit->graphicOptions.NoDamageParticles) { + return; + } + + // Destroyed units (dying?) don't sparkle + if(unit->GetHull() <= 0) { + return; + } + + // Units with no meshes, don't sparkle + if(unit->nummesh() <= 0) { + return; + } + + // Undamaged units don't sparkle + float damage_level = unit->hull/unit->maxhull; + if(damage_level >= .99) { + return; + } + + double sparkle_accum = GetElapsedTime() * game_options.sparklerate; + int spawn = (int) (sparkle_accum); + sparkle_accum -= spawn; + + + // Pretty sure the following is the equivalent of the commented code + // unsigned int switcher = (damagelevel > .8) ? 1 + //: (damagelevel > .6) ? 2 : (damagelevel > .4) ? 3 : (damagelevel > .2) ? 4 : 5; + unsigned int switcher = 5 * (1 - damage_level); + + long seed = (long) this; + + while (spawn-- > 0) { + switch (switcher) + { + case 5: + seed += 165; + case 4: + seed += 47; + case 3: + seed += 61; + case 2: + seed += 65537; + default: + seed += 257; + } + + LaunchOneParticle( *ctm, velocity, seed, unit, damage_level, unit->faction ); + } +} + + +void Drawable::DrawHalo(bool on_screen, float apparent_size, Matrix wmat, int cloak) { + Unit *unit = static_cast(this); + GameUnit *game_unit = static_cast(this); + + // Units not shown don't emit a halo + if(!on_screen) { + return; + } + + // Units with no halo + if(game_unit->phalos->NumHalos() == 0) { + return; + } + + // Docked units + if(unit->docked&(unit->DOCKED|unit->DOCKED_INSIDE)) { + return; + } + + // Small units + if ( apparent_size <= 5.0f ) { + return; + } + + + Vector linaccel = unit->GetAcceleration(); + Vector angaccel = unit->GetAngularAcceleration(); + float maxaccel = unit->GetMaxAccelerationInDirectionOf( wmat.getR(), true ); + Vector velocity = unit->GetVelocity(); + + float cmas = unit->computer.max_ab_speed()*unit->computer.max_ab_speed(); + if (cmas == 0) + cmas = 1; + + Vector Scale( 1, 1, 1 ); //Now, HaloSystem handles that + float damage_level = unit->hull/unit->maxhull; + //WARNING: cmas is not a valid maximum speed for the upcoming multi-direction thrusters, + //nor is maxaccel. Instead, each halo should have its own limits specified in units.csv + float nebd = (_Universe->AccessCamera()->GetNebula() == unit->nebula && unit->nebula != nullptr) ? -1 : 0; + float hulld = unit->GetHull() > 0 ? damage_level : 1.0; + game_unit->phalos->Draw( wmat, Scale, cloak, nebd, hulld, velocity, + linaccel, angaccel, maxaccel, cmas, unit->faction ); + +} + + +void Drawable::DrawSubunits(bool on_screen, Matrix wmat, int cloak, float average_scale, unsigned char char_damage) { + Unit *unit = static_cast(this); + Transformation *ct = &unit->cumulative_transformation; + + // Units not shown don't draw subunits + if(!on_screen) { + return; + } + + // Don't draw mounts if game is set not to... + if(!game_options.draw_weapons) { + return; + } + + for (int i = 0; (int) i < unit->getNumMounts(); i++) { + Mount *mount = &unit->mounts[i]; + Mesh *gun = mount->type->gun; + + // Don't bother drawing small mounts ?! + if (mount->xyscale == 0 || mount->zscale == 0) { + continue; + } + + // Don't draw unchosen GUN mounts, whatever that means + // Does not cover beams for some reason. + if (gun && mount->status == Mount::UNCHOSEN) { + continue; + } + + Transformation mountLocation( mount->GetMountOrientation(), mount->GetMountLocation().Cast() ); + mountLocation.Compose( *ct, wmat ); + Matrix mat; + mountLocation.to_matrix( mat ); + if (GFXSphereInFrustum( mountLocation.position, gun->rSize()*average_scale ) > 0) { + float d = ( mountLocation.position-_Universe->AccessCamera()->GetPosition() ).Magnitude(); + float pixradius = gun->rSize()*perspectiveFactor( + (d-gun->rSize() < g_game.znear) ? g_game.znear : d-gun->rSize() ); + float lod = pixradius * g_game.detaillevel; + if (lod > 0.5 && pixradius > 2.5) { + ScaleMatrix( mat, Vector( mount->xyscale, mount->xyscale, mount->zscale ) ); + gun->setCurrentFrame( unit->mounts[i].ComputeAnimatedFrame( gun ) ); + gun->Draw( lod, mat, d, cloak, + (_Universe->AccessCamera()->GetNebula() == unit->nebula && unit->nebula != NULL) ? -1 : 0, + char_damage, + true ); //cloakign and nebula + } + if (mount->type->gun1) { + pixradius = gun->rSize()*perspectiveFactor( + (d-gun->rSize() < g_game.znear) ? g_game.znear : d-gun->rSize() ); + lod = pixradius * g_game.detaillevel; + if (lod > 0.5 && pixradius > 2.5) { + gun = mount->type->gun1; + gun->setCurrentFrame( unit->mounts[i].ComputeAnimatedFrame( gun ) ); + gun->Draw( lod, mat, d, cloak, + (_Universe->AccessCamera()->GetNebula() == unit->nebula && unit->nebula + != NULL) ? -1 : 0, + char_damage, true ); //cloakign and nebula + } + } + } + + + // If not a beam weapon, stop processing + if (unit->mounts[i].type->type != WEAPON_TYPE::BEAM) { + continue; + } + + // If gun is null (?) + if (!unit->mounts[i].ref.gun) { + continue; + } + + unit->mounts[i].ref.gun->Draw( *ct, wmat, + ( isAutoTrackingMount(unit->mounts[i].size) + && (unit->mounts[i].time_to_lock <= 0) + && unit->TargetTracked() ) ? unit->Target() : NULL, + unit->computer.radar.trackingcone ); + } +} + diff --git a/engine/src/cmd/drawable.h b/engine/src/cmd/drawable.h index 1d42249e1c..0c145f5167 100644 --- a/engine/src/cmd/drawable.h +++ b/engine/src/cmd/drawable.h @@ -121,8 +121,12 @@ class Drawable virtual void Draw( const Transformation &quat = identity_transformation, const Matrix &m = identity_matrix ) = 0; virtual void DrawNow( const Matrix &m = identity_matrix, float lod = 1000000000 ) = 0; virtual std::string drawableGetName() = 0; -}; + void Sparkle(bool on_screen, Matrix *ctm); + void DrawHalo(bool on_screen, float apparent_size, Matrix wmat, int cloak); + void DrawSubunits(bool on_screen, Matrix wmat, int cloak, float average_scale, unsigned char char_damage); +}; +Matrix* GetCumulativeTransformationMatrix(Unit *unit, const Matrix &parentMatrix, Matrix invview); #endif // DRAWABLE_H diff --git a/engine/src/cmd/unit.cpp b/engine/src/cmd/unit.cpp index 928464f5f8..3a3059f197 100644 --- a/engine/src/cmd/unit.cpp +++ b/engine/src/cmd/unit.cpp @@ -350,19 +350,8 @@ void GameUnit::Draw( const Transformation &parent, const Matrix &parentMatrix ) this->cumulative_transformation.Compose( parent, parentMatrix ); this->cumulative_transformation.to_matrix( this->cumulative_transformation_matrix ); - ctm = &this->cumulative_transformation_matrix; + ctm = GetCumulativeTransformationMatrix(this, parentMatrix, invview); ct = &this->cumulative_transformation; - if (this->graphicOptions.FaceCamera == 1) { - Vector p, q, r; - QVector pos( ctm->p ); - float wid, hei; - float magr = parentMatrix.getR().Magnitude(); - float magp = parentMatrix.getP().Magnitude(); - float magq = parentMatrix.getQ().Magnitude(); - CalculateOrientation( pos, p, q, r, wid, hei, 0, false, ctm ); - VectorAndPositionToMatrix( invview, p*magp, q*magq, r*magr, ctm->p ); - ctm = &invview; - } #ifdef PERFRAMESOUND AUDAdjustSound( sound.engine, cumulative_transformation.position, GetVelocity() ); @@ -515,99 +504,10 @@ void GameUnit::Draw( const Transformation &parent, const Matrix &parentMatrix ) * delete tmpiter; **/ if (cam_setup_phase) return; - int nummounts = this->getNumMounts(); - for (i = 0; (int) i < nummounts; i++) { - Mount *mahnt = &this->mounts[i]; - if (game_options.draw_weapons && On_Screen) - if (mahnt->xyscale != 0 && mahnt->zscale != 0) { - Mesh *gun = mahnt->type->gun; - if (gun && mahnt->status != Mount::UNCHOSEN) { - Transformation mountLocation( mahnt->GetMountOrientation(), mahnt->GetMountLocation().Cast() ); - mountLocation.Compose( *ct, wmat ); - Matrix mat; - mountLocation.to_matrix( mat ); - if (GFXSphereInFrustum( mountLocation.position, gun->rSize()*avgscale ) > 0) { - float d = ( mountLocation.position-_Universe->AccessCamera()->GetPosition() ).Magnitude(); - float pixradius = gun->rSize()*perspectiveFactor( - (d-gun->rSize() < g_game.znear) ? g_game.znear : d-gun->rSize() ); - float lod = pixradius * g_game.detaillevel; - if (lod > 0.5 && pixradius > 2.5) { - ScaleMatrix( mat, Vector( mahnt->xyscale, mahnt->xyscale, mahnt->zscale ) ); - gun->setCurrentFrame( this->mounts[i].ComputeAnimatedFrame( gun ) ); - gun->Draw( lod, mat, d, cloak, - (_Universe->AccessCamera()->GetNebula() == this->nebula && this->nebula != NULL) ? -1 : 0, - chardamage, - true ); //cloakign and nebula - } - if (mahnt->type->gun1) { - pixradius = gun->rSize()*perspectiveFactor( - (d-gun->rSize() < g_game.znear) ? g_game.znear : d-gun->rSize() ); - lod = pixradius * g_game.detaillevel; - if (lod > 0.5 && pixradius > 2.5) { - gun = mahnt->type->gun1; - gun->setCurrentFrame( this->mounts[i].ComputeAnimatedFrame( gun ) ); - gun->Draw( lod, mat, d, cloak, - (_Universe->AccessCamera()->GetNebula() == this->nebula && this->nebula - != NULL) ? -1 : 0, - chardamage, true ); //cloakign and nebula - } - } - } - } - } - if (this->mounts[i].type->type == WEAPON_TYPE::BEAM) - if (this->mounts[i].ref.gun) - this->mounts[i].ref.gun->Draw( *ct, wmat, - ( isAutoTrackingMount(this->mounts[i].size) - && (this->mounts[i].time_to_lock <= 0) - && Unit::TargetTracked() ) ? Unit::Target() : NULL, - this->computer.radar.trackingcone ); - } - if ( On_Screen && (phalos->NumHalos() > 0) && !( this->docked&(DOCKED|DOCKED_INSIDE) ) && (Apparent_Size > 5.0f) ) { - Vector linaccel = this->GetAcceleration(); - Vector angaccel = this->GetAngularAcceleration(); - float maxaccel = this->GetMaxAccelerationInDirectionOf( wmat.getR(), true ); - Vector velocity = this->GetVelocity(); - - float cmas = this->computer.max_ab_speed()*this->computer.max_ab_speed(); - if (cmas == 0) - cmas = 1; - Vector Scale( 1, 1, 1 ); //Now, HaloSystem handles that - //WARNING: cmas is not a valid maximum speed for the upcoming multi-direction thrusters, - //nor is maxaccel. Instead, each halo should have its own limits specified in units.csv - float nebd = (_Universe->AccessCamera()->GetNebula() == this->nebula && this->nebula != NULL) ? -1 : 0; - float hulld = this->GetHull() > 0 ? damagelevel : 1.0; - phalos->Draw( wmat, Scale, cloak, nebd, hulld, velocity, linaccel, angaccel, maxaccel, cmas, this->faction ); - } - if ( On_Screen && !graphicOptions.NoDamageParticles - && !( this->docked&(DOCKED|DOCKED_INSIDE) ) ) { - if (damagelevel < .99 && this->nummesh() > 0 && this->GetHull() > 0) { - unsigned int switcher = (damagelevel > .8) ? 1 - : (damagelevel > .6) ? 2 : (damagelevel > .4) ? 3 : (damagelevel > .2) ? 4 : 5; - sparkle_accum += GetElapsedTime()*game_options.sparklerate; - int spawn = (int) (sparkle_accum); - sparkle_accum -= spawn; - while (spawn-- > 0) { - switch (switcher) - { - case 5: - LaunchOneParticle( *ctm, this->GetVelocity(), ( (long) this )+165, this, damagelevel, this->faction ); - case 4: - LaunchOneParticle( *ctm, this->GetVelocity(), ( (long) this )+47, this, damagelevel, this->faction ); - case 3: - LaunchOneParticle( *ctm, this->GetVelocity(), ( (long) this )+61, this, damagelevel, this->faction ); - case 2: - LaunchOneParticle( *ctm, this->GetVelocity(), ( (long) this )+65537, this, damagelevel, this->faction ); - default: - LaunchOneParticle( *ctm, this->GetVelocity(), ( (long) this )+257, this, damagelevel, this->faction ); - } - } - } else { - sparkle_accum = 0; - } - } else { - sparkle_accum = 0; - } + + DrawSubunits(On_Screen, wmat, cloak, avgscale, chardamage); + DrawHalo(On_Screen, Apparent_Size, wmat, cloak); + Sparkle(On_Screen, ctm); } @@ -623,72 +523,6 @@ void GameUnit::Draw() } -static float parseFloat( const std::string &s ) -{ - if ( s.empty() ) { - VS_LOG(info, (boost::format("WARNING: invalid float: %1%") % s)); - return 0.f; - } else { - return XMLSupport::parse_floatf( s ); - } -} - -static void parseFloat4( const std::string &s, float value[4] ) -{ - string::size_type ini = 0, end; - int i = 0; - while (i < 4 && ini != string::npos) { - value[i++] = parseFloat( s.substr( ini, end = s.find_first_of( ',', ini ) ) ); - ini = ( (end == string::npos) ? end : (end+1) ); - } - //if (i >= 4 && ini != string::npos) { - // VS_LOG(info, (boost::format("WARNING: invalid float4: %1%") % s)); - //} - while (i < 4) { - value[i++] = 0; - } -} - - -void GameUnit::applyTechniqueOverrides(const map &overrides) -{ - for (vector::iterator mesh = this->meshdata.begin(); mesh != this->meshdata.end(); ++mesh) { - if (*mesh != NULL) { - // First check to see if the technique holds any parameter being overridden - TechniquePtr technique = (*mesh)->getTechnique(); - if (technique.get() != NULL) { - bool doOverride = false; - for (int passno = 0; !doOverride && passno < technique->getNumPasses(); ++passno) { - const Pass &pass = technique->getPass(passno); - for (size_t paramno = 0; !doOverride && paramno < pass.getNumShaderParams(); ++paramno) { - if (overrides.count(pass.getShaderParam(paramno).name) > 0) - doOverride = true; - } - } - - if (doOverride) { - // Prepare a new technique with the overrides - // (make sure the technique has been compiled though - - // parameter values don't really need recompilation) - TechniquePtr newtechnique = TechniquePtr(new Technique(*technique)); - for (int passno = 0; passno < technique->getNumPasses(); ++passno) { - Pass &pass = technique->getPass(passno); - for (size_t paramno = 0; paramno < pass.getNumShaderParams(); ++paramno) { - Pass::ShaderParam ¶m = pass.getShaderParam(paramno); - map::const_iterator override = overrides.find(param.name); - if (override != overrides.end()) - parseFloat4(override->second, param.value); - } - } - - (*mesh)->setTechnique(newtechnique); - } - } - } - } -} - - Matrix GameUnit::WarpMatrix( const Matrix &ctm ) const { if ( this->GetWarpVelocity().MagnitudeSquared() < (game_options.warp_stretch_cutoff * game_options.warp_stretch_cutoff * game_options.game_speed * game_options.game_speed ) @@ -711,24 +545,3 @@ Matrix GameUnit::WarpMatrix( const Matrix &ctm ) const } } -using Orders::FireAt; - -#if 0 - -void GameUnit::SwapOutHalos() -{ - for (int i = 0; i < numhalos; i++) - //float x,y; - //halos[i]->GetDimensions (x,y); //halos[i]->SetDimensions (x/(1024),y/(1024)); - halos[i]->Draw( cumulative_transformation, cumulative_transformation_matrix, 0 ); -} - -void GameUnit::SwapInHalos() -{ - for (int i = 0; i < numhalos; i++) { - //float x,y; - //halos[i]->GetDimensions (x,y); - //halos[i]->SetDimensions (x*(1024),y*(1024)); - } -} -#endif diff --git a/engine/src/cmd/unit.h b/engine/src/cmd/unit.h index 61337691c9..a089594327 100644 --- a/engine/src/cmd/unit.h +++ b/engine/src/cmd/unit.h @@ -119,9 +119,7 @@ class GameUnit : public Unit const GFXColor &col, std::string halo_type, float halo_speed ); - - virtual void applyTechniqueOverrides(const std::map &overrides); - + /* ************************************************************************************** **** STAR SYSTEM STUFF *** diff --git a/engine/src/cmd/unit_generic.cpp b/engine/src/cmd/unit_generic.cpp index 52ed67e180..48a0bcece2 100644 --- a/engine/src/cmd/unit_generic.cpp +++ b/engine/src/cmd/unit_generic.cpp @@ -4884,10 +4884,77 @@ void Unit::RequestPhysics() getStarSystem()->RequestPhysics( this, cur_sim_queue_slot ); } +static float parseFloat( const std::string &s ) +{ + if ( s.empty() ) { + return 0.0f; + } + + return XMLSupport::parse_floatf( s ); +} + +static inline void parseFloat4( const std::string &s, float value[4] ) +{ + string::size_type ini = 0, end; + int i = 0; + while (i < 4 && ini != string::npos) { + value[i++] = parseFloat( s.substr( ini, end = s.find_first_of( ',', ini ) ) ); + ini = ( (end == string::npos) ? end : (end+1) ); + } + //if (i >= 4 && ini != string::npos) { + //VS_LOG(info, (boost::format("WARNING: invalid float4: %1%") % s)); + //} + while (i < 4) { + value[i++] = 0; + } +} void Unit::applyTechniqueOverrides(const std::map &overrides) { - // No-op - // FIXME ? + //for (vector::iterator mesh = this->meshdata.begin(); mesh != this->meshdata.end(); ++mesh) { + for(Mesh* mesh: meshdata) { + if (mesh == nullptr) { + continue; + } + + // First check to see if the technique holds any parameter being overridden + TechniquePtr technique = mesh->getTechnique(); + + if (technique.get() == nullptr) { + continue; + } + + // Can't be any lower, or goto won't work + TechniquePtr newtechnique; + + for (int passno = 0; passno < technique->getNumPasses(); ++passno) { + const Pass &pass = technique->getPass(passno); + for (size_t paramno = 0; paramno < pass.getNumShaderParams(); ++paramno) { + if (overrides.count(pass.getShaderParam(paramno).name) > 0) { + goto do_not_override; + } + } + } + + // Prepare a new technique with the overrides + // (make sure the technique has been compiled though - + // parameter values don't really need recompilation) + newtechnique = TechniquePtr(new Technique(*technique)); + for (int passno = 0; passno < technique->getNumPasses(); ++passno) { + Pass &pass = technique->getPass(passno); + for (size_t paramno = 0; paramno < pass.getNumShaderParams(); ++paramno) { + Pass::ShaderParam ¶m = pass.getShaderParam(paramno); + map::const_iterator override = overrides.find(param.name); + if (override != overrides.end()) { + parseFloat4(override->second, param.value); + } + } + } + + mesh->setTechnique(newtechnique); + + + do_not_override: ; + } } std::map< string, Unit * > Drawable::Units; diff --git a/engine/src/cmd/unit_generic.h b/engine/src/cmd/unit_generic.h index bc796c4f37..9a6f6d4b3d 100644 --- a/engine/src/cmd/unit_generic.h +++ b/engine/src/cmd/unit_generic.h @@ -380,9 +380,12 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, p void setUnitRole( const std::string &s ); const std::string& getAttackPreference() const; void setAttackPreference( const std::string &s ); -protected: Nebula *nebula = nullptr; + +protected: + + //The orbit needs to have access to the velocity directly to disobey physics laws to precalculate orbits friend class PlanetaryOrbit; friend class ContinuousTerrain; diff --git a/engine/src/gfx/technique.cpp b/engine/src/gfx/technique.cpp index 09f9c1f2c5..665da1b06c 100644 --- a/engine/src/gfx/technique.cpp +++ b/engine/src/gfx/technique.cpp @@ -151,28 +151,9 @@ Pass::BlendMode parseBlendMode(const std::string &s) return parseEnum(s, enumMap); } -static float parseFloat( const std::string &s ) -{ - if ( s.empty() ) throw InvalidParameters( "Invalid float attribute" ); - else - return XMLSupport::parse_floatf( s ); -} -void parseFloat4( const std::string &s, float value[4] ) -{ - string::size_type ini = 0, end; - int i = 0; - while (i < 4 && ini != string::npos) { - value[i++] = parseFloat( s.substr( ini, end = s.find_first_of( ',', ini ) ) ); - ini = ( (end == string::npos) ? end : (end+1) ); - } - //if (i >= 4 && ini != string::npos) { - //VS_LOG(info, (boost::format("WARNING: invalid float4: %1%") % s)); - //} - while (i < 4) { - value[i++] = 0; - } -} + + //end namespace };