16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-17 23:39:18 +02:00

scale/endscale/localscale per node model

This commit is contained in:
2026-05-10 13:12:49 +02:00
parent 587077629f
commit 3419666070
9 changed files with 277 additions and 31 deletions

View File

@@ -273,6 +273,7 @@ TAnimModel::is_keyword( std::string const &Token ) const {
|| ( Token == "lights" )
|| ( Token == "lightcolors" )
|| ( Token == "angles" )
|| ( Token == "scale" )
|| ( Token == "notransition" );
}
@@ -369,6 +370,23 @@ bool TAnimModel::Load(cParser *parser, bool ter)
>> vAngle[ 2 ];
}
if( token == "scale" ) {
// Per-node scale: `scale <x> <y> <z>` (always three tokens, mirroring
// the `angles` syntax). For uniform scaling, write the same value
// three times (e.g. `scale 2 2 2`). Combines multiplicatively with
// any active scale block from the scenariostateserializer (which is
// applied at deserialize_model time before Load() is called, so
// m_scale already reflects the outer block when we arrive here).
parser->getTokens( 3 );
glm::vec3 factor;
*parser >> factor.x >> factor.y >> factor.z;
if( factor.x > 0.0f && factor.y > 0.0f && factor.z > 0.0f ) {
m_scale.x *= factor.x;
m_scale.y *= factor.y;
m_scale.z *= factor.z;
}
}
if( token == "notransition" ) {
m_transition = false;
}
@@ -677,20 +695,21 @@ void TAnimModel::AnimUpdate(double dt)
});
}
// radius() subclass details, calculates node's bounding radius
// radius() subclass details, calculates node's bounding radius.
// For non-uniform scale we use the largest axis factor so the bounding sphere
// fully contains the scaled model — undersizing would cause incorrect culling.
float
TAnimModel::radius_() {
return (
pModel ?
pModel->bounding_radius() :
0.f );
if( pModel == nullptr ) { return 0.f; }
float const max_scale = std::max( { m_scale.x, m_scale.y, m_scale.z } );
return pModel->bounding_radius() * max_scale;
}
// serialize() subclass details, sends content of the subclass to provided stream
void
TAnimModel::serialize_( std::ostream &Output ) const {
// TODO: implement
}
// deserialize() subclass details, restores content of the subclass from provided stream
@@ -700,18 +719,29 @@ TAnimModel::deserialize_( std::istream &Input ) {
// TODO: implement
}
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream.
// Smart export: omit fields that match defaults so reloaded scenarios stay clean.
// - if X and Z rotation are zero, fold Y rotation into the 4th token slot
// (the legacy `node ... model X Y Z <rotation.y> ...` format) and skip the
// `angles` block entirely
// - if any axis of rotation needs all three components, emit `angles X Y Z`
// - emit `scale X Y Z` only when m_scale isn't (1,1,1)
void
TAnimModel::export_as_text_( std::ostream &Output ) const {
// header
Output << "model ";
// location and rotation
Output << std::fixed << std::setprecision(3) // ustawienie dokładnie 3 cyfr po przecinku
<< location().x << ' '
<< location().y << ' '
<< location().z << ' ';
Output
<< "0 " ;
// location and rotation. The 4th token after location is a legacy
// shorthand for the Y rotation. We use it (and skip the angles block)
// whenever the rotation is purely around Y, which is the common case.
bool const xz_rotation_zero = ( vAngle.x == 0.0f && vAngle.z == 0.0f );
Output << std::fixed << std::setprecision( 3 )
<< location().x << ' '
<< location().y << ' '
<< location().z << ' '
<< ( xz_rotation_zero ? vAngle.y : 0.0f ) << ' ';
// 3d shape
auto modelfile { (
pModel ?
@@ -748,11 +778,22 @@ TAnimModel::export_as_text_( std::ostream &Output ) const {
if( false == m_transition ) {
Output << "notransition" << ' ';
}
// footer
Output << "angles "
<< vAngle.x << ' '
<< vAngle.y << ' '
<< vAngle.z << ' ';
// angles directive only when X or Z are rotated — otherwise the Y angle
// already lives in the 4th token slot above.
if( false == xz_rotation_zero ) {
Output << "angles "
<< vAngle.x << ' '
<< vAngle.y << ' '
<< vAngle.z << ' ';
}
// scale directive only when actually scaled — keeps default-scale models
// from being polluted with redundant `scale 1 1 1` entries on every save.
if( m_scale.x != 1.0f || m_scale.y != 1.0f || m_scale.z != 1.0f ) {
Output << "scale "
<< m_scale.x << ' '
<< m_scale.y << ' '
<< m_scale.z << ' ';
}
// footer
Output
<< "endmodel"

View File

@@ -143,6 +143,26 @@ public:
glm::vec3
Angles() const {
return vAngle; }
// per-axis scale, applied between rotation and the submodel-local transform chain.
// (1,1,1) = unchanged. Set by the `scale`/`endscale` scenario directives or the
// optional `scale <factor>` / `scale <x> <y> <z>` token inside a `node model` block.
// Per-axis values let you stretch a model along a single dimension; uniform input
// (single float) broadcasts to all three axes.
inline
void
Scale( glm::vec3 const &Factor ) {
m_scale = glm::vec3(
Factor.x > 0.0f ? Factor.x : 1.0f,
Factor.y > 0.0f ? Factor.y : 1.0f,
Factor.z > 0.0f ? Factor.z : 1.0f ); }
inline
void
Scale( float const Factor ) {
Scale( glm::vec3( Factor ) ); }
inline
glm::vec3 const &
Scale() const {
return m_scale; }
// members
std::list<std::shared_ptr<TAnimContainer>> m_animlist;
@@ -169,6 +189,7 @@ public:
std::shared_ptr<TAnimContainer> pRoot; // pojemniki sterujące, tylko dla aniomowanych submodeli
TModel3d *pModel { nullptr };
glm::vec3 vAngle; // bazowe obroty egzemplarza względem osi
glm::vec3 m_scale { 1.0f, 1.0f, 1.0f }; // per-axis scale (see Scale() accessors above)
material_data m_materialdata;
std::string asText; // tekst dla wyświetlacza znakowego