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:
1744
scene/scene.cpp
Normal file
1744
scene/scene.cpp
Normal file
File diff suppressed because it is too large
Load Diff
463
scene/scene.h
Normal file
463
scene/scene.h
Normal 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
201
scene/sceneeditor.cpp
Normal 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
64
scene/sceneeditor.h
Normal 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
811
scene/scenenode.cpp
Normal 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
437
scene/scenenode.h
Normal 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
333
scene/scenenodegroups.cpp
Normal 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
78
scene/scenenodegroups.h
Normal 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
262
scene/sn_utils.cpp
Normal 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
37
scene/sn_utils.h
Normal 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 &);
|
||||
};
|
||||
Reference in New Issue
Block a user