mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 07:29:20 +02:00
* refactor(sonar): use override instead of redundant virtual (cpp:S3471) * refactor(sonar): remove empty statements, one declaration per statement (cpp:S1116, cpp:S1659) * refactor(sonar): drop redundant lambda return types (cpp:S3574) * refactor(sonar): prefer using over typedef (cpp:S5416) * refactor(sonar): use =default for trivial special members (cpp:S3490) * refactor(sonar): use nullptr literal (cpp:S4962) * refactor(sonar): remove redundant access specifiers (cpp:S3539) * refactor(sonar): remove redundant casts (cpp:S1905) * refactor(sonar): use std::scoped_lock instead of std::lock_guard (cpp:S5997) * refactor(sonar): construct elements in place with emplace_back (cpp:S6003) * refactor(sonar): make narrowing conversions explicit (cpp:S5276) * refactor(sonar): mark unused parameters (cpp:S1172) Unused names are commented out; parameters used only on some platforms are marked [[maybe_unused]]. * refactor(sonar): declare non-mutating member functions const (cpp:S5817) Virtual functions are left untouched so overrides keep matching. * refactor(sonar): merge collapsible nested if statements (cpp:S1066) Comments that preceded the inner if are kept above the merged statement. * refactor(sonar): scope single-use variables in if init-statements (cpp:S6004) * fix(sonar): give polymorphic base classes virtual destructors (cpp:S1235) * refactor(sonar): use try_emplace for map insertion (cpp:S6030) * refactor(sonar): use auto where the type is spelled out on the right (cpp:S5827) * refactor(sonar): remove redundant parentheses (cpp:S1110) * refactor(sonar): document intentionally empty functions (cpp:S1186) * refactor(sonar): remove dead stores (cpp:S1854) Side effects (e.g. LISTLINE++) are kept. * refactor(sonar): remove unused variables, captures and private fields (cpp:S1481, cpp:S1068) * refactor(sonar): use structured bindings for pair access (cpp:S6005) * refactor(sonar): use range-based loops and ranges algorithms (cpp:S5566) * refactor(sonar): split chained assignments (cpp:S1121) * refactor(sonar): add default cases to switch statements (cpp:S3562) * refactor(sonar): use std::size for array lengths (cpp:S7127) * refactor(sonar): use std::ranges algorithms and std::erase(_if) (cpp:S6197) * refactor(sonar): access static members through the class (cpp:S2209) * refactor(sonar): add braces around nested if-else (cpp:S5261) * refactor(sonar): avoid side effects inside typeid operands (cpp:S5279) * refactor(sonar): drop top-level const from by-value return types (cpp:S5951) * refactor(sonar): declare variables separately from their type definitions (cpp:S3646) * refactor(sonar): declare loop counters in the for statement (cpp:S5955) * refactor(sonar): rely on class template argument deduction (cpp:S6012) * refactor(sonar): inherit base constructors instead of forwarding them (cpp:S5952) * refactor(sonar): rename locals that shadow outer declarations (cpp:S1117) Only local variables and definition parameters are renamed; shadowing struct fields and enumerators in headers are left for a separate change. * refactor(sonar): reduce enum qualification with using enum (cpp:S6177) Applied only where no unqualified name in the block could be captured by the introduced enumerators. * refactor(sonar): mark converting constructors explicit (cpp:S1709) sound_source and the gl::object conversion operators stay implicit; they are relied upon by copy-list-initialisation and GL calls. * refactor(sonar): initialise members in initialiser lists or declarations (cpp:S3230) * refactor(sonar): make read-only local references and pointers const (cpp:S5350) * refactor(sonar): take read-only pointer and reference parameters as const (cpp:S995) TTrain command handlers keep their signature, it is fixed by the handler table. * refactor(sonar): pass expensive-to-copy parameters by const reference (cpp:S1238) Signatures declared in files touched by PR 206 (Driver.h, EU07.cpp) and virtual overrides are left unchanged. * refactor(sonar): use transparent comparators for string-keyed maps (cpp:S6045) std::unordered_map instances and map types exposed in headers used by other modules are left as they are. * fix(sonar): do not use UI text as a printf format string (cpp:S5281) Text lines containing '%' would otherwise be interpreted as format specifiers by ImGui::TextColored. * refactor(sonar): use std::bind_front for callbacks (cpp:S5995) * refactor(sonar): drop const_cast on string literals passed to the Python C API (cpp:S859) * refactor(sonar): take read-only string parameters as std::string_view (cpp:S6009) The Windows messaging helpers keep std::string, they need c_str(). * refactor(sonar): make read-only global tables const (cpp:S5421) Mutable global state (flags, timers, singletons) is left as is. * refactor(sonar): replace single-case switch statements with if (cpp:S1301) * refactor(sonar): format UUIDs and log timestamps with std::format (cpp:S6494, cpp:S5945) * refactor(sonar): use std::array for local fixed-size arrays (cpp:S5945) Arrays that are class members or part of headers are left unchanged. * fix(sonar): test the bearing damage bit, not bit 0 (cpp:S872) 'DamageFlag & dtrain_bearing > 0' parsed as 'DamageFlag & (dtrain_bearing > 0)', i.e. DamageFlag & 1, so bearing friction reacted to the wrong damage flag. * refactor(sonar): assorted small fixes - empty() instead of size() comparisons (cpp:S1155) - member initialisers in declaration order (cpp:S3229) - no redundant c_str() when a std::string is accepted (cpp:S7121) - contains() on associative containers (cpp:S6171) - const reference instead of a copying range-for (cpp:S6032) - starts_with() for prefix checks (cpp:S6178) - no std::move on temporaries (cpp:S5274) - has_value() on std::optional (cpp:S7172) - bool literals for bool members (cpp:S5820) - inline only on the definition (cpp:S3548) - explicit base access specifier (cpp:S5965) - explicit element access instead of dereferencing an array (cpp:S7116) - rely on the C++20 rewritten operator!= (cpp:S6186) * refactor(sonar): more small fixes - std::format instead of iostream manipulators in to_string/to_hex_str (cpp:S6495); output verified identical, negative hex values keep the unsigned form - remove an empty compound statement (cpp:S108) - drop an always-false unsigned < 0 branch (cpp:S1768) - explicit lambda capture (cpp:S3608) - std::make_unique (cpp:S5950) * chore: strip stray carriage returns left in TTrain constructor comments * fix: keep quantize() in its own value type The S5276 change cast the result of the generic quantize<Type_>() to unsigned int, which truncated float/double callers (shadow angle slider, editor rotation snapping, AI speed control steps) and broke negative values. * fix(sonar): use integer loop counters in segment sampling (cpp:S2193) * refactor(sonar): clean up issues reported on PR 209 - remove locals left unused after the dead-store cleanup (cpp:S1481, cpp:S1854) - unnamed unused parameter (cpp:S1172) - single-use declarations moved into if init-statements (cpp:S6004) - auto for casted initialisers (cpp:S5827) - const references and pointers where nothing is modified (cpp:S5350) - rename structured bindings shadowed by inner declarations (cpp:S1117)
516 lines
18 KiB
C++
516 lines
18 KiB
C++
/*
|
|
This Source Code Form is subject to the
|
|
terms of the Mozilla Public License, v.
|
|
2.0. If a copy of the MPL was not
|
|
distributed with this file, You can
|
|
obtain one at
|
|
http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
/*
|
|
MaSzyna EU07 locomotive simulator
|
|
Copyright (C) 2001-2004 Marcin Wozniak
|
|
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "vehicle/Gauge.h"
|
|
#include "utilities/parser.h"
|
|
#include "model/Model3d.h"
|
|
#include "vehicle/DynObj.h"
|
|
#include "utilities/Timer.h"
|
|
#include "utilities/Logs.h"
|
|
#include "rendering/renderer.h"
|
|
|
|
TGauge::TGauge( sound_source const &Soundtemplate ) :
|
|
m_soundtemplate( Soundtemplate )
|
|
{
|
|
m_soundfxincrease = m_soundtemplate;
|
|
m_soundfxdecrease = m_soundtemplate;
|
|
m_soundfxon = m_soundtemplate;
|
|
m_soundfxoff = m_soundtemplate;
|
|
}
|
|
|
|
void TGauge::Init(TSubModel *Submodel, TSubModel *Submodelon, TGaugeAnimation Type, float Scale, float Offset, float Friction, float Value, float const Endvalue, float const Endscale, bool const Interpolatescale )
|
|
{ // ustawienie parametrów animacji submodelu
|
|
SubModel = Submodel;
|
|
SubModelOn = Submodelon;
|
|
m_value = Value;
|
|
m_animation = Type;
|
|
m_scale = Scale;
|
|
m_offset = Offset;
|
|
m_friction = Friction;
|
|
m_interpolatescale = Interpolatescale;
|
|
m_endvalue = Endvalue;
|
|
m_endscale = Endscale;
|
|
|
|
if( m_animation == TGaugeAnimation::gt_Digital ) {
|
|
|
|
auto *sm { ( SubModel ? SubModel->ChildGet() : nullptr ) };
|
|
while( sm != nullptr ) {
|
|
// pętla po submodelach potomnych i obracanie ich o kąt zależy od cyfry w (fValue)
|
|
if( sm->pName.size() // musi mieć niepustą nazwę
|
|
&& std::isdigit(sm->pName[0]) ) {
|
|
sm->WillBeAnimated(); // wyłączenie optymalizacji
|
|
}
|
|
sm = sm->NextGet();
|
|
}
|
|
// do the same for the active version
|
|
sm = SubModelOn ? SubModelOn->ChildGet() : nullptr;
|
|
while( sm != nullptr ) {
|
|
// pętla po submodelach potomnych i obracanie ich o kąt zależy od cyfry w (fValue)
|
|
if( sm->pName.size() // musi mieć niepustą nazwę
|
|
&& std::isdigit(sm->pName[0]) ) {
|
|
sm->WillBeAnimated(); // wyłączenie optymalizacji
|
|
}
|
|
sm = sm->NextGet();
|
|
}
|
|
}
|
|
else {
|
|
// wyłączenie ignowania jedynkowego transformu
|
|
// a banan może być z optymalizacją?
|
|
if( SubModel != nullptr ) { SubModel->WillBeAnimated(); }
|
|
if( SubModelOn != nullptr ) { SubModelOn->WillBeAnimated(); }
|
|
}
|
|
// pass submodel location to defined sounds
|
|
auto const nulloffset { glm::vec3{} };
|
|
auto const offset { model_offset() };
|
|
{
|
|
std::vector soundfxs = {
|
|
&m_soundfxincrease,
|
|
&m_soundfxdecrease,
|
|
&m_soundfxon,
|
|
&m_soundfxoff
|
|
};
|
|
for( auto *soundfx : soundfxs ) {
|
|
if( soundfx->offset() == nulloffset ) {
|
|
soundfx->offset( offset );
|
|
}
|
|
}
|
|
}
|
|
for( auto &[soundfxkey, soundfxsource] : m_soundfxvalues ) {
|
|
if( soundfxsource.offset() == nulloffset ) {
|
|
soundfxsource.offset( offset );
|
|
}
|
|
}
|
|
};
|
|
|
|
void TGauge::Load( cParser &Parser, TDynamicObject const *Owner, double const mul ) {
|
|
|
|
std::string submodelname;
|
|
std::string gaugetypename;
|
|
float scale;
|
|
float endscale;
|
|
float endvalue;
|
|
float offset;
|
|
float friction;
|
|
endscale = -1;
|
|
endvalue = -1;
|
|
bool interpolatescale { false };
|
|
|
|
Parser.getTokens();
|
|
if( Parser.peek() != "{" ) {
|
|
// old fixed size config
|
|
Parser >> submodelname;
|
|
gaugetypename = Parser.getToken<std::string>( true );
|
|
Parser.getTokens( 3, false );
|
|
Parser
|
|
>> scale
|
|
>> offset
|
|
>> friction;
|
|
if( gaugetypename == "rotvar"
|
|
|| gaugetypename == "movvar" ) {
|
|
interpolatescale = true;
|
|
Parser.getTokens( 2, false );
|
|
Parser
|
|
>> endvalue
|
|
>> endscale;
|
|
}
|
|
}
|
|
else {
|
|
// new, block type config
|
|
// TODO: rework the base part into yaml-compatible flow style mapping
|
|
submodelname = Parser.getToken<std::string>( false );
|
|
gaugetypename = Parser.getToken<std::string>( true );
|
|
Parser.getTokens( 3, false );
|
|
Parser
|
|
>> scale
|
|
>> offset
|
|
>> friction;
|
|
if( gaugetypename == "rotvar"
|
|
|| gaugetypename == "movvar" ) {
|
|
interpolatescale = true;
|
|
Parser.getTokens( 2, false );
|
|
Parser
|
|
>> endvalue
|
|
>> endscale;
|
|
}
|
|
// new, variable length section
|
|
{
|
|
scratch_data scratchpad;
|
|
while( true == Load_mapping( Parser, scratchpad ) ) {
|
|
// all work done by while()
|
|
}
|
|
// post-deserialization cleanup
|
|
// set provided custom soundproofing to assigned sounds (for sounds without their own custom soundproofing)
|
|
if( scratchpad.soundproofing ) {
|
|
if( !m_soundfxincrease.soundproofing() ) {
|
|
m_soundfxincrease.soundproofing() = scratchpad.soundproofing;
|
|
}
|
|
if( !m_soundfxdecrease.soundproofing() ) {
|
|
m_soundfxdecrease.soundproofing() = scratchpad.soundproofing;
|
|
}
|
|
if( !m_soundfxon.soundproofing() ) {
|
|
m_soundfxon.soundproofing() = scratchpad.soundproofing;
|
|
}
|
|
if( !m_soundfxoff.soundproofing() ) {
|
|
m_soundfxoff.soundproofing() = scratchpad.soundproofing;
|
|
}
|
|
for( auto &[soundfxkey, soundfxsource] : m_soundfxvalues ) {
|
|
if( !soundfxsource.soundproofing() ) {
|
|
soundfxsource.soundproofing() = scratchpad.soundproofing;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// bind defined sounds with the button owner
|
|
m_soundfxincrease.owner( Owner );
|
|
m_soundfxdecrease.owner( Owner );
|
|
m_soundfxon.owner( Owner );
|
|
m_soundfxoff.owner( Owner );
|
|
for( auto &[soundfxkey, soundfxsource] : m_soundfxvalues ) {
|
|
soundfxsource.owner( Owner );
|
|
}
|
|
|
|
scale *= static_cast<float>(mul);
|
|
if( interpolatescale ) {
|
|
endscale *= static_cast<float>(mul);
|
|
}
|
|
TSubModel *submodel { nullptr };
|
|
std::array<TModel3d *, 2> sources { Owner->mdKabina, Owner->mdLowPolyInt };
|
|
for( auto const *source : sources ) {
|
|
if( source != nullptr
|
|
&& ( submodel = source->GetFromName( submodelname ) ) != nullptr ) {
|
|
// got what we wanted, bail out
|
|
break;
|
|
}
|
|
// there's a possibility the default submodel was named with _off suffix
|
|
if( source != nullptr
|
|
&& ( submodel = source->GetFromName( submodelname + "_off" ) ) != nullptr ) {
|
|
// got what we wanted, bail out
|
|
break;
|
|
}
|
|
}
|
|
if( submodel == nullptr ) {
|
|
ErrorLog( "Bad model: failed to locate sub-model \"" + submodelname + "\" in 3d model(s) of \"" + Owner->name() + "\"", logtype::model );
|
|
}
|
|
// see if we can locate optional submodel for active state, with _on suffix
|
|
TSubModel *submodelon { nullptr };
|
|
for( auto const *source : sources ) {
|
|
if( source != nullptr
|
|
&& ( submodelon = source->GetFromName( submodelname + "_on" ) ) != nullptr ) {
|
|
// got what we wanted, bail out
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::map<std::string, TGaugeAnimation, std::less<>> gaugetypes {
|
|
{ "rot", TGaugeAnimation::gt_Rotate },
|
|
{ "rotvar", TGaugeAnimation::gt_Rotate },
|
|
{ "mov", TGaugeAnimation::gt_Move },
|
|
{ "movvar", TGaugeAnimation::gt_Move },
|
|
{ "wip", TGaugeAnimation::gt_Wiper },
|
|
{ "dgt", TGaugeAnimation::gt_Digital }
|
|
};
|
|
auto lookup = gaugetypes.find( gaugetypename );
|
|
auto const type = lookup != gaugetypes.end() ? lookup->second : TGaugeAnimation::gt_Unknown;
|
|
|
|
Init( submodel, submodelon, type, scale, offset, friction, 0, endvalue, endscale, interpolatescale );
|
|
|
|
// return md2 != nullptr; // true, gdy podany model zewnętrzny, a w kabinie nie było
|
|
};
|
|
|
|
bool
|
|
TGauge::Load_mapping( cParser &Input, TGauge::scratch_data &Scratchpad ) {
|
|
|
|
// token can be a key or block end
|
|
auto const key { Input.getToken<std::string>( true, "\n\r\t ,;" ) };
|
|
if( true == key.empty() || key == "}" ) { return false; }
|
|
// if not block end then the key is followed by assigned value or sub-block
|
|
if( key == "type:" ) {
|
|
using enum TGaugeType;
|
|
auto const gaugetype { Input.getToken<std::string>( true, "\n\r\t ,;" ) };
|
|
m_type = gaugetype == "push" ? push :
|
|
gaugetype == "impulse" ? push :
|
|
gaugetype == "return" ? push :
|
|
gaugetype == "delayed" ? push_delayed :
|
|
gaugetype == "pushtoggle" ? pushtoggle :
|
|
toggle; // "toggle" and default
|
|
}
|
|
else if( key == "soundinc:" ) {
|
|
m_soundfxincrease.deserialize( Input, sound_type::single );
|
|
}
|
|
else if( key == "sounddec:" ) {
|
|
m_soundfxdecrease.deserialize( Input, sound_type::single );
|
|
}
|
|
else if( key == "soundon:" ) {
|
|
m_soundfxon.deserialize( Input, sound_type::single );
|
|
}
|
|
else if( key == "soundoff:" ) {
|
|
m_soundfxoff.deserialize( Input, sound_type::single );
|
|
}
|
|
else if( key == "soundproofing:" ) {
|
|
// custom soundproofing in format [ p1, p2, p3, p4, p5, p6 ]
|
|
Input.getTokens( 6, false, "\n\r\t ,;[]" );
|
|
std::array<float, 6> soundproofing;
|
|
Input
|
|
>> soundproofing[ 0 ]
|
|
>> soundproofing[ 1 ]
|
|
>> soundproofing[ 2 ]
|
|
>> soundproofing[ 3 ]
|
|
>> soundproofing[ 4 ]
|
|
>> soundproofing[ 5 ];
|
|
Scratchpad.soundproofing = soundproofing;
|
|
}
|
|
else if( key.starts_with("sound") ) {
|
|
// sounds assigned to specific gauge values, defined by key soundX: where X = value
|
|
auto const indexstart { key.find_first_of( "-1234567890" ) };
|
|
auto const indexend { key.find_first_not_of( "-1234567890", indexstart ) };
|
|
if( indexstart != std::string::npos ) {
|
|
m_soundfxvalues.try_emplace(
|
|
std::stoi( key.substr( indexstart, indexend - indexstart ) ),
|
|
sound_source( m_soundtemplate ).deserialize( Input, sound_type::single ) );
|
|
}
|
|
}
|
|
return true; // return value marks a key: value pair was extracted, nothing about whether it's recognized
|
|
}
|
|
|
|
// ustawienie wartości docelowej. plays provided fallback sound, if no sound was defined in the control itself
|
|
bool
|
|
TGauge::UpdateValue( float fNewDesired, std::optional<sound_source> &Fallbacksound ) {
|
|
|
|
if (false == UpdateValue( fNewDesired ) && Fallbacksound) {
|
|
Fallbacksound->play( m_soundtype );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
TGauge::UpdateValue( float fNewDesired ) {
|
|
|
|
auto const desiredtimes100 = static_cast<int>( std::round( 100.0 * fNewDesired ) );
|
|
if( desiredtimes100 == static_cast<int>( std::round( 100.0 * m_targetvalue ) ) ) {
|
|
return true;
|
|
}
|
|
m_targetvalue = fNewDesired;
|
|
// if there's any sound associated with new requested value, play it
|
|
// check value-specific table first...
|
|
if( auto const fullinteger { desiredtimes100 % 100 == 0 }; fullinteger ) {
|
|
// filter out values other than full integers
|
|
auto const lookup = m_soundfxvalues.find( desiredtimes100 / 100 );
|
|
if( lookup != m_soundfxvalues.end() ) {
|
|
lookup->second.play();
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
// toggle the control to continous range/exclusive sound mode from now on
|
|
m_soundtype = sound_flags::exclusive;
|
|
}
|
|
// ...and if there isn't any, fall back on the basic set...
|
|
// HACK: crude way to discern controls with continuous and quantized value range
|
|
if( auto const currentvalue = GetValue(); currentvalue < fNewDesired ) {
|
|
// shift up
|
|
if( false == m_soundfxincrease.empty() ) {
|
|
m_soundfxincrease.play( m_soundtype );
|
|
return true;
|
|
}
|
|
}
|
|
else if( currentvalue > fNewDesired ) {
|
|
// shift down
|
|
if( false == m_soundfxdecrease.empty() ) {
|
|
m_soundfxdecrease.play( m_soundtype );
|
|
return true;
|
|
}
|
|
}
|
|
return false; // no suitable sound was found
|
|
};
|
|
|
|
void TGauge::PutValue(float fNewDesired)
|
|
{ // McZapkie-281102: natychmiastowe wpisanie wartosci
|
|
m_targetvalue = fNewDesired;
|
|
m_value = m_targetvalue;
|
|
};
|
|
|
|
float TGauge::GetValue() const {
|
|
// we feed value in range 0-1 so we should be getting it reported in the same range
|
|
return m_value;
|
|
}
|
|
|
|
float TGauge::GetDesiredValue() const {
|
|
// we feed value in range 0-1 so we should be getting it reported in the same range
|
|
return m_targetvalue;
|
|
}
|
|
|
|
void TGauge::Update( bool const Power ) {
|
|
// update value
|
|
// TODO: remove passing manually power state when LD is in place
|
|
if( m_value != m_targetvalue ) {
|
|
auto dt = static_cast<float>(Timer::GetDeltaTime());
|
|
if( m_friction > 0 && dt < 0.5 * m_friction ) {
|
|
// McZapkie-281102: zabezpieczenie przed oscylacjami dla dlugich czasow
|
|
m_value += dt * ( m_targetvalue - m_value ) / m_friction;
|
|
if( std::abs( m_targetvalue - m_value ) <= 0.0001 ) {
|
|
// close enough, we can stop updating the model
|
|
m_value = m_targetvalue; // set it exactly as requested just in case it matters
|
|
}
|
|
}
|
|
else {
|
|
m_value = m_targetvalue;
|
|
}
|
|
}
|
|
// update submodel visibility
|
|
auto const state { Power && ( m_stateinput ? *m_stateinput : false ) };
|
|
if( state != m_state ) {
|
|
// on state change play assigned sound
|
|
if( true == state ) { m_soundfxon.play(); }
|
|
else { m_soundfxoff.play(); }
|
|
}
|
|
m_state = state;
|
|
// toggle submodel visibility only if the active state submodel is present,
|
|
// keep the default model always visible otherwise
|
|
if( SubModelOn != nullptr ) {
|
|
SubModelOn->iVisible = m_state;
|
|
if( SubModel != nullptr ) {
|
|
SubModel->iVisible = !m_state;
|
|
}
|
|
}
|
|
// update submodel animations
|
|
UpdateAnimation( SubModel );
|
|
UpdateAnimation( SubModelOn );
|
|
};
|
|
|
|
void TGauge::AssignFloat(float *fValue)
|
|
{
|
|
m_datatype = 'f';
|
|
fData = fValue;
|
|
};
|
|
|
|
void TGauge::AssignDouble(double *dValue)
|
|
{
|
|
m_datatype = 'd';
|
|
dData = dValue;
|
|
};
|
|
|
|
void TGauge::AssignInt(int *iValue)
|
|
{
|
|
m_datatype = 'i';
|
|
iData = iValue;
|
|
};
|
|
|
|
void TGauge::AssignBool(bool *bValue)
|
|
{
|
|
m_datatype = 'b';
|
|
bData = bValue;
|
|
};
|
|
|
|
void TGauge::UpdateValue()
|
|
{ // ustawienie wartości docelowej z parametru
|
|
switch (m_datatype)
|
|
{ // to nie jest zbyt optymalne, można by zrobić osobne funkcje
|
|
case 'f': {
|
|
UpdateValue( *fData );
|
|
break;
|
|
}
|
|
case 'd': {
|
|
UpdateValue( static_cast<float>(*dData) );
|
|
break;
|
|
}
|
|
case 'i': {
|
|
UpdateValue( static_cast<float>(*iData) );
|
|
break;
|
|
}
|
|
case 'b': {
|
|
UpdateValue( *bData ? 1.f : 0.f );
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
void TGauge::AssignState( bool const *State ) {
|
|
|
|
m_stateinput = State;
|
|
}
|
|
|
|
float TGauge::GetScaledValue() const {
|
|
|
|
return false == m_interpolatescale ? m_value * m_scale + m_offset : m_value * std::lerp(m_scale, m_endscale, std::clamp(m_value / m_endvalue, 0.f, 1.f)) + m_offset;
|
|
}
|
|
|
|
void
|
|
TGauge::UpdateAnimation( TSubModel *Submodel ) const {
|
|
|
|
if( Submodel == nullptr ) { return; }
|
|
|
|
switch (m_animation) {
|
|
case TGaugeAnimation::gt_Rotate: {
|
|
Submodel->SetRotate( float3( 0, 1, 0 ), static_cast<float>(GetScaledValue() * 360.0) );
|
|
break;
|
|
}
|
|
case TGaugeAnimation::gt_Move: {
|
|
Submodel->SetTranslate( float3( 0, 0, GetScaledValue() ) );
|
|
break;
|
|
}
|
|
case TGaugeAnimation::gt_Wiper: {
|
|
auto const scaledvalue { GetScaledValue() };
|
|
Submodel->SetRotate( float3( 0, 1, 0 ), static_cast<float>(scaledvalue * 360.0) );
|
|
if( auto *sm = Submodel->ChildGet(); sm ) {
|
|
sm->SetRotate( float3( 0, 1, 0 ), static_cast<float>(scaledvalue * 360.0) );
|
|
sm = sm->ChildGet();
|
|
if( sm )
|
|
sm->SetRotate( float3( 0, 1, 0 ), static_cast<float>(scaledvalue * 360.0) );
|
|
}
|
|
break;
|
|
}
|
|
case TGaugeAnimation::gt_Digital: {
|
|
// Ra 2014-07: licznik cyfrowy
|
|
auto *sm = Submodel->ChildGet();
|
|
/* std::string n = FormatFloat( "0000000000", floor( fValue ) ); // na razie tak trochę bez sensu
|
|
*/ std::string n( "000000000" + std::to_string( static_cast<int>( std::floor( GetScaledValue() ) ) ) );
|
|
if( n.length() > 10 ) { n.erase( 0, n.length() - 10 ); } // also dumb but should work for now
|
|
do { // pętla po submodelach potomnych i obracanie ich o kąt zależy od cyfry w (fValue)
|
|
if( sm->pName.size()
|
|
&& std::isdigit(sm->pName[0]) ) {
|
|
sm->SetRotate(
|
|
float3( 0, 1, 0 ),
|
|
static_cast<float>(-36.0 * ( n[ '0' + 9 - sm->pName[ 0 ] ] - '0' )) );
|
|
}
|
|
sm = sm->NextGet();
|
|
} while( sm );
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// returns offset of submodel associated with the button from the model centre
|
|
glm::vec3
|
|
TGauge::model_offset() const {
|
|
|
|
return SubModel != nullptr ? SubModel->offset(1.f) : glm::vec3();
|
|
}
|
|
|
|
TGaugeType
|
|
TGauge::type() const {
|
|
return m_type;
|
|
}
|
|
//---------------------------------------------------------------------------
|