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

Reorganize source files into logical subdirectories

Co-authored-by: Hirek193 <23196899+Hirek193@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-14 19:01:57 +00:00
parent f981f81d55
commit 0531086bb9
221 changed files with 131 additions and 108 deletions

1744
scene/scene.cpp Normal file

File diff suppressed because it is too large Load Diff

463
scene/scene.h Normal file
View File

@@ -0,0 +1,463 @@
/*
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/.
*/
#pragma once
#include <vector>
#include <deque>
#include <array>
#include <stack>
#include <unordered_set>
#include <map>
#include "parser.h"
#include "geometrybank.h"
#include "scenenode.h"
#include "Track.h"
#include "Traction.h"
#include "sound.h"
#include "command.h"
class opengl_renderer;
class opengl33_renderer;
namespace scene {
int const EU07_CELLSIZE = 250;
int const EU07_SECTIONSIZE = 1000;
int const EU07_REGIONSIDESECTIONCOUNT = 500; // number of sections along a side of square region
struct scratch_data {
struct binary_data {
bool terrain{ false };
bool terrain_included{false};
} binary;
struct location_data {
std::stack<glm::dvec3> offset;
glm::vec3 rotation;
} location;
struct trainset_data {
std::string name;
std::string track;
float offset { 0.f };
float velocity { 0.f };
std::vector<TDynamicObject *> vehicles;
std::vector<int> couplings;
TDynamicObject * driver { nullptr };
bool is_open { false };
std::unordered_map<std::string, std::string> assignment;
} trainset;
std::string name;
std::string terrain_name;
bool initialized { false };
bool time_initialized { false };
};
// basic element of rudimentary partitioning scheme for the section. fixed size, no further subdivision
// TBD, TODO: replace with quadtree scheme?
class basic_cell {
friend opengl_renderer;
friend opengl33_renderer;
public:
// constructors
basic_cell() = default;
// methods
// potentially activates event handler with the same name as provided node, and within handler activation range
void
on_click( TAnimModel const *Instance );
// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle
void
update_traction( TDynamicObject *Vehicle, int const Pantographindex );
// legacy method, polls event launchers within radius around specified point
void
update_events();
// legacy method, updates sounds within radius around specified point
void
update_sounds();
// legacy method, triggers radio-stop procedure for all vehicles located on paths in the cell
void
radio_stop();
// legacy method, adds specified path to the list of pieces undergoing state change
bool
RaTrackAnimAdd( TTrack *Track );
// legacy method, updates geometry for pieces in the animation list
void
RaAnimate( unsigned int const Framestamp );
// sends content of the class to provided stream
void
serialize( std::ostream &Output ) const;
// restores content of the class from provided stream
void
deserialize( std::istream &Input );
// sends content of the class in legacy (text) format to provided stream
void
export_as_text( std::ostream &Output ) const;
// adds provided shape to the cell
void
insert( shape_node Shape );
// adds provided lines to the cell
void
insert( lines_node Lines );
// adds provided path to the cell
void
insert( TTrack *Path );
// adds provided path to the cell
void
insert( TTraction *Traction );
// adds provided model instance to the cell
void
insert( TAnimModel *Instance );
// adds provided sound instance to the cell
void
insert( sound_source *Sound );
// adds provided event launcher to the cell
void
insert( TEventLauncher *Launcher );
// adds provided memory cell to the cell
void
insert( TMemCell *Memorycell );
// registers provided path in the lookup directory of the cell
void
register_end( TTrack *Path );
// registers provided traction piece in the lookup directory of the cell
void
register_end( TTraction *Traction );
// removes provided model instance from the cell
void
erase( TAnimModel *Instance );
// removes provided memory cell from the cell
void
erase( TMemCell *Memorycell );
// find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance
std::tuple<TDynamicObject *, float>
find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler ) const;
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
std::tuple<TTrack *, int>
find( glm::dvec3 const &Point, TTrack const *Exclude ) const;
// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint
std::tuple<TTraction *, int>
find( glm::dvec3 const &Point, TTraction const *Exclude ) const;
// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece
std::tuple<TTraction *, int, float>
find( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection ) const;
// sets center point of the cell
void
center( glm::dvec3 Center );
// generates renderable version of held non-instanced geometry in specified geometry bank
void
create_geometry( gfx::geometrybank_handle const &Bank );
void
create_map_geometry(std::vector<gfx::basic_vertex> &Bank, const gfx::geometrybank_handle Extra);
void
get_map_active_paths(map_colored_paths &handles);
glm::vec3 find_nearest_track_point(const glm::dvec3 &pos);
// provides access to bounding area data
bounding_area const &
area() const {
return m_area; }
//private:
// types
using path_sequence = std::vector<TTrack *>;
using shapenode_sequence = std::vector<shape_node>;
using linesnode_sequence = std::vector<lines_node>;
using traction_sequence = std::vector<TTraction *>;
using instance_sequence = std::vector<TAnimModel *>;
using sound_sequence = std::vector<sound_source *>;
using eventlauncher_sequence = std::vector<TEventLauncher *>;
using memorycell_sequence = std::vector<TMemCell *>;
// methods
void
launch_event(TEventLauncher *Launcher, bool local_only);
void
enclose_area( scene::basic_node *Node );
// members
scene::bounding_area m_area { glm::dvec3(), static_cast<float>( 0.5 * M_SQRT2 * EU07_CELLSIZE ) };
bool m_active { false }; // whether the cell holds any actual data content
shapenode_sequence m_shapesopaque; // opaque pieces of geometry
shapenode_sequence m_shapestranslucent; // translucent pieces of geometry
linesnode_sequence m_lines;
path_sequence m_paths; // path pieces
instance_sequence m_instancesopaque;
instance_sequence m_instancetranslucent;
traction_sequence m_traction;
sound_sequence m_sounds;
eventlauncher_sequence m_eventlaunchers;
memorycell_sequence m_memorycells;
// search helpers
struct lookup_data {
path_sequence paths;
traction_sequence traction;
} m_directories;
// animation of owned items (legacy code, clean up along with track refactoring)
bool m_geometrycreated { false };
unsigned int m_framestamp { 0 }; // id of last rendered gfx frame
TTrack *tTrackAnim = nullptr; // obiekty do przeliczenia animacji
command_relay m_relay;
};
// basic scene partitioning structure, holds terrain geometry and collection of cells
class basic_section {
friend opengl_renderer;
friend opengl33_renderer;
public:
// constructors
basic_section() = default;
// methods
// potentially activates event handler with the same name as provided node, and within handler activation range
void
on_click( TAnimModel const *Instance );
// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle
void
update_traction( TDynamicObject *Vehicle, int const Pantographindex );
// legacy method, updates sounds and polls event launchers within radius around specified point
void
update_events( glm::dvec3 const &Location, float const Radius );
// legacy method, updates sounds and polls event launchers within radius around specified point
void
update_sounds( glm::dvec3 const &Location, float const Radius );
// legacy method, triggers radio-stop procedure for all vehicles in 2km radius around specified location
void
radio_stop( glm::dvec3 const &Location, float const Radius );
// sends content of the class to provided stream
void
serialize( std::ostream &Output ) const;
// restores content of the class from provided stream
void
deserialize( std::istream &Input );
// sends content of the class in legacy (text) format to provided stream
void
export_as_text( std::ostream &Output ) const;
// adds provided shape to the section
void
insert( shape_node Shape );
// adds provided lines to the section
void
insert( lines_node Lines );
// adds provided node to the section
template <class Type_>
void
insert( Type_ *Node ) {
auto &targetcell { cell( Node->location() ) };
targetcell.insert( Node );
// some node types can extend bounding area of the target cell
m_area.radius = std::max(
m_area.radius,
static_cast<float>( glm::length( m_area.center - targetcell.area().center ) + targetcell.area().radius ) ); }
// erases provided node from the section
template <class Type_>
void
erase( Type_ *Node ) {
auto &targetcell { cell( Node->location() ) };
// TODO: re-calculate bounding area after removal
targetcell.erase( Node ); }
// registers provided node in the lookup directory of the section enclosing specified point
template <class Type_>
void
register_node( Type_ *Node, glm::dvec3 const &Point ) {
cell( Point ).register_end( Node ); }
// find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance
std::tuple<TDynamicObject *, float>
find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler );
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
std::tuple<TTrack *, int>
find( glm::dvec3 const &Point, TTrack const *Exclude );
// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint
std::tuple<TTraction *, int>
find( glm::dvec3 const &Point, TTraction const *Exclude );
// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece
std::tuple<TTraction *, int, float>
find( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection );
// sets center point of the section
void
center( glm::dvec3 Center );
// generates renderable version of held non-instanced geometry
void
create_geometry();
void
create_map_geometry(const gfx::geometrybank_handle handle);
void
get_map_active_paths(map_colored_paths &handles);
// provides access to bounding area data
bounding_area const &
area() const {
return m_area; }
const gfx::geometrybank_handle get_map_geometry()
{ return m_map_geometryhandle;}
glm::vec3 find_nearest_track_point(const glm::dvec3 &point);
//private:
// types
using cell_array = std::array<basic_cell, (EU07_SECTIONSIZE / EU07_CELLSIZE) * (EU07_SECTIONSIZE / EU07_CELLSIZE)>;
using shapenode_sequence = std::vector<shape_node>;
// methods
// provides access to section enclosing specified point
basic_cell &
cell(glm::dvec3 const &Location, const glm::ivec2 &offset = glm::ivec2(0));
// members
// placement and visibility
scene::bounding_area m_area { glm::dvec3(), static_cast<float>( 0.5 * M_SQRT2 * EU07_SECTIONSIZE ) };
// content
cell_array m_cells; // partitioning scheme
shapenode_sequence m_shapes; // large pieces of opaque geometry and (legacy) terrain
// TODO: implement dedicated, higher fidelity, fixed resolution terrain mesh item
// gfx renderer data
gfx::geometrybank_handle m_geometrybank;
bool m_geometrycreated { false };
gfx::geometrybank_handle m_map_geometryhandle;
};
// top-level of scene spatial structure, holds collection of sections
class basic_region {
friend opengl_renderer;
friend opengl33_renderer;
public:
// constructors
basic_region();
// destructor
~basic_region();
// methods
// potentially activates event handler with the same name as provided node, and within handler activation range
void
on_click( TAnimModel const *Instance );
// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle
void
update_traction( TDynamicObject *Vehicle, int const Pantographindex );
// legacy method, polls event launchers around camera
void
update_events();
// legacy method, updates sounds around camera
void
update_sounds();
// checks whether specified file is a valid region data file
bool
is_scene( std::string const &Scenariofile ) const;
// stores content of the class in file with specified name
void
serialize( std::string const &Scenariofile ) const;
// restores content of the class from file with specified name. returns: true on success, false otherwise
bool
deserialize( std::string const &Scenariofile );
// sends content of the class in legacy (text) format to provided stream
void
export_as_text( std::ostream &Output ) const;
// legacy method, links specified path piece with potential neighbours
void
TrackJoin( TTrack *Track );
// legacy method, triggers radio-stop procedure for all vehicles in 2km radius around specified location
void
RadioStop( glm::dvec3 const &Location );
// inserts provided shape in the region
void
insert( shape_node Shape, scratch_data &Scratchpad, bool const Transform );
// inserts provided lines in the region
void
insert( lines_node Lines, scratch_data &Scratchpad );
// inserts provided node in the region
template <class Type_>
void
insert( Type_ *Node ) {
auto const location { Node->location() };
if( false == point_inside( location ) ) {
// NOTE: nodes placed outside of region boundaries are discarded
// TBD, TODO: clamp coordinates to region boundaries?
return; }
section( location ).insert( Node ); }
// inserts provided node in the region and registers its ends in lookup directory
template <class Type_>
void
insert_and_register( Type_ *Node ) {
insert( Node );
for( auto const &point : Node->endpoints() ) {
if( point_inside( point ) ) {
section( point ).register_node( Node, point ); } } }
// removes specified node from the region
template <class Type_>
void
erase( Type_ *Node ) {
auto const location{ Node->location() };
if( point_inside( location ) ) {
section( location ).erase( Node ); } }
// find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance
std::tuple<TDynamicObject *, float>
find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler );
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
std::tuple<TTrack *, int>
find_path( glm::dvec3 const &Point, TTrack const *Exclude );
// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint
std::tuple<TTraction *, int>
find_traction( glm::dvec3 const &Point, TTraction const *Exclude );
// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece
std::tuple<TTraction *, int>
find_traction( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection );
// finds sections inside specified sphere. returns: list of sections
std::vector<basic_section *> const &
sections( glm::dvec3 const &Point, float const Radius );
void
create_map_geometry();
void
update_poi_geometry();
basic_section* get_section(size_t section)
{ return m_sections[section]; }
gfx::geometrybank_handle
get_map_poi_geometry() { return m_map_poipoints; }
glm::vec3 find_nearest_track_point(const glm::dvec3 &pos)
{ return section(pos).find_nearest_track_point(pos); }
//private:
// types
using section_array = std::array<basic_section *, EU07_REGIONSIDESECTIONCOUNT * EU07_REGIONSIDESECTIONCOUNT>;
struct region_scratchpad {
std::vector<basic_section *> sections;
};
gfx::geometrybank_handle m_map_geometrybank;
gfx::geometrybank_handle m_map_poipoints;
// methods
// checks whether specified point is within boundaries of the region
bool
point_inside( glm::dvec3 const &Location );
// legacy method, trims provided shape to fit into a section. adds trimmed part at the end of provided list, returns true if changes were made
static
bool
RaTriangleDivider( shape_node &Shape, std::deque<shape_node> &Shapes );
// provides access to section enclosing specified point
basic_section &
section( glm::dvec3 const &Location );
// members
section_array m_sections;
region_scratchpad m_scratchpad;
};
// global hierarchy map for scene nodes
extern std::map<std::string, basic_node *> Hierarchy;
} // scene
//---------------------------------------------------------------------------

201
scene/sceneeditor.cpp Normal file
View File

@@ -0,0 +1,201 @@
/*
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/.
*/
#include "stdafx.h"
#include "sceneeditor.h"
#include "scenenodegroups.h"
#include "Globals.h"
#include "application.h"
#include "simulation.h"
#include "MemCell.h"
#include "Camera.h"
#include "AnimModel.h"
#include "renderer.h"
namespace scene {
void
basic_editor::translate( scene::basic_node *Node, glm::dvec3 const &Location, bool const Snaptoground ) {
auto &initiallocation { Node->location() };
// fixup NaNs
if (std::isnan(initiallocation.x))
initiallocation.x = Location.x;
if (std::isnan(initiallocation.y))
initiallocation.y = Location.y;
if (std::isnan(initiallocation.z))
initiallocation.z = Location.z;
auto targetlocation { Location };
if( false == Snaptoground ) {
targetlocation.y = initiallocation.y;
}
// NOTE: bit of a waste for single nodes, for the sake of less varied code down the road
auto const translation { targetlocation - initiallocation };
Node->mark_dirty();
if( Node->group() <= 1 ) {
translate_node( Node, Node->location() + translation );
}
else {
// translate entire group
// TODO: contextual switch between group and item translation
// TODO: translation of affected/relevant events
auto &nodegroup { scene::Groups.group( Node->group() ).nodes };
std::for_each(
std::begin( nodegroup ), std::end( nodegroup ),
[&]( auto *node ) {
translate_node( node, node->location() + translation ); } );
}
}
void
basic_editor::translate( scene::basic_node *Node, float const Offset ) {
// NOTE: offset scaling is calculated early so the same multiplier can be applied to potential whole group
auto location { Node->location() };
auto const distance { glm::length( location - glm::dvec3{ Global.pCamera.Pos } ) };
auto const offset { static_cast<float>( Offset * std::max( 1.0, distance * 0.01 ) ) };
if( Node->group() <= 1 ) {
translate_node( Node, offset );
}
else {
// translate entire group
// TODO: contextual switch between group and item translation
// TODO: translation of affected/relevant events
auto &nodegroup { scene::Groups.group( Node->group() ).nodes };
std::for_each(
std::begin( nodegroup ), std::end( nodegroup ),
[&]( auto *node ) {
translate_node( node, offset ); } );
}
}
void
basic_editor::translate_node( scene::basic_node *Node, glm::dvec3 const &Location ) {
if( typeid( *Node ) == typeid( TAnimModel ) ) {
translate_instance( static_cast<TAnimModel *>( Node ), Location );
}
else if( typeid( *Node ) == typeid( TMemCell ) ) {
translate_memorycell( static_cast<TMemCell *>( Node ), Location );
}
}
void
basic_editor::translate_node( scene::basic_node *Node, float const Offset ) {
if( typeid( *Node ) == typeid( TAnimModel ) ) {
translate_instance( static_cast<TAnimModel *>( Node ), Offset );
}
else if( typeid( *Node ) == typeid( TMemCell ) ) {
translate_memorycell( static_cast<TMemCell *>( Node ), Offset );
}
}
void
basic_editor::translate_instance( TAnimModel *Instance, glm::dvec3 const &Location ) {
simulation::Region->erase( Instance );
Instance->location( Location );
simulation::Region->insert( Instance );
}
void
basic_editor::translate_instance( TAnimModel *Instance, float const Offset ) {
auto location { Instance->location() };
location.y += Offset;
Instance->location( location );
}
void
basic_editor::translate_memorycell( TMemCell *Memorycell, glm::dvec3 const &Location ) {
simulation::Region->erase( Memorycell );
Memorycell->location( Location );
simulation::Region->insert( Memorycell );
}
void
basic_editor::translate_memorycell( TMemCell *Memorycell, float const Offset ) {
auto location { Memorycell->location() };
location.y += Offset;
Memorycell->location( location );
}
void
basic_editor::rotate( scene::basic_node *Node, glm::vec3 const &Angle, float const Quantization ) {
glm::vec3 rotation{Angle.x, Angle.y, Angle.z};
// quantize resulting angle if requested and type of the node allows it
// TBD, TODO: angle quantization for types other than instanced models
if( ( Quantization > 0.f )
&& ( typeid( *Node ) == typeid( TAnimModel ) ) ) {
auto const initialangle { static_cast<TAnimModel *>( Node )->Angles() };
rotation += initialangle;
// TBD, TODO: adjustable quantization step
rotation.y = quantize( rotation.y, Quantization );
rotation -= initialangle;
}
if( Node->group() <= 1 ) {
rotate_node( Node, rotation );
}
else {
// rotate entire group
// TODO: contextual switch between group and item rotation
// TODO: translation of affected/relevant events
auto const &rotationcenter { Node->location() };
auto const &nodegroup { scene::Groups.group( Node->group() ).nodes };
std::for_each(
std::begin( nodegroup ), std::end( nodegroup ),
[&]( auto *node ) {
rotate_node( node, rotation );
if( node != Node ) {
translate_node(
node,
rotationcenter
+ glm::rotateY(
node->location() - rotationcenter,
glm::radians<double>( rotation.y ) ) ); } } );
}
}
void
basic_editor::rotate_node( scene::basic_node *Node, glm::vec3 const &Angle ) {
if( typeid( *Node ) == typeid( TAnimModel ) ) {
rotate_instance( static_cast<TAnimModel *>( Node ), Angle );
}
}
void
basic_editor::rotate_instance( TAnimModel *Instance, glm::vec3 const &Angle ) {
auto targetangle { Instance->Angles() + Angle };
targetangle.x = clamp_circular( targetangle.x, 360.f );
targetangle.y = clamp_circular( targetangle.y, 360.f );
targetangle.z = clamp_circular( targetangle.z, 360.f );
Instance->Angles( targetangle );
}
} // scene
//---------------------------------------------------------------------------

64
scene/sceneeditor.h Normal file
View File

@@ -0,0 +1,64 @@
/*
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/.
*/
#pragma once
#include "scenenode.h"
namespace scene {
// TODO: move the snapshot to history stack
struct node_snapshot {
scene::basic_node *node;
std::string data;
node_snapshot( scene::basic_node *Node ) :
node( Node ) {
if( Node != nullptr ) {
Node->export_as_text( data ); } };
};
inline bool operator==( node_snapshot const &Left, node_snapshot const &Right ) { return ( ( Left.node == Right.node ) && ( Left.data == Right.data ) ); }
inline bool operator!=( node_snapshot const &Left, node_snapshot const &Right ) { return ( !( Left == Right ) ); }
class basic_editor {
public:
// methods
void
translate( scene::basic_node *Node, glm::dvec3 const &Location, bool const Snaptoground );
void
translate( scene::basic_node *Node, float const Offset );
void
rotate( scene::basic_node *Node, glm::vec3 const &Angle, float const Quantization );
private:
// methods
void
translate_node( scene::basic_node *Node, glm::dvec3 const &Location );
void
translate_node( scene::basic_node *Node, float const Offset );
void
translate_instance( TAnimModel *Instance, glm::dvec3 const &Location );
void
translate_instance( TAnimModel *Instance, float const Offset );
void
translate_memorycell( TMemCell *Memorycell, glm::dvec3 const &Location );
void
translate_memorycell( TMemCell *Memorycell, float const Offset );
void
rotate_node( scene::basic_node *Node, glm::vec3 const &Angle );
void
rotate_instance( TAnimModel *Instance, glm::vec3 const &Angle );
};
} // scene
//---------------------------------------------------------------------------

811
scene/scenenode.cpp Normal file
View File

@@ -0,0 +1,811 @@
/*
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/.
*/
#include "stdafx.h"
#include "scenenode.h"
#include "Model3d.h"
#include "renderer.h"
#include "parser.h"
#include "Logs.h"
#include "sn_utils.h"
// stores content of the struct in provided output stream
void
lighting_data::serialize( std::ostream &Output ) const {
sn_utils::s_vec4( Output, diffuse );
sn_utils::s_vec4( Output, ambient );
sn_utils::s_vec4( Output, specular );
}
// restores content of the struct from provided input stream
void
lighting_data::deserialize( std::istream &Input ) {
diffuse = sn_utils::d_vec4( Input );
ambient = sn_utils::d_vec4( Input );
specular = sn_utils::d_vec4( Input );
}
namespace scene {
// stores content of the struct in provided output stream
void
bounding_area::serialize( std::ostream &Output ) const {
// center
sn_utils::s_dvec3( Output, center );
// radius
sn_utils::ls_float32( Output, radius );
}
// restores content of the struct from provided input stream
void
bounding_area::deserialize( std::istream &Input, bool const Preserveradius ) {
center = sn_utils::d_dvec3( Input );
radius = ( Preserveradius ?
std::max( radius, sn_utils::ld_float32( Input ) ) :
sn_utils::ld_float32( Input ) );
}
// sends content of the struct to provided stream
void
shape_node::shapenode_data::serialize( std::ostream &Output ) const {
// bounding area
area.serialize( Output );
bool has_userdata = !userdata.empty();
// visibility
sn_utils::ls_float64( Output, rangesquared_min );
sn_utils::ls_float64( Output, rangesquared_max );
sn_utils::s_bool( Output, visible );
// material
sn_utils::s_bool( Output, translucent );
sn_utils::s_bool( Output, has_userdata );
// NOTE: material handle is created dynamically on load
sn_utils::s_str(
Output,
( material != null_handle ?
GfxRenderer->Material( material )->GetName() :
"" ) );
lighting.serialize( Output );
// geometry
sn_utils::s_dvec3( Output, origin );
// NOTE: geometry handle is created dynamically on load
// vertex count, followed by vertex data
sn_utils::ls_uint32( Output, vertices.size() );
for( int i = 0; i < vertices.size(); ++i ) {
gfx::basic_vertex::convert(vertices[i], origin)
.serialize( Output, false );
if(has_userdata){
userdata[i].serialize(Output);
}
}
}
// restores content of the struct from provided input stream
void
shape_node::shapenode_data::deserialize( std::istream &Input ) {
// bounding area
area.deserialize( Input );
// visibility
rangesquared_min = sn_utils::ld_float64( Input );
rangesquared_max = sn_utils::ld_float64( Input );
visible = sn_utils::d_bool( Input );
// material
translucent = sn_utils::d_bool( Input );
bool has_userdata = sn_utils::d_bool( Input );
auto const materialname { sn_utils::d_str( Input ) };
if( false == materialname.empty() ) {
material = GfxRenderer->Fetch_Material( materialname );
}
lighting.deserialize( Input );
// geometry
origin = sn_utils::d_dvec3( Input );
// NOTE: geometry handle is acquired during geometry creation
// vertex data
vertices.resize( sn_utils::ld_uint32( Input ) );
if(has_userdata)
userdata.resize(vertices.size());
gfx::basic_vertex localvertex;
for( int i = 0; i < vertices.size(); ++i ) {
localvertex.deserialize( Input, false );
vertices[i] = localvertex.to_world(origin);
if(has_userdata)
userdata[i].deserialize( Input );
}
}
// sends content of the class to provided stream
void
shape_node::serialize( std::ostream &Output ) const {
// name
sn_utils::s_str( Output, m_name );
// node data
m_data.serialize( Output );
}
// restores content of the node from provided input stream
shape_node &
shape_node::deserialize( std::istream &Input ) {
// name
m_name = sn_utils::d_str( Input );
// node data
m_data.deserialize( Input );
return *this;
}
// restores content of the node from provided input stream
shape_node &
shape_node::import( cParser &Input, scene::node_data const &Nodedata ) {
// import common data
m_name = Nodedata.name;
m_data.rangesquared_min = Nodedata.range_min * Nodedata.range_min;
m_data.rangesquared_max = (
Nodedata.range_max >= 0.0 ?
Nodedata.range_max * Nodedata.range_max :
std::numeric_limits<double>::max() );
std::string token = Input.getToken<std::string>();
if( token == "material" ) {
// lighting settings
token = Input.getToken<std::string>();
while( token != "endmaterial" ) {
if( token == "ambient:" ) {
Input.getTokens( 3 );
Input
>> m_data.lighting.ambient.r
>> m_data.lighting.ambient.g
>> m_data.lighting.ambient.b;
m_data.lighting.ambient /= 255.f;
m_data.lighting.ambient.a = 1.f;
}
else if( token == "diffuse:" ) {
Input.getTokens( 3 );
Input
>> m_data.lighting.diffuse.r
>> m_data.lighting.diffuse.g
>> m_data.lighting.diffuse.b;
m_data.lighting.diffuse /= 255.f;
m_data.lighting.diffuse.a = 1.f;
}
else if( token == "specular:" ) {
Input.getTokens( 3 );
Input
>> m_data.lighting.specular.r
>> m_data.lighting.specular.g
>> m_data.lighting.specular.b;
m_data.lighting.specular /= 255.f;
m_data.lighting.specular.a = 1.f;
}
token = Input.getToken<std::string>();
}
token = Input.getToken<std::string>();
}
// assigned material
replace_slashes(token);
m_data.material = GfxRenderer->Fetch_Material( token );
// determine way to proceed from the assigned diffuse texture
// TBT, TODO: add methods to material manager to access these simpler
auto const texturehandle = (
m_data.material != null_handle ?
GfxRenderer->Material( m_data.material )->GetTexture(0) :
null_handle );
auto const &texture = (
texturehandle ?
GfxRenderer->Texture( texturehandle ) :
*ITexture::null_texture() ); // dirty workaround for lack of better api
bool const clamps = (
texturehandle ?
contains( texture.get_traits(), 's' ) :
false );
bool const clampt = (
texturehandle ?
contains( texture.get_traits(), 't' ) :
false );
// remainder of legacy 'problend' system -- geometry assigned a texture with '@' in its name is treated as translucent, opaque otherwise
if( texturehandle != null_handle ) {
m_data.translucent = (
( ( contains( texture.get_name(), '@' ) )
&& ( true == texture.get_has_alpha() ) ) ?
true :
false );
}
else {
m_data.translucent = false;
}
// geometry
enum subtype {
triangles,
triangle_strip,
triangle_fan
};
subtype const nodetype = (
Nodedata.type == "triangles" ? triangles :
Nodedata.type == "triangle_strip" ? triangle_strip :
triangle_fan );
std::size_t vertexcount{ 0 };
world_vertex vertex, vertex1, vertex2;
do {
Input.getTokens( 8, false );
Input
>> vertex.position.x
>> vertex.position.y
>> vertex.position.z
>> vertex.normal.x
>> vertex.normal.y
>> vertex.normal.z
>> vertex.texture.s
>> vertex.texture.t;
// clamp texture coordinates if texture wrapping is off
if( true == clamps ) { vertex.texture.s = clamp( vertex.texture.s, 0.001f, 0.999f ); }
if( true == clampt ) { vertex.texture.t = clamp( vertex.texture.t, 0.001f, 0.999f ); }
// convert all data to gl_triangles to allow data merge for matching nodes
switch( nodetype ) {
case triangles: {
if( vertexcount == 0 ) { vertex1 = vertex; }
else if( vertexcount == 1 ) { vertex2 = vertex; }
else if( vertexcount >= 2 ) {
if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex2 );
m_data.vertices.emplace_back( vertex );
}
else {
ErrorLog(
"Bad geometry: degenerate triangle encountered"
+ ( m_name != "" ? " in node \"" + m_name + "\"" : "" )
+ " (vertices: " + to_string( vertex1.position ) + " + " + to_string( vertex2.position ) + " + " + to_string( vertex.position ) + ")" );
}
}
++vertexcount;
if( vertexcount > 2 ) { vertexcount = 0; } // start new triangle if needed
break;
}
case triangle_fan: {
if( vertexcount == 0 ) { vertex1 = vertex; }
else if( vertexcount == 1 ) { vertex2 = vertex; }
else if( vertexcount >= 2 ) {
if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex2 );
m_data.vertices.emplace_back( vertex );
vertex2 = vertex;
}
else {
ErrorLog(
"Bad geometry: degenerate triangle encountered"
+ ( m_name != "" ? " in node \"" + m_name + "\"" : "" )
+ " (vertices: " + to_string( vertex1.position ) + " + " + to_string( vertex2.position ) + " + " + to_string( vertex.position ) + ")" );
}
}
++vertexcount;
break;
}
case triangle_strip: {
if( vertexcount == 0 ) { vertex1 = vertex; }
else if( vertexcount == 1 ) { vertex2 = vertex; }
else if( vertexcount >= 2 ) {
if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) {
// swap order every other triangle, to maintain consistent winding
if( vertexcount % 2 == 0 ) {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex2 );
}
else {
m_data.vertices.emplace_back( vertex2 );
m_data.vertices.emplace_back( vertex1 );
}
m_data.vertices.emplace_back( vertex );
vertex1 = vertex2;
vertex2 = vertex;
}
else {
ErrorLog(
"Bad geometry: degenerate triangle encountered"
+ ( m_name != "" ? " in node \"" + m_name + "\"" : "" )
+ " (vertices: " + to_string( vertex1.position ) + " + " + to_string( vertex2.position ) + " + " + to_string( vertex.position ) + ")" );
}
}
++vertexcount;
break;
}
default: { break; }
}
token = Input.getToken<std::string>();
} while( token != "endtri" );
return *this;
}
// imports data from provided submodel
shape_node &
shape_node::convert( TSubModel const *Submodel ) {
m_name = Submodel->pName;
m_data.lighting.ambient = Submodel->f4Ambient;
m_data.lighting.diffuse = Submodel->f4Diffuse;
m_data.lighting.specular = Submodel->f4Specular;
m_data.material = Submodel->m_material;
m_data.translucent = ( GfxRenderer->Material( m_data.material )->get_or_guess_opacity() == 0.0f );
// NOTE: we set unlimited view range typical for terrain, because we don't expect to convert any other 3d models
m_data.rangesquared_max = std::numeric_limits<double>::max();
if( Submodel->m_geometry.handle == null_handle ) { return *this; }
int vertexcount { 0 };
std::vector<world_vertex> importedvertices;
gfx::userdata_array importeduserdata;
if(!GfxRenderer->Indices(Submodel->m_geometry.handle).empty()){
const auto& vertices = GfxRenderer->Vertices(Submodel->m_geometry.handle);
const auto& userdatas = GfxRenderer->UserData(Submodel->m_geometry.handle);
bool has_userdata = !userdatas.empty();
world_vertex vertex;
for(const auto index : GfxRenderer->Indices(Submodel->m_geometry.handle)){
vertex = vertices[index].to_world();
importedvertices.emplace_back(vertex);
if (has_userdata)
importeduserdata.emplace_back(userdatas[index]);
}
}
else{
world_vertex vertex, vertex1, vertex2;
gfx::vertex_userdata userdata, userdata1, userdata2;
const auto& vertices = GfxRenderer->Vertices(Submodel->m_geometry.handle);
const auto& userdatas = GfxRenderer->UserData(Submodel->m_geometry.handle);
bool has_userdata = !userdatas.empty();
for( int i = 0; i < vertices.size(); ++i ) {
vertex = vertices[i].to_world();
if( has_userdata ) userdata = userdatas[i];
if( vertexcount == 0 ) { vertex1 = vertex; userdata1 = userdata; }
else if( vertexcount == 1 ) { vertex2 = vertex; userdata2 = userdata; }
else if( vertexcount >= 2 ) {
if( !degenerate( vertex1.position, vertex2.position, vertex.position ) ) {
importedvertices.emplace_back( vertex1 );
importedvertices.emplace_back( vertex2 );
importedvertices.emplace_back( vertex );
if( has_userdata ) {
importeduserdata.emplace_back( userdata1 );
importeduserdata.emplace_back( userdata2 );
importeduserdata.emplace_back( userdata );
}
}
// start a new triangle
vertexcount = -1;
}
++vertexcount;
}
}
if( true == importedvertices.empty() ) { return *this; }
// assign imported geometry to the node...
m_data.vertices.swap( importedvertices );
m_data.userdata.swap( importeduserdata );
// ...and calculate center...
for( auto const &vertex : m_data.vertices ) {
m_data.area.center += vertex.position;
}
m_data.area.center /= m_data.vertices.size();
// ...and bounding area
double squareradius { 0.0 };
for( auto const &vertex : m_data.vertices ) {
squareradius = std::max(
squareradius,
glm::length2( vertex.position - m_data.area.center ) );
}
m_data.area.radius = std::max(
m_data.area.radius,
static_cast<float>( std::sqrt( squareradius ) ) );
return *this;
}
// adds content of provided node to already enclosed geometry. returns: true if merge could be performed
bool
shape_node::merge( shape_node &Shape ) {
if( ( m_data.material != Shape.m_data.material )
|| ( m_data.lighting != Shape.m_data.lighting ) ) {
// can't merge nodes with different appearance
return false;
}
// add geometry from provided node
m_data.area.center =
interpolate(
m_data.area.center, Shape.m_data.area.center,
static_cast<double>( Shape.m_data.vertices.size() ) / ( Shape.m_data.vertices.size() + m_data.vertices.size() ) );
m_data.vertices.insert(
std::end( m_data.vertices ),
std::begin( Shape.m_data.vertices ), std::end( Shape.m_data.vertices ) );
invalidate_radius();
return true;
}
// generates renderable version of held non-instanced geometry in specified geometry bank
void
shape_node::create_geometry( gfx::geometrybank_handle const &Bank ) {
gfx::vertex_array vertices; vertices.reserve( m_data.vertices.size() );
for( auto const &vertex : m_data.vertices ) {
vertices.emplace_back(gfx::basic_vertex::convert(vertex, m_data.origin));
}
m_data.geometry = GfxRenderer->Insert(vertices, m_data.userdata, Bank, GL_TRIANGLES);
std::vector<world_vertex>().swap( m_data.vertices ); // hipster shrink_to_fit
}
// calculates shape's bounding radius
void
shape_node::compute_radius() {
auto squaredradius { 0.0 };
for( auto const &vertex : m_data.vertices ) {
squaredradius = std::max(
squaredradius,
glm::length2( vertex.position - m_data.area.center ) );
}
m_data.area.radius = static_cast<float>( std::sqrt( squaredradius ) );
}
void shape_node::invalidate_radius() {
m_data.area.radius = -1.0f;
}
float shape_node::radius() {
if (m_data.area.radius == -1.0f)
compute_radius();
return m_data.area.radius;
}
// sends content of the struct to provided stream
void
lines_node::linesnode_data::serialize( std::ostream &Output ) const {
// bounding area
area.serialize( Output );
// visibility
sn_utils::ls_float64( Output, rangesquared_min );
sn_utils::ls_float64( Output, rangesquared_max );
sn_utils::s_bool( Output, visible );
// material
sn_utils::ls_float32( Output, line_width );
lighting.serialize( Output );
// geometry
sn_utils::s_dvec3( Output, origin );
// NOTE: geometry handle is created dynamically on load
// vertex count, followed by vertex data
sn_utils::ls_uint32( Output, vertices.size() );
for( auto const &vertex : vertices ) {
gfx::basic_vertex(
glm::vec3{ vertex.position - origin },
vertex.normal,
vertex.texture )
.serialize( Output );
}
}
// restores content of the struct from provided input stream
void
lines_node::linesnode_data::deserialize( std::istream &Input ) {
// bounding area
area.deserialize( Input );
// visibility
rangesquared_min = sn_utils::ld_float64( Input );
rangesquared_max = sn_utils::ld_float64( Input );
visible = sn_utils::d_bool( Input );
// material
line_width = sn_utils::ld_float32( Input );
lighting.deserialize( Input );
// geometry
origin = sn_utils::d_dvec3( Input );
// NOTE: geometry handle is acquired during geometry creation
// vertex data
vertices.resize( sn_utils::ld_uint32( Input ) );
gfx::basic_vertex localvertex;
for( auto &vertex : vertices ) {
localvertex.deserialize( Input );
vertex.position = origin + glm::dvec3{ localvertex.position };
vertex.normal = localvertex.normal;
vertex.texture = localvertex.texture;
}
}
// sends content of the class to provided stream
void
lines_node::serialize( std::ostream &Output ) const {
// name
sn_utils::s_str( Output, m_name );
// node data
m_data.serialize( Output );
}
// restores content of the node from provided input stream
lines_node &
lines_node::deserialize( std::istream &Input ) {
// name
m_name = sn_utils::d_str( Input );
// node data
m_data.deserialize( Input );
return *this;
}
// restores content of the node from provded input stream
lines_node &
lines_node::import( cParser &Input, scene::node_data const &Nodedata ) {
// import common data
m_name = Nodedata.name;
m_data.rangesquared_min = Nodedata.range_min * Nodedata.range_min;
m_data.rangesquared_max = (
Nodedata.range_max >= 0.0 ?
Nodedata.range_max * Nodedata.range_max :
std::numeric_limits<double>::max() );
// material
Input.getTokens( 3, false );
Input
>> m_data.lighting.diffuse.r
>> m_data.lighting.diffuse.g
>> m_data.lighting.diffuse.b;
m_data.lighting.diffuse /= 255.f;
m_data.lighting.diffuse.a = 1.f;
Input.getTokens( 1, false );
Input
>> m_data.line_width;
m_data.line_width = std::min( 30.f, m_data.line_width ); // 30 pix equals rougly width of a signal pole viewed from ~1m away
// geometry
enum subtype {
lines,
line_strip,
line_loop
};
subtype const nodetype = (
Nodedata.type == "lines" ? lines :
Nodedata.type == "line_strip" ? line_strip :
line_loop );
std::size_t vertexcount { 0 };
world_vertex vertex, vertex0, vertex1;
std::string token = Input.getToken<std::string>();
do {
vertex.position.x = std::atof( token.c_str() );
Input.getTokens( 2, false );
Input
>> vertex.position.y
>> vertex.position.z;
// convert all data to gl_lines to allow data merge for matching nodes
switch( nodetype ) {
case lines: {
m_data.vertices.emplace_back( vertex );
break;
}
case line_strip: {
if( vertexcount > 0 ) {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex );
}
vertex1 = vertex;
++vertexcount;
break;
}
case line_loop: {
if( vertexcount == 0 ) {
vertex0 = vertex;
vertex1 = vertex;
}
else {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex );
}
vertex1 = vertex;
++vertexcount;
break;
}
default: { break; }
}
token = Input.getToken<std::string>();
} while( token != "endline" );
// add closing line for the loop
if( ( nodetype == line_loop )
&& ( vertexcount > 2 ) ) {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex0 );
}
if( m_data.vertices.size() % 2 != 0 ) {
ErrorLog( "Lines node specified odd number of vertices, defined in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" );
m_data.vertices.pop_back();
}
return *this;
}
// adds content of provided node to already enclosed geometry. returns: true if merge could be performed
bool
lines_node::merge( lines_node &Lines ) {
if( ( m_data.line_width != Lines.m_data.line_width )
|| ( m_data.lighting != Lines.m_data.lighting ) ) {
// can't merge nodes with different appearance
return false;
}
// add geometry from provided node
m_data.area.center =
interpolate(
m_data.area.center, Lines.m_data.area.center,
static_cast<double>( Lines.m_data.vertices.size() ) / ( Lines.m_data.vertices.size() + m_data.vertices.size() ) );
m_data.vertices.insert(
std::end( m_data.vertices ),
std::begin( Lines.m_data.vertices ), std::end( Lines.m_data.vertices ) );
// NOTE: we could recalculate radius with something other than brute force, but it'll do
compute_radius();
return true;
}
// generates renderable version of held non-instanced geometry in specified geometry bank
void
lines_node::create_geometry( gfx::geometrybank_handle const &Bank ) {
gfx::vertex_array vertices; vertices.reserve( m_data.vertices.size() );
for( auto const &vertex : m_data.vertices ) {
vertices.emplace_back(
vertex.position - m_data.origin,
vertex.normal,
vertex.texture );
}
m_data.geometry = GfxRenderer->Insert( vertices, m_data.userdata, Bank, GL_LINES );
std::vector<world_vertex>().swap( m_data.vertices ); // hipster shrink_to_fit
}
// calculates node's bounding radius
void
lines_node::compute_radius() {
auto squaredradius { 0.0 };
for( auto const &vertex : m_data.vertices ) {
squaredradius = std::max(
squaredradius,
glm::length2( vertex.position - m_data.area.center ) );
}
m_data.area.radius = static_cast<float>( std::sqrt( squaredradius ) );
}
/*
memory_node &
memory_node::deserialize( cParser &Input, node_data const &Nodedata ) {
// import common data
m_name = Nodedata.name;
Input.getTokens( 3 );
Input
>> m_data.area.center.x
>> m_data.area.center.y
>> m_data.area.center.z;
TMemCell memorycell( Nodedata.name );
memorycell.Load( &Input );
}
*/
basic_node::basic_node( scene::node_data const &Nodedata ) :
m_name( Nodedata.name )
{
uuid = UID::random();
node_type = Nodedata.type;
m_rangesquaredmin = Nodedata.range_min * Nodedata.range_min;
m_rangesquaredmax = (
Nodedata.range_max >= 0.0 ?
Nodedata.range_max * Nodedata.range_max :
std::numeric_limits<double>::max() );
}
// sends content of the class to provided stream
void
basic_node::serialize( std::ostream &Output ) const {
// bounding area
m_area.serialize( Output );
// visibility
sn_utils::ls_float64( Output, m_rangesquaredmin );
sn_utils::ls_float64( Output, m_rangesquaredmax );
sn_utils::s_bool( Output, m_visible );
// name
sn_utils::s_str( Output, m_name );
// template method implementation
serialize_( Output );
}
// restores content of the class from provided stream
void
basic_node::deserialize( std::istream &Input ) {
// bounding area
m_area.deserialize( Input );
// visibility
m_rangesquaredmin = sn_utils::ld_float64( Input );
m_rangesquaredmax = sn_utils::ld_float64( Input );
m_visible = sn_utils::d_bool( Input );
// name
m_name = sn_utils::d_str( Input );
// template method implementation
deserialize_( Input );
}
// sends basic content of the class in legacy (text) format to provided stream
void
basic_node::export_as_text( std::ostream &Output ) const {
Output
// header
<< "node"
// visibility
<< ' ' << ( m_rangesquaredmax < std::numeric_limits<double>::max() ? std::sqrt( m_rangesquaredmax ) : -1 )
<< ' ' << std::sqrt( m_rangesquaredmin )
// name
<< ' ' << ( m_name.empty() ? "none" : m_name ) << ' ';
// template method implementation
export_as_text_( Output );
}
void
basic_node::export_as_text( std::string &Output ) const {
std::stringstream converter;
export_as_text( converter );
Output += converter.str();
}
float const &
basic_node::radius() {
if( m_area.radius == -1.0 ) {
// calculate if needed
m_area.radius = radius_();
}
return m_area.radius;
}
// radius() subclass details, calculates node's bounding radius
// by default nodes are 'virtual don't extend from their center point
float
basic_node::radius_() {
return 0.f;
}
} // scene
//---------------------------------------------------------------------------

437
scene/scenenode.h Normal file
View File

@@ -0,0 +1,437 @@
/*
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/.
*/
#pragma once
#include <vector>
#include "Classes.h"
#include "material.h"
#include "vertex.h"
#include "geometrybank.h"
#include "utils/uuid.hpp"
struct lighting_data {
glm::vec4 diffuse { 0.8f, 0.8f, 0.8f, 1.0f };
glm::vec4 ambient { 0.2f, 0.2f, 0.2f, 1.0f };
glm::vec4 specular { 0.0f, 0.0f, 0.0f, 1.0f };
// stores content of the struct in provided output stream
void
serialize( std::ostream &Output ) const;
// restores content of the struct from provided input stream
void
deserialize( std::istream &Input );
};
inline
bool
operator==( lighting_data const &Left, lighting_data const &Right ) {
return ( ( Left.diffuse == Right.diffuse )
&& ( Left.ambient == Right.ambient )
&& ( Left.specular == Right.specular ) );
}
inline
bool
operator!=( lighting_data const &Left, lighting_data const &Right ) {
return !( Left == Right );
}
namespace scene {
struct bounding_area {
glm::highp_dvec3 center; // mid point of the rectangle
float radius { -1.0f }; // radius of the bounding sphere
bounding_area() = default;
bounding_area( glm::dvec3 Center, float Radius ) :
center( Center ),
radius( Radius )
{}
// stores content of the struct in provided output stream
void
serialize( std::ostream &Output ) const;
// restores content of the struct from provided input stream.
void
deserialize( std::istream &Input, bool const Preserveradius = true );
};
//using group_handle = std::size_t;
struct node_data {
double range_min { 0.0 };
double range_max { std::numeric_limits<double>::max() };
std::string name;
std::string type;
};
// holds unique piece of geometry, covered with single material
class shape_node
{
friend class basic_region; // region might want to modify node content when it's being inserted
public:
// types
struct shapenode_data {
// members:
// placement and visibility
scene::bounding_area area; // bounding area, in world coordinates
double rangesquared_min { 0.0 }; // visibility range, min
double rangesquared_max { 0.0 }; // visibility range, max
bool visible { true }; // visibility flag
// material data
bool translucent { false }; // whether opaque or translucent
material_handle material { null_handle };
lighting_data lighting;
// geometry data
glm::highp_dvec3 origin; // world position of the relative coordinate system origin
gfx::geometry_handle geometry { 0, 0 }; // relative origin-centered chunk of geometry held by gfx renderer
std::vector<world_vertex> vertices; // world space source data of the geometry
gfx::userdata_array userdata;
// methods:
// sends content of the struct to provided stream
void
serialize( std::ostream &Output ) const;
// restores content of the struct from provided input stream
void
deserialize( std::istream &Input );
};
// methods
// sends content of the class to provided stream
void
serialize( std::ostream &Output ) const;
// restores content of the node from provided input stream
shape_node &
deserialize( std::istream &Input );
// restores content of the node from provided input stream
shape_node &
import( cParser &Input, scene::node_data const &Nodedata );
// imports data from provided submodel
shape_node &
convert( TSubModel const *Submodel );
// adds content of provided node to already enclosed geometry. returns: true if merge could be performed
bool
merge( shape_node &Shape );
// generates renderable version of held non-instanced geometry in specified geometry bank
void
create_geometry( gfx::geometrybank_handle const &Bank );
// calculates shape's bounding radius
void
compute_radius();
// invalidates shape's bounding radius
void
invalidate_radius();
// set visibility
void
visible( bool State );
// set origin point
void
origin( glm::dvec3 Origin );
// data access
shapenode_data const &
data() const;
// get bounding radius
// NOTE: use this method instead of direct access to the data member, due to lazy radius evaluation
float radius();
private:
// members
std::string m_name;
shapenode_data m_data;
};
// set visibility
inline
void
shape_node::visible( bool State ) {
m_data.visible = State;
}
// set origin point
inline
void
shape_node::origin( glm::dvec3 Origin ) {
m_data.origin = Origin;
}
// data access
inline
shape_node::shapenode_data const &
shape_node::data() const {
return m_data;
}
// holds a group of untextured lines
class lines_node
{
friend class basic_region; // region might want to modify node content when it's being inserted
public:
// types
struct linesnode_data {
// members:
// placement and visibility
scene::bounding_area area; // bounding area, in world coordinates
double rangesquared_min { 0.0 }; // visibility range, min
double rangesquared_max { 0.0 }; // visibility range, max
bool visible { true }; // visibility flag
// material data
float line_width { 1.f }; // thickness of stored lines
lighting_data lighting;
// geometry data
glm::dvec3 origin; // world position of the relative coordinate system origin
gfx::geometry_handle geometry { 0, 0 }; // relative origin-centered chunk of geometry held by gfx renderer
std::vector<world_vertex> vertices; // world space source data of the geometry
gfx::userdata_array userdata;
// methods:
// sends content of the struct to provided stream
void
serialize( std::ostream &Output ) const;
// restores content of the struct from provided input stream
void
deserialize( std::istream &Input );
};
// methods
// sends content of the class to provided stream
void
serialize( std::ostream &Output ) const;
// restores content of the node from provided input stream
lines_node &
deserialize( std::istream &Input );
// restores content of the node from provided input stream
lines_node &
import( cParser &Input, scene::node_data const &Nodedata );
// adds content of provided node to already enclosed geometry. returns: true if merge could be performed
bool
merge( lines_node &Lines );
// generates renderable version of held non-instanced geometry in specified geometry bank
void
create_geometry( gfx::geometrybank_handle const &Bank );
// calculates shape's bounding radius
void
compute_radius();
// set visibility
void
visible( bool State );
// set origin point
void
origin( glm::dvec3 Origin );
// data access
linesnode_data const &
data() const;
private:
// members
std::string m_name;
linesnode_data m_data;
};
// set visibility
inline
void
lines_node::visible( bool State ) {
m_data.visible = State;
}
// set origin point
inline
void
lines_node::origin( glm::dvec3 Origin ) {
m_data.origin = Origin;
}
// data access
inline
lines_node::linesnode_data const &
lines_node::data() const {
return m_data;
}
/*
// holds geometry for specific piece of track/road/waterway
class path_node {
friend class basic_region; // region might want to modify node content when it's being inserted
public:
// types
// TODO: enable after track class refactoring
struct pathnode_data {
// placement and visibility
bounding_area area; // bounding area, in world coordinates
bool visible { true }; // visibility flag
// material data
material_handle material_1 { 0 };
material_handle material_2 { 0 };
lighting_data lighting;
TEnvironmentType environment { e_flat };
// geometry data
std::vector<world_vertex> vertices; // world space source data of the geometry
glm::dvec3 origin; // world position of the relative coordinate system origin
using geometryhandle_sequence = std::vector<geometry_handle>;
geometryhandle_sequence geometry_1; // geometry chunks textured with texture 1
geometryhandle_sequence geometry_2; // geometry chunks textured with texture 2
};
// methods
// restores content of the node from provded input stream
// TODO: implement
path_node &
deserialize( cParser &Input, node_data const &Nodedata );
// binds specified track to the node
// TODO: remove after track class refactoring
void
path( TTrack *Path ) {
m_path = Path; }
TTrack *
path() {
return m_path; }
private:
// members
// // TODO: enable after track class refactoring
// pathnode_data m_data;
TTrack * m_path;
};
*/
// base interface for nodes which can be actvated in scenario editor
class basic_node {
public:
// constructor
explicit basic_node( scene::node_data const &Nodedata );
// destructor
virtual ~basic_node() = default;
// methods
// sends content of the class to provided stream
void
serialize( std::ostream &Output ) const;
// restores content of the class from provided stream
void
deserialize( std::istream &Input );
// sends basic content of the class in legacy (text) format to provided stream
void
export_as_text( std::ostream &Output ) const;
void
export_as_text( std::string &Output ) const;
std::string const &
name() const;
virtual std::string tooltip() const;
void
location( glm::dvec3 const Location );
glm::dvec3 const &
location() const;
glm::dvec3 &
location();
float const &
radius();
void
visible( bool const Visible );
bool
visible() const;
void
group( scene::group_handle Group );
scene::group_handle
group() const;
void
mark_dirty() { m_dirty = true; }
bool
dirty() const { return m_dirty; }
std::string node_type;
public:
// members
scene::group_handle m_group { null_handle }; // group this node belongs to, if any
scene::bounding_area m_area;
double m_rangesquaredmin { 0.0 }; // visibility range, min
double m_rangesquaredmax { 0.0 }; // visibility range, max
bool m_visible { true }; // visibility flag
std::string m_name;
bool m_dirty { false };
UID uuid;
private:
// methods
// radius() subclass details, calculates node's bounding radius
virtual float radius_();
// serialize() subclass details, sends content of the subclass to provided stream
virtual void serialize_( std::ostream &Output ) const = 0;
// deserialize() subclass details, restores content of the subclass from provided stream
virtual void deserialize_( std::istream &Input ) = 0;
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
virtual void export_as_text_( std::ostream &Output ) const = 0;
};
inline
std::string const &
basic_node::name() const {
return m_name;
}
// Returns the tooltip of this Node when hovered with the mouse cursor.
inline
std::string basic_node::tooltip() const
{
return m_name;
}
inline
void
basic_node::location( glm::dvec3 const Location ) {
m_area.center = Location;
}
inline
glm::dvec3 const &
basic_node::location() const {
return m_area.center;
}
inline
glm::dvec3 &
basic_node::location() {
return m_area.center;
}
inline
void
basic_node::visible( bool const Visible ) {
m_visible = Visible;
}
inline
bool
basic_node::visible() const {
return m_visible;
}
inline
void
basic_node::group( scene::group_handle Group ) {
m_group = Group;
}
inline
scene::group_handle
basic_node::group() const {
return m_group;
}
} // scene
//---------------------------------------------------------------------------

333
scene/scenenodegroups.cpp Normal file
View File

@@ -0,0 +1,333 @@
/*
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/.
*/
#include "stdafx.h"
#include "scenenodegroups.h"
#include "Event.h"
#include "MemCell.h"
#include "AnimModel.h"
#include "widgets/map_objects.h"
namespace scene {
node_groups Groups;
// requests creation of a new node group. returns: handle to the group
scene::group_handle
node_groups::create() {
m_activegroup.push( create_handle() );
return handle();
}
// indicates creation of current group ended. returns: handle to the parent group or null_handle if group stack is empty
scene::group_handle
node_groups::close()
{
if( false == m_activegroup.empty() ) {
auto const closinggroup { m_activegroup.top() };
m_activegroup.pop();
// if the completed group holds only one item and there's no chance more items will be added, disband it
if( ( true == m_activegroup.empty() )
|| ( m_activegroup.top() != closinggroup ) ) {
auto lookup { m_groupmap.find( closinggroup ) };
if( ( lookup != m_groupmap.end() )
&& ( ( lookup->second.nodes.size() + lookup->second.events.size() ) <= 1 ) ) {
erase( lookup );
}
}
}
return handle();
}
bool node_groups::assign_cross_switch(map::track_switch& sw, std::string &sw_name, std::string const &id, size_t idx)
{
sw.action[idx] = simulation::Events.FindEvent(sw_name + ":" + id);
if (!sw.action[idx])
sw.action[idx] = simulation::Events.FindEvent(sw_name + id);
if (!sw.action[idx])
return false;
multi_event *multi = dynamic_cast<multi_event*>(sw.action[idx]);
if (!multi)
return false;
auto names = multi->dump_children_names();
for (auto it = names.begin(); it != names.end(); it++) {
*it = it->substr(sw_name.size());
if (it->size() > 4)
continue;
int pos_a = it->find_last_of('a');
int pos_b = it->find_last_of('b');
int pos_c = it->find_last_of('c');
int pos_d = it->find_last_of('d');
int pos_0 = it->find_last_of('0');
int pos_1 = it->find_last_of('1');
int pos;
if (pos_a > pos_b && pos_a > pos_c && pos_a > pos_d)
pos = 0;
else if (pos_b > pos_a && pos_b > pos_c && pos_b > pos_d)
pos = 1;
else if (pos_c > pos_a && pos_c > pos_b && pos_c > pos_d)
pos = 2;
else
pos = 3;
sw.preview[idx][pos] = ((pos_0 > pos_1) ? '0' : '1');
}
return true;
}
void
node_groups::update_map()
{
map::Objects.entries.clear();
std::unordered_map<std::string, std::shared_ptr<map::track_switch>> last_switch_map;
for (auto const &pair : m_groupmap) {
auto const &group = pair.second;
for (basic_node *node : group.nodes) {
std::string postfix { "_sem_mem" };
if (typeid(*node) == typeid(TMemCell) && string_ends_with(node->name(), postfix)) {
std::string sem_name = node->name().substr(0, node->name().length() - postfix.length());
auto sem_info = std::make_shared<map::semaphore>();
map::Objects.entries.push_back(sem_info);
sem_info->location = node->location();
sem_info->memcell = static_cast<TMemCell*>(node);
sem_info->name = sem_name;
for (basic_event *event : group.events) {
if (string_starts_with(event->name(), sem_name)
&& event->name().substr(sem_name.length()).find("sem") == std::string::npos) {
sem_info->events.push_back(event);
}
}
for (basic_node *node : group.nodes)
if (auto *model = dynamic_cast<TAnimModel*>(node))
if (string_starts_with(model->name(), sem_name))
sem_info->models.push_back(model);
}
if (Global.map_manualswitchcontrol) {
if (TTrack *track = dynamic_cast<TTrack*>(node)) {
if (track->eType != tt_Switch)
continue;
basic_event *sw_p = simulation::Events.FindEvent(track->name() + "+");
basic_event *sw_m = simulation::Events.FindEvent(track->name() + "-");
if (sw_p && sw_m) {
auto map_launcher = std::make_shared<map::track_switch>();
map::Objects.entries.push_back(map_launcher);
map_launcher->location = node->location();
map_launcher->name = node->name();
map_launcher->action[0] = sw_p;
map_launcher->action[1] = sw_m;
map_launcher->track[0] = track;
map_launcher->preview[0][0] = '0';
map_launcher->preview[1][0] = '1';
continue;
}
std::string sw_name = track->name();
if (sw_name.size() <= 2)
continue;
char lastc = sw_name.back();
sw_name.pop_back();
if (sw_name.back() == '_')
sw_name.pop_back();
if (!(simulation::Events.FindEvent(sw_name + ":ac") || simulation::Events.FindEvent(sw_name + "ac")))
continue;
std::shared_ptr<map::track_switch> last_switch;
auto it = last_switch_map.find(sw_name);
if (it != last_switch_map.end()) {
last_switch = it->second;
} else {
last_switch = std::make_shared<map::track_switch>();
last_switch->name = sw_name;
last_switch_map.insert(std::make_pair(sw_name, last_switch));
}
if (lastc < 'a' || lastc > 'd')
continue;
last_switch->track[lastc - 'a'] = track;
for (auto trk : last_switch->track)
if (!trk)
goto skip_e;
if (!assign_cross_switch(*last_switch, sw_name, "ac", 0))
skip_e: continue;
if (!assign_cross_switch(*last_switch, sw_name, "ad", 1))
continue;
if (!assign_cross_switch(*last_switch, sw_name, "bc", 2))
continue;
if (!assign_cross_switch(*last_switch, sw_name, "bd", 3))
continue;
last_switch->location = (last_switch->track[0]->location() + last_switch->track[1]->location() + last_switch->track[2]->location() + last_switch->track[3]->location()) / 4.0;
map::Objects.entries.push_back(last_switch);
last_switch_map.erase(sw_name);
}
} else {
if (TEventLauncher *launcher = dynamic_cast<TEventLauncher*>(node)) {
if (!launcher || !launcher->Event1 || !launcher->Event2)
continue;
auto map_launcher = std::make_shared<map::launcher>();
map::Objects.entries.push_back(map_launcher);
map_launcher->location = node->location();
map_launcher->name = node->name();
map_launcher->first_event = launcher->Event1;
map_launcher->second_event = launcher->Event2;
if (map_launcher->name.empty())
map_launcher->name = launcher->Event1->name();
if (launcher->Event1->name().find_first_of("-+:") != std::string::npos)
map_launcher->type = map::launcher::track_switch;
else
map_launcher->type = map::launcher::level_crossing;
}
}
}
}
}
// returns current active group, or null_handle if group stack is empty
group_handle
node_groups::handle() const {
return (
m_activegroup.empty() ?
null_handle :
m_activegroup.top() );
}
// places provided node in specified group
void
node_groups::insert( scene::group_handle const Group, scene::basic_node *Node ) {
// TBD, TODO: automatically unregister the node from its current group?
Node->group( Group );
if( Group == null_handle ) { return; }
auto &nodesequence { m_groupmap[ Group ].nodes };
if( std::find( std::begin( nodesequence ), std::end( nodesequence ), Node ) == std::end( nodesequence ) ) {
// don't add the same node twice
nodesequence.emplace_back( Node );
}
}
// places provided event in specified group
void
node_groups::insert( scene::group_handle const Group, basic_event *Event ) {
// TBD, TODO: automatically unregister the event from its current group?
Event->group( Group );
if( Group == null_handle ) { return; }
auto &eventsequence { m_groupmap[ Group ].events };
if( std::find( std::begin( eventsequence ), std::end( eventsequence ), Event ) == std::end( eventsequence ) ) {
// don't add the same node twice
eventsequence.emplace_back( Event );
}
}
// sends basic content of the class in legacy (text) format to provided stream
void
node_groups::export_as_text( std::ostream &Output, bool Dirty ) const {
for( auto const &group : m_groupmap ) {
bool any = false;
for( auto *node : group.second.nodes ) {
if (node->dirty() != Dirty)
continue;
// HACK: auto-generated memory cells aren't exported, so we check for this
// TODO: is_exportable as basic_node method
if( ( typeid( *node ) == typeid( TMemCell ) )
&& ( false == static_cast<TMemCell *>( node )->is_exportable ) ) {
continue;
}
if (!any)
Output << "group\n";
any = true;
node->export_as_text( Output );
}
for( auto *event : group.second.events ) {
if (Dirty)
continue;
if (!any)
Output << "group\n";
any = true;
event->export_as_text( Output );
}
if (any)
Output << "endgroup\n";
}
}
// removes specified group from the group list and group information from the group's nodes
void
node_groups::erase( group_map::const_iterator Group ) {
for( auto *node : Group->second.nodes ) {
node->group( null_handle );
}
for( auto *event : Group->second.events ) {
event->group( null_handle );
}
m_groupmap.erase( Group );
}
// creates handle for a new group
group_handle
node_groups::create_handle() {
// NOTE: for simplification nested group structure are flattened
return(
m_activegroup.empty() ?
m_groupmap.size() + 1 : // new group isn't created until node registration
m_activegroup.top() );
}
} // scene
//---------------------------------------------------------------------------

78
scene/scenenodegroups.h Normal file
View File

@@ -0,0 +1,78 @@
/*
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/.
*/
#pragma once
#include "scenenode.h"
#include "widgets/map_objects.h"
namespace scene {
struct basic_group {
// members
std::vector<scene::basic_node *> nodes;
std::vector<basic_event *> events;
};
// holds lists of grouped scene nodes
class node_groups {
// NOTE: during scenario deserialization encountering *.inc file causes creation of a new group on the group stack
// this allows all nodes listed in this *.inc file to be grouped and potentially modified together by the editor.
public:
// constructors
node_groups() = default;
// methods
// requests creation of a new node group. returns: handle to the group
group_handle
create();
// indicates creation of current group ended. returns: handle to the parent group or null_handle if group stack is empty
group_handle
close();
// update minimap objects
void
update_map();
// returns current active group, or null_handle if group stack is empty
group_handle
handle() const;
// places provided node in specified group
void
insert( scene::group_handle const Group, scene::basic_node *Node );
// places provided event in specified group
void
insert( scene::group_handle const Group, basic_event *Event );
// grants direct access to specified group
scene::basic_group &
group( scene::group_handle const Group ) {
return m_groupmap[ Group ]; }
// sends basic content of the class in legacy (text) format to provided stream
void
export_as_text( std::ostream &Output, bool const Dirty ) const;
private:
// types
using group_map = std::unordered_map<scene::group_handle, scene::basic_group>;
// methods
// removes specified group from the group list and group information from the group's nodes
void
erase( group_map::const_iterator Group );
// creates handle for a new group
group_handle
create_handle();
bool
assign_cross_switch(map::track_switch&sw, std::string &sw_name, const std::string &id, size_t idx);
// members
group_map m_groupmap; // map of established node groups
std::stack<scene::group_handle> m_activegroup; // helper, group to be assigned to newly created nodes
};
extern node_groups Groups;
} // scene
//---------------------------------------------------------------------------

262
scene/sn_utils.cpp Normal file
View File

@@ -0,0 +1,262 @@
/* 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/. */
#include "stdafx.h"
#include "sn_utils.h"
// sanity checks
static_assert(std::numeric_limits<double>::is_iec559, "IEEE754 required");
static_assert(sizeof(float) == 4, "Float must be 4 bytes");
static_assert(sizeof(double) == 8, "Double must be 8 bytes");
static_assert(-1 == ~0, "Two's complement required");
// deserialize little endian uint16
uint16_t sn_utils::ld_uint16(std::istream &s)
{
uint8_t buf[2];
s.read((char*)buf, 2);
uint16_t v = (buf[1] << 8) | buf[0];
return reinterpret_cast<uint16_t&>(v);
}
// deserialize little endian uint32
uint32_t sn_utils::ld_uint32(std::istream &s)
{
uint8_t buf[4];
s.read((char*)buf, 4);
uint32_t v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
return reinterpret_cast<uint32_t&>(v);
}
// deserialize little endian int32
int32_t sn_utils::ld_int32(std::istream &s)
{
uint8_t buf[4];
s.read((char*)buf, 4);
uint32_t v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
return reinterpret_cast<int32_t&>(v);
}
// deserialize little endian uint64
uint64_t sn_utils::ld_uint64(std::istream &s)
{
uint8_t buf[8];
s.read((char*)buf, 8);
uint64_t v = ((uint64_t)buf[7] << 56) | ((uint64_t)buf[6] << 48) |
((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) |
((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) |
((uint64_t)buf[1] << 8) | (uint64_t)buf[0];
return reinterpret_cast<uint64_t&>(v);
}
// deserialize little endian int64
int64_t sn_utils::ld_int64(std::istream &s)
{
uint8_t buf[8];
s.read((char*)buf, 8);
uint64_t v = ((uint64_t)buf[7] << 56) | ((uint64_t)buf[6] << 48) |
((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) |
((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) |
((uint64_t)buf[1] << 8) | (uint64_t)buf[0];
return reinterpret_cast<int64_t&>(v);
}
// deserialize little endian ieee754 float32
float sn_utils::ld_float32(std::istream &s)
{
uint8_t buf[4];
s.read((char*)buf, 4);
uint32_t v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
return reinterpret_cast<float&>(v);
}
// deserialize little endian ieee754 float64
double sn_utils::ld_float64(std::istream &s)
{
uint8_t buf[8];
s.read((char*)buf, 8);
uint64_t v = ((uint64_t)buf[7] << 56) | ((uint64_t)buf[6] << 48) |
((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) |
((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) |
((uint64_t)buf[1] << 8) | (uint64_t)buf[0];
return reinterpret_cast<double&>(v);
}
// deserialize null-terminated string
std::string sn_utils::d_str(std::istream &s)
{
std::string r;
r.reserve(32);
char buf[1];
while (true)
{
s.read(buf, 1);
if (buf[0] == 0)
break;
r.push_back(buf[0]);
}
return r;
}
bool sn_utils::d_bool(std::istream& s)
{
return ( ld_uint16( s ) == 1 );
}
glm::dvec3 sn_utils::d_dvec3(std::istream& s)
{
return {
ld_float64(s),
ld_float64(s),
ld_float64(s) };
}
glm::vec3 sn_utils::d_vec3( std::istream& s )
{
return {
ld_float32(s),
ld_float32(s),
ld_float32(s) };
}
glm::vec4 sn_utils::d_vec4( std::istream& s )
{
return {
ld_float32(s),
ld_float32(s),
ld_float32(s),
ld_float32(s) };
}
uint8_t sn_utils::d_uint8( std::istream& s ) {
uint8_t buf;
s.read((char*)&buf, 1);
return buf;
}
void sn_utils::ls_uint16(std::ostream &s, uint16_t v)
{
uint8_t buf[2];
buf[0] = v;
buf[1] = v >> 8;
s.write((char*)buf, 2);
}
void sn_utils::ls_uint32(std::ostream &s, uint32_t v)
{
uint8_t buf[4];
buf[0] = v;
buf[1] = v >> 8;
buf[2] = v >> 16;
buf[3] = v >> 24;
s.write((char*)buf, 4);
}
void sn_utils::ls_int32(std::ostream &s, int32_t v)
{
uint8_t buf[4];
buf[0] = v;
buf[1] = v >> 8;
buf[2] = v >> 16;
buf[3] = v >> 24;
s.write((char*)buf, 4);
}
void sn_utils::ls_uint64(std::ostream &s, uint64_t v)
{
uint8_t buf[8];
buf[0] = v;
buf[1] = v >> 8;
buf[2] = v >> 16;
buf[3] = v >> 24;
buf[4] = v >> 32;
buf[5] = v >> 40;
buf[6] = v >> 48;
buf[7] = v >> 56;
s.write((char*)buf, 8);
}
void sn_utils::ls_int64(std::ostream &s, int64_t v)
{
uint8_t buf[8];
buf[0] = v;
buf[1] = v >> 8;
buf[2] = v >> 16;
buf[3] = v >> 24;
buf[4] = v >> 32;
buf[5] = v >> 40;
buf[6] = v >> 48;
buf[7] = v >> 56;
s.write((char*)buf, 8);
}
void sn_utils::ls_float32(std::ostream &s, float t)
{
uint32_t v = reinterpret_cast<uint32_t&>(t);
uint8_t buf[4];
buf[0] = v;
buf[1] = v >> 8;
buf[2] = v >> 16;
buf[3] = v >> 24;
s.write((char*)buf, 4);
}
void sn_utils::ls_float64(std::ostream &s, double t)
{
uint64_t v = reinterpret_cast<uint64_t&>(t);
uint8_t buf[8];
buf[0] = v;
buf[1] = v >> 8;
buf[2] = v >> 16;
buf[3] = v >> 24;
buf[4] = v >> 32;
buf[5] = v >> 40;
buf[6] = v >> 48;
buf[7] = v >> 56;
s.write((char*)buf, 8);
}
void sn_utils::s_uint8(std::ostream &s, uint8_t v)
{
s.write((char*)&v, 1);
}
void sn_utils::s_str(std::ostream &s, std::string v)
{
const char* buf = v.c_str();
s.write(buf, v.size() + 1);
}
void sn_utils::s_bool(std::ostream &s, bool v)
{
ls_uint16(
s,
( true == v ?
1 :
0 ) );
}
void sn_utils::s_dvec3(std::ostream &s, glm::dvec3 const &v)
{
ls_float64(s, v.x);
ls_float64(s, v.y);
ls_float64(s, v.z);
}
void sn_utils::s_vec3(std::ostream &s, glm::vec3 const &v)
{
ls_float32(s, v.x);
ls_float32(s, v.y);
ls_float32(s, v.z);
}
void sn_utils::s_vec4(std::ostream &s, glm::vec4 const &v)
{
ls_float32(s, v.x);
ls_float32(s, v.y);
ls_float32(s, v.z);
ls_float32(s, v.w);
}

37
scene/sn_utils.h Normal file
View File

@@ -0,0 +1,37 @@
/* 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/. */
#include <string>
class sn_utils
{
public:
static uint16_t ld_uint16(std::istream&);
static uint32_t ld_uint32(std::istream&);
static int32_t ld_int32(std::istream&);
static uint64_t ld_uint64(std::istream&);
static int64_t ld_int64(std::istream&);
static float ld_float32(std::istream&);
static double ld_float64(std::istream&);
static uint8_t d_uint8(std::istream&);
static std::string d_str(std::istream&);
static bool d_bool(std::istream&);
static glm::dvec3 d_dvec3(std::istream&);
static glm::vec3 d_vec3(std::istream&);
static glm::vec4 d_vec4(std::istream&);
static void ls_uint16(std::ostream&, uint16_t);
static void ls_uint32(std::ostream&, uint32_t);
static void ls_int32(std::ostream&, int32_t);
static void ls_uint64(std::ostream&, uint64_t);
static void ls_int64(std::ostream&, int64_t);
static void ls_float32(std::ostream&, float);
static void ls_float64(std::ostream&, double);
static void s_uint8(std::ostream&, uint8_t);
static void s_str(std::ostream&, std::string);
static void s_bool(std::ostream&, bool);
static void s_dvec3(std::ostream&, glm::dvec3 const &);
static void s_vec3(std::ostream&, glm::vec3 const &);
static void s_vec4(std::ostream&, glm::vec4 const &);
};