// Copyright (C) 2010, Guy Barrand. All rights reserved. // See the file tools.license for terms. #ifndef tools_sg_polyhedron #define tools_sg_polyhedron #include "node" #include "sf" #include "render_action" #include "pick_action" #include "bbox_action" #include "state" #include "render_manager" #include "gstos" #include "../hep/sf_polyhedron" #include "../vmanip" #include "tessellate" namespace tools { namespace sg { class polyhedron : public node, public gstos { TOOLS_NODE(polyhedron,tools::sg::polyhedron,node) public: hep::sf_polyhedron ph; sf solid; sf reduced_wire_frame; sf enforce_edges; public: virtual const desc_fields& node_desc_fields() const { TOOLS_FIELD_DESC_NODE_CLASS(tools::sg::polyhedron) static const desc_fields s_v(parent::node_desc_fields(),4, //WARNING : take care of count. TOOLS_ARG_FIELD_DESC(ph), TOOLS_ARG_FIELD_DESC(solid), TOOLS_ARG_FIELD_DESC(reduced_wire_frame), TOOLS_ARG_FIELD_DESC(enforce_edges) ); return s_v; } private: void add_fields(){ add_field(&ph); add_field(&solid); add_field(&reduced_wire_frame); add_field(&enforce_edges); } public: //node typedef tessellate::prims_t prims_t; protected: //gstos virtual unsigned int create_gsto(std::ostream& a_out,render_manager& a_mgr) { m_size = 0; if(solid.value()) { size_t sz = 0; if(m_draw_edges) sz += m_xyzs.size(); {tools_vforcit(prims_t,m_primss,itt) { const prims_t& prims = *itt; tools_vforcit(tess_prim*,prims,it) { const tess_prim& item = *(*it); const std::vector& coords = item.m_coords; const std::vector& norms = item.m_norms; if(coords.empty()) continue; if(norms.size()!=coords.size()) { a_out << "tools::sg::polyhedron::create_gsto :" << " bad number of normals." << std::endl; continue; } sz += coords.size()+norms.size(); } }} if(sz) { float* buffer = new float[sz]; char* pos = (char*)buffer; size_t psz = 0; if(m_draw_edges) { if(m_xyzs.size()) { size_t _sz = m_xyzs.size()*sizeof(float); ::memcpy(pos,vec_data(m_xyzs),_sz); pos += _sz; psz += _sz; m_size = m_xyzs.size(); //m_xyzs.clear(); //we need them for pick,write,... } } {tools_vforcit(prims_t,m_primss,itt) { const prims_t& prims = *itt; tools_vforcit(tess_prim*,prims,it) { tess_prim& item = *(*it); const std::vector& coords = item.m_coords; const std::vector& norms = item.m_norms; if(coords.empty()) continue; if(norms.size()!=coords.size()) continue; item.m_size = coords.size(); {item.m_pos_coords = psz; size_t _sz = coords.size()*sizeof(float); ::memcpy(pos,vec_data(coords),_sz); pos += _sz; psz += _sz;} {item.m_pos_norms = psz; size_t _sz = norms.size()*sizeof(float); ::memcpy(pos,vec_data(norms),_sz); pos += _sz; psz += _sz;} // we don't need coords, norms for rendering // but needed for pick,write... //coords.clear(); //norms.clear(); //we still need m_prims. } }} unsigned int id = a_mgr.create_gsto_from_data(sz,buffer); delete [] buffer; return id; } } else { m_size = m_xyzs.size(); return a_mgr.create_gsto_from_data(m_xyzs); } return 0; } public: virtual void render(render_action& a_action) { const state& state = a_action.state(); bool draw_edges = false; if(solid.value()) { draw_edges = state.m_GL_LIGHTING?false:true; if(enforce_edges.value()) draw_edges = true; } if( touched() || (draw_edges && m_xyzs.empty()) || (solid.value() && m_primss.empty()) ){ update_what what = lines; if(solid.value()) what = draw_edges?faces_and_lines:faces; update_sg(a_action.out(),what); reset_touched(); } unsigned int _id = 0; if(state.m_use_gsto) { m_draw_edges = draw_edges; _id = get_gsto_id(a_action.out(),a_action.render_manager()); } else { clean_gstos(&a_action.render_manager()); } //a_action.out() << "debug : tools::sg::polyhedron::render_action :" // << " m_xyzs.size() " << m_xyzs.size() // << " m_primss.size() " << m_primss.size() // << std::endl; if(solid.value()) { if(_id) { a_action.begin_gsto(_id); if(draw_edges && m_size) { //Same logic as Inventor SoLightModel.model = BASE_COLOR. a_action.set_lighting(false); a_action.color4f(0,0,0,1); a_action.draw_gsto_v(gl::lines(),m_size/3,0); //pushes back the filled polygons to avoid z-fighting with lines a_action.set_polygon_offset(true); a_action.color4f(state.m_color); a_action.set_lighting(state.m_GL_LIGHTING); } {tools_vforcit(prims_t,m_primss,itt) { const prims_t& prims = *itt; tools_vforcit(tess_prim*,prims,it) { const tess_prim& item = *(*it); a_action.draw_gsto_vn(item.m_mode,item.m_size/3,item.m_pos_coords,item.m_pos_norms); } }} a_action.set_polygon_offset(state.m_GL_POLYGON_OFFSET_FILL); a_action.end_gsto(); } else { if(draw_edges){ if(m_xyzs.size()) { //Same logic as Inventor SoLightModel.model = BASE_COLOR. a_action.set_lighting(false); a_action.color4f(0,0,0,1); a_action.draw_vertex_array(gl::lines(),m_xyzs); //pushes back the filled polygons to avoid z-fighting with lines a_action.set_polygon_offset(true); a_action.color4f(state.m_color); a_action.set_lighting(state.m_GL_LIGHTING); } } {tools_vforcit(prims_t,m_primss,itt) { const prims_t& prims = *itt; tools_vforcit(tess_prim*,prims,it) { const tess_prim& item = *(*it); const std::vector& coords = item.m_coords; if(coords.empty()) continue; const std::vector& norms = item.m_norms; if(norms.size()!=coords.size()) { a_action.out() << "tools::sg::polyhedron::render :" << " bad number of normals." << std::endl; continue; } a_action.draw_vertex_normal_array(item.m_mode,coords,norms); } }} a_action.set_polygon_offset(state.m_GL_POLYGON_OFFSET_FILL); } } else { //not solid //Same logic as Inventor SoLightModel.model = BASE_COLOR. a_action.set_lighting(false); // do we want that ? if(_id) { a_action.begin_gsto(_id); a_action.draw_gsto_v(gl::lines(),m_size/3,0); a_action.end_gsto(); } else { a_action.draw_vertex_array(gl::lines(),m_xyzs); } a_action.set_lighting(state.m_GL_LIGHTING); } } virtual void bbox(bbox_action& a_action) { if(touched()) { update_sg(a_action.out(),lines); reset_touched(); } if(solid.value()) { tools_vforcit(prims_t,m_primss,itt) { const prims_t& prims = *itt; tools_vforcit(tess_prim*,prims,it) { const tess_prim& item = *(*it); const std::vector& coords = item.m_coords; a_action.add_points(coords); } } } else { a_action.add_points(m_xyzs); } } virtual void pick(pick_action& a_action) { if(touched()) { update_what what = solid.value()?faces:lines; update_sg(a_action.out(),what); reset_touched(); } if(solid.value()) { tools_vforcit(prims_t,m_primss,itt) { const prims_t& prims = *itt; tools_vforcit(tess_prim*,prims,it) { const tess_prim& item = *(*it); const std::vector& coords = item.m_coords; if(a_action.add__primitive(*this,item.m_mode,coords,true)) return; } } } else { a_action.add__lines(*this,m_xyzs,true); } } public: polyhedron() :parent() ,ph(hep::polyhedron()) ,solid(true) ,reduced_wire_frame(true) ,enforce_edges(false) ,m_tess(0) { add_fields(); } virtual ~polyhedron(){ _clear(); delete m_tess; } public: polyhedron(const polyhedron& a_from) :parent(a_from) ,gstos(a_from) ,ph(a_from.ph) ,solid(a_from.solid) ,reduced_wire_frame(a_from.reduced_wire_frame) ,enforce_edges(a_from.enforce_edges) ,m_xyzs(a_from.m_xyzs) ,m_tess(0) { add_fields(); } polyhedron& operator=(const polyhedron& a_from){ parent::operator=(a_from); gstos::operator=(a_from); ph = a_from.ph; solid = a_from.solid; reduced_wire_frame = a_from.reduced_wire_frame; enforce_edges = a_from.enforce_edges; m_xyzs = a_from.m_xyzs; return *this; } public: enum update_what { faces = 0, lines = 1, faces_and_lines }; void update_sg(std::ostream& a_out,const update_what& a_what) { //a_out << "debug : tools::sg::polyhedron::update_sg" << std::endl; clean_gstos(); //must reset for all render_manager. _clear(); const hep::polyhedron& sbPolyhedron = ph.value(); if(sbPolyhedron.GetNoFacets()<=0) { //a_out << "tools::sg::polyhedron::update_sg : abnormal polyhedron." << std::endl; return; // Abnormal polyhedron. } if((a_what==faces)||(a_what==faces_and_lines)) { std::vector doubles; vec3d unitNormal; vec3d* pV = sbPolyhedron.GetPV(); // Assume all facets are convex quadrilaterals : bool notLastFace; do { notLastFace = sbPolyhedron.GetNextUnitNormal(unitNormal); doubles.clear(); //begin POLYGON bool notLastEdge; int edgeFlag = 1; do { // optimize to avoid copying vec3f. //notLastEdge = sbPolyhedron.GetNextVertex(vertex,edgeFlag); int index; notLastEdge = sbPolyhedron.GetNextVertexIndex(index,edgeFlag); vec3d* vertex = pV+index; //WARNING : must not be modified ! doubles.push_back((*vertex)[0]); doubles.push_back((*vertex)[1]); doubles.push_back((*vertex)[2]); } while (notLastEdge); if(!m_tess) m_tess = new tessellate(a_out); m_primss.push_back(prims_t()); prims_t& prims = m_primss.back(); m_tess->do_it(doubles.size()/3,vec_data(doubles),prims); m_tess->clear(); } while (notLastFace); } if((a_what==lines)||(a_what==faces_and_lines)) { float pvbx = 0; float pvby = 0; float pvbz = 0; float pvex,pvey,pvez; vec3d unitNormal; vec3d* pV = sbPolyhedron.GetPV(); bool notLastFace; do { notLastFace = sbPolyhedron.GetNextUnitNormal(unitNormal); // Treat edges : int edgeFlag = 1; int prevEdgeFlag = edgeFlag; bool notLastEdge; bool firstEdge = true; do { int index; notLastEdge = sbPolyhedron.GetNextVertexIndex(index,edgeFlag); vec3d* vertex = pV+index; //WARNING : must not be modified ! if(!reduced_wire_frame) edgeFlag = 1; if(firstEdge) { if(edgeFlag) { pvbx = (float)(*vertex)[0]; pvby = (float)(*vertex)[1]; pvbz = (float)(*vertex)[2]; } else { } firstEdge = false; prevEdgeFlag = edgeFlag; } else { if(edgeFlag!=prevEdgeFlag) { if(edgeFlag) { // Pass to a visible edge : pvbx = (float)(*vertex)[0]; pvby = (float)(*vertex)[1]; pvbz = (float)(*vertex)[2]; } else { // Pass to an invisible edge : pvex = (float)(*vertex)[0]; pvey = (float)(*vertex)[1]; pvez = (float)(*vertex)[2]; m_xyzs.push_back(pvbx); m_xyzs.push_back(pvby); m_xyzs.push_back(pvbz); m_xyzs.push_back(pvex); m_xyzs.push_back(pvey); m_xyzs.push_back(pvez); } prevEdgeFlag = edgeFlag; } else { if(edgeFlag) { pvex = (float)(*vertex)[0]; pvey = (float)(*vertex)[1]; pvez = (float)(*vertex)[2]; m_xyzs.push_back(pvbx); m_xyzs.push_back(pvby); m_xyzs.push_back(pvbz); m_xyzs.push_back(pvex); m_xyzs.push_back(pvey); m_xyzs.push_back(pvez); pvbx = pvex; pvby = pvey; pvbz = pvez; } else { } } } } while (notLastEdge); } while (notLastFace); } } const std::vector& xyzs() const {return m_xyzs;} protected: void _clear(){ tools_vforit(prims_t,m_primss,it) raw_clear(*it); m_primss.clear(); m_xyzs.clear(); } protected: std::vector m_xyzs; //gluTess tessellate* m_tess; std::vector m_primss; size_t m_size; bool m_draw_edges; }; }} #endif