Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:49:38

0001 #pragma once
0002 /**
0003 SGLM : Header Only Viz Math giving ray tracing basis and rasterization projection transforms
0004 ===============================================================================================
0005 
0006 Critical usage for ray trace rendering done in CSGOptiX::prepareRenderParam
0007 
0008 SGLM.h is a single header that is replacing a boatload of classes
0009 used by old OpticksCore okc : Composition, View, Camera, ...
0010 Using this enabled CSGOptiX to drop dependency on okc, npy, brap
0011 and instead depend only on QUDARap, SysRap, CSG.
0012 
0013 Normal inputs WH, CE, EYE, LOOK, UP are held in static variables with envvar defaults
0014 These can be changed with static methods before instanciating SGLM.
0015 NB it will usually be too late for setenv in code to influence SGLM
0016 as the static initialization would have happened already
0017 
0018 * TODO: reincarnate animated interpolation between view bookmarks
0019 * TODO: provide persistency into ~16 quad4 for debugging view/cam/projection state
0020 
0021 
0022 SGLM::desc
0023 -------------
0024 
0025 NB : to dump view param from interactive viz use "P" key invoking SGLM::desc
0026 
0027 
0028 Controlling orthographic views
0029 ---------------------------------
0030 
0031 When using orthographic projection usual position movement navigation
0032 does not change the view like it perspective projection.
0033 Changing EYE actually does have the effect of changing
0034 the side and hence the coloring from the normal direction
0035 but it does not change the apparent size of the render.
0036 Instead EXTENT_FUDGE can be used to do that.
0037 
0038 ::
0039 
0040    CAM=orthographic EXTENT_FUDGE=1.5 EYE=0,1,-0.1 LOOK=0,0,-0.1 cxr_min.sh
0041 
0042 
0043 DONE : rasterized and raytrace render consistency
0044 -------------------------------------------------
0045 
0046 Using get_transverse_scale proportional to get_near_abs
0047 and longitudinally using get_near_abs from updateEyeBasis/updateProjection
0048 for raytrace/rasterized succeeds to get the two perspective
0049 renders to match closely. This follows the technique used in okc/Camera.cc.
0050 
0051 * TODO: regain orthographic, above changes for perspective matching have messed that up
0052 * TODO: flipping between raytrace and raster with C:CUDA key looses quaternion rotation causing jump back
0053 
0054 
0055 SGLM.h tests
0056 --------------
0057 
0058 SGLMTest.cc
0059    check a few statics, standardly built
0060 
0061 SGLM_test.{sh,cc}
0062    standalone test for a few SGLM methods
0063 
0064 SGLM_set_frame_test.{sh,cc}
0065    loads sframe sets into SGLM and dumps
0066 
0067 SGLM_frame_targetting_test.{sh,cc}
0068    compares SGLM A,B from two different center_extent sframe a,b
0069 
0070 
0071 Review coordinate systems, following along the below description
0072 -----------------------------------------------------------------
0073 
0074 * https://unspecified.wordpress.com/2012/06/21/calculating-the-gluperspective-matrix-and-other-opengl-matrix-maths/
0075 * https://learnopengl.com/Getting-started/Camera
0076 * https://feepingcreature.github.io/math.html
0077 
0078 OpenGL coordinate systems
0079 ~~~~~~~~~~~~~~~~~~~~~~~~~~
0080 
0081 Right hand systems:
0082 
0083 * +X : right
0084 * +Y : up
0085 * +Z : towards camera
0086 * -Z : into scene
0087 
0088 
0089 Object
0090    vertices relative to center of Model
0091 World
0092    relative to one world origin
0093 Eye
0094    relative to camera
0095 
0096    * vertices are transformed into Eye Coordinates by the model-view matrix
0097 
0098 Clip
0099    funny coordinates : that get transformed by "divide-by-w" into NDC coordinates
0100 
0101    * shader pipeline (vertex or geometry shader) outputs Clip coordinates, that
0102      OpenGL does the “divide-by-w” step to givew NDC coordinates
0103 
0104    * the ".w" of clip coordinates often set to "-z" as trick to do perspective Divide
0105 
0106 
0107 NDC/Viewport
0108    normalized device coordinates : on screen position coordinates
0109 
0110    * (x,y)
0111    * (-1,-1) : lower left
0112    * (+1,+1) : upper right
0113    * z=-1 : nearest point in depth buffer
0114    * z=+1 : farthest point in depth buffer
0115 
0116    The z values are mapped on to the depth buffer space by the projection matrix.
0117    Thats why zNear,ZFar settings are important.
0118 
0119 Screen
0120    (x,y) coordinate on screen in pixels
0121 
0122    * (0,0) lower left pixel
0123    * (w,h) upper right pixel
0124 
0125 
0126 **/
0127 
0128 #include <string>
0129 #include <vector>
0130 #include <iostream>
0131 #include <iomanip>
0132 #include <sstream>
0133 #include <string>
0134 #include <array>
0135 
0136 #include <glm/glm.hpp>
0137 #include <glm/gtc/matrix_transform.hpp>
0138 #include <glm/gtc/type_ptr.hpp>
0139 #include <glm/gtx/transform.hpp>
0140 #include <glm/gtx/string_cast.hpp>
0141 
0142 #include "scuda.h"
0143 #include "sqat4.h"
0144 #include "SCenterExtentFrame.h"
0145 #include "SCAM.h"
0146 #include "SBAS.h"
0147 #include "SBitSet.h"
0148 
0149 #include "SScene.h"
0150 #include "stree.h"
0151 
0152 #include "SGLM_View.h"
0153 #include "SGLM_Arcball.h"
0154 
0155 #include "sfr.h"     // formerly sframe.h
0156 #include "SCE.h"     // moving from sframe to SCE
0157 
0158 #include "ssys.h"
0159 #include "sstr.h"
0160 #include "NP.hh"
0161 
0162 #include "SYSRAP_API_EXPORT.hh"
0163 
0164 #include "SRecord.h"
0165 #include "SGen.h"
0166 #include "SCMD.h"
0167 #include "SGLM_Modifiers.h"
0168 #include "SGLM_Parse.h"
0169 
0170 
0171 
0172 
0173 
0174 // inputs to projection matrix
0175 struct SYSRAP_API lrbtnf
0176 {
0177     float left ;
0178     float right ;
0179     float bottom ;
0180     float top ;
0181     float near ;
0182     float far ;
0183 
0184     // see http://www.songho.ca/opengl/gl_projectionmatrix.html
0185     float A_frustum() const ;
0186     float B_frustum() const ;
0187     float A_ortho() const ;
0188     float B_ortho() const ;
0189     std::string desc() const ;
0190 };
0191 
0192 
0193 
0194 
0195 
0196 
0197 
0198 struct SGLM_Setting
0199 {
0200     int value = 0;
0201     int num_modes = 2; // Default for boolean-like toggles
0202 
0203     void next() { value = (value + 1) % num_modes; }
0204 
0205     // Helper to keep existing bool logic working where needed
0206     bool operator!() const { return value == 0; }
0207     explicit operator bool() const { return value != 0; }
0208 };
0209 
0210 
0211 
0212 
0213 /**
0214 SGLM_Toggle
0215 -------------
0216 
0217 **/
0218 
0219 struct SYSRAP_API SGLM_Toggle
0220 {
0221     SGLM_Setting zoom{0, 2}; // Z
0222     SGLM_Setting tmin{0, 2}; // N
0223     SGLM_Setting tmax{0, 2}; // F
0224     SGLM_Setting lrot{0, 2}; // R
0225     SGLM_Setting cuda{0, 2}; // C
0226     SGLM_Setting norm{0, 2}; // U
0227     SGLM_Setting time{0, 2}; // T
0228     SGLM_Setting spin{0, 5}; // L
0229     SGLM_Setting stop{0, 2}; // SPACE
0230 
0231     std::string desc() const;
0232 };
0233 
0234 inline std::string SGLM_Toggle::desc() const
0235 {
0236     std::stringstream ss;
0237     ss << "SGLM_Toggle::desc"
0238        << " zoom: " << zoom.value
0239        << " tmin: " << tmin.value
0240        << " tmax: " << tmax.value
0241        << " lrot: " << lrot.value
0242        << " cuda: " << cuda.value
0243        << " norm: " << norm.value
0244        << " time: " << time.value
0245        << " spin: " << spin.value
0246        << " stop: " << stop.value
0247        ;
0248     std::string str = ss.str();
0249     return str ;
0250 }
0251 
0252 
0253 struct SYSRAP_API SGLM_Option
0254 {
0255     bool A = false ;
0256     bool B = false ;
0257     bool G = false ;
0258     bool M = true ;
0259     bool O = true ;
0260     std::string desc() const ;
0261 };
0262 
0263 inline std::string SGLM_Option::desc() const
0264 {
0265     std::stringstream ss ;
0266     ss << "SGLM_Option::desc"
0267        << " A:" << ( A ? "Y" : "N" )
0268        << " B:" << ( B ? "Y" : "N" )
0269        << " G:" << ( G ? "Y" : "N" )
0270        << " M:" << ( M ? "Y" : "N" )
0271        << " O:" << ( O ? "Y" : "N" )
0272        ;
0273     std::string str = ss.str();
0274     return str ;
0275 }
0276 
0277 
0278 
0279 
0280 
0281 
0282 struct SYSRAP_API SGLM : public SCMD
0283 {
0284     static SGLM* INSTANCE ;
0285     static SGLM* Get();
0286 
0287     static constexpr const char* kTITLE = "TITLE" ;
0288     static constexpr const char* kWH = "WH" ;
0289     static constexpr const char* kCE = "CE" ;
0290     static constexpr const char* kEYE = "EYE" ;
0291     static constexpr const char* kLOOK = "LOOK" ;
0292     static constexpr const char* kUP = "UP" ;
0293     static constexpr const char* kZOOM = "ZOOM" ;
0294     static constexpr const char* kTMIN = "TMIN" ;
0295     static constexpr const char* kTMAX = "TMAX" ;
0296     static constexpr const char* kCAM = "CAM" ;
0297     static constexpr const char* kNEARFAR = "NEARFAR" ;
0298     static constexpr const char* kFOCAL = "FOCAL" ;
0299     static constexpr const char* kFULLSCREEN = "FULLSCREEN" ;
0300     static constexpr const char* kESCALE = "ESCALE" ;
0301     static constexpr const char* kEXTENT_FUDGE = "EXTENT_FUDGE" ;
0302 
0303     static constexpr const char* kVIZMASK = "VIZMASK" ;
0304     static constexpr const char* kTRACEYFLIP = "TRACEYFLIP" ;
0305     static constexpr const char* kLEVEL = "SGLM_LEVEL" ;
0306     static constexpr const char* kTIMESCALE = "TIMESCALE" ;
0307 
0308     static constexpr const char* kT0 = "T0" ;
0309     static constexpr const char* kT1 = "T1" ;
0310     static constexpr const char* kTT = "TT" ;
0311     static constexpr const char* kTN = "TN" ;  // int:number of render loop time bumps to go from T0 to T1
0312 
0313 
0314 
0315 
0316     static constexpr const char* _SGLM_DESC = "SGLM_DESC" ;
0317     static constexpr const char* __setTreeScene_DUMP = "SGLM__setTreeScene_DUMP" ;
0318     static constexpr const char* __init_time_DUMP = "SGLM__init_time_DUMP" ;
0319 
0320 
0321     // static defaults, some can be overridden in the instance
0322     static const char* TITLE ;
0323     static glm::ivec2 WH ;
0324 
0325     static glm::vec4 CE ; // CE IS GEOMETRY NOT VIEW RELATED BUT ITS EXPEDIENT TO BE HERE
0326     static glm::vec4 EYE ;
0327     static glm::vec4 LOOK ;
0328     static glm::vec4 UP ;
0329 
0330 
0331     static float ZOOM ;
0332     static float TMIN ;
0333     static float TMAX ;
0334     static int   CAM ;
0335     static int   NEARFAR ;
0336     static int   FOCAL ;
0337     static int   FULLSCREEN ;
0338     static int   ESCALE ;
0339     static float EXTENT_FUDGE ;
0340     static uint32_t VIZMASK ;
0341     static int   TRACEYFLIP ;
0342     static int   LEVEL ;
0343 
0344     static float TIMESCALE ;
0345 
0346     // record time range
0347     static float  T0 ;
0348     static float  T1 ;
0349     static float  TT ;
0350     static int    TN ;
0351 
0352 
0353     static void SetWH( int width, int height );
0354     static void SetCE( float x, float y, float z, float extent );
0355     static void SetEYE( float x, float y, float z );
0356     static void SetLOOK( float x, float y, float z );
0357     static void SetUP( float x, float y, float z );
0358 
0359     static void SetZOOM( float v );
0360     static void SetTMIN( float v );
0361     static void SetTMAX( float v );
0362 
0363     static void IncZOOM( float v );
0364     static void IncTMIN( float v );
0365     static void IncTMAX( float v );
0366 
0367     static void SetCAM( const char* cam );
0368     static void SetNEARFAR( const char* nearfar );
0369     static void SetFOCAL( const char* focal );
0370     static void SetFULLSCREEN( const char* fullscreen );
0371     static void SetESCALE( const char* escale );
0372     static void SetVIZMASK( const char* vizmask );
0373     static void SetTRACEYFLIP( const char* traceyflip );
0374 
0375     bool is_vizmask_set(unsigned ibit) const ;
0376 
0377 
0378     // querying of static defaults
0379     static std::string DescInput() ;
0380     static int Width() ;
0381     static int Height() ;
0382     static int Width_Height() ;
0383 
0384     static float Aspect();
0385     static const char* CAM_Label() ;
0386     static const char* NEARFAR_Label() ;
0387     static const char* FOCAL_Label() ;
0388     static const char* FULLSCREEN_Label() ;
0389     static const char* ESCALE_Label() ;
0390     static std::string VIZMASK_Label() ;
0391     static const char* TRACEYFLIP_Label() ;
0392 
0393     static void Copy(float* dst, const glm::vec3& src );
0394     static void Copy(float* dst, const glm::vec4& src );
0395 
0396     // member methods
0397     std::string descInput() const ;
0398 
0399     SGLM();
0400     void init();
0401 
0402     bool SGLM_DESC ;
0403 
0404     stree*  tree ;
0405     SScene* scene ;
0406     void setTreeScene( stree* _tree, SScene* _scene );
0407 
0408     void handle_frame_hop(int wanted_frame_idx);
0409 
0410     void setLookRotation(float angle_deg, glm::vec3 axis );
0411     void setLookRotation( const glm::vec2& a, const glm::vec2& b );
0412 
0413     void setEyeRotation(float angle_deg, glm::vec3 axis );
0414     void setEyeRotation( const glm::vec2& a, const glm::vec2& b );
0415 
0416     void setDepthTest(int _depth_test);
0417     int depthTest() const ;
0418 
0419     void cursor_moved_action( const glm::vec2& a, const glm::vec2& b, unsigned modifiers );
0420     void key_pressed_action( unsigned modifiers );
0421 
0422     void home();
0423     std::string descEyeShift() const ;
0424     static std::string DescQuat( const glm::quat& q );
0425 
0426 
0427     void tcam();
0428     void toggle_traceyflip();
0429     void toggle_rendertype();
0430 
0431     static void Command(const SGLM_Parse& parse, SGLM* gm, bool dump);
0432     int command(const char* cmd);
0433 
0434 
0435     sfr moi_fr = {} ;
0436     sfr fr = {} ;  // CAUTION: SEvt also holds an sframe used for input photon targetting
0437 
0438     static constexpr const char* _DUMP = "SGLM__set_frame_DUMP" ;
0439     void set_frame();
0440     void set_frame( const char* q_spec );
0441     void set_frame( const float4& ce );
0442     void set_frame( const sfr& fr );
0443 
0444     int get_frame_idx() const ;
0445     bool has_frame_idx(int idx) const ;
0446     const std::string& get_frame_name() const ;
0447 
0448     float extent() const ;
0449     float tmin_abs() const ;
0450     float tmax_abs() const ;
0451 
0452     bool rtp_tangential ;
0453     void set_rtp_tangential( bool rtp_tangential_ );
0454 
0455     bool extent_scale ;
0456     void set_extent_scale( bool extent_scale_ );
0457 
0458     // matrices taken from fr or derived from ce when fr only holds identity
0459     glm::mat4 model2world ;
0460     glm::mat4 world2model ;
0461     int initModelMatrix_branch ;
0462 
0463     void initModelMatrix();  // depends on ce, unless non-identity m2w and w2m matrices provided in frame
0464     std::string descModelMatrix() const ;
0465 
0466     // world frame View converted from static model frame
0467     // initELU
0468 
0469     void  initView();
0470     SGLM_View view = {} ;
0471 
0472 
0473     void  initELU();   // depends on CE and EYE, LOOK, UP
0474     void updateGaze();
0475     std::string descELU() const ;
0476 
0477     std::vector<glm::vec3> axes ;
0478 
0479 
0480     glm::vec3 eye ;
0481     glm::vec3 look ;
0482     glm::vec3 up ;
0483 
0484     // updateGaze
0485     glm::vec3 gaze ;
0486     glm::mat4 eye2look ;
0487     glm::mat4 look2eye ;
0488 
0489     float spin_degrees_per_frame ;
0490     glm::vec3 q_spin_axis ;
0491     glm::quat q_spin;
0492 
0493     glm::quat q_lookrot ;
0494     glm::quat q_eyerot ;
0495     glm::vec3 eyeshift  ;
0496 
0497     int       depth_test ;
0498     int       home_count ;
0499 
0500     float     get_escale_() const ;
0501     glm::mat4 get_escale() const ;
0502     float getGazeLength() const ;
0503     float getGazeCrossUp() const ;
0504     void avoidDegenerateBasisByChangingUp();
0505 
0506     void updateNearFar();
0507     std::string descNearFar() const ;
0508 
0509     // results from updateEyeSpace
0510 
0511     glm::vec3 forward_ax ;
0512     glm::vec3 right_ax ;
0513     glm::vec3 top_ax ;
0514     glm::mat4 rot_ax ;
0515     glm::mat4 world2camera ;
0516     glm::mat4 camera2world ;
0517 
0518     void updateEyeSpace();
0519     std::string descEyeSpace() const ;
0520 
0521     // results from updateEyeBasis
0522     glm::vec3 u ;
0523     glm::vec3 v ;
0524     glm::vec3 w ;
0525     glm::vec3 wnorm ;
0526     glm::vec3 e ;
0527 
0528 
0529     void updateEyeBasis();
0530     static std::string DescEyeBasis( const glm::vec3& E, const glm::vec3& U, const glm::vec3& V, const glm::vec3& W );
0531     std::string descEyeBasis() const ;
0532 
0533 
0534 
0535 
0536     // modes
0537     int  cam ;
0538     int  nearfar ;
0539     int  focal ;
0540     int  fullscreen ;
0541     uint32_t vizmask ;
0542     int   traceyflip ;
0543     int   rendertype ;
0544 
0545 
0546     float nearfar_manual ;
0547     float focal_manual ;
0548     float near ;
0549     float far ;
0550     float orthographic_scale ;
0551 
0552 
0553     std::vector<std::string> log ;
0554 
0555 
0556 
0557     void set_near( float near_ );
0558     void set_far( float far_ );
0559     void set_near_abs( float near_abs_ );
0560     void set_far_abs(  float far_abs_ );
0561 
0562     float get_near() const ;
0563     float get_far() const ;
0564     float get_near_abs() const ;
0565     float get_far_abs() const ;
0566 
0567     std::string descFrame() const ;
0568     std::string descBasis() const ;
0569     std::string descProj() const ;
0570     std::string descProjection() const ;
0571     std::string descComposite() const ;
0572 
0573     lrbtnf    proj ;
0574     glm::mat4 projection ;
0575     glm::vec4 zproj ;
0576 
0577     glm::mat4 MV ;
0578     glm::mat4 IMV ;
0579 
0580     float*    MV_ptr ;
0581     glm::mat4 MVP ;    // aka world2clip
0582     float*    MVP_ptr ;
0583     glm::mat4 IDENTITY ;
0584     float* IDENTITY_ptr ;
0585     std::string title ;
0586 
0587     void updateTitle();
0588     void left_right_bottom_top_near_far(lrbtnf& p) const ;
0589 
0590     static constexpr const char* _updateProjection_DEBUG = "SGLM__updateProjection_DEBUG" ;
0591     void updateProjection();
0592 
0593     static void FillZProjection(  glm::vec4& _zproj, const glm::mat4& _proj );
0594     float zdepth_pos( const glm::tvec4<float>& p_eye ) const ;
0595     float zdepth0(    const float& z_eye ) const ;
0596     float zdepth1(    const float& z_eye ) const ;
0597     float zproj_A() const ;
0598     float zproj_B() const ;
0599 
0600     static void FillAltProjection(glm::vec4& _aproj, const glm::mat4& _proj );
0601 
0602     float get_transverse_scale() const ;
0603 
0604 
0605 
0606     void increment_spin();
0607 
0608     void updateComposite();
0609 
0610 
0611     template<typename T> void ce_corners_world( std::vector<glm::tvec4<T>>& v_world ) const ;
0612     template<typename T> void ce_midface_world( std::vector<glm::tvec4<T>>& v_world ) const ;
0613 
0614     template<typename T>
0615     static void Apply_XF( std::vector<glm::tvec4<float>>& v_out, const std::vector<glm::tvec4<T>>& v_in, const glm::tmat4x4<float>& XF, bool flip );
0616 
0617     template<typename T>
0618     void apply_MV(  std::vector<glm::tvec4<float>>& v_eye,  const std::vector<glm::tvec4<T>>& v_world, bool flip ) const ;
0619     template<typename T>
0620     void apply_MVP( std::vector<glm::tvec4<float>>& v_clip, const std::vector<glm::tvec4<T>>& v_world, bool flip ) const ;
0621     template<typename T>
0622     void apply_P(   std::vector<glm::tvec4<float>>& v_clip, const std::vector<glm::tvec4<T>>& v_eye  , bool flip ) const ;
0623 
0624     std::string desc_MVP() const ;
0625     std::string desc_MVP_ce_corners() const ;
0626     std::string desc_MV_P_MVP_ce_corners() const ;
0627     std::string desc_MVP_ce_midface() const ;
0628     static bool IsClipped(const glm::vec4& _ndc );
0629 
0630     void set_nearfar_mode(const char* mode);
0631     void set_focal_mode(const char* mode);
0632 
0633     const char* get_nearfar_mode() const ;
0634     const char* get_focal_mode() const ;
0635 
0636     void set_nearfar_manual(float nearfar_manual_);
0637     void set_focal_manual(float focal_manual_);
0638 
0639     float get_nearfar_basis() const ;
0640     float get_focal_basis() const ;
0641 
0642     void save(const char* dir, const char* stem) const ;
0643     void writeDesc(const char* dir, const char* name="SGLM__writeDesc", const char* ext=".log" ) const ;
0644     std::string desc() const ;
0645 
0646 
0647     void dump() const ;
0648     void update();
0649     void constrain() const ;
0650 
0651 
0652     void addlog( const char* label, float value       ) ;
0653     void addlog( const char* label, const char* value ) ;
0654     std::string descLog() const ;
0655 
0656 
0657     template <typename T> static T ato_( const char* a );
0658     template <typename T> static void Str2Vector( std::vector<T>& vec, const char* uval );
0659     template <typename T> static void GetEVector(std::vector<T>& vec, const char* key, const char* fallback );
0660     template <typename T> static std::string Present(std::vector<T>& vec);
0661     template <typename T> static std::string Present(const T* tt, int num);
0662 
0663     static std::string Present(const glm::ivec2& v, int wid=6 );
0664     static std::string Present(const float v, int wid=10, int prec=3);
0665     static std::string Present(const glm::vec2& v, int wid=10, int prec=3);
0666     static std::string Present(const glm::vec3& v, int wid=10, int prec=3);
0667     static std::string Present(const glm::vec4& v, int wid=10, int prec=3);
0668     static std::string Present(const float4& v,    int wid=10, int prec=3);
0669     static std::string Present(const glm::mat4& m, int wid=10, int prec=3);
0670 
0671     template<typename T> static std::string Present_(const glm::tmat4x4<T>& m, int wid=10, int prec=3);
0672 
0673     static void GetEVec(glm::vec3& v, const char* key, const char* fallback );
0674     static void GetEVec(glm::vec4& v, const char* key, const char* fallback );
0675 
0676     template <typename T> static T SValue(const char* uval );
0677     template <typename T> static T EValue(const char* key, const char* fallback );
0678     static glm::ivec2 EVec2i(const char* key, const char* fallback);
0679     static glm::vec3 EVec3(const char* key, const char* fallback);
0680     static glm::vec4 EVec4(const char* key, const char* fallback, float missing );
0681     static glm::vec4 SVec4(const char* str, float missing );
0682     static glm::vec3 SVec3(const char* str, float missing );
0683 
0684     template<typename T> static glm::tmat4x4<T> DemoMatrix(T scale);
0685 
0686 
0687     SRecord* ar ;
0688     SRecord* br ;
0689     SGen*    gs ;
0690 
0691     void setRecord( SRecord* ar, SRecord* br );
0692     void setGenstep( SGen* gs );
0693 
0694     bool enabled_time_bump = true ;
0695     bool enabled_time_halt = false ;
0696     glm::vec4 timeparam = {} ;
0697     const float* timeparam_ptr ;
0698 
0699 
0700     void init_time();
0701     void reset_time();
0702     void reset_time_TT();
0703     void toggle_time_halt();
0704 
0705     std::string desc_time() const ;
0706 
0707     float get_t0() const ;
0708     float get_t1() const ;
0709     float get_ts() const ;
0710     int get_tn() const ;
0711 
0712 
0713     float get_time() const ;
0714     bool in_timerange(float t) const ;
0715     void set_time( float t );
0716     void time_bump();
0717     void inc_time( float dy );
0718 
0719     SGLM_Toggle toggle = {} ;
0720     SGLM_Option option = {} ;
0721 
0722     void renderloop_head();
0723     void renderloop_tail();
0724 
0725 };
0726 
0727 SGLM* SGLM::INSTANCE = nullptr ;
0728 SGLM* SGLM::Get(){  return INSTANCE ? INSTANCE : new SGLM  ; }
0729 
0730 const char* SGLM::TITLE = ssys::getenvvar(kTITLE, "TITLE") ;
0731 glm::ivec2 SGLM::WH = EVec2i(kWH,"1920,1080") ;
0732 
0733 glm::vec4  SGLM::CE = EVec4(kCE,"0,0,0,100", 100.f) ;
0734 
0735 glm::vec4  SGLM::EYE  = EVec4(kEYE, "-1,-1,0,1", 1.f) ;
0736 glm::vec4  SGLM::LOOK = EVec4(kLOOK, "0,0,0,1" , 1.f) ;
0737 glm::vec4  SGLM::UP  =  EVec4(kUP,   "0,0,1,0" , 0.f) ;
0738 
0739 
0740 float      SGLM::ZOOM = EValue<float>(kZOOM, "1");
0741 float      SGLM::TMIN = EValue<float>(kTMIN, "0.1");
0742 float      SGLM::TMAX = EValue<float>(kTMAX, "100.0");
0743 int        SGLM::CAM  = SCAM::EGet(kCAM, "perspective") ;
0744 int        SGLM::NEARFAR = SBAS::EGet(kNEARFAR, "gazelength") ;
0745 int        SGLM::FOCAL   = SBAS::EGet(kFOCAL,   "gazelength") ;
0746 int        SGLM::FULLSCREEN  = EValue<int>(kFULLSCREEN,   "0") ;
0747 int        SGLM::ESCALE  = SBAS::EGet(kESCALE,  "extent") ;  // "asis"
0748 float      SGLM::EXTENT_FUDGE = EValue<float>(kEXTENT_FUDGE, "1");
0749 uint32_t   SGLM::VIZMASK = SBitSet::Value<uint32_t>(32, kVIZMASK, "t" );
0750 int        SGLM::TRACEYFLIP  = ssys::getenvint(kTRACEYFLIP,  0 ) ;
0751 int        SGLM::LEVEL  = ssys::getenvint(kLEVEL,  0 ) ;
0752 float      SGLM::TIMESCALE = EValue<float>(kTIMESCALE, "1.0");
0753 
0754 
0755 float      SGLM::T0 = EValue<float>(kT0, "0.0" );
0756 float      SGLM::T1 = EValue<float>(kT1, "0.0" );
0757 float      SGLM::TT = EValue<float>(kTT, "0.0" );
0758 int        SGLM::TN = ssys::getenvint(kTN, 5000 );
0759 
0760 
0761 inline void SGLM::SetWH( int width, int height ){ WH.x = width ; WH.y = height ; }
0762 inline void SGLM::SetCE(  float x, float y, float z, float w){ CE.x = x ; CE.y = y ; CE.z = z ;  CE.w = w ; }
0763 
0764 // HMM: these are setting the statics
0765 inline void SGLM::SetEYE( float x, float y, float z){ EYE.x = x  ; EYE.y = y  ; EYE.z = z  ;  EYE.w = 1.f ; }
0766 inline void SGLM::SetLOOK(float x, float y, float z){ LOOK.x = x ; LOOK.y = y ; LOOK.z = z ;  LOOK.w = 1.f ; }
0767 inline void SGLM::SetUP(  float x, float y, float z){ UP.x = x   ; UP.y = y   ; UP.z = z   ;  UP.w = 0.f ; }  // 0.f as treat as direction
0768 
0769 inline void SGLM::SetZOOM( float v ){ ZOOM = v ; if(LEVEL>0) std::cout << "SGLM::SetZOOM " << ZOOM << std::endl ; }
0770 inline void SGLM::SetTMIN( float v ){ TMIN = v ; if(LEVEL>0) std::cout << "SGLM::SetTMIN " << TMIN << std::endl ; }
0771 inline void SGLM::SetTMAX( float v ){ TMAX = v ; if(LEVEL>0) std::cout << "SGLM::SetTMAX " << TMAX << std::endl ; }
0772 
0773 inline void SGLM::IncZOOM( float v ){ ZOOM += v ; /*std::cout << "SGLM::IncZOOM " << ZOOM << std::endl ;*/ }
0774 inline void SGLM::IncTMIN( float v ){ TMIN += v ; /*std::cout << "SGLM::IncTMIN " << TMIN << std::endl ;*/ }
0775 inline void SGLM::IncTMAX( float v ){ TMAX += v ; if(LEVEL>0) std::cout << "SGLM::IncTMAX " << TMAX << std::endl ; }
0776 
0777 
0778 inline void SGLM::SetCAM( const char* cam ){ CAM = SCAM::Type(cam) ; }
0779 inline void SGLM::SetNEARFAR( const char* nearfar ){ NEARFAR = SBAS::Type(nearfar) ; }
0780 inline void SGLM::SetFOCAL( const char* focal ){ FOCAL = SBAS::Type(focal) ; }
0781 inline void SGLM::SetVIZMASK( const char* vizmask ){ VIZMASK = SBitSet::Value<uint32_t>(32, vizmask) ; }
0782 inline void SGLM::SetTRACEYFLIP( const char* traceyflip ){ TRACEYFLIP = SBAS::AsInt(traceyflip) ; }
0783 
0784 bool SGLM::is_vizmask_set(unsigned ibit) const { return SBitSet::IsSet<uint32_t>(vizmask, ibit ); }
0785 
0786 
0787 
0788 
0789 
0790 inline int SGLM::Width(){  return WH.x ; }
0791 inline int SGLM::Height(){ return WH.y ; }
0792 inline int SGLM::Width_Height(){ return Width()*Height() ; }
0793 
0794 inline float SGLM::Aspect() { return float(WH.x)/float(WH.y) ; }
0795 inline const char* SGLM::CAM_Label(){ return SCAM::Name(CAM) ; }
0796 inline const char* SGLM::NEARFAR_Label(){ return SBAS::Name(NEARFAR) ; }
0797 inline const char* SGLM::FOCAL_Label(){   return SBAS::Name(FOCAL) ; }
0798 inline const char* SGLM::ESCALE_Label(){   return SBAS::Name(ESCALE) ; }
0799 inline std::string SGLM::VIZMASK_Label(){   return SBitSet::DescValue(VIZMASK) ; }
0800 inline const char* SGLM::TRACEYFLIP_Label(){  return SBAS::DescInt(TRACEYFLIP) ; }
0801 
0802 inline void SGLM::Copy(float* dst, const glm::vec3& src )
0803 {
0804     dst[0] = src.x ;
0805     dst[1] = src.y ;
0806     dst[2] = src.z ;
0807 }
0808 inline void SGLM::Copy(float* dst, const glm::vec4& src )
0809 {
0810     dst[0] = src.x ;
0811     dst[1] = src.y ;
0812     dst[2] = src.z ;
0813     dst[3] = src.w ;
0814 }
0815 
0816 
0817 
0818 
0819 
0820 inline SGLM::SGLM()
0821     :
0822     SGLM_DESC(ssys::getenvbool(_SGLM_DESC)),
0823     tree(nullptr),
0824     scene(nullptr),
0825     rtp_tangential(false),
0826     extent_scale(false),
0827     model2world(1.f),
0828     world2model(1.f),
0829     initModelMatrix_branch(-1),
0830     eye(   0.f,0.f,0.f),
0831     look(  0.f,0.f,0.f),
0832     up(    0.f,0.f,0.f),
0833     gaze(  0.f,0.f,0.f),
0834     eye2look(1.f),
0835     look2eye(1.f),
0836     spin_degrees_per_frame(0.15f),
0837     q_spin_axis(0.f,0.f,1.f),
0838     q_spin(1.f,0.f,0.f,0.f),     // identity quaternion
0839     q_lookrot(1.f,0.f,0.f,0.f),   // identity quaternion
0840     q_eyerot( 1.f,0.f,0.f,0.f),   // identity quaternion
0841     eyeshift(0.f,0.f,0.f),
0842     depth_test(1),
0843     home_count(0),
0844     forward_ax(0.f,0.f,0.f),
0845     right_ax(0.f,0.f,0.f),
0846     top_ax(0.f,0.f,0.f),
0847     rot_ax(1.f),
0848     world2camera(1.f),
0849     camera2world(1.f),
0850     u(0.f,0.f,0.f),
0851     v(0.f,0.f,0.f),
0852     w(0.f,0.f,0.f),
0853     e(0.f,0.f,0.f),
0854     cam(CAM),
0855     nearfar(NEARFAR),   // gazelength default
0856     focal(FOCAL),
0857     fullscreen(FULLSCREEN),
0858     vizmask(VIZMASK),
0859     traceyflip(TRACEYFLIP),
0860     rendertype(0),
0861     nearfar_manual(0.f),
0862     focal_manual(0.f),
0863 
0864     near(0.1f),   // units of get_nearfar_basis
0865     far(5.f),     // units of get_nearfar_basis
0866     orthographic_scale(1.f),
0867 
0868     projection(1.f),
0869     zproj(0.f, 0.f, 0.f, 0.f),
0870     MV(1.f),
0871     IMV(1.f),
0872     MV_ptr(glm::value_ptr(MV)),
0873     MVP(1.f),
0874     MVP_ptr(glm::value_ptr(MVP)),
0875     IDENTITY(1.f),
0876     IDENTITY_ptr(glm::value_ptr(IDENTITY)),
0877     title("SGLM"),
0878     ar(nullptr),
0879     br(nullptr),
0880     gs(nullptr),
0881     enabled_time_bump(true),
0882     enabled_time_halt(false),
0883     timeparam_ptr(glm::value_ptr(timeparam))
0884 {
0885     init();
0886 }
0887 
0888 inline void SGLM::init()
0889 {
0890     addlog("SGLM::init", "ctor");
0891     INSTANCE = this ;
0892 
0893     axes.push_back( {1.f,0.f,0.f} );
0894     axes.push_back( {0.f,1.f,0.f} );
0895     axes.push_back( {0.f,0.f,1.f} );
0896 
0897     constrain();
0898 }
0899 
0900 /**
0901 SGLM::setTreeScene
0902 -------------------
0903 
0904 This is invoked during initialization of some test executables,
0905 such as from::
0906 
0907    CSGOptiXRenderInteractiveTest::init
0908    sysrap/tests/SGLFW_SOPTIX_Scene_test.cc:main
0909 
0910 **/
0911 
0912 
0913 inline void SGLM::setTreeScene( stree* _tree, SScene* _scene )
0914 {
0915     tree = _tree ;
0916     scene = _scene ;
0917 
0918     moi_fr = tree->get_frame_moi();
0919 
0920     bool DUMP = ssys::getenvbool(__setTreeScene_DUMP) ;
0921     if(DUMP) std::cout
0922         << "SGLM::setTreeScene "
0923         << __setTreeScene_DUMP
0924         << " DUMP " << ( DUMP ? "YES" : "NO " )
0925         << " moi_fr \n"
0926         << moi_fr.desc()
0927         << "\n"
0928         ;
0929 
0930 }
0931 
0932 inline void SGLM::handle_frame_hop(int wanted_frame_idx)
0933 {
0934     bool frame_hop = !has_frame_idx(wanted_frame_idx) ;
0935 
0936     if(SGLM_DESC) std::cout
0937         << "SGLM::handle_frame_hop"
0938         << " wanted_frame_idx " << wanted_frame_idx
0939         << " frame_hop " << ( frame_hop ? "YES" : "NO " )
0940         << "\n"
0941         ;
0942 
0943     if(frame_hop)
0944     {
0945         if( wanted_frame_idx == -2 )
0946         {
0947             if(SGLM_DESC) std::cout << _SGLM_DESC << "\n"  << desc() ;
0948             set_frame(moi_fr);
0949         }
0950         else if( wanted_frame_idx >= 0 )
0951         {
0952             assert(scene);  // must setTreeScene before using handle_frame_hop
0953             sfr wfr = scene->getFrame(wanted_frame_idx) ;
0954             set_frame(wfr);
0955         }
0956     }
0957 }
0958 
0959 
0960 
0961 
0962 
0963 void SGLM::setLookRotation(float angle_deg, glm::vec3 axis )
0964 {
0965     q_lookrot = glm::angleAxis( glm::radians(angle_deg), glm::normalize(axis) );
0966 }
0967 void SGLM::setEyeRotation(float angle_deg, glm::vec3 axis )
0968 {
0969     q_eyerot = glm::angleAxis( glm::radians(angle_deg), glm::normalize(axis) );
0970 }
0971 
0972 
0973 void SGLM::setDepthTest(int _depth_test)
0974 {
0975     depth_test = _depth_test ;
0976 }
0977 int SGLM::depthTest() const
0978 {
0979     return depth_test ;
0980 }
0981 
0982 
0983 
0984 /**
0985 SGLM::setLookRotation
0986 --------------------------
0987 
0988 In "Rotate" mode, after pressing "R", as drag the mouse
0989 around get different orientations of the look position.
0990 
0991 **/
0992 
0993 void SGLM::setLookRotation( const glm::vec2& a, const glm::vec2& b )
0994 {
0995     //std::cout << "SGLM::setLookRotation " << glm::to_string(a) << " " << glm::to_string(b) << std::endl ;
0996     q_lookrot = SGLM_Arcball::A2B_Screen( a, b );
0997 }
0998 void SGLM::setEyeRotation( const glm::vec2& a, const glm::vec2& b )
0999 {
1000     //std::cout << "SGLM::setEyeRotation " << glm::to_string(a) << " " << glm::to_string(b) << std::endl ;
1001     q_eyerot = SGLM_Arcball::A2B_Screen( a, b );
1002 }
1003 
1004 
1005 
1006 void SGLM::cursor_moved_action( const glm::vec2& a, const glm::vec2& b, unsigned modifiers )
1007 {
1008     if(SGLM_Modnav::IsR(modifiers))
1009     {
1010         setLookRotation(a,b);
1011     }
1012     else if(SGLM_Modnav::IsY(modifiers))
1013     {
1014         setEyeRotation(a,b);
1015     }
1016     else
1017     {
1018         setEyeRotation(a,b);
1019         //key_pressed_action(modifiers);
1020     }
1021 }
1022 
1023 
1024 /**
1025 SGLM::key_pressed_action
1026 -------------------------
1027 
1028 Currently only from SGLFW::key_repeated
1029 
1030 **/
1031 
1032 void SGLM::key_pressed_action( unsigned modifiers )
1033 {
1034     float factor = SGLM_Modifiers::IsShift(modifiers) ? 5.f : 1.f ;
1035     float speed = factor*extent()/100. ;
1036 
1037     if(SGLM_Modnav::IsW(modifiers)) eyeshift.z += speed ;
1038     if(SGLM_Modnav::IsS(modifiers)) eyeshift.z -= speed ;
1039 
1040     if(SGLM_Modnav::IsA(modifiers)) eyeshift.x += speed ; // sign surprised me here
1041     if(SGLM_Modnav::IsD(modifiers)) eyeshift.x -= speed ;
1042 
1043     if(SGLM_Modnav::IsQ(modifiers)) eyeshift.y += speed ;
1044     if(SGLM_Modnav::IsE(modifiers)) eyeshift.y -= speed ;
1045 }
1046 
1047 
1048 void SGLM::home()
1049 {
1050     if(LEVEL > 3) std::cout << "SGLM::home [" << home_count << "]" << descEyeShift();
1051     home_count += 1 ;
1052 
1053     eyeshift.x = 0.f ;
1054     eyeshift.y = 0.f ;
1055     eyeshift.z = 0.f ;
1056     q_lookrot = SGLM_Arcball::Identity();
1057     q_eyerot = SGLM_Arcball::Identity();
1058 
1059     SetZOOM(1.f);
1060 }
1061 
1062 std::string SGLM::descEyeShift() const
1063 {
1064     std::stringstream ss ;
1065     ss
1066        << "[SGLM::descEyeShift\n"
1067        << " eyeshift " << Present(eyeshift) << "\n"
1068        << " q_lookrot " << DescQuat(q_lookrot) << "\n"
1069        << " q_eyerot " << DescQuat(q_eyerot) << "\n"
1070        << "]SGLM::descEyeShift\n"
1071        ;
1072     std::string str = ss.str() ;
1073     return str ;
1074 }
1075 
1076 std::string SGLM::DescQuat( const glm::quat& q ) // static
1077 {
1078     glm::mat4 m = glm::mat4_cast(q);
1079     std::stringstream ss ;
1080 
1081     ss << "q_wxyz{"
1082        << " " << std::setw(10) << std::fixed << std::setprecision(3) << q.w
1083        << "," << std::setw(10) << std::fixed << std::setprecision(3) << q.x
1084        << "," << std::setw(10) << std::fixed << std::setprecision(3) << q.y
1085        << "," << std::setw(10) << std::fixed << std::setprecision(3) << q.z
1086        << "}\n"
1087        << m
1088        << "\n"
1089        ;
1090 
1091     std::string str = ss.str() ;
1092     return str ;
1093 }
1094 
1095 
1096 
1097 
1098 
1099 void SGLM::tcam()
1100 {
1101     cam = SCAM::Next(cam);
1102 }
1103 
1104 void SGLM::toggle_traceyflip()
1105 {
1106     traceyflip = !traceyflip ;
1107 }
1108 void SGLM::toggle_rendertype()
1109 {
1110     rendertype = !rendertype ;
1111 }
1112 
1113 /**
1114 SGLM::Command
1115 --------------
1116 
1117 **/
1118 
1119 void SGLM::Command(const SGLM_Parse& parse, SGLM* gm, bool dump)  // static
1120 {
1121     assert( parse.key.size() == parse.val.size() );
1122     int num_kv = parse.key.size();
1123     int num_op = parse.opt.size();
1124 
1125     for(int i=0 ; i < num_kv ; i++)
1126     {
1127         const char* k = parse.key[i].c_str();
1128         const char* v = parse.val[i].c_str();
1129 
1130         if(dump) std::cout
1131            << "SGLM::Command"
1132            << " k[" << ( k ? k : "-" ) << "]"
1133            << " v[" << ( v ? v : "-" ) << "]"
1134            << std::endl
1135            ;
1136 
1137         if(     strcmp(k,"ce")==0)
1138         {
1139             glm::vec4 tmp = SVec4(v, 0.f) ;
1140             SetCE( tmp.x, tmp.y, tmp.z, tmp.w );
1141         }
1142         else if(     strcmp(k,"eye")==0)
1143         {
1144             glm::vec3 tmp = SVec3(v, 0.f) ;
1145             SetEYE( tmp.x, tmp.y, tmp.z );
1146         }
1147         else if(strcmp(k,"look")==0)
1148         {
1149             glm::vec3 tmp = SVec3(v, 0.f) ;
1150             SetLOOK( tmp.x, tmp.y, tmp.z );
1151         }
1152         else if(strcmp(k,"up")==0)
1153         {
1154             glm::vec3 tmp = SVec3(v, 0.f) ;
1155             SetUP( tmp.x, tmp.y, tmp.z );
1156         }
1157         else if(strcmp(k,"zoom")==0)     SetZOOM(SValue<float>(v)) ;
1158         else if(strcmp(k,"tmin")==0)     SetTMIN(SValue<float>(v)) ;
1159         else if(strcmp(k,"tmax")==0)     SetTMAX(SValue<float>(v)) ;
1160         else if(strcmp(k,"inc-zoom")==0) IncZOOM(SValue<float>(v)) ;
1161         else if(strcmp(k,"inc-tmin")==0) IncTMIN(SValue<float>(v)) ;
1162         else if(strcmp(k,"inc-tmax")==0) IncTMAX(SValue<float>(v)) ;
1163         else if(strcmp(k,"inc-time")==0) gm->inc_time(SValue<float>(v)) ;
1164         else
1165         {
1166             std::cout << "SGLM::Command unhandled kv [" << k << "," << v << "]" << std::endl ;
1167         }
1168     }
1169 
1170     for(int i=0 ; i < num_op ; i++)
1171     {
1172         const char* op = parse.opt[i].c_str();
1173         if(     strcmp(op,"desc")==0) std::cout << gm->desc() << std::endl ;
1174         else if(strcmp(op,"home")==0) gm->home();
1175         else if(strcmp(op,"tcam")==0) gm->tcam();
1176         else if(strcmp(op,"traceyflip")==0) gm->toggle_traceyflip();
1177         else if(strcmp(op,"rendertype")==0) gm->toggle_rendertype();
1178         else
1179         {
1180             std::cout << "SGLM::Command IGNORING op [" << ( op ? op : "-" ) << "]" << std::endl;
1181         }
1182     }
1183 }
1184 
1185 
1186 /**
1187 SGLM::command
1188 --------------
1189 
1190 The objective of this method is to provide a generic method
1191 to control view parameters without requiring tight coupling between
1192 this struct which handles view maths and various rendering systems.
1193 For example key callbacks into SGLFW yield control strings that
1194 can be passed here to change the view, where SGLFW need only know
1195 the SCMD interface that this method fulfils.
1196 Similarly UDP commands from remote commandlines picked up
1197 by async listeners can similarly change the view.
1198 
1199 From old opticks see::
1200 
1201     oglrap/OpticksViz::command
1202     okc/Composition::command
1203     okc/Camera::commandNear
1204 
1205 **/
1206 
1207 int SGLM::command(const char* cmd)
1208 {
1209     SGLM_Parse parse(cmd);
1210 
1211     bool dump = false ;
1212     if(dump) std::cout << "SGLM::command" << std::endl << parse.desc() ;
1213     Command(parse, this, dump);
1214     update();
1215     return 0 ;
1216 }
1217 
1218 
1219 void SGLM::save(const char* dir, const char* stem) const
1220 {
1221     fr.save( dir, stem ); // .npy
1222     writeDesc( dir, stem, ".log" );
1223 }
1224 
1225 void SGLM::writeDesc(const char* dir, const char* name_ , const char* ext_ ) const
1226 {
1227     std::string ds = desc() ;
1228     const char* name = name_ ? name_ : "SGLM__writeDesc" ;
1229     const char* ext  = ext_ ? ext_ : ".log" ;
1230     NP::WriteString(dir, name, ext,  ds );
1231 }
1232 
1233 
1234 /**
1235 SGLM::desc
1236 ------------
1237 
1238 Invoke this from interactive viz using "P" key
1239 
1240 **/
1241 
1242 std::string SGLM::desc() const
1243 {
1244     std::stringstream ss ;
1245     ss << descFrame() << std::endl ;
1246     ss << DescInput() << std::endl ;
1247     ss << descInput() << std::endl ;
1248     ss << descModelMatrix() << std::endl ;
1249     ss << descELU() << std::endl ;
1250     ss << descNearFar() << std::endl ;
1251     ss << descEyeSpace() << std::endl ;
1252     ss << descEyeBasis() << std::endl ;
1253     ss << descProj() << std::endl ;
1254     ss << descProjection() << std::endl ;
1255     ss << descBasis() << std::endl ;
1256     ss << descLog() << std::endl ;
1257     ss << desc_MVP() << std::endl ;
1258     ss << desc_MVP_ce_corners() << std::endl ;
1259     ss << desc_MVP_ce_midface() << std::endl ;
1260     std::string s = ss.str();
1261     return s ;
1262 }
1263 void SGLM::dump() const
1264 {
1265     std::cout << desc() << std::endl ;
1266 }
1267 
1268 std::string SGLM::DescInput() // static
1269 {
1270     std::stringstream ss ;
1271     ss << "SGLM::DescInput" << std::endl ;
1272     ss << std::setw(15) << "SGLM::CAM"  << " " << SGLM::CAM << std::endl ;
1273     ss << std::setw(15) << kCAM << " " << CAM_Label() << std::endl ;
1274     ss << std::setw(15) << kNEARFAR << " " << NEARFAR_Label() << std::endl ;
1275     ss << std::setw(15) << kFOCAL   << " " << FOCAL_Label() << std::endl ;
1276     ss << std::setw(15) << kESCALE  << " " << ESCALE_Label() << std::endl ;
1277     ss << std::setw(15) << kEXTENT_FUDGE  << " " << EXTENT_FUDGE << std::endl ;
1278     ss << std::setw(15) << kWH    << Present( WH )   << " Aspect " << Aspect() << std::endl ;
1279     ss << std::setw(15) << kCE    << Present( CE )   << std::endl ;
1280     ss << std::setw(15) << kEYE   << Present( EYE )  << std::endl ;
1281     ss << std::setw(15) << kLOOK  << Present( LOOK ) << std::endl ;
1282     ss << std::setw(15) << kUP    << Present( UP )   << std::endl ;
1283     ss << std::setw(15) << kZOOM  << Present( ZOOM ) << std::endl ;
1284     ss << std::endl ;
1285     std::string s = ss.str();
1286     return s ;
1287 }
1288 
1289 std::string SGLM::descInput() const
1290 {
1291     std::stringstream ss ;
1292     ss << "SGLM::descInput" << std::endl ;
1293     ss << std::setw(25) << " sglm.fr.desc "  << fr.desc()  << std::endl ;
1294     ss << std::setw(25) << " sglm.cam " << cam << std::endl ;
1295     ss << std::setw(25) << " SCAM::Name(sglm.cam) " << SCAM::Name(cam) << std::endl ;
1296     std::string s = ss.str();
1297     return s ;
1298 }
1299 
1300 /**
1301 SGLM::set_frame
1302 -----------------
1303 
1304 **/
1305 
1306 inline void SGLM::set_frame()
1307 {
1308     assert(tree && "MUST CALL SGLM::setTreeScene BEFORE SGLM::set_frame");
1309     sfr f = tree->get_frame_moi();
1310     set_frame(f);
1311 }
1312 
1313 inline void SGLM::set_frame( const char* q_spec )
1314 {
1315     assert(tree && "MUST CALL SGLM::setTreeScene BEFORE SGLM::set_frame");
1316     sfr f = tree->get_frame(q_spec);
1317     set_frame(f);
1318 }
1319 
1320 inline void SGLM::set_frame( const float4& ce )
1321 {
1322     assert(tree && "MUST CALL SGLM::setTreeScene BEFORE SGLM::set_frame");
1323     sfr f = sfr::MakeFromCE<float>(&ce.x);
1324     set_frame(f);
1325 }
1326 
1327 
1328 inline void SGLM::set_frame( const sfr& fr_ )
1329 {
1330     fr = fr_ ;
1331     //std::cout << "SGLM::set_frame [" << fr.get_name() << "]\n";
1332 
1333     update();
1334 
1335     int DUMP = ssys::getenvint(_DUMP, 0);
1336     if(DUMP > 0) std::cout << _DUMP << ":" << DUMP << "\n" << desc() ;
1337 }
1338 
1339 inline int SGLM::get_frame_idx() const { return fr.get_idx(); }
1340 inline bool SGLM::has_frame_idx(int q_idx) const
1341 {
1342     int curr_idx = get_frame_idx() ;
1343     if(false) std::cout << "SGLM::has_frame_idx" << " q_idx " << q_idx << " curr_idx " << curr_idx << "\n" ;
1344     return q_idx == curr_idx ;
1345 }
1346 inline const std::string& SGLM::get_frame_name() const { return fr.get_name(); }
1347 
1348 /**
1349 SGLM::extent
1350 -------------
1351 
1352 When looking at small objects a fudged increase in the extent with EXTENT_FUDGE improves
1353 the viz interface by avoiding overly tight near,far and also avoiding overly slow
1354 WASDQE navigation.
1355 
1356 **/
1357 
1358 inline float SGLM::extent() const {   return EXTENT_FUDGE*(fr.ce.w > 0 ? fr.ce.w : CE.w) ; }
1359 inline float SGLM::tmin_abs() const { return extent()*TMIN ; }  // HUH:extent might not be the basis ?
1360 inline float SGLM::tmax_abs() const { return extent()*TMAX ; }  // HUH:extent might not be the basis ?
1361 
1362 /**
1363 SGLM::update
1364 --------------
1365 
1366 initModelMatrix
1367     model2world, world2model from frame or ce (translation only, not including extent scale)
1368     [note the only? use of these is from initELU]
1369 
1370 
1371 initView
1372 
1373 
1374 initELU
1375     eye,look,up in world frame from EYE,LOOK,UP in "ce" frame by using model2world
1376     and doing extent scaling
1377 
1378 
1379 updateGaze
1380     eye,look -> gaze (world frame)
1381     eye2look,look2eye (eye frame)
1382 
1383 updateEyeSpace
1384     gaze,up,eye -> world2camera
1385 
1386 updateEyeBasis
1387     Transforms eye/camera basis vectors using *camera2world* matrix
1388     obtained from SGLM::updateEyeSpace into world frame, with
1389     scaling depending on Aspect, ZOOM and focal_basis to
1390     yield (u,v,w,e) basis vec3 that are used by CSGOptiX::prepareRenderParam
1391     to setup the raytrace render params.
1392 
1393 updateNearFar
1394     scales extent relative inputs TMIN(eg 0.1) and TMAX(eg 100) by extent to get
1395     world frame Near and Far distances ... HUH: not so simple as near far are divided
1396     by the nearfar_basis that defaults to gazelength but can be extent
1397 
1398     HMM: thats non-intuitive, could explain mis-behaviour
1399 
1400     [recently moved this from after initELU to before updateProjection
1401      as ELU+EyeSpace+EyeBasis belong together as do NearFar+Projection ]
1402 
1403 updateProjection
1404 
1405 updateComposite
1406     putting together the composite transforms that OpenGL uses
1407 
1408 
1409 **/
1410 
1411 
1412 inline void SGLM::update()
1413 {
1414     addlog("SGLM::update", "[");
1415 
1416     constrain();
1417 
1418     initModelMatrix();  //  fr.ce(center)->model2world translation
1419 
1420     initView();         // EYE,LOOK,UP -> view.EYE, view.LOOK, view.UP
1421     initELU();          //  view.EYE,view.LOOK,view.UP,model2world,extent->eye,look,up
1422 
1423     updateGaze();       //  eye,look,up->gaze,eye2look,look2eye
1424     updateEyeSpace();   //  gaze,up,eye->world2camera,camera2world
1425 
1426     updateNearFar();     // TMIN,TMAX-> near,far
1427     updateProjection();  // focal_basis,ZOOM,aspect,near,far -> projection
1428 
1429     updateComposite();   // projection,word2camera -> MVP
1430     updateEyeBasis();   //  IMV, camera2world,apect,ZOOM,focal_basis,...->u,v,w,e  [used for ray trace rendering]
1431 
1432     updateTitle();
1433 
1434     constrain();
1435     addlog("SGLM::update", "]");
1436 }
1437 
1438 /**
1439 SGLM::constrain
1440 ----------------
1441 
1442 UP is a direction vector, not a position, so non-zero UP.w would be a bug
1443 
1444 **/
1445 
1446 inline void SGLM::constrain() const
1447 {
1448     bool expect_UP_w = UP.w == 0.f ;
1449     if(!expect_UP_w) std::cerr
1450         << "SGLM::constrain"
1451         << " expect_UP_w " << ( expect_UP_w ? "YES" : "NO " )
1452         << " UP " << Present(UP)
1453         << descELU()
1454         << "\n"
1455         ;
1456 
1457     assert( expect_UP_w );
1458 }
1459 
1460 
1461 inline void SGLM::set_rtp_tangential(bool rtp_tangential_ )
1462 {
1463     rtp_tangential = rtp_tangential_ ;
1464     addlog("set_rtp_tangential", rtp_tangential );
1465 }
1466 
1467 inline void SGLM::set_extent_scale(bool extent_scale_ )
1468 {
1469     extent_scale = extent_scale_ ;
1470     addlog("set_extent_scale", extent_scale );
1471 }
1472 
1473 
1474 
1475 /**
1476 SGLM::initModelMatrix   (formerly updateModelMatrix)
1477 ------------------------------------------------------
1478 
1479 Because this depends on the input geometry ce it
1480 seems more appropriate to prefix with "init" rather than "update"
1481 Thats because changing CE is currently an initialization only thing.
1482 
1483 Called by SGLM::update.
1484 
1485 initModelMatrix_branch:1
1486     used when the sframe transforms are not identity,
1487     just take model2world and world2model from sframe m2w w2m
1488 
1489 initModelMatrix_branch:2
1490     used for rtp_tangential:true (not default)
1491     TODO: this calc now done in CSGTarget::getFrameComponents
1492     does it need to be here too ?
1493 
1494 initModelMatrix_branch:3
1495     form model2world and world2model matrices
1496     from fr.ce alone, ignoring the frame transforms
1497 
1498     For consistency with the transforms from sframe.h
1499     the escale is not included into model2world/world2model,
1500     that is done in SGLM::initELU.
1501 
1502     So currently initModelMatrix only handles translation from CE center,
1503     not extent scaling.
1504 
1505 **/
1506 
1507 inline void SGLM::initModelMatrix()
1508 {
1509     initModelMatrix_branch = 0 ;
1510 
1511     // NOTE THAT THESE SPECIAL CASES ARE THE ONLY NON-CE USES OF sframe.h
1512     // SUGGESTS REMOVE sframe.h from SGLM passing instead normally the ce
1513     // and the transforms in the special case where needed
1514 
1515     //bool m2w_not_identity = fr.m2w.is_identity(sframe::EPSILON) == false ;
1516     //bool w2m_not_identity = fr.w2m.is_identity(sframe::EPSILON) == false ;
1517 
1518     bool fr_has_transform = !fr.is_identity() ;
1519     if( fr_has_transform )
1520     {
1521         initModelMatrix_branch = 1 ;
1522         //model2world = glm::make_mat4x4<float>(fr.m2w.cdata());
1523         //world2model = glm::make_mat4x4<float>(fr.w2m.cdata());
1524 
1525         model2world = fr.m2w ;
1526         world2model = fr.w2m ;
1527     }
1528     else if( rtp_tangential )
1529     {
1530         initModelMatrix_branch = 2 ;
1531         SCenterExtentFrame<double> cef( fr.ce.x, fr.ce.y, fr.ce.z, fr.ce.w, rtp_tangential, extent_scale );
1532         model2world = cef.model2world ;
1533         world2model = cef.world2model ;
1534         // HMM: these matrix might have extent scaling already ?
1535     }
1536     else
1537     {
1538         initModelMatrix_branch = 3 ;
1539         glm::vec3 tr(fr.ce.x, fr.ce.y, fr.ce.z) ;
1540 
1541         float f = 1.f ; // get_escale_() ;
1542         assert( f > 0.f );
1543         glm::vec3 sc(f, f, f) ;
1544         glm::vec3 isc(1.f/f, 1.f/f, 1.f/f) ;
1545 
1546         addlog("initModelMatrix.3.fabricate", f );
1547 
1548         model2world = glm::scale(glm::translate(glm::mat4(1.0), tr), sc);
1549         world2model = glm::translate( glm::scale(glm::mat4(1.0), isc), -tr);
1550     }
1551     addlog("initModelMatrix", initModelMatrix_branch );
1552 }
1553 std::string SGLM::descModelMatrix() const
1554 {
1555     std::stringstream ss ;
1556     ss << "SGLM::descModelMatrix" << std::endl ;
1557     ss << " sglm.model2world \n" << Present( model2world ) << std::endl ;
1558     ss << " sglm.world2model \n" << Present( world2model ) << std::endl ;
1559     ss << " sglm.initModelMatrix_branch " << initModelMatrix_branch << std::endl ;
1560     ss << std::endl ;
1561     std::string s = ss.str();
1562     return s ;
1563 }
1564 
1565 float SGLM::get_escale_() const
1566 {
1567     float escale = 0.f ;
1568     switch(ESCALE)
1569     {
1570         case BAS_EXTENT: escale = extent() ; break ;
1571         case BAS_ASIS:   escale = 1.f      ; break ;
1572     }
1573     return escale ;
1574 }
1575 
1576 /**
1577 SGLM::get_escale
1578 ==================
1579 
1580 Returns escale matrix (which typically comes from extent fr.ce.w), eg with extent of 9.0::
1581 
1582     9.0   0.0    0.0    0.0
1583     0.0   9.0    0.0    0.0
1584     0.0   0.0    9.0    0.0
1585     0.0   0.0    0.0    1.0
1586 
1587 **/
1588 
1589 glm::mat4 SGLM::get_escale() const
1590 {
1591     float f = get_escale_();
1592     //std::cout << "SGLM::get_escale f " << f << "\n" ;
1593 
1594     glm::vec3 sc(f,f,f) ;
1595     glm::mat4 esc = glm::scale(glm::mat4(1.f), sc);
1596     return esc ;
1597 }
1598 
1599 /**
1600 SGLM::initView
1601 ----------------
1602 
1603 For standard non interpolated view animation mode the view is populated
1604 from the EYE, LOOK,UP statics that are populated at lib load time from envvars.
1605 
1606 **/
1607 
1608 
1609 void SGLM::initView()
1610 {
1611     view.EYE = EYE ;
1612     view.LOOK = LOOK ;
1613     view.UP = UP ;
1614 }
1615 
1616 
1617 
1618 /**
1619 SGLM::initELU
1620 -----------------
1621 
1622 Uses escale matrix (which typically comes from extent fr.ce.w), eg with extent of 9.0
1623 to convert the inputs (EYE, LOOK, UP) in units of extent, eg defaults::
1624 
1625     UP     (0,0,1,0)
1626     LOOK   (0,0,0,1)
1627     EYE    (-1,-1,0,1)
1628     "GAZE" (1,1,0,0)
1629 
1630 into world frame by applying the extent with the escale matrix
1631 to give vec3 : up,look,eye  eg::
1632 
1633     up     (0,0,9)
1634     look   (0,0,0)
1635     eye    (0,0,9)
1636 
1637 The advantage of using units of extent for the view inputs
1638 is that the view will then often provide something visible
1639 with geometry of any size.
1640 
1641 
1642 Q: Why not include extent scaling in the model2world matrix ?
1643 A: This is for consistency with sframe.h transforms which are used when
1644    non-identity transforms are provided with the frame.
1645 
1646 **/
1647 
1648 void SGLM::initELU()
1649 {
1650     glm::mat4 escale = get_escale();
1651 
1652     eye  = glm::vec3( model2world * escale * view.EYE ) ;
1653     look = glm::vec3( model2world * escale * view.LOOK ) ;
1654     up   = glm::vec3( model2world * escale * view.UP ) ;
1655 
1656 
1657 
1658     if(LEVEL > 0) std::cout
1659         << "[ SGLM::initELU\n"
1660         << descELU()
1661         << "] SGLM::initELU\n"
1662         ;
1663 }
1664 
1665 /**
1666 SGLM::updateGaze
1667 ------------------
1668 
1669 gaze
1670     vector from eye->look  look-eye::
1671 
1672 
1673                look
1674                 +
1675                /
1676               / / gaze
1677              /
1678             +
1679            eye
1680 
1681 eye2look
1682     transform that translates from eye to look
1683     (as in camera/eye frame this is along z-direction only)
1684 
1685 look2eye
1686     transform that translates from look to eye
1687     (as in camera/eye frame this is along z-direction only)
1688 
1689 
1690 * NB using gazelen invariance between world and eye frames
1691   (no scaling for those makes that valid)
1692 
1693 * HMM: is the sign convention here correct ? (OpenGL -Z is forward)
1694 
1695 
1696 **/
1697 
1698 void SGLM::updateGaze()
1699 {
1700     gaze = glm::vec3( look - eye ) ;
1701     avoidDegenerateBasisByChangingUp();
1702 
1703     float gazlen = getGazeLength();
1704     eye2look = glm::translate( glm::mat4(1.), glm::vec3(0,0,gazlen));
1705     look2eye = glm::translate( glm::mat4(1.), glm::vec3(0,0,-gazlen));
1706 }
1707 
1708 float SGLM::getGazeLength()  const { return glm::length(gaze) ; }   // must be after updateGaze
1709 float SGLM::getGazeCrossUp() const { return glm::length(glm::cross(glm::normalize(gaze), up))  ; }
1710 
1711 
1712 void SGLM::avoidDegenerateBasisByChangingUp()
1713 {
1714     float eps = 1e-4f ;
1715     float gcu = getGazeCrossUp() ;
1716     if(gcu > eps) return ;
1717 
1718     for(int i=0 ; i < 3 ; i++)
1719     {
1720         up = axes[i] ;
1721         gcu = getGazeCrossUp() ;
1722         if(gcu > eps)
1723         {
1724             std::cout
1725                 << "SGLM::avoidDegenerateBasisByChangingUp"
1726                 << " gaze " << glm::to_string(gaze)
1727                 << " up " << glm::to_string(up)
1728                 << " GazeCrossUp " << gcu
1729                 << " ( avoid this message by starting with more sensible EYE,LOOK,UP basis ) "
1730                 << std::endl
1731                 ;
1732             return ;
1733         }
1734     }
1735     assert( gcu > eps );
1736 }
1737 
1738 
1739 
1740 
1741 std::string SGLM::descELU() const
1742 {
1743     float escale_ = get_escale_();
1744     glm::mat4 escale = get_escale();
1745     std::stringstream ss ;
1746     ss << "[SGLM::descELU\n"
1747        << " [" << kLEVEL << "] " << LEVEL   << "\n"
1748        << " EYE  "  << Present( EYE )       << "\n"
1749        << " LOOK "  << Present( LOOK )      << "\n"
1750        << " UP   "  << Present( UP )        << "\n"
1751        << " GAZE "  << Present( LOOK-EYE )  << "\n"
1752        << "\n"
1753        << " escale_ " << Present( escale_ ) << "\n"
1754        << " escale\n" << Present( escale )  << "\n"
1755        << " model2world\n" << Present( model2world ) << "\n"
1756        << " (model2world * escale)\n"   << Present( model2world * escale ) << "\n"
1757        << "\n"
1758        << " EYE*escale  "  << Present( EYE*escale )  << "\n"
1759        << " LOOK*escale "  << Present( LOOK*escale ) << "\n"
1760        << " UP*escale   "  << Present( UP*escale )   << "\n"
1761        << " GAZE*escale "  << Present( (LOOK-EYE)*escale ) << "\n"
1762        << "\n"
1763        << " eye  = (model2world * escale * EYE  ) "  << Present( model2world * escale * EYE ) << "\n"
1764        << " look = (model2world * escale * LOOK ) "  << Present( model2world * escale * LOOK ) << "\n"
1765        << " up   = (model2world * escale * UP   ) "  << Present( model2world * escale * UP  ) << "\n"
1766        << " gaze                                  "  << Present( gaze ) << "\n"
1767        << "]SGLM::descELU\n"
1768        ;
1769     std::string str = ss.str();
1770     return str ;
1771 }
1772 
1773 
1774 
1775 
1776 /**
1777 SGLM::updateEyeSpace
1778 ---------------------
1779 
1780 NB "eye" and "camera" are used interchangeably, meaning the same thing
1781 
1782 Form world2camera camera2world from eye position and
1783 gaze and up directions in world frame together with
1784 OpenGL convention.
1785 
1786 Normalized eye space oriented via world frame gaze and up directions.
1787 
1788         +Y    -Z
1789      top_ax  forward_ax    (from normalized gaze vector)
1790          |  /
1791          | /
1792          |/
1793          +----- right_ax  (+X)
1794         .
1795        .
1796       .
1797     -forward_ax
1798     +Z
1799 
1800 
1801 world2camera
1802     transforms a world frame coordinate into a camera(aka eye) frame coordinate
1803     the transform is formed from first a translation to the origin
1804     of the "eye" world frame coordinate followed by a rotation following
1805     the OpenGL eye space convention : -Z is forward, +X to right, +Y up
1806 
1807 **/
1808 
1809 void SGLM::updateEyeSpace() // gaze,up,eye -> world2camera
1810 {
1811     forward_ax = glm::normalize(gaze);  // gaze is from eye->look "look - eye"
1812     right_ax   = glm::normalize(glm::cross(forward_ax,up));
1813     top_ax     = glm::normalize(glm::cross(right_ax,forward_ax));
1814 
1815     // OpenGL eye space convention : -Z is forward, +X to right, +Y up
1816     rot_ax[0] = glm::vec4( right_ax, 0.f );
1817     rot_ax[1] = glm::vec4( top_ax  , 0.f );
1818     rot_ax[2] = glm::vec4( -forward_ax, 0.f );
1819     rot_ax[3] = glm::vec4( 0.f, 0.f, 0.f, 1.f );
1820 
1821     glm::mat4 ti(glm::translate(glm::vec3(eye)));  // origin to eye
1822     glm::mat4 t(glm::translate(glm::vec3(-eye)));  // eye to origin
1823 
1824     world2camera = glm::transpose(rot_ax) * t  ;
1825     camera2world = ti * rot_ax ;
1826 }
1827 
1828 std::string SGLM::descEyeSpace() const
1829 {
1830     std::stringstream ss ;
1831     ss << "SGLM::descEyeSpace" << std::endl ;
1832     ss << std::setw(15) << "sglm.forward_ax" << Present(forward_ax) << std::endl ;
1833     ss << std::setw(15) << "sglm.right_ax"   << Present(right_ax) << std::endl ;
1834     ss << std::setw(15) << "sglm.top_ax"     << Present(top_ax) << std::endl ;
1835     ss << std::endl ;
1836 
1837     ss << " sglm.world2camera \n" << Present( world2camera ) << std::endl ;
1838     ss << " sglm.camera2world \n" << Present( camera2world ) << std::endl ;
1839     ss << std::endl ;
1840 
1841     std::string s = ss.str();
1842     return s ;
1843 }
1844 
1845 
1846 
1847 
1848 /**
1849 SGLM::updateEyeBasis
1850 ----------------------
1851 
1852 Transforms eye/camera basis vectors using *camera2world* matrix
1853 obtained from SGLM::updateEyeSpace into world frame, with
1854 scaling depending on Aspect, ZOOM and focal_basis to
1855 yield (u,v,w,e) basis vec3 that are used by CSGOptiX::prepareRenderParam
1856 to setup the raytrace render params.
1857 
1858 Note how are using inverted transforms for the ray tracing
1859 basis compared to the rasterization ones. Also no projection
1860 matrix for ray tracing as thats inherent in the technique.
1861 
1862 Getting this to feel the look rotation quaternion
1863 was done by changing from using the camera2world
1864 matrix to use the IVM InverseModelView
1865 thats calculated in updateComposite.  As a result
1866 the order of the update calculation was changed
1867 moving this to after updateComposite.
1868 
1869 ::
1870 
1871     Y:top
1872        |  .-Z:gaz
1873        | .
1874        |.
1875        +----- X:rht
1876       /
1877     +Z
1878 
1879 
1880 **/
1881 
1882 void SGLM::updateEyeBasis()
1883 {
1884     // eye basis vectors using OpenGL convention
1885     glm::vec4 rht( 1., 0., 0., 0.);  // +X
1886     glm::vec4 top( 0., 1., 0., 0.);  // +Y
1887     glm::vec4 gaz( 0., 0.,-1., 0.);  // -Z
1888 
1889     // eye position in eye frame
1890     glm::vec4 ori( 0., 0., 0., 1.);
1891 
1892     float aspect = Aspect() ;
1893     //float fsc = get_focal_basis() ;   // default is gazelength
1894     float fsc = get_transverse_scale() ;
1895 
1896     float fscz = fsc/ZOOM  ;          // increased ZOOM decreases field-of-view
1897     //float lsc = getGazeLength() ;
1898     float lsc = get_near_abs() ;
1899 
1900     u = glm::vec3( IMV * rht ) * fscz * aspect ;
1901     v = glm::vec3( IMV * top ) * fscz  ;
1902     w = glm::vec3( IMV * gaz ) * lsc ;
1903     e = glm::vec3( IMV * ori );
1904 
1905     wnorm = glm::normalize(w);
1906 
1907 }
1908 
1909 
1910 
1911 
1912 /**
1913 SGLM::updateNearFar
1914 --------------------------
1915 
1916 scales extent relative inputs TMIN(eg 0.1) and TMAX(eg 100) by extent to get
1917 world frame Near and Far distances
1918 
1919 HUH: but then tmi,tmx get divided by nearfar basis, which could be extent
1920 (default is gazelength). Thats confusing.
1921 
1922 As the basis needs gazelength, this must be done after updateELU
1923 but isnt there still a problem of basis consistency between tmin_abs and set_near_abs ?
1924 For example extent scaling vs gazelength scaling ?
1925 
1926 **/
1927 void SGLM::updateNearFar()
1928 {
1929     float tmi = tmin_abs() ;
1930     addlog("updateNearFar.tmi", tmi);
1931     set_near_abs(tmi) ;
1932 
1933     float tmx = tmax_abs() ;
1934     addlog("updateNearFar.tmx", tmx);
1935     set_far_abs(tmx) ;
1936 
1937 }
1938 std::string SGLM::descNearFar() const
1939 {
1940     std::stringstream ss ;
1941     ss << "SGLM::descNearFar" << std::endl ;
1942     std::string s = ss.str();
1943     return s ;
1944 }
1945 
1946 
1947 /**
1948 SGLM::updateTitle
1949 ------------------
1950 
1951 The *title* is set as the cxr_min.sh OpenGL window title by SGLFW::renderloop_tail
1952 
1953 **/
1954 
1955 
1956 
1957 void SGLM::updateTitle()
1958 {
1959     std::stringstream ss ;
1960     ss
1961        << fr.get_name()
1962        << " "
1963        << fr.desc_ce()
1964        << " sglm.e(c2w*ori) [" << Present(e) << "]"
1965        << " " << TITLE
1966        ;
1967     title = ss.str();
1968 }
1969 
1970 
1971 void SGLM::left_right_bottom_top_near_far(lrbtnf& p) const
1972 {
1973     //float fsc = get_focal_basis() ;
1974     float fsc = get_transverse_scale() ;
1975     float fscz = fsc/ZOOM  ;
1976     float aspect = Aspect();
1977 
1978     p.left   = -aspect*fscz ;
1979     p.right  =  aspect*fscz ;
1980     p.bottom = -fscz ;
1981     p.top    =  fscz ;
1982     p.near   =  get_near_abs() ;
1983     p.far    =  get_far_abs()  ;
1984 }
1985 
1986 
1987 /**
1988 SGLM::updateProjection
1989 -----------------------
1990 
1991 Suspect that for consistency of rasterized and ray traced
1992 renders this will need to match SGLM::updateEyeBasis better in
1993 the z-direction.
1994 
1995 glm::frustum
1996 
1997 
1998 **/
1999 
2000 void SGLM::updateProjection()
2001 {
2002     left_right_bottom_top_near_far(proj);
2003     assert( cam == CAM_PERSPECTIVE || cam == CAM_ORTHOGRAPHIC );
2004     switch(cam)
2005     {
2006        case CAM_PERSPECTIVE:  projection = glm::frustum( proj.left, proj.right, proj.bottom, proj.top, proj.near, proj.far ) ; break ;
2007        case CAM_ORTHOGRAPHIC: projection = glm::ortho(   proj.left, proj.right, proj.bottom, proj.top, proj.near, proj.far ) ; break ;
2008     }
2009 
2010     FillZProjection(zproj, projection);
2011 
2012 
2013 
2014     if(ssys::getenvbool(_updateProjection_DEBUG))
2015     {
2016         std::cout
2017             << _updateProjection_DEBUG
2018             << " zproj("
2019             << std::setw(10) << std::fixed << std::setprecision(4) << zproj.x << " "
2020             << std::setw(10) << std::fixed << std::setprecision(4) << zproj.y << " "
2021             << std::setw(10) << std::fixed << std::setprecision(4) << zproj.z << " "
2022             << std::setw(10) << std::fixed << std::setprecision(4) << zproj.w << " "
2023             << ")"
2024             << "\n"
2025             ;
2026 
2027         if(cam == CAM_PERSPECTIVE) std::cout
2028             << " proj.A_frustum "
2029             << std::setw(10) << std::fixed << std::setprecision(4) << proj.A_frustum()
2030             << " proj.B_frustum "
2031             << std::setw(10) << std::fixed << std::setprecision(4) << proj.B_frustum()
2032             << "\n"
2033             ;
2034 
2035         if(cam == CAM_ORTHOGRAPHIC) std::cout
2036             << " proj.A_ortho "
2037             << std::setw(10) << std::fixed << std::setprecision(4) << proj.A_ortho()
2038             << " proj.B_ortho "
2039             << std::setw(10) << std::fixed << std::setprecision(4) << proj.B_ortho()
2040             << "\n"
2041             ;
2042      }
2043 
2044 }
2045 
2046 /**
2047 SGLM::FillZProjection
2048 -----------------------
2049 
2050 After the ancient okc Camera::fillZProjection
2051 
2052 See ~/o/notes/issues/impl_composited_rendering_in_7plus_workflow.rst
2053 
2054 Opticks conventional matrix memory layout has
2055 translation in slots [12 13 14]
2056 
2057           0   1   2    3
2058           4   5   6    7
2059           8   9  10   11
2060         [12  13  14]  15
2061 
2062 glm::mat4 element addressing is (row, col) (see stra_test::Elements)
2063 so this is grabbing the third column.
2064 
2065           0  1 | 2|  3
2066           4  5 | 6|  7
2067           8  9 |10| 11
2068          12 13 |14| 15
2069 
2070 Looking at glm/ext/matrix_clip_space.inl::
2071 
2072     159     template<typename T>
2073     160     GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumRH_NO(T left, T right, T bottom, T top, T nearVal, T farVal)
2074     161     {
2075     162         mat<4, 4, T, defaultp> Result(0);
2076     163         Result[0][0] = (static_cast<T>(2) * nearVal) / (right - left);
2077     164         Result[1][1] = (static_cast<T>(2) * nearVal) / (top - bottom);
2078     165         Result[2][0] = (right + left) / (right - left);
2079     166         Result[2][1] = (top + bottom) / (top - bottom);
2080     167         Result[2][2] = - (farVal + nearVal) / (farVal - nearVal);
2081     168         Result[2][3] = static_cast<T>(-1);
2082     169         Result[3][2] = - (static_cast<T>(2) * farVal * nearVal) / (farVal - nearVal);
2083     170         return Result;
2084     171     }
2085 
2086 Copying from above frustumRH_NO and expressing in glm::mat4 memory element order
2087 as standard for Opticks (NB transposed representation is more commonly shown)::
2088 
2089 
2090    |     2n/(r-l)          0       {    0          }   0   |
2091    |                               {               }       |
2092    |         0        2n/(t-b)     {    0          }   0   |
2093    |                               {               }       |
2094    |     (r+l)/(r-l)  (t+b)/(t-b)  { -(f+n)/(f-n)  }  -1   |
2095    |                               {               }       |
2096    |         0            0        { -2.*f*n/(f-n) }   0   |
2097 
2098 
2099 For perspective projection this grabs::
2100 
2101    { 0 ,   0,  -(f+n)/(f-n),  -2.*f*n/(f-n) }
2102 
2103 
2104 Looking at glm/ext/matrix_clip_space.inl::
2105 
2106      54     template<typename T>
2107      55     GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoRH_NO(T left, T right, T bottom, T top, T zNear, T zFar)
2108      56     {
2109      57         mat<4, 4, T, defaultp> Result(1);
2110      58         Result[0][0] = static_cast<T>(2) / (right - left);
2111      59         Result[1][1] = static_cast<T>(2) / (top - bottom);
2112      60         Result[2][2] = - static_cast<T>(2) / (zFar - zNear);
2113      61         Result[3][0] = - (right + left) / (right - left);
2114      62         Result[3][1] = - (top + bottom) / (top - bottom);
2115      63         Result[3][2] = - (zFar + zNear) / (zFar - zNear);
2116      64         return Result;
2117      65     }
2118 
2119 Copying from above orthoRH_NO and expressing in glm::mat4 memory element order
2120 as standard for Opticks (NB transposed representation is more commonly shown)::
2121 
2122 
2123     |   2/(r-l)        0              0             0   |
2124     |     0           2/(t-b)         0             0   |
2125     |     0            0            -2/(f-n)        0   |
2126     |  -(r+l)/(r-l)   -(t+b)/(t-b)  -(f+n)/(f-n)    1   |
2127 
2128 
2129 For ortho this grabs::
2130 
2131     { 0,   0,   -2/(f-n),   -(f+n)/(f-n)  }
2132 
2133 
2134 **/
2135 
2136 
2137 void SGLM::FillZProjection(glm::vec4& _ZProj, const glm::mat4& _Proj) // static
2138 {
2139     _ZProj.x = _Proj[0][2] ;
2140     _ZProj.y = _Proj[1][2] ;
2141     _ZProj.z = _Proj[2][2] ;
2142     _ZProj.w = _Proj[3][2] ;
2143 }
2144 
2145 /**
2146 SGLM::FillAltProjection
2147 ----------------------
2148 
2149 For perspective projection this grabs::
2150 
2151    {   (r+l)/(r-l) , (t+b)/(t-b) , -(f+n)/(f-n) ,  -1 }
2152 
2153 
2154 **/
2155 
2156 void SGLM::FillAltProjection(glm::vec4& _AProj, const glm::mat4& _Proj) // static
2157 {
2158     _AProj.x = _Proj[2][0] ;
2159     _AProj.y = _Proj[2][1] ;
2160     _AProj.z = _Proj[2][2] ;
2161     _AProj.w = _Proj[2][3] ;
2162 }
2163 
2164 
2165 inline float lrbtnf::A_frustum() const { return -(far+near)/(far-near) ; }
2166 inline float lrbtnf::B_frustum() const { return -2.f*far*near/(far-near) ; }
2167 inline float lrbtnf::A_ortho() const   { return -2.f/(far-near) ; }
2168 inline float lrbtnf::B_ortho() const   { return -(far+near)/(far-near) ; }
2169 
2170 inline std::string lrbtnf::desc() const
2171 {
2172     std::stringstream ss ;
2173     ss << "lrbtnf::desc (inputs to glm::frustum OR glm::ortho )"
2174        << " l " << std::setw(7) << std::fixed << std::setprecision(3) << left
2175        << " r " << std::setw(7) << std::fixed << std::setprecision(3) << right
2176        << " b " << std::setw(7) << std::fixed << std::setprecision(3) << bottom
2177        << " t " << std::setw(7) << std::fixed << std::setprecision(3) << top
2178        << " n " << std::setw(7) << std::fixed << std::setprecision(3) << near
2179        << " f " << std::setw(7) << std::fixed << std::setprecision(3) << far
2180        << " A_frustum:-(f+n)(f-n) " << std::setw(7) << std::fixed << std::setprecision(3) << A_frustum()
2181        << " B_frustum:-2fn/(f-n)  " << std::setw(7) << std::fixed << std::setprecision(3) << B_frustum()
2182        << " A_ortho:-2/(f-n)      " << std::setw(7) << std::fixed << std::setprecision(3) << A_ortho()
2183        << " B_ortho:-(f+n)/(f-n)  " << std::setw(7) << std::fixed << std::setprecision(3) << B_ortho()
2184        << "\n"
2185        ;
2186     std::string str = ss.str();
2187     return str ;
2188 }
2189 
2190 /**
2191 SGLM::zdepth_pos
2192 -------------------
2193 
2194 http://www.songho.ca/opengl/gl_projectionmatrix.html
2195 
2196 **/
2197 
2198 
2199 float SGLM::zdepth_pos( const glm::tvec4<float>& p_eye ) const
2200 {
2201     glm::tvec4<float> c = projection * p_eye ;
2202     float zd = c.z/c.w ;
2203     return zd ;
2204 }
2205 
2206 /**
2207 SGLM::zdepth0
2208 --------------
2209 
2210 
2211 **/
2212 
2213 
2214 float SGLM::zdepth0( const float& z_eye ) const
2215 {
2216     const float& ze = z_eye ;
2217     float A = zproj_A() ;
2218     float B = zproj_B() ;
2219     float zd(0.f) ;
2220 
2221     assert( cam == CAM_PERSPECTIVE || cam == CAM_ORTHOGRAPHIC );
2222     switch(cam)
2223     {
2224         case CAM_PERSPECTIVE:  zd = -(A + B/ze) ; break ;    //zd = (A*ze + B)/(-ze) ;
2225         case CAM_ORTHOGRAPHIC: zd = A*ze + B    ; break ;
2226     }
2227     return zd ;
2228 }
2229 
2230 /**
2231 SGLM::zdepth1
2232 --------------
2233 
2234 Compare with CSGOptiX7.cu:render::
2235 
2236     235     float eye_z = -prd->distance()*dot(params.WNORM, direction) ;
2237     236     const float& A = params.ZPROJ.z ;
2238     237     const float& B = params.ZPROJ.w ;
2239     238     float zdepth = cameratype == 0u ? -(A + B/eye_z) : A*eye_z + B  ;  // cf SGLM::zdepth1
2240 
2241 **/
2242 float SGLM::zdepth1( const float& z_eye ) const
2243 {
2244     const float& ze = z_eye ;
2245     const float& A = zproj.z ;
2246     const float& B = zproj.w ;
2247     assert( cam == CAM_PERSPECTIVE || cam == CAM_ORTHOGRAPHIC );
2248     return cam == CAM_PERSPECTIVE ? -(A + B/ze) : A*ze + B  ;
2249 }
2250 
2251 float SGLM::zproj_A() const
2252 {
2253     assert( cam == CAM_PERSPECTIVE || cam == CAM_ORTHOGRAPHIC );
2254     float A(0.f);
2255     switch(cam)
2256     {
2257         case CAM_PERSPECTIVE:  A = proj.A_frustum() ; break ;
2258         case CAM_ORTHOGRAPHIC: A = proj.A_ortho()   ; break ;
2259     }
2260     return A ;
2261 }
2262 
2263 float SGLM::zproj_B() const
2264 {
2265     assert( cam == CAM_PERSPECTIVE || cam == CAM_ORTHOGRAPHIC );
2266     float B(0.f);
2267     switch(cam)
2268     {
2269         case CAM_PERSPECTIVE:  B = proj.B_frustum() ; break ;
2270         case CAM_ORTHOGRAPHIC: B = proj.B_ortho()   ; break ;
2271     }
2272     return B ;
2273 }
2274 
2275 
2276 
2277 
2278 
2279 
2280 
2281 float SGLM::get_transverse_scale() const
2282 {
2283     assert( cam == CAM_PERSPECTIVE || cam == CAM_ORTHOGRAPHIC );
2284     //return cam == CAM_ORTHOGRAPHIC ? orthographic_scale : get_near_abs() ;
2285     return get_near_abs() ;
2286 }
2287 
2288 
2289 
2290 void SGLM::increment_spin()
2291 {
2292     if(toggle.spin.value == 0 ) return ;
2293 
2294     float spin_speed = spin_degrees_per_frame*float(toggle.spin.value) ;
2295 
2296     glm::quat step_spin = glm::angleAxis(glm::radians(spin_speed), q_spin_axis );
2297 
2298     q_spin = step_spin * q_spin ;      // Global spin
2299     //q_spin = q_spin * step_spin ;      // Local spin (relative to current view)
2300 }
2301 
2302 
2303 /**
2304 SGLM::updateComposite
2305 ----------------------
2306 
2307 Putting together the composite transforms that OpenGL needs
2308 
2309 * contrast with old Opticks ~/o/optickscore/Composition.cc Composition::update
2310 
2311 * note the conjugte of a quaternion rotation represents the inverse rotation
2312 
2313 **/
2314 
2315 void SGLM::updateComposite()
2316 {
2317     //std::cout << "SGLM::updateComposite" << std::endl ;
2318 
2319     glm::mat4 _worldspin = glm::mat4_cast(q_spin);
2320     glm::mat4 _eyeshift = glm::translate(glm::mat4(1.0), eyeshift ) ;    // eyeshift starts (0,0,0) changed by WASDQE keys
2321     glm::mat4 _lookrot = glm::mat4_cast(q_lookrot) ;
2322     glm::mat4 _eyerot = glm::mat4_cast(q_eyerot) ;
2323 
2324     glm::mat4 _iworldspin = glm::mat4_cast(glm::conjugate(q_spin));
2325     glm::mat4 _ilookrot   = glm::mat4_cast(glm::conjugate(q_lookrot) ) ;
2326     glm::mat4 _ieyerot    = glm::mat4_cast(glm::conjugate(q_eyerot )) ;
2327     glm::mat4 _ieyeshift  = glm::translate(glm::mat4(1.0), -eyeshift ) ;
2328 
2329     MV = _eyeshift * _eyerot * look2eye * _lookrot * eye2look * world2camera * _worldspin ;  // just world2camera before shifts, rotations
2330 
2331     IMV = _iworldspin * camera2world * look2eye * _ilookrot * eye2look * _ieyerot  * _ieyeshift  ;
2332     //IMV = glm::inverse( MV );
2333 
2334     MVP = projection * MV ;    // MVP aka world2clip (needed by OpenGL shader pipeline)
2335 }
2336 
2337 
2338 
2339 
2340 
2341 std::string SGLM::descEyeBasis() const
2342 {
2343     std::stringstream ss ;
2344     ss << "SGLM::descEyeBasis : camera frame basis vectors transformed into world and focal plane scaled " << std::endl ;
2345 
2346     int wid = 25 ;
2347     float aspect = Aspect() ;
2348     float fsc = get_focal_basis() ;
2349     float fscz = fsc/ZOOM ;
2350     float gazlen = getGazeLength() ;
2351 
2352     ss << std::setw(wid) << "aspect" << Present(aspect) << std::endl ;
2353     ss << std::setw(wid) << "near " << Present(near) << std::endl ;
2354     ss << std::setw(wid) << "ZOOM " << Present(ZOOM) << std::endl ;
2355     ss << std::setw(wid) << "get_focal_basis"      << Present(fsc) << std::endl ;
2356     ss << std::setw(wid) << "get_focal_basis/ZOOM" << Present(fscz) << std::endl ;
2357     ss << std::setw(wid) << "getGazeLength " << Present(gazlen) << std::endl ;
2358 
2359     ss << std::setw(wid) << "sglm.e " << Present(e) << " glm::vec3( camera2world * ori ) " << std::endl ;
2360     ss << std::setw(wid) << "sglm.u " << Present(u) << " glm::vec3( camera2world * rht ) * fsc * aspect " << std::endl ;
2361     ss << std::setw(wid) << "sglm.v " << Present(v) << " glm::vec3( camera2world * top ) * fsc  " << std::endl ;
2362     ss << std::setw(wid) << "sglm.w " << Present(w) << " glm::vec3( camera2world * gaz ) * gazlen  " << std::endl ;
2363     std::string s = ss.str();
2364     return s ;
2365 }
2366 
2367 std::string SGLM::DescEyeBasis( const glm::vec3& E, const glm::vec3& U, const glm::vec3& V, const glm::vec3& W )
2368 {
2369     int wid = 15 ;
2370     std::stringstream ss ;
2371     ss << "SGLM::DescEyeBasis E,U,V,W " << std::endl ;
2372     ss << std::setw(wid) << "E " << Present(E) << std::endl ;
2373     ss << std::setw(wid) << "U " << Present(U) << std::endl ;
2374     ss << std::setw(wid) << "V " << Present(V) << std::endl ;
2375     ss << std::setw(wid) << "W " << Present(W) << std::endl ;
2376     std::string s = ss.str();
2377     return s ;
2378 }
2379 
2380 
2381 
2382 
2383 
2384 
2385 
2386 
2387 
2388 
2389 void SGLM::set_nearfar_mode(const char* mode){    addlog("set_nearfar_mode",  mode) ; nearfar = SBAS::Type(mode) ; }
2390 void SGLM::set_focal_mode(  const char* mode){    addlog("set_focal_mode",    mode) ; focal   = SBAS::Type(mode) ; }
2391 
2392 const char* SGLM::get_nearfar_mode() const { return SBAS::Name(nearfar) ; }
2393 const char* SGLM::get_focal_mode() const {   return SBAS::Name(focal) ; }
2394 
2395 void SGLM::set_nearfar_manual(float nearfar_manual_ ){ addlog("set_nearfar_manual", nearfar_manual_ ) ; nearfar_manual = nearfar_manual_  ; }
2396 void SGLM::set_focal_manual(float focal_manual_ ){     addlog("set_focal_manual", focal_manual_ )     ; focal_manual = focal_manual_  ; }
2397 
2398 /**
2399 SGLM::get_focal_basis
2400 ----------------------
2401 
2402 BAS_NEARABS problematic as
2403 
2404 **/
2405 
2406 float SGLM::get_focal_basis() const
2407 {
2408     float basis = 0.f ;
2409     switch(focal)
2410     {
2411         case BAS_MANUAL:     basis = focal_manual        ; break ;
2412         case BAS_EXTENT:     basis = fr.ce.w             ; break ;
2413         case BAS_GAZELENGTH: basis = getGazeLength()     ; break ;  // only available after updateELU
2414         case BAS_NEARABS:    basis = get_near_abs()      ; break ;
2415     }
2416     return basis ;
2417 }
2418 
2419 
2420 
2421 void SGLM::set_near( float near_ ){ near = near_ ; addlog("set_near", near); }
2422 void SGLM::set_far(  float far_ ){  far = far_   ; addlog("set_far", far);   }
2423 float SGLM::get_near() const  { return near ; }
2424 float SGLM::get_far()  const  { return far  ; }
2425 
2426 /**
2427 SGLM::get_nearfar_basis
2428 -------------------------
2429 
2430 Default is gazelength
2431 
2432 
2433 
2434 **/
2435 
2436 float SGLM::get_nearfar_basis() const
2437 {
2438     float basis = 0.f ;
2439     switch(nearfar)
2440     {
2441         case BAS_MANUAL:     basis = nearfar_manual      ; break ;
2442         case BAS_EXTENT:     basis = extent()            ; break ;  // only after set_frame
2443         case BAS_GAZELENGTH: basis = getGazeLength()     ; break ;  // only available after updateELU (default)
2444         case BAS_NEARABS:    assert(0)                   ; break ;  // this mode only valud for get_focal_basis (as near far in units of this)
2445     }
2446     return basis ;
2447 }
2448 
2449 
2450 // CAUTION: depends on get_nearfar_basis
2451 void SGLM::set_near_abs( float near_abs_ )
2452 {
2453     float nfb = get_nearfar_basis() ;
2454     float nab = near_abs_/nfb ;
2455     addlog("set_near_abs.arg", near_abs_) ;
2456     addlog("set_near_abs.nfb", nfb );
2457     addlog("set_near_abs.nab", nab );
2458     set_near( nab ) ;
2459 }
2460 void SGLM::set_far_abs(  float far_abs_ )
2461 {
2462     float nfb = get_nearfar_basis() ;
2463     float fab = far_abs_/nfb ;
2464     addlog("set_far_abs.arg", far_abs_)   ;
2465     set_far( fab ) ;
2466 }
2467 
2468 /**
2469 SGLM::get_near_abs
2470 --------------------
2471 
2472 Used from CSGOptiX::prepareRenderParam for tmin
2473 
2474 OptixLaunch with negative tmin throws exception  OPTIX_EXCEPTION_CODE_INVALID_RAY
2475 
2476 **/
2477 float SGLM::get_near_abs() const { return std::max(0.f, near*get_nearfar_basis()) ; }
2478 
2479 /**
2480 SGLM::get_far_abs
2481 --------------------
2482 
2483 Used from CSGOptiX::prepareRenderParam for tmax
2484 
2485 **/
2486 float SGLM::get_far_abs() const { return   far*get_nearfar_basis() ; }
2487 
2488 
2489 std::string SGLM::descFrame() const
2490 {
2491     return fr.desc();
2492 }
2493 
2494 std::string SGLM::descBasis() const
2495 {
2496     int wid = 25 ;
2497     std::stringstream ss ;
2498     ss << "SGLM::descBasis" << std::endl ;
2499     ss << std::setw(wid) << " sglm.get_nearfar_mode " << get_nearfar_mode()  << std::endl ;
2500     ss << std::setw(wid) << " sglm.nearfar_manual "   << Present( nearfar_manual ) << std::endl ;
2501     ss << std::setw(wid) << " sglm.fr.ce.w  "     << Present( fr.ce.w )  << std::endl ;
2502     ss << std::setw(wid) << " sglm.getGazeLength  " << Present( getGazeLength() ) << std::endl ;
2503     ss << std::setw(wid) << " sglm.get_nearfar_basis " << Present( get_nearfar_basis() ) << std::endl ;
2504     ss << std::endl ;
2505     ss << std::setw(wid) << " sglm.near  "     << Present( near )  << " (units of nearfar basis) " << std::endl ;
2506     ss << std::setw(wid) << " sglm.far   "     << Present( far )   << " (units of nearfar basis) " << std::endl ;
2507     ss << std::setw(wid) << " sglm.get_near_abs  " << Present( get_near_abs() ) << " near*get_nearfar_basis() " << std::endl ;
2508     ss << std::setw(wid) << " sglm.get_far_abs  " << Present( get_far_abs() )   << " far*get_nearfar_basis() " << std::endl ;
2509     ss << std::endl ;
2510     ss << std::setw(wid) << " sglm.get_focal_mode " << get_focal_mode()  << std::endl ;
2511     ss << std::setw(wid) << " sglm.get_focal_basis " << Present( get_focal_basis() ) << std::endl ;
2512     ss << std::endl ;
2513     std::string s = ss.str();
2514     return s ;
2515 }
2516 
2517 std::string SGLM::descProj() const
2518 {
2519     int wid = 25 ;
2520     std::stringstream ss ;
2521     ss << "SGLM::descProj" << std::endl ;
2522     ss << std::setw(wid) << " (lrbtnf)proj.desc " << proj.desc() << "\n" ;
2523     std::string str = ss.str();
2524     return str ;
2525 }
2526 
2527 std::string SGLM::descProjection() const
2528 {
2529     float fsc = get_focal_basis() ;
2530     float fscz = fsc/ZOOM  ;
2531     float aspect = Aspect();
2532     float left   = -aspect*fscz ;
2533     float right  =  aspect*fscz ;
2534     float bottom = -fscz ;
2535     float top    =  fscz ;
2536     float near_abs   = get_near_abs() ;
2537     float far_abs    = get_far_abs()  ;
2538 
2539     int wid = 25 ;
2540     std::stringstream ss ;
2541     ss << "SGLM::descProjection" << std::endl ;
2542     ss << std::setw(wid) << "Aspect" << Present(aspect) << std::endl ;
2543     ss << std::setw(wid) << "get_focal_basis" << Present(fsc) << std::endl ;
2544     ss << std::setw(wid) << "get_focal_basis/ZOOM" << Present(fscz) << std::endl ;
2545     ss << std::setw(wid) << "ZOOM" << Present(ZOOM) << std::endl ;
2546     ss << std::setw(wid) << "left"   << Present(left) << std::endl ;
2547     ss << std::setw(wid) << "right"  << Present(right) << std::endl ;
2548     ss << std::setw(wid) << "top"    << Present(top) << std::endl ;
2549     ss << std::setw(wid) << "bottom" << Present(bottom) << std::endl ;
2550     ss << std::setw(wid) << "get_near_abs" << Present(near_abs) << std::endl ;
2551     ss << std::setw(wid) << "get_far_abs" << Present(far_abs) << std::endl ;
2552 
2553     ss << std::setw(wid) << "near" << Present(near) << std::endl ;
2554     ss << std::setw(wid) << "far"  << Present(far) << std::endl ;
2555     ss << std::setw(wid) << "sglm.projection\n" << Present(projection) << std::endl ;
2556     ss << descProj() << std::endl ;
2557 
2558     std::string str = ss.str();
2559     return str ;
2560 }
2561 
2562 
2563 std::string SGLM::descComposite() const
2564 {
2565     int wid = 25 ;
2566     std::stringstream ss ;
2567     ss << "SGLM::descComposite" << std::endl ;
2568     ss << std::setw(wid) << "sglm.MVP\n" << Present(MVP) << std::endl ;
2569     std::string str = ss.str();
2570     return str ;
2571 }
2572 
2573 
2574 
2575 
2576 
2577 
2578 template<typename T>
2579 void SGLM::ce_corners_world( std::vector<glm::tvec4<T>>& v_world ) const
2580 {
2581     std::vector<glm::tvec4<T>> corners ;
2582     SCE::Corners<T>( corners, fr.ce );
2583     assert(corners.size() == 8 );
2584 
2585     for(int i=0 ; i < 8 ; i++ )
2586     {
2587         const glm::tvec4<T>& corner = corners[i];
2588         v_world.push_back(corner);
2589     }
2590 }
2591 
2592 template<typename T>
2593 void SGLM::ce_midface_world( std::vector<glm::tvec4<T>>& v_world ) const
2594 {
2595     std::vector<glm::tvec4<T>> midface ;
2596     SCE::Midface( midface, fr.ce );
2597     assert(midface.size() == 6+1 );
2598 
2599     for(int i=0 ; i < 6+1 ; i++ )
2600     {
2601         v_world.push_back(midface[i]);
2602     }
2603 }
2604 
2605 
2606 
2607 template<typename T>
2608 void SGLM::Apply_XF( std::vector<glm::tvec4<float>>& v_out, const std::vector<glm::tvec4<T>>& v_in, const glm::tmat4x4<float>& XF, bool flip )  // static
2609 {
2610     int num = v_in.size();
2611     for(int i=0 ; i < num ; i++ )
2612     {
2613         const glm::tvec4<float> in = v_in[i] ;  // not by ref, to allow changing type
2614         glm::tvec4<float> ou = flip ? XF*in : in*XF ;
2615         v_out.push_back(ou);
2616     }
2617 }
2618 
2619 
2620 template<typename T>
2621 void SGLM::apply_MV( std::vector<glm::tvec4<float>>& v_eye, const std::vector<glm::tvec4<T>>& v_world, bool flip ) const
2622 {
2623     Apply_XF(v_eye, v_world, MV, flip);
2624 }
2625 template<typename T>
2626 void SGLM::apply_MVP( std::vector<glm::tvec4<float>>& v_clip, const std::vector<glm::tvec4<T>>& v_world, bool flip ) const
2627 {
2628     Apply_XF(v_clip, v_world, MVP, flip);
2629 }
2630 template<typename T>
2631 void SGLM::apply_P( std::vector<glm::tvec4<float>>& v_clip, const std::vector<glm::tvec4<T>>& v_eye, bool flip ) const
2632 {
2633     const glm::tmat4x4<float>& P = projection ;
2634     Apply_XF(v_clip, v_eye, P, flip);
2635 }
2636 
2637 
2638 std::string SGLM::desc_MVP() const
2639 {
2640     std::stringstream ss ;
2641     ss << "SGLM::desc_MVP" << std::endl ;
2642     ss << " MVP " << std::endl  << Present(MVP) << std::endl ;
2643     ss << " MVP_ptr " << std::endl  << Present<float>(MVP_ptr,16) << std::endl ;
2644     std::string str = ss.str();
2645     return str ;
2646 }
2647 
2648 /**
2649 SGLM::desc_MVP_ce_corners
2650 ---------------------------------
2651 **/
2652 
2653 std::string SGLM::desc_MVP_ce_corners() const
2654 {
2655     static const int NUM = 8 ;
2656 
2657     std::vector<glm::tvec4<double>> v_world ;
2658     ce_corners_world<double>(v_world);
2659     assert( v_world.size() == NUM );
2660 
2661     std::vector<glm::tvec4<float>> v_clip ;
2662     bool flip = true ;
2663     apply_MVP( v_clip, v_world, flip );
2664     assert( v_clip.size() == NUM );
2665 
2666     std::stringstream ss ;
2667     ss << "SGLM::desc_MVP_ce_corners (clipped in {})" << std::endl ;
2668     for(int i=0 ; i < NUM ; i++ )
2669     {
2670         const glm::tvec4<double>& _world = v_world[i] ;
2671         const glm::tvec4<float>& _clip = v_clip[i] ;
2672         glm::vec4 _ndc(_clip.x/_clip.w, _clip.y/_clip.w, _clip.z/_clip.w, 1.f );
2673         // normalized device coordinates : from division by clip.w
2674 
2675         bool clipped = IsClipped(_ndc) ;
2676         char bef = clipped ? '{' : ' ' ;
2677         char aft = clipped ? '}' : ' ' ;
2678 
2679         ss
2680             << "[" << i << "]"
2681             << " world " << stra<double>::Desc(_world)
2682             << " clip  " << stra<float>::Desc(_clip)
2683             << " ndc " << bef << stra<float>::Desc(_ndc) << aft
2684             << std::endl
2685             ;
2686     }
2687     std::string str = ss.str();
2688     return str ;
2689 }
2690 
2691 /**
2692 SGLM::desc_MV_P_MVP_ce_corners
2693 --------------------------------
2694 
2695 Used as testing ground for the zdepth calc, see:
2696 
2697 * notes/issues/impl_composited_rendering_in_7plus_workflow.rst
2698 * http://www.songho.ca/opengl/gl_projectionmatrix.html
2699 
2700 **/
2701 
2702 std::string SGLM::desc_MV_P_MVP_ce_corners() const
2703 {
2704     std::vector<glm::tvec4<float>> v_world ;
2705     std::vector<glm::tvec4<float>> v_eye ;
2706     std::vector<glm::tvec4<float>> v_clip_0 ;
2707     std::vector<glm::tvec4<float>> v_clip_1 ;
2708 
2709     static const int NUM = 8 ;
2710     ce_corners_world(v_world);
2711     assert( v_world.size() == NUM );
2712 
2713     bool flip = true ;
2714     apply_MV(   v_eye   ,  v_world, flip );
2715     apply_P(    v_clip_0,  v_eye  , flip );
2716     apply_MVP(  v_clip_1,  v_world, flip );
2717 
2718     assert( v_eye.size() == NUM );
2719     assert( v_clip_0.size() == NUM );
2720     assert( v_clip_1.size() == NUM );
2721 
2722     int wid = 25 ;
2723 
2724     std::stringstream ss ;
2725     ss << "SGLM::desc_MV_P_MVP_ce_corners" << std::endl ;
2726 
2727     const glm::tvec3<float>& ray_origin = eye ;
2728     ss << std::setw(wid) << " ray_origin    " << ' '   << stra<float>::Desc(ray_origin) << ' ' << "\n" ;
2729     ss << std::setw(wid) << " forward_ax    " << ' '   << stra<float>::Desc(forward_ax) << ' ' << "\n" ;
2730     ss << std::setw(wid) << " wnorm(front)  " << ' '   << stra<float>::Desc(wnorm)      << ' ' << "\n" ;
2731 
2732     for(int i=0 ; i < NUM ; i++ )
2733     {
2734         const glm::tvec4<float>& _world  = v_world[i] ;
2735         glm::tvec3<float> world(_world);
2736 
2737         const glm::tvec4<float>& _eye    = v_eye[i] ;
2738         const glm::tvec4<float>& _clip_0 = v_clip_0[i] ;
2739         const glm::tvec4<float>& _clip_1 = v_clip_1[i] ;
2740 
2741         // imagine ray tracing intersects at each of the corners
2742         glm::tvec3<float> ray_direction = glm::normalize( world - ray_origin  );
2743         float distance = glm::length( world - ray_origin );
2744         float ray_eye_z = -distance*glm::dot(forward_ax, ray_direction) ;
2745 
2746         float zd_p = zdepth_pos(_eye) ;
2747         float zd_0 = zdepth0(ray_eye_z) ;
2748         float zd_1 = zdepth1(ray_eye_z) ;
2749 
2750         glm::vec4 _ndc_0(_clip_0.x/_clip_0.w, _clip_0.y/_clip_0.w, _clip_0.z/_clip_0.w, 1.f );
2751         glm::vec4 _ndc_1(_clip_1.x/_clip_1.w, _clip_1.y/_clip_1.w, _clip_1.z/_clip_1.w, 1.f );
2752         // normalized device coordinates : from division by clip_0.w
2753 
2754         bool clipped_0 = IsClipped(_ndc_0) ;
2755         char bef_0 = clipped_0 ? '{' : ' ' ;
2756         char aft_0 = clipped_0 ? '}' : ' ' ;
2757 
2758         bool clipped_1 = IsClipped(_ndc_1) ;
2759         char bef_1 = clipped_1 ? '{' : ' ' ;
2760         char aft_1 = clipped_1 ? '}' : ' ' ;
2761 
2762         ss
2763             << "[" << i << "]\n"
2764             << std::setw(wid) << " ray_origin    " << ' '   << stra<float>::Desc(ray_origin) << ' ' << "\n"
2765             << std::setw(wid) << " _world        " << ' '   << stra<float>::Desc(_world)    << ' ' << "\n"
2766             << std::setw(wid) << " ray_direction " << ' '   << stra<float>::Desc(ray_direction) << ' ' << "\n"
2767             << std::setw(wid) << " _eye          " << ' '   << stra<float>::Desc(_eye)      << ' ' << "\n"
2768             << std::setw(wid) << " _clip_0       " << ' '   << stra<float>::Desc(_clip_0)   << ' ' << "\n"
2769             << std::setw(wid) << " _clip_1       " << ' '   << stra<float>::Desc(_clip_1)   << ' ' << "\n"
2770             << std::setw(wid) << " _ndc_0        " << bef_0 << stra<float>::Desc(_ndc_0)    << aft_0 << "\n"
2771             << std::setw(wid) << " _ndc_1        " << bef_1 << stra<float>::Desc(_ndc_1)    << aft_1 << "\n"
2772             << std::setw(wid) << " zd_p          " << ' '   << stra<float>::Desc(zd_p)      << ' ' << "\n"
2773             << std::setw(wid) << " zd_0          " << ' '   << stra<float>::Desc(zd_0)      << ' ' << "\n"
2774             << std::setw(wid) << " zd_1          " << ' '   << stra<float>::Desc(zd_1)      << ' ' << "\n"
2775             << std::setw(wid) << " ray_eye_z     " << ' '   << stra<float>::Desc(ray_eye_z) << ' ' << "\n"
2776             << std::endl
2777             ;
2778     }
2779     std::string str = ss.str();
2780     return str ;
2781 }
2782 
2783 
2784 std::string SGLM::desc_MVP_ce_midface() const
2785 {
2786     static const int NUM = 6+1 ;
2787 
2788     std::vector<glm::tvec4<double>> v_world ;
2789     ce_midface_world(v_world);
2790     assert( v_world.size() == NUM );
2791 
2792     std::vector<glm::tvec4<float>> v_clip ;
2793     bool flip = true ;
2794     apply_MVP( v_clip, v_world, flip );
2795     assert( v_clip.size() == NUM );
2796 
2797     std::stringstream ss ;
2798     ss << "SGLM::desc_MVP_ce_midface (clipped in {})" << std::endl ;
2799     ss << " MVP " << std::endl  << Present(MVP) << std::endl ;
2800     for(int i=0 ; i < NUM ; i++ )
2801     {
2802         const glm::tvec4<double>& _world = v_world[i] ;
2803         const glm::tvec4<float>& _clip = v_clip[i] ;
2804         glm::vec4 _ndc(_clip.x/_clip.w, _clip.y/_clip.w, _clip.z/_clip.w, 1.f );
2805         // normalized device coordinates : from division by clip.w
2806 
2807         bool clipped = IsClipped(_ndc) ;
2808         char bef = clipped ? '[' : ' ' ;
2809         char aft = clipped ? ']' : ' ' ;
2810 
2811         ss
2812             << "[" << i << "]"
2813             << " world " << stra<double>::Desc(_world)
2814             << " clip  " << stra<float>::Desc(_clip)
2815             << " ndc " << bef << Present(_ndc) << aft
2816             << std::endl
2817             ;
2818 
2819     }
2820     std::string str = ss.str();
2821     return str ;
2822 }
2823 
2824 
2825 bool SGLM::IsClipped(const glm::vec4& _ndc ) // static
2826 {
2827     return _ndc.x < -1.f || _ndc.y < -1.f || _ndc.z < -1.f
2828         || _ndc.x >  1.f || _ndc.y >  1.f || _ndc.z >  1.f ;
2829 }
2830 
2831 
2832 
2833 
2834 
2835 
2836 
2837 
2838 
2839 inline void SGLM::addlog( const char* label, float value )
2840 {
2841     std::stringstream ss ;
2842     ss << std::setw(25) << label << " : " << std::setw(10) << std::fixed << std::setprecision(3) << value ;
2843     std::string s = ss.str();
2844     log.push_back(s);
2845 }
2846 
2847 inline void SGLM::addlog( const char* label, const char* value )
2848 {
2849     std::stringstream ss ;
2850     ss << std::setw(25) << label << " : " << value ;
2851     std::string s = ss.str();
2852     log.push_back(s);
2853 }
2854 
2855 inline std::string SGLM::descLog() const
2856 {
2857     std::stringstream ss ;
2858     ss << "SGLM::descLog" << std::endl ;
2859     for(unsigned i=0 ; i < log.size() ; i++) ss << log[i] << std::endl ;
2860     std::string s = ss.str();
2861     return s ;
2862 }
2863 
2864 
2865 
2866 template <typename T>
2867 inline T SGLM::ato_( const char* a )   // static
2868 {
2869     std::string s(a);
2870     std::istringstream iss(s);
2871     T v ;
2872     iss >> v ;
2873     return v ;
2874 }
2875 
2876 template float    SGLM::ato_<float>( const char* a );
2877 template unsigned SGLM::ato_<unsigned>( const char* a );
2878 template int      SGLM::ato_<int>( const char* a );
2879 
2880 
2881 template <typename T>
2882 inline void SGLM::Str2Vector( std::vector<T>& vec, const char* uval ) // static
2883 {
2884     std::stringstream ss(uval);
2885     std::string s ;
2886     while(getline(ss, s, ',')) vec.push_back(ato_<T>(s.c_str()));
2887 }
2888 
2889 template void SGLM::Str2Vector(std::vector<float>& vec,    const char* uval );
2890 template void SGLM::Str2Vector(std::vector<int>& vec,      const char* uval );
2891 template void SGLM::Str2Vector(std::vector<unsigned>& vec, const char* uval );
2892 
2893 
2894 template <typename T>
2895 inline void SGLM::GetEVector(std::vector<T>& vec, const char* key, const char* fallback )  // static
2896 {
2897     const char* sval = getenv(key);
2898     const char* uval = sval ? sval : fallback ;
2899     Str2Vector(vec, uval);
2900 }
2901 
2902 template void SGLM::GetEVector(std::vector<float>& vec, const char* key, const char* fallback ) ;
2903 template void SGLM::GetEVector(std::vector<int>& vec, const char* key, const char* fallback ) ;
2904 template void SGLM::GetEVector(std::vector<unsigned>& vec, const char* key, const char* fallback ) ;
2905 
2906 
2907 
2908 template <typename T>
2909 inline std::string SGLM::Present(std::vector<T>& vec) // static
2910 {
2911     std::stringstream ss ;
2912     for(unsigned i=0 ; i < vec.size() ; i++) ss << vec[i] << " " ;
2913     return ss.str();
2914 }
2915 
2916 template<typename T>
2917 inline std::string SGLM::Present(const T* tt, int num) // static
2918 {
2919     std::stringstream ss ;
2920     for(int i=0 ; i < num ; i++)
2921         ss
2922             << ( i % 4 == 0 && num > 4 ? ".\n" : "" )
2923             << " " << std::fixed << std::setw(10) << std::setprecision(4) << tt[i]
2924             << ( i == num-1 && num > 4 ? ".\n" : "" )
2925             ;
2926 
2927     std::string str = ss.str();
2928     return str ;
2929 }
2930 
2931 
2932 
2933 
2934 
2935 
2936 inline void SGLM::GetEVec(glm::vec3& v, const char* key, const char* fallback ) // static
2937 {
2938     std::vector<float> vec ;
2939     SGLM::GetEVector<float>(vec, key, fallback);
2940     std::cout << key << " " << Present(vec) << std::endl ;
2941     assert( vec.size() == 3 );
2942     for(int i=0 ; i < 3 ; i++) v[i] = vec[i] ;
2943 }
2944 
2945 inline void SGLM::GetEVec(glm::vec4& v, const char* key, const char* fallback ) // static
2946 {
2947     std::vector<float> vec ;
2948     SGLM::GetEVector<float>(vec, key, fallback);
2949     std::cout << key << " " << Present(vec) << std::endl ;
2950     assert( vec.size() == 4 );
2951     for(int i=0 ; i < 4 ; i++) v[i] = vec[i] ;
2952 }
2953 
2954 inline glm::vec4 SGLM::EVec4(const char* key, const char* fallback, float missing) // static
2955 {
2956     std::vector<float> vec ;
2957     SGLM::GetEVector<float>(vec, key, fallback);
2958     glm::vec4 v ;
2959     for(int i=0 ; i < 4 ; i++) v[i] = i < int(vec.size()) ? vec[i] : missing   ;
2960     return v ;
2961 }
2962 inline glm::vec4 SGLM::SVec4(const char* str, float missing) // static
2963 {
2964     std::vector<float> vec ;
2965     SGLM::Str2Vector<float>(vec, str);
2966     glm::vec4 v ;
2967     for(int i=0 ; i < 4 ; i++) v[i] = i < int(vec.size()) ? vec[i] : missing   ;
2968     return v ;
2969 }
2970 inline glm::vec3 SGLM::SVec3(const char* str, float missing) // static
2971 {
2972     std::vector<float> vec ;
2973     SGLM::Str2Vector<float>(vec, str);
2974     glm::vec3 v ;
2975     for(int i=0 ; i < 3 ; i++) v[i] = i < int(vec.size()) ? vec[i] : missing   ;
2976     return v ;
2977 }
2978 
2979 
2980 
2981 
2982 template <typename T>
2983 inline T SGLM::SValue( const char* uval )  // static
2984 {
2985     std::string s(uval);
2986     T value = ato_<T>(s.c_str());
2987     return value ;
2988 }
2989 template <typename T>
2990 inline T SGLM::EValue( const char* key, const char* fallback )  // static
2991 {
2992     const char* sval = getenv(key);
2993     const char* uval = sval ? sval : fallback ;
2994     return SValue<T>(uval);
2995 }
2996 
2997 
2998 
2999 
3000 inline glm::ivec2 SGLM::EVec2i(const char* key, const char* fallback ) // static
3001 {
3002     std::vector<int> vec ;
3003     SGLM::GetEVector<int>(vec, key, fallback);
3004     glm::ivec2 v ;
3005     for(int i=0 ; i < 2 ; i++) v[i] = i < int(vec.size()) ? vec[i] : 0  ;
3006     return v ;
3007 }
3008 
3009 inline glm::vec3 SGLM::EVec3(const char* key, const char* fallback ) // static
3010 {
3011     std::vector<float> vec ;
3012     SGLM::GetEVector<float>(vec, key, fallback);
3013     glm::vec3 v ;
3014     for(int i=0 ; i < 3 ; i++) v[i] = i < int(vec.size()) ? vec[i] : 0.f  ;
3015     return v ;
3016 }
3017 inline std::string SGLM::Present(const glm::ivec2& v, int wid )
3018 {
3019     std::stringstream ss ;
3020     ss << std::setw(wid) << v.x << " " ;
3021     ss << std::setw(wid) << v.y << " " ;
3022     std::string s = ss.str();
3023     return s;
3024 }
3025 
3026 inline std::string SGLM::Present(const float v, int wid, int prec)
3027 {
3028     std::stringstream ss ;
3029     ss << std::fixed << std::setw(wid) << std::setprecision(prec) << v ;
3030     std::string s = ss.str();
3031     return s;
3032 }
3033 
3034 
3035 inline std::string SGLM::Present(const glm::vec2& v, int wid, int prec)
3036 {
3037     std::stringstream ss ;
3038     ss << std::fixed << std::setw(wid) << std::setprecision(prec) << v.x << " " ;
3039     ss << std::fixed << std::setw(wid) << std::setprecision(prec) << v.y << " " ;
3040     std::string s = ss.str();
3041     return s;
3042 }
3043 
3044 inline std::string SGLM::Present(const glm::vec3& v, int wid, int prec)
3045 {
3046     std::stringstream ss ;
3047     ss << std::fixed << std::setw(wid) << std::setprecision(prec) << v.x << " " ;
3048     ss << std::fixed << std::setw(wid) << std::setprecision(prec) << v.y << " " ;
3049     ss << std::fixed << std::setw(wid) << std::setprecision(prec) << v.z << " " ;
3050     std::string s = ss.str();
3051     return s;
3052 }
3053 
3054 
3055 inline std::string SGLM::Present(const glm::vec4& v, int wid, int prec)
3056 {
3057     std::stringstream ss ;
3058     ss << std::fixed << std::setw(wid) << std::setprecision(prec) << v.x << " " ;
3059     ss << std::fixed << std::setw(wid) << std::setprecision(prec) << v.y << " " ;
3060     ss << std::fixed << std::setw(wid) << std::setprecision(prec) << v.z << " " ;
3061     ss << std::fixed << std::setw(wid) << std::setprecision(prec) << v.w << " " ;
3062     std::string s = ss.str();
3063     return s;
3064 }
3065 
3066 inline std::string SGLM::Present(const float4& v, int wid, int prec)
3067 {
3068     std::stringstream ss ;
3069     ss << std::fixed << std::setw(wid) << std::setprecision(prec) << v.x << " " ;
3070     ss << std::fixed << std::setw(wid) << std::setprecision(prec) << v.y << " " ;
3071     ss << std::fixed << std::setw(wid) << std::setprecision(prec) << v.z << " " ;
3072     ss << std::fixed << std::setw(wid) << std::setprecision(prec) << v.w << " " ;
3073     std::string s = ss.str();
3074     return s;
3075 }
3076 
3077 
3078 
3079 
3080 
3081 inline std::string SGLM::Present(const glm::mat4& m, int wid, int prec)
3082 {
3083     std::stringstream ss ;
3084     for (int j=0; j<4; j++)
3085     {
3086         for (int i=0; i<4; i++) ss << std::fixed << std::setprecision(prec) << std::setw(wid) << m[i][j] ;
3087         ss << std::endl ;
3088     }
3089     return ss.str();
3090 }
3091 
3092 
3093 template<typename T>
3094 inline std::string SGLM::Present_(const glm::tmat4x4<T>& m, int wid, int prec)
3095 {
3096     std::stringstream ss ;
3097     for (int j=0; j<4; j++)
3098     {
3099         for (int i=0; i<4; i++) ss << std::fixed << std::setprecision(prec) << std::setw(wid) << m[i][j] ;
3100         ss << std::endl ;
3101     }
3102     return ss.str();
3103 }
3104 
3105 
3106 template<typename T>
3107 inline glm::tmat4x4<T> SGLM::DemoMatrix(T scale)  // static
3108 {
3109     std::array<T, 16> demo = {{
3110         T(1.)*scale,   T(2.)*scale,   T(3.)*scale,   T(4.)*scale ,
3111         T(5.)*scale,   T(6.)*scale,   T(7.)*scale,   T(8.)*scale ,
3112         T(9.)*scale,   T(10.)*scale,  T(11.)*scale,  T(12.)*scale ,
3113         T(13.)*scale,  T(14.)*scale,  T(15.)*scale,  T(16.)*scale
3114       }} ;
3115     return glm::make_mat4x4<T>(demo.data()) ;
3116 }
3117 
3118 
3119 /**
3120 SGLM::setRecord
3121 --------------------
3122 
3123 Invoked for example from renderers like cxr_min.sh to enable
3124 rendering of record step points together with geometry.
3125 
3126 **/
3127 
3128 
3129 inline void SGLM::setRecord( SRecord* _ar, SRecord* _br )
3130 {
3131     ar = _ar ;
3132     br = _br ;
3133 
3134     init_time();
3135 }
3136 
3137 inline void SGLM::setGenstep( SGen* _gs )
3138 {
3139     gs = _gs ;
3140 }
3141 
3142 
3143 
3144 /**
3145 SGLM::init_time
3146 ----------------------
3147 
3148 t0
3149     start time
3150 t1
3151     end time
3152 
3153 tn:int
3154     number of render calls with which to increment
3155     the event time from t0 to t1 : typically a large
3156     value like 5000 to avoid excessively fast animation
3157 
3158 
3159 timeparam quad:
3160 
3161    +---------+----------+--------------+-------------+
3162    | t_start | t_stop   |  t_step      | t_current   |
3163    +=========+==========+==============+=============+
3164    |  t0     |   t1     |  (t1-t0)/tn  |     t       |
3165    +---------+----------+--------------+-------------+
3166 
3167 **/
3168 
3169 
3170 inline void SGLM::init_time()
3171 {
3172     float t0 = T0 ;
3173     float t1 = T1 ;
3174     int tn = TN ;
3175 
3176     bool t_noenv = t0 == 0.f && t1 == 0.f ;  // envvars T0 and T1 unset or non-sensical
3177     if(t_noenv)
3178     {
3179         if( ar && br )
3180         {
3181             t0 = std::min( ar->get_t0(), br->get_t0() );
3182             t1 = std::max( ar->get_t1(), br->get_t1() );
3183         }
3184         else if( ar )
3185         {
3186             t0 = ar->get_t0();
3187             t1 = ar->get_t1();
3188         }
3189         else if( br )
3190         {
3191             t0 = br->get_t0();
3192             t1 = br->get_t1();
3193         }
3194     }
3195 
3196     assert( tn > 1 );
3197 
3198     timeparam.x = t0 ;
3199     timeparam.y = t1 ;
3200     timeparam.z = (t1-t0)/float(tn) ;
3201     timeparam.w = t0 ;
3202 
3203     bool init_time_DUMP = ssys::getenvbool(__init_time_DUMP) ;
3204 
3205     if(init_time_DUMP) std::cout
3206          << "SGLM::init_time"
3207          << " [" << __init_time_DUMP << "] "
3208          << " " << ( init_time_DUMP ? "YES" : "NO " )
3209          << "\n"
3210          << " t_noenv " << ( t_noenv ? "YES" : "NO " )
3211          << "\n"
3212          << " ar\n "
3213          << ( ar ? ar->desc() : "-" )
3214          << "\n"
3215          << " br\n "
3216          << ( br ? br->desc() : "-" )
3217          << "\n"
3218          << desc_time()
3219          << "\n"
3220          ;
3221 
3222 }
3223 
3224 inline void SGLM::reset_time()
3225 {
3226     float t0 = get_t0();
3227     set_time(t0) ;
3228 }
3229 
3230 /**
3231 SGLM::reset_time_TT
3232 --------------------
3233 
3234 SGLFW.h invokes this from renderloop when press shift+T
3235 causing time to be reset to value of TT envvar (default 0.f)
3236 and the animation to be disabled.
3237 
3238 Resume animation with alt+T
3239 
3240 **/
3241 
3242 inline void SGLM::reset_time_TT()
3243 {
3244     std::cout << "SGLM::reset_time_TT " << TT << "\n" ;
3245     set_time(TT) ;
3246 }
3247 
3248 inline void SGLM::toggle_time_halt()
3249 {
3250     enabled_time_halt = !enabled_time_halt ;
3251 }
3252 
3253 
3254 std::string SGLM::desc_time() const
3255 {
3256     float t = get_time();
3257     float t0 = get_t0();
3258     float t1 = get_t1();
3259     float ts = get_ts();
3260     int   tn = get_tn();
3261 
3262     std::stringstream ss ;
3263     ss
3264        << "[SGLM::desc_time\n"
3265        << " T0 " << std::fixed << std::setw(7) << std::setprecision(3) << T0 << "\n"
3266        << " T1 " << std::fixed << std::setw(7) << std::setprecision(3) << T1 << "\n"
3267        << " t  " << std::fixed << std::setw(7) << std::setprecision(3) << t  << "\n"
3268        << " t0 " << std::fixed << std::setw(7) << std::setprecision(3) << t0 << "\n"
3269        << " t1 " << std::fixed << std::setw(7) << std::setprecision(3) << t1 << "\n"
3270        << " ts " << std::fixed << std::setw(7) << std::setprecision(3) << ts << "\n"
3271        << " tn "  <<  std::setw(6) << tn << "\n"
3272        << "]SGLM::desc_time\n"
3273        ;
3274 
3275     std::string str = ss.str();
3276     return str ;
3277 
3278 }
3279 
3280 
3281 
3282 inline float SGLM::get_t0() const
3283 {
3284     return timeparam.x ;
3285 }
3286 inline float SGLM::get_t1() const
3287 {
3288     return timeparam.y ;
3289 }
3290 inline float SGLM::get_ts() const
3291 {
3292     return timeparam.z ;
3293 }
3294 inline int SGLM::get_tn() const
3295 {
3296     float t0 = get_t0();
3297     float t1 = get_t1();
3298     float ts = get_ts();
3299     return int((t1 - t0)/ts ) ;
3300 }
3301 
3302 
3303 
3304 inline float SGLM::get_time() const
3305 {
3306     return timeparam.w ;
3307 }
3308 inline bool SGLM::in_timerange(float t) const
3309 {
3310     return t >= timeparam.x && t <= timeparam.y ;
3311 }
3312 
3313 
3314 
3315 /**
3316 SGLM::set_time
3317 ----------------
3318 
3319 Whem t is not within the t0 to t1 range sets time to t0
3320 
3321 **/
3322 
3323 inline void SGLM::set_time( float t )
3324 {
3325     timeparam.w = in_timerange(t) ? t : timeparam.x ;
3326 }
3327 
3328 
3329 inline void SGLM::time_bump()
3330 {
3331     if(!enabled_time_bump || enabled_time_halt) return ;
3332     set_time( get_time() + get_ts() );
3333 
3334 }
3335 
3336 inline void SGLM::inc_time(float dy)
3337 {
3338     std::cout
3339         << "SGLM::inc_time"
3340         << " dy " << dy
3341         << " TIMESCALE " << TIMESCALE
3342         << " dy*TIMESCALE " << dy*TIMESCALE
3343         << "\n"
3344         ;
3345 
3346     set_time( get_time() + dy*TIMESCALE );
3347 }
3348 
3349 
3350 
3351 inline void SGLM::renderloop_head()
3352 {
3353     increment_spin();
3354 }
3355 
3356 
3357 /**
3358 SGLM::renderloop_tail
3359 ----------------------
3360 
3361 Invoked from SGLFW::renderloop_tail
3362 
3363 At each call the simulation time is bumped until the
3364 time exceeds t1 at which point it is returned to t0.
3365 
3366 **/
3367 
3368 inline void SGLM::renderloop_tail()
3369 {
3370     if( option.A || option.B ) time_bump();
3371 }
3372