diff --git a/modules/gfx/pymod/__init__.py b/modules/gfx/pymod/__init__.py index 0cd783f9fa44d36dd8ab1696257b943e1d2732c6..4d96c534e4e7978849ae4406dc2b828524d860c2 100644 --- a/modules/gfx/pymod/__init__.py +++ b/modules/gfx/pymod/__init__.py @@ -243,7 +243,7 @@ def _primlist_add_text(self,text,pos,color=None,point_size=None): if not point_size: point_size=1.0 self._add_text(text,pos,color,point_size) - + PrimList.AddPoint=_primlist_add_point PrimList.AddLine=_primlist_add_line PrimList.AddSphere=_primlist_add_sphere diff --git a/modules/gfx/pymod/export_primlist.cc b/modules/gfx/pymod/export_primlist.cc index b8326509d8ed7065f120ab4760a541b5766e207b..27b46737645328b01d212d6601f2b45807b6ca76 100644 --- a/modules/gfx/pymod/export_primlist.cc +++ b/modules/gfx/pymod/export_primlist.cc @@ -25,8 +25,88 @@ using namespace boost::python; using namespace ost; using namespace ost::gfx; +#if OST_NUMPY_SUPPORT_ENABLED +#include <numpy/arrayobject.h> +#endif + +namespace { + void add_mesh(PrimList& p, object ova, object ona, object oca, object oia) + { +#if OST_NUMPY_SUPPORT_ENABLED + if(!PyArray_Check(ova.ptr())) { + throw std::runtime_error("ova is not a numpy array"); + } + PyArrayObject* va=reinterpret_cast<PyArrayObject*>(ova.ptr()); + if(!PyArray_ISCONTIGUOUS(va)) { + throw std::runtime_error("expected vertex array to be contiguous"); + } + if(!PyArray_TYPE(va)==NPY_FLOAT) { + throw std::runtime_error("expected vertex array to be of dtype=float32"); + } + size_t v_size=PyArray_SIZE(va); + if(v_size%3!=0) { + throw std::runtime_error("expected vertex array size to be divisible by 3"); + } + size_t v_count=v_size/3; + float* vp=reinterpret_cast<float*>(PyArray_DATA(va)); + float* np=0; + float* cp=0; + if(ona!=object()) { + if(!PyArray_Check(ona.ptr())) { + throw std::runtime_error("ona is not a numpy array"); + } + PyArrayObject* na=reinterpret_cast<PyArrayObject*>(ona.ptr()); + if(!PyArray_ISCONTIGUOUS(na)) { + throw std::runtime_error("expected normal array to be contiguous"); + } + if(!PyArray_TYPE(na)==NPY_FLOAT) { + throw std::runtime_error("expected normal array to be of dtype=float32"); + } + if(PyArray_SIZE(na)!=v_size) { + throw std::runtime_error("expected normal array size to match vertex array size"); + } + np=reinterpret_cast<float*>(PyArray_DATA(na)); + } + if(oca!=object()) { + if(!PyArray_Check(oca.ptr())) { + throw std::runtime_error("oca is not a numpy array"); + } + PyArrayObject* ca=reinterpret_cast<PyArrayObject*>(oca.ptr()); + if(!PyArray_ISCONTIGUOUS(ca)) { + throw std::runtime_error("expected color array to be contiguous"); + } + if(!PyArray_TYPE(ca)==NPY_FLOAT) { + throw std::runtime_error("expected color array to be of dtype=float32"); + } + if(PyArray_SIZE(ca)!=v_count*4) { + throw std::runtime_error("expected color array size to equal vertex-count x 4"); + } + cp=reinterpret_cast<float*>(PyArray_DATA(ca)); + } + if(!PyArray_Check(oia.ptr())) { + throw std::runtime_error("oia is not a numpy array"); + } + PyArrayObject* ia=reinterpret_cast<PyArrayObject*>(oia.ptr()); + if(!PyArray_ISCONTIGUOUS(ia)) { + throw std::runtime_error("expected vertex array to be contiguous"); + } + if(!PyArray_TYPE(ia)==NPY_UINT) { + throw std::runtime_error("expected vertex array to be of dtype=uint32"); + } + size_t i_size=PyArray_SIZE(ia); + unsigned int* ip=reinterpret_cast<unsigned int*>(PyArray_DATA(ia)); + + p.AddMesh(vp,np,cp,v_count,ip,i_size/3); +#else + throw std::runtime_error("AddMesh requires compiled-in numpy support"); +#endif + } +} + void export_primlist() { + import_array(); // magic handshake for numpy module + class_<PrimList, bases<GfxObj>, boost::shared_ptr<PrimList>, boost::noncopyable>("PrimList", init<const String& >()) .def("Clear",&PrimList::Clear) .def("_add_line",&PrimList::AddLine) @@ -34,6 +114,7 @@ void export_primlist() .def("_add_sphere",&PrimList::AddSphere) .def("_add_cyl",&PrimList::AddCyl) .def("_add_text",&PrimList::AddText) + .def("AddMesh",add_mesh) .def("SetColor",&PrimList::SetColor) .def("SetDiameter",&PrimList::SetDiameter) .def("SetRadius",&PrimList::SetRadius) diff --git a/modules/gfx/src/prim_list.cc b/modules/gfx/src/prim_list.cc index 6113aecfb45e02867496f1c6129a92d9da8db2a1..a9debc0edd67dfa55d9a4057fb9ad0b77bdf4d2f 100644 --- a/modules/gfx/src/prim_list.cc +++ b/modules/gfx/src/prim_list.cc @@ -38,7 +38,8 @@ PrimList::PrimList(const String& name): texts_(), sphere_detail_(4), arc_detail_(4), - simple_va_() + simple_va_(), + vas_() {} void PrimList::Clear() @@ -48,13 +49,14 @@ void PrimList::Clear() spheres_.clear(); cyls_.clear(); texts_.clear(); + vas_.clear(); Scene::Instance().RequestRedraw(); this->FlagRebuild(); } geom::AlignedCuboid PrimList::GetBoundingBox() const { - if(points_.empty() && lines_.empty() && spheres_.empty() && cyls_.empty()) { + if(points_.empty() && lines_.empty() && spheres_.empty() && cyls_.empty() && texts_.empty() && vas_.empty()) { return geom::AlignedCuboid(geom::Vec3(-1,-1,-1),geom::Vec3(1,1,1)); } geom::Vec3 minc(std::numeric_limits<float>::max(), @@ -101,6 +103,11 @@ void PrimList::ProcessLimits(geom::Vec3& minc, geom::Vec3& maxc, minc=geom::Min(minc,tpos); maxc=geom::Max(maxc,tpos); } + for(std::vector<IndexedVertexArray>::const_iterator it=vas_.begin();it!=vas_.end();++it) { + geom::AlignedCuboid bb=it->GetBoundingBox(); + minc=geom::Min(minc,bb.GetMin()); + maxc=geom::Max(maxc,bb.GetMax()); + } minc-=1.0; maxc+=1.0; } @@ -152,12 +159,15 @@ void PrimList::CustomRenderGL(RenderPass pass) va_.RenderGL(); simple_va_.RenderGL(); render_text(); + for(std::vector<IndexedVertexArray>::iterator it=vas_.begin();it!=vas_.end();++it) { + it->RenderGL(); + } } } void PrimList::CustomRenderPov(PovState& pov) { - if(points_.empty() && lines_.empty()) return; + if(points_.empty() && lines_.empty() && spheres_.empty() && cyls_.empty()) return; pov.write_merge_or_union(GetName()); for(SpherePrimList::const_iterator it=points_.begin();it!=points_.end();++it) { @@ -262,6 +272,42 @@ void PrimList::SetColor(const Color& c) FlagRebuild(); } +void PrimList::AddMesh(float* v, float* n, float* c, size_t nv, unsigned int* i, size_t ni) +{ + static float dummy_normal[]={0.0,0.0,1.0}; + static float dummy_color[]={1.0,1.0,1.0,1.0}; + vas_.push_back(IndexedVertexArray()); + IndexedVertexArray& va=vas_.back(); + va.SetLighting(true); + va.SetTwoSided(true); + va.SetColorMaterial(true); + va.SetCullFace(false); + float* vv=v; + float* nn=n; + if(!n) { + nn=dummy_normal; + va.SetLighting(false); + } + float* cc=c; + if(!c) { + cc=dummy_color; + } + for(size_t k=0;k<nv;++k) { + va.Add(geom::Vec3(vv[0],vv[1],vv[2]), + geom::Vec3(nn[0],nn[1],nn[2]), + Color(cc[0],cc[1],cc[2],cc[3])); + vv+=3; + if(n) nn+=3; + if(c) cc+=4; + } + unsigned int* ii=i; + for(size_t k=0;k<ni;++k) { + va.AddTri(ii[0],ii[1],ii[2]); + ii+=3; + } + Scene::Instance().RequestRedraw(); + FlagRebuild(); +} //////////////////////////////// // private methods diff --git a/modules/gfx/src/prim_list.hh b/modules/gfx/src/prim_list.hh index 4e8a4c297ea044eda07d2cc199a4988b3d7e4e23..395e53e2ad22ce359b37281caead22cad1a423e9 100644 --- a/modules/gfx/src/prim_list.hh +++ b/modules/gfx/src/prim_list.hh @@ -139,6 +139,24 @@ class DLLEXPORT_OST_GFX PrimList: public GfxObj // TODO: add point and line pixel width + /*! + \brief add triangle mesh + + v : pointer to nv*3 floats for the positions (mandatory) + n : pointer to nv*3 floats for the normals (may be NULL) + c : pointer to nv*4 floats for the colors (may be NULL) + nv: number of vertices, normals, and colors + i : pointer to ni*3 vertex indices + ni: number of index triplets + + Python interface, using numpy arrays: + + AddMesh(vertex_array, normal_array, color_array, index_array) + + where normal_array and color_array may be None + */ + void AddMesh(float* v, float* n, float* c, size_t nv, unsigned int* i, size_t ni); + protected: virtual void CustomPreRenderGL(bool flag); @@ -152,6 +170,8 @@ class DLLEXPORT_OST_GFX PrimList: public GfxObj unsigned int arc_detail_; IndexedVertexArray simple_va_; + + std::vector<IndexedVertexArray> vas_; void prep_simple_va(); void prep_va(); diff --git a/modules/gfx/tests/test_gfx.py b/modules/gfx/tests/test_gfx.py index b6911eee12fc2a5401a11d1284dc14d1eb7e3f17..ff6e462e7f73f9478073618398b3700149c156a2 100644 --- a/modules/gfx/tests/test_gfx.py +++ b/modules/gfx/tests/test_gfx.py @@ -9,6 +9,11 @@ import ost.mol as mol import ost.gfx as gfx import ost.geom as geom +has_numpy=True +import numpy +if not has_numpy: + has_numpy=False + def col_delta(c1,c2): return geom.Distance(geom.Vec3(c1[0],c1[1],c1[2]),geom.Vec3(c2[0],c2[1],c2[2])) @@ -71,6 +76,16 @@ class TestGfx(unittest.TestCase): pl.AddCyl(geom.Vec3(0,0,0),geom.Vec3(1,2,3),radius1=0.5,radius2=0.1,color1=gfx.BLUE,color2=gfx.GREEN) pl.AddText("foo",[0,2,3]) pl.AddText("bar",[-2,0,0],color=gfx.WHITE,point_size=8) + if has_numpy: + pl.AddMesh(numpy.zeros((5,3),dtype=numpy.float32), + numpy.zeros((5,3),dtype=numpy.float32), + numpy.zeros((5,4),dtype=numpy.float32), + numpy.zeros((2,3),dtype=numpy.uint32)) + pl.AddMesh(numpy.zeros((7,3),dtype=numpy.float32), + None, + None, + numpy.zeros((4,3),dtype=numpy.uint32)) + if __name__== '__main__': unittest.main()