mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-22 17:29:18 +02:00
scale/endscale/localscale per node model
This commit is contained in:
@@ -20,7 +20,7 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#include "application/editoruilayer.h"
|
#include "application/editoruilayer.h"
|
||||||
#include "rendering/renderer.h"
|
#include "rendering/renderer.h"
|
||||||
|
|
||||||
void itemproperties_panel::update(scene::basic_node const *Node)
|
void itemproperties_panel::update(scene::basic_node *Node)
|
||||||
{
|
{
|
||||||
m_node = Node;
|
m_node = Node;
|
||||||
|
|
||||||
@@ -290,12 +290,71 @@ void itemproperties_panel::render()
|
|||||||
{
|
{
|
||||||
ImGui::TextColored(ImVec4(line.color.r, line.color.g, line.color.b, line.color.a), line.data.c_str());
|
ImGui::TextColored(ImVec4(line.color.r, line.color.g, line.color.b, line.color.a), line.data.c_str());
|
||||||
}
|
}
|
||||||
|
// transform editor (position/rotation/scale) — TAnimModel only
|
||||||
|
render_transform_editor();
|
||||||
// group section
|
// group section
|
||||||
render_group();
|
render_group();
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In-place editor for position (double precision), rotation (degrees, 0-360),
|
||||||
|
// and uniform scale (per-axis float, 1.000) of a picked TAnimModel.
|
||||||
|
// Other node subclasses don't expose these knobs through the same API, so the
|
||||||
|
// editor short-circuits when the bound node isn't a TAnimModel.
|
||||||
|
void itemproperties_panel::render_transform_editor()
|
||||||
|
{
|
||||||
|
if (m_node == nullptr) { return; }
|
||||||
|
if (typeid(*m_node) != typeid(TAnimModel)) { return; }
|
||||||
|
auto *picked = static_cast<TAnimModel *>(m_node);
|
||||||
|
|
||||||
|
if (false == ImGui::CollapsingHeader("Transform", ImGuiTreeNodeFlags_DefaultOpen))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position — full double precision via DragScalarN. World coordinates can grow
|
||||||
|
// large; %.6f gives sub-millimetre resolution at typical scenario distances.
|
||||||
|
{
|
||||||
|
glm::dvec3 location = picked->location();
|
||||||
|
double pos[3] = { location.x, location.y, location.z };
|
||||||
|
if (ImGui::DragScalarN("position", ImGuiDataType_Double, pos, 3, 0.05f, nullptr, nullptr, "%.6f"))
|
||||||
|
{
|
||||||
|
picked->location(glm::dvec3(pos[0], pos[1], pos[2]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotation — wrapped into [0,360) for display; slider clamps drags to that range.
|
||||||
|
{
|
||||||
|
auto const wrap360 = [](float v) {
|
||||||
|
v = std::fmod(v, 360.0f);
|
||||||
|
if (v < 0.0f) { v += 360.0f; }
|
||||||
|
return v;
|
||||||
|
};
|
||||||
|
glm::vec3 angles{
|
||||||
|
wrap360(picked->Angles().x),
|
||||||
|
wrap360(picked->Angles().y),
|
||||||
|
wrap360(picked->Angles().z)};
|
||||||
|
if (ImGui::DragFloat3("rotation (deg)", &angles.x, 0.5f, 0.0f, 360.0f, "%.3f"))
|
||||||
|
{
|
||||||
|
picked->Angles(angles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale — per-axis float, 1.000 display. Clamped to a reasonable positive range.
|
||||||
|
{
|
||||||
|
glm::vec3 scale = picked->Scale();
|
||||||
|
if (ImGui::DragFloat3("scale (x,y,z)", &scale.x, 0.01f, 0.001f, 100.0f, "%.3f"))
|
||||||
|
{
|
||||||
|
picked->Scale(scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("reset rotation")) { picked->Angles(glm::vec3(0.0f)); }
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("reset scale")) { picked->Scale(glm::vec3(1.0f)); }
|
||||||
|
}
|
||||||
|
|
||||||
bool itemproperties_panel::render_group()
|
bool itemproperties_panel::render_group()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -32,16 +32,22 @@ class itemproperties_panel : public ui_panel
|
|||||||
public:
|
public:
|
||||||
itemproperties_panel(std::string const &Name, bool const Isopen) : ui_panel(Name, Isopen) {}
|
itemproperties_panel(std::string const &Name, bool const Isopen) : ui_panel(Name, Isopen) {}
|
||||||
|
|
||||||
void update(scene::basic_node const *Node);
|
// non-const node pointer so the transform editor in render() can mutate
|
||||||
|
// position/rotation/scale of TAnimModel nodes in place. Other node types
|
||||||
|
// are still treated as read-only.
|
||||||
|
void update(scene::basic_node *Node);
|
||||||
void render() override;
|
void render() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// methods
|
// methods
|
||||||
void update_group();
|
void update_group();
|
||||||
bool render_group();
|
bool render_group();
|
||||||
|
// renders DragFloat3/DragScalarN widgets for position, rotation and scale
|
||||||
|
// of the currently bound TAnimModel; no-op for other node subclasses.
|
||||||
|
void render_transform_editor();
|
||||||
|
|
||||||
// members
|
// members
|
||||||
scene::basic_node const *m_node{nullptr}; // scene node bound to the panel
|
scene::basic_node *m_node{nullptr}; // scene node bound to the panel
|
||||||
scene::group_handle m_grouphandle{null_handle}; // scene group bound to the panel
|
scene::group_handle m_grouphandle{null_handle}; // scene group bound to the panel
|
||||||
std::string m_groupprefix;
|
std::string m_groupprefix;
|
||||||
std::vector<text_line> m_grouplines;
|
std::vector<text_line> m_grouplines;
|
||||||
|
|||||||
@@ -99,9 +99,14 @@ void gl::glsl_common_setup()
|
|||||||
return instance_modelview[gl_InstanceID] * modelview;
|
return instance_modelview[gl_InstanceID] * modelview;
|
||||||
}
|
}
|
||||||
mat3 effective_modelviewnormal() {
|
mat3 effective_modelviewnormal() {
|
||||||
// instance_modelview is rotation+translation only (no scale), so its
|
// instance_modelview can include per-instance scale (uniform or per-axis),
|
||||||
// upper-3x3 is its own inverse-transpose (rotation matrix).
|
// so its upper-3x3 is NOT its own inverse-transpose in the non-uniform
|
||||||
return mat3(instance_modelview[gl_InstanceID]) * modelviewnormal;
|
// case. Compute the correct normal matrix via transpose(inverse(...)).
|
||||||
|
// For pure rotation+translation this still produces the rotation matrix
|
||||||
|
// (one extra mat3 inverse per vertex — modern GPUs handle it cheaply).
|
||||||
|
mat3 instance_mv3 = mat3(instance_modelview[gl_InstanceID]);
|
||||||
|
mat3 instance_normal = transpose(inverse(instance_mv3));
|
||||||
|
return instance_normal * modelviewnormal;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -273,6 +273,7 @@ TAnimModel::is_keyword( std::string const &Token ) const {
|
|||||||
|| ( Token == "lights" )
|
|| ( Token == "lights" )
|
||||||
|| ( Token == "lightcolors" )
|
|| ( Token == "lightcolors" )
|
||||||
|| ( Token == "angles" )
|
|| ( Token == "angles" )
|
||||||
|
|| ( Token == "scale" )
|
||||||
|| ( Token == "notransition" );
|
|| ( Token == "notransition" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,6 +370,23 @@ bool TAnimModel::Load(cParser *parser, bool ter)
|
|||||||
>> vAngle[ 2 ];
|
>> 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" ) {
|
if( token == "notransition" ) {
|
||||||
m_transition = false;
|
m_transition = false;
|
||||||
}
|
}
|
||||||
@@ -677,14 +695,15 @@ 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
|
float
|
||||||
TAnimModel::radius_() {
|
TAnimModel::radius_() {
|
||||||
|
|
||||||
return (
|
if( pModel == nullptr ) { return 0.f; }
|
||||||
pModel ?
|
float const max_scale = std::max( { m_scale.x, m_scale.y, m_scale.z } );
|
||||||
pModel->bounding_radius() :
|
return pModel->bounding_radius() * max_scale;
|
||||||
0.f );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// serialize() subclass details, sends content of the subclass to provided stream
|
// serialize() subclass details, sends content of the subclass to provided stream
|
||||||
@@ -700,18 +719,29 @@ TAnimModel::deserialize_( std::istream &Input ) {
|
|||||||
// TODO: implement
|
// 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
|
void
|
||||||
TAnimModel::export_as_text_( std::ostream &Output ) const {
|
TAnimModel::export_as_text_( std::ostream &Output ) const {
|
||||||
|
|
||||||
// header
|
// header
|
||||||
Output << "model ";
|
Output << "model ";
|
||||||
// location and rotation
|
|
||||||
Output << std::fixed << std::setprecision(3) // ustawienie dokładnie 3 cyfr po przecinku
|
// location and rotation. The 4th token after location is a legacy
|
||||||
<< location().x << ' '
|
// 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().y << ' '
|
||||||
<< location().z << ' ';
|
<< location().z << ' '
|
||||||
Output
|
<< ( xz_rotation_zero ? vAngle.y : 0.0f ) << ' ';
|
||||||
<< "0 " ;
|
|
||||||
// 3d shape
|
// 3d shape
|
||||||
auto modelfile { (
|
auto modelfile { (
|
||||||
pModel ?
|
pModel ?
|
||||||
@@ -748,11 +778,22 @@ TAnimModel::export_as_text_( std::ostream &Output ) const {
|
|||||||
if( false == m_transition ) {
|
if( false == m_transition ) {
|
||||||
Output << "notransition" << ' ';
|
Output << "notransition" << ' ';
|
||||||
}
|
}
|
||||||
// footer
|
// angles directive only when X or Z are rotated — otherwise the Y angle
|
||||||
Output << "angles "
|
// already lives in the 4th token slot above.
|
||||||
<< vAngle.x << ' '
|
if( false == xz_rotation_zero ) {
|
||||||
<< vAngle.y << ' '
|
Output << "angles "
|
||||||
<< vAngle.z << ' ';
|
<< 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
|
// footer
|
||||||
Output
|
Output
|
||||||
<< "endmodel"
|
<< "endmodel"
|
||||||
|
|||||||
@@ -143,6 +143,26 @@ public:
|
|||||||
glm::vec3
|
glm::vec3
|
||||||
Angles() const {
|
Angles() const {
|
||||||
return vAngle; }
|
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
|
// members
|
||||||
std::list<std::shared_ptr<TAnimContainer>> m_animlist;
|
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
|
std::shared_ptr<TAnimContainer> pRoot; // pojemniki sterujące, tylko dla aniomowanych submodeli
|
||||||
TModel3d *pModel { nullptr };
|
TModel3d *pModel { nullptr };
|
||||||
glm::vec3 vAngle; // bazowe obroty egzemplarza względem osi
|
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;
|
material_data m_materialdata;
|
||||||
|
|
||||||
std::string asText; // tekst dla wyświetlacza znakowego
|
std::string asText; // tekst dla wyświetlacza znakowego
|
||||||
|
|||||||
@@ -709,6 +709,7 @@ void opengl33_renderer::draw_debug_ui()
|
|||||||
ImGui::Image(reinterpret_cast<void *>(m_pick_tex->id), ImGui::GetContentRegionAvail(), ImVec2(0, 1.0), ImVec2(1.0, 0));
|
ImGui::Image(reinterpret_cast<void *>(m_pick_tex->id), ImGui::GetContentRegionAvail(), ImVec2(0, 1.0), ImVec2(1.0, 0));
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// runs jobs needed to generate graphics for specified render pass
|
// runs jobs needed to generate graphics for specified render pass
|
||||||
@@ -2914,8 +2915,32 @@ void opengl33_renderer::Render(TAnimModel *Instance)
|
|||||||
Instance->RaPrepare();
|
Instance->RaPrepare();
|
||||||
if (Instance->pModel)
|
if (Instance->pModel)
|
||||||
{
|
{
|
||||||
// renderowanie rekurencyjne submodeli
|
// per-instance uniform scale: applied between the rotation and the
|
||||||
Render(Instance->pModel, Instance->Material(), distancesquared, Instance->location() - m_renderpass.pass_camera.position(), Instance->vAngle);
|
// submodel-local transform chain. Pushing it on the matrix stack here
|
||||||
|
// means Render(TModel3d, ..., position, angle) doesn't need a new
|
||||||
|
// signature and the same scale flows naturally into Render_Alpha and
|
||||||
|
// any other recursive callers.
|
||||||
|
auto const &scale = Instance->Scale();
|
||||||
|
bool const scaled = ( scale.x != 1.0f || scale.y != 1.0f || scale.z != 1.0f );
|
||||||
|
if( scaled ) {
|
||||||
|
// Per-axis scale applied between rotation and the submodel-local
|
||||||
|
// chain: build the per-instance transform here so we can inject
|
||||||
|
// glScalef before the submodel walk.
|
||||||
|
::glPushMatrix();
|
||||||
|
auto const Position = Instance->location() - m_renderpass.pass_camera.position();
|
||||||
|
auto const Angle = Instance->vAngle;
|
||||||
|
::glTranslated( Position.x, Position.y, Position.z );
|
||||||
|
if( Angle.y != 0.0 ) ::glRotated( Angle.y, 0.f, 1.f, 0.f );
|
||||||
|
if( Angle.x != 0.0 ) ::glRotated( Angle.x, 1.f, 0.f, 0.f );
|
||||||
|
if( Angle.z != 0.0 ) ::glRotated( Angle.z, 0.f, 0.f, 1.f );
|
||||||
|
::glScalef( scale.x, scale.y, scale.z );
|
||||||
|
Render( Instance->pModel, Instance->Material(), distancesquared );
|
||||||
|
::glPopMatrix();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// fast path for unscaled (the common case): existing behaviour
|
||||||
|
Render(Instance->pModel, Instance->Material(), distancesquared, Instance->location() - m_renderpass.pass_camera.position(), Instance->vAngle);
|
||||||
|
}
|
||||||
// debug data
|
// debug data
|
||||||
++m_renderpass.draw_stats.models;
|
++m_renderpass.draw_stats.models;
|
||||||
}
|
}
|
||||||
@@ -2987,7 +3012,11 @@ void opengl33_renderer::Render_Instanced( TModel3d *Model, std::vector<TAnimMode
|
|||||||
closest_distancesquared = std::min<float>( closest_distancesquared, static_cast<float>(distancesquared) );
|
closest_distancesquared = std::min<float>( closest_distancesquared, static_cast<float>(distancesquared) );
|
||||||
|
|
||||||
// Build the camera-relative root modelview for this instance:
|
// Build the camera-relative root modelview for this instance:
|
||||||
// mv = view * translate(instance_pos - camera_pos) * rotate(instance_angles)
|
// mv = view * translate(instance_pos - camera_pos) * rotate(instance_angles) * scale(m_scale)
|
||||||
|
// The scale is folded in here so the GPU-instanced path produces visually
|
||||||
|
// identical output to the regular per-instance path: each instance gets
|
||||||
|
// its own (translate × rotate × scale) baked into instance_modelview[i],
|
||||||
|
// which the shader applies via effective_modelview = instance_mv * model_local.
|
||||||
glm::dvec3 const offset = Instance->location() - m_renderpass.pass_camera.position();
|
glm::dvec3 const offset = Instance->location() - m_renderpass.pass_camera.position();
|
||||||
glm::mat4 mv = view_matrix;
|
glm::mat4 mv = view_matrix;
|
||||||
mv = glm::translate( mv, glm::vec3( offset ) );
|
mv = glm::translate( mv, glm::vec3( offset ) );
|
||||||
@@ -2995,6 +3024,10 @@ void opengl33_renderer::Render_Instanced( TModel3d *Model, std::vector<TAnimMode
|
|||||||
if( angle.y != 0.0f ) { mv = glm::rotate( mv, glm::radians( angle.y ), glm::vec3( 0.f, 1.f, 0.f ) ); }
|
if( angle.y != 0.0f ) { mv = glm::rotate( mv, glm::radians( angle.y ), glm::vec3( 0.f, 1.f, 0.f ) ); }
|
||||||
if( angle.x != 0.0f ) { mv = glm::rotate( mv, glm::radians( angle.x ), glm::vec3( 1.f, 0.f, 0.f ) ); }
|
if( angle.x != 0.0f ) { mv = glm::rotate( mv, glm::radians( angle.x ), glm::vec3( 1.f, 0.f, 0.f ) ); }
|
||||||
if( angle.z != 0.0f ) { mv = glm::rotate( mv, glm::radians( angle.z ), glm::vec3( 0.f, 0.f, 1.f ) ); }
|
if( angle.z != 0.0f ) { mv = glm::rotate( mv, glm::radians( angle.z ), glm::vec3( 0.f, 0.f, 1.f ) ); }
|
||||||
|
auto const &scale = Instance->Scale();
|
||||||
|
if( scale.x != 1.0f || scale.y != 1.0f || scale.z != 1.0f ) {
|
||||||
|
mv = glm::scale( mv, scale );
|
||||||
|
}
|
||||||
instance_modelviews.emplace_back( mv );
|
instance_modelviews.emplace_back( mv );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4028,8 +4061,26 @@ void opengl33_renderer::Render_Alpha(TAnimModel *Instance)
|
|||||||
Instance->RaPrepare();
|
Instance->RaPrepare();
|
||||||
if (Instance->pModel)
|
if (Instance->pModel)
|
||||||
{
|
{
|
||||||
// renderowanie rekurencyjne submodeli
|
auto const &scale = Instance->Scale();
|
||||||
Render_Alpha(Instance->pModel, Instance->Material(), distancesquared, Instance->location() - m_renderpass.pass_camera.position(), Instance->vAngle);
|
bool const scaled = ( scale.x != 1.0f || scale.y != 1.0f || scale.z != 1.0f );
|
||||||
|
if( scaled ) {
|
||||||
|
// scaled instance: build the per-instance transform locally and call
|
||||||
|
// the distance-only Render_Alpha so we can inject glScalef before
|
||||||
|
// the submodel walk. Mirror of the opaque path in Render(TAnimModel*).
|
||||||
|
::glPushMatrix();
|
||||||
|
auto const Position = Instance->location() - m_renderpass.pass_camera.position();
|
||||||
|
auto const Angle = Instance->vAngle;
|
||||||
|
::glTranslated( Position.x, Position.y, Position.z );
|
||||||
|
if( Angle.y != 0.0 ) ::glRotated( Angle.y, 0.f, 1.f, 0.f );
|
||||||
|
if( Angle.x != 0.0 ) ::glRotated( Angle.x, 1.f, 0.f, 0.f );
|
||||||
|
if( Angle.z != 0.0 ) ::glRotated( Angle.z, 0.f, 0.f, 1.f );
|
||||||
|
::glScalef( scale.x, scale.y, scale.z );
|
||||||
|
Render_Alpha( Instance->pModel, Instance->Material(), distancesquared );
|
||||||
|
::glPopMatrix();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Render_Alpha(Instance->pModel, Instance->Material(), distancesquared, Instance->location() - m_renderpass.pass_camera.position(), Instance->vAngle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ struct scratch_data {
|
|||||||
struct location_data {
|
struct location_data {
|
||||||
|
|
||||||
std::stack<glm::dvec3> offset;
|
std::stack<glm::dvec3> offset;
|
||||||
|
// per-axis scale stack — mirrors `offset` for the `scale`/`endscale`
|
||||||
|
// scenario directives. Effective scale at any nesting depth is the
|
||||||
|
// component-wise product of all stack entries (outer `scale 2 2 2` ×
|
||||||
|
// inner `scale 1.5 1.5 1.5` yields (3,3,3)). Empty stack means (1,1,1).
|
||||||
|
std::stack<glm::vec3> scale;
|
||||||
glm::vec3 rotation;
|
glm::vec3 rotation;
|
||||||
} location;
|
} location;
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,8 @@ state_serializer::deserialize_begin( std::string const &Scenariofile ) {
|
|||||||
{ "node", &state_serializer::deserialize_node },
|
{ "node", &state_serializer::deserialize_node },
|
||||||
{ "origin", &state_serializer::deserialize_origin },
|
{ "origin", &state_serializer::deserialize_origin },
|
||||||
{ "endorigin", &state_serializer::deserialize_endorigin },
|
{ "endorigin", &state_serializer::deserialize_endorigin },
|
||||||
|
{ "scale", &state_serializer::deserialize_scale },
|
||||||
|
{ "endscale", &state_serializer::deserialize_endscale },
|
||||||
{ "rotate", &state_serializer::deserialize_rotate },
|
{ "rotate", &state_serializer::deserialize_rotate },
|
||||||
{ "sky", &state_serializer::deserialize_sky },
|
{ "sky", &state_serializer::deserialize_sky },
|
||||||
{ "test", &state_serializer::deserialize_test },
|
{ "test", &state_serializer::deserialize_test },
|
||||||
@@ -664,6 +666,43 @@ state_serializer::deserialize_endorigin( cParser &Input, scene::scratch_data &Sc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
state_serializer::deserialize_scale( cParser &Input, scene::scratch_data &Scratchpad ) {
|
||||||
|
// Syntax: `scale <x> <y> <z>` (three tokens, mirroring `rotate`/`angles`).
|
||||||
|
// For uniform scaling write the same value three times (e.g. `scale 2 2 2`).
|
||||||
|
// Affects both:
|
||||||
|
// 1. positions of nodes inside the block (transform() multiplies offset by scale)
|
||||||
|
// 2. the per-instance m_scale stamped onto each TAnimModel created inside the block
|
||||||
|
// The two together let you scale a multi-node-model group built around a common
|
||||||
|
// origin: positions of the parts spread out by the factor AND each part is itself
|
||||||
|
// scaled by the same factor, preserving the visual shape of the assembly.
|
||||||
|
glm::vec3 factor;
|
||||||
|
Input.getTokens( 3 );
|
||||||
|
Input >> factor.x >> factor.y >> factor.z;
|
||||||
|
if( factor.x <= 0.0f || factor.y <= 0.0f || factor.z <= 0.0f ) {
|
||||||
|
ErrorLog( "Bad scale: non-positive scale factor in file \""
|
||||||
|
+ Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + "); scale (1,1,1) used" );
|
||||||
|
factor = glm::vec3( 1.0f );
|
||||||
|
}
|
||||||
|
// scales compose component-wise, mirroring how origin offsets compose additively.
|
||||||
|
glm::vec3 const parent = (
|
||||||
|
Scratchpad.location.scale.empty() ?
|
||||||
|
glm::vec3( 1.0f ) :
|
||||||
|
Scratchpad.location.scale.top() );
|
||||||
|
Scratchpad.location.scale.emplace( factor * parent );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
state_serializer::deserialize_endscale( cParser &Input, scene::scratch_data &Scratchpad ) {
|
||||||
|
|
||||||
|
if( false == Scratchpad.location.scale.empty() ) {
|
||||||
|
Scratchpad.location.scale.pop();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ErrorLog( "Bad scale: endscale instruction with empty scale stack in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
state_serializer::deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad ) {
|
state_serializer::deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad ) {
|
||||||
|
|
||||||
@@ -915,6 +954,12 @@ state_serializer::deserialize_model( cParser &Input, scene::scratch_data &Scratc
|
|||||||
|
|
||||||
auto *instance = new TAnimModel( Nodedata );
|
auto *instance = new TAnimModel( Nodedata );
|
||||||
instance->Angles( Scratchpad.location.rotation + rotation ); // dostosowanie do pochylania linii
|
instance->Angles( Scratchpad.location.rotation + rotation ); // dostosowanie do pochylania linii
|
||||||
|
// pick up the scale active at this point in the scenario stream — outer
|
||||||
|
// `scale`/`endscale` blocks compose multiplicatively in the scratchpad.
|
||||||
|
// Load() may further multiply this by an inline `scale <factor>` token.
|
||||||
|
if( false == Scratchpad.location.scale.empty() ) {
|
||||||
|
instance->Scale( Scratchpad.location.scale.top() );
|
||||||
|
}
|
||||||
|
|
||||||
if( instance->Load( &Input, false ) ) {
|
if( instance->Load( &Input, false ) ) {
|
||||||
instance->location( transform( location, Scratchpad ) );
|
instance->location( transform( location, Scratchpad ) );
|
||||||
@@ -1091,7 +1136,7 @@ state_serializer::skip_until( cParser &Input, std::string const &Token ) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// transforms provided location by specifed rotation and offset
|
// transforms provided location by specifed rotation, scale and offset
|
||||||
glm::dvec3
|
glm::dvec3
|
||||||
state_serializer::transform( glm::dvec3 Location, scene::scratch_data const &Scratchpad ) {
|
state_serializer::transform( glm::dvec3 Location, scene::scratch_data const &Scratchpad ) {
|
||||||
|
|
||||||
@@ -1099,6 +1144,17 @@ state_serializer::transform( glm::dvec3 Location, scene::scratch_data const &Scr
|
|||||||
auto const rotation = glm::radians( Scratchpad.location.rotation );
|
auto const rotation = glm::radians( Scratchpad.location.rotation );
|
||||||
Location = glm::rotateY<double>( Location, rotation.y ); // Ra 2014-11: uwzględnienie rotacji
|
Location = glm::rotateY<double>( Location, rotation.y ); // Ra 2014-11: uwzględnienie rotacji
|
||||||
}
|
}
|
||||||
|
// Scale applies in local origin space — positions inside a `scale 2 2 2` block
|
||||||
|
// are pushed twice as far from the local origin along each axis, so a
|
||||||
|
// multi-node-model group (e.g. a building made of separate node models built
|
||||||
|
// around a shared origin) ends up looking uniformly scaled rather than just
|
||||||
|
// having one piece grow. Per-axis values stretch the assembly anisotropically.
|
||||||
|
if( false == Scratchpad.location.scale.empty() ) {
|
||||||
|
auto const &s = Scratchpad.location.scale.top();
|
||||||
|
Location.x *= static_cast<double>( s.x );
|
||||||
|
Location.y *= static_cast<double>( s.y );
|
||||||
|
Location.z *= static_cast<double>( s.z );
|
||||||
|
}
|
||||||
if( false == Scratchpad.location.offset.empty() ) {
|
if( false == Scratchpad.location.offset.empty() ) {
|
||||||
Location += Scratchpad.location.offset.top();
|
Location += Scratchpad.location.offset.top();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ private:
|
|||||||
void deserialize_node( cParser &Input, scene::scratch_data &Scratchpad );
|
void deserialize_node( cParser &Input, scene::scratch_data &Scratchpad );
|
||||||
void deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad );
|
void deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad );
|
||||||
void deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad );
|
void deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad );
|
||||||
|
void deserialize_scale( cParser &Input, scene::scratch_data &Scratchpad );
|
||||||
|
void deserialize_endscale( cParser &Input, scene::scratch_data &Scratchpad );
|
||||||
void deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad );
|
void deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad );
|
||||||
void deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad );
|
void deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad );
|
||||||
void deserialize_test( cParser &Input, scene::scratch_data &Scratchpad );
|
void deserialize_test( cParser &Input, scene::scratch_data &Scratchpad );
|
||||||
|
|||||||
Reference in New Issue
Block a user