1 // leaf.cxx -- function to build and ssg leaf from higher level data.
3 // Written by Curtis Olson, started October 1997.
5 // Copyright (C) 1997 - 2003 Curtis L. Olson - http://www.flightgear.org/~curt
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 # include <simgear_config.h>
28 #include <simgear/compiler.h>
30 #ifdef SG_MATH_EXCEPTION_CLASH
37 #include <osg/Geometry>
39 #include <osg/StateSet>
40 #include <osg/TriangleFunctor>
42 #include <simgear/debug/logstream.hxx>
43 #include <simgear/math/sg_random.h>
44 #include <simgear/scene/material/mat.hxx>
45 #include <simgear/scene/material/matlib.hxx>
53 typedef vector < int > int_list;
54 typedef int_list::iterator int_list_iterator;
55 typedef int_list::const_iterator int_point_list_iterator;
59 osg::Vec3 random_pt_inside_tri(const osg::Vec3& n1, const osg::Vec3& n2,
62 double a = sg_random();
63 double b = sg_random();
70 return n1*a + n2*b + n3*c;
73 /// class to implement the TrinagleFunctor class
74 struct SGRandomSurfacePointsFill {
75 osg::Vec3Array* lights;
78 void operator () (const osg::Vec3& v1, const osg::Vec3& v2,
79 const osg::Vec3& v3, bool)
82 float area = 0.5*((v1 - v2)^(v3 - v2)).length();
83 float num = area / factor;
85 // generate a light point for each unit of area
87 lights->push_back(random_pt_inside_tri( v1, v2, v3 ));
90 // for partial units of area, use a zombie door method to
91 // create the proper random chance of a light being created
94 if ( sg_random() <= num ) {
95 // a zombie made it through our door
96 lights->push_back(random_pt_inside_tri( v1, v2, v3 ));
102 static void SGGenRandomSurfacePoints( osg::Geometry *leaf, double factor,
103 osg::Vec3Array *lights )
105 osg::TriangleFunctor<SGRandomSurfacePointsFill> triangleFunctor;
106 triangleFunctor.lights = lights;
107 triangleFunctor.factor = factor;
108 leaf->accept(triangleFunctor);
112 ////////////////////////////////////////////////////////////////////////
114 ////////////////////////////////////////////////////////////////////////
116 osg::Node* SGMakeLeaf( const string& path,
118 SGMaterialLib *matlib, const string& material,
119 const point_list& nodes, const point_list& normals,
120 const point_list& texcoords,
121 const int_list& node_index,
122 const int_list& normal_index,
123 const int_list& tex_index,
124 const bool calc_lights, osg::Vec3Array *lights )
126 double tex_width = 1000.0, tex_height = 1000.0;
127 osg::StateSet *state = 0;
130 SGMaterial *mat = matlib->find( material );
132 // see if this is an on the fly texture
134 string::size_type pos = file.rfind( "/" );
135 file = file.substr( 0, pos );
136 // cout << "current file = " << file << endl;
139 // cout << "current file = " << file << endl;
140 if ( ! matlib->add_item( file ) ) {
141 SG_LOG( SG_TERRAIN, SG_ALERT,
142 "Ack! unknown usemtl name = " << material
145 // locate our newly created material
146 mat = matlib->find( material );
148 SG_LOG( SG_TERRAIN, SG_ALERT,
149 "Ack! bad on the fly material create = "
150 << material << " in " << path );
156 // set the texture width and height values for this
158 tex_width = mat->get_xsize();
159 tex_height = mat->get_ysize();
160 state = mat->get_state();
161 coverage = mat->get_light_coverage();
162 // cout << "(w) = " << tex_width << " (h) = "
163 // << tex_width << endl;
171 int size = node_index.size();
173 SG_LOG( SG_TERRAIN, SG_ALERT, "Woh! node list size < 1" );
176 osg::Vec3Array* vl = new osg::Vec3Array;
178 for ( i = 0; i < size; ++i ) {
179 Point3D node = nodes[ node_index[i] ];
180 vl->push_back(osg::Vec3(node[0], node[1], node[2]));
184 osg::Vec3Array* nl = new osg::Vec3Array;
186 if ( normal_index.size() ) {
187 // object file specifies normal indices (i.e. normal indices
189 for ( i = 0; i < size; ++i ) {
190 Point3D normal = normals[ normal_index[i] ];
191 nl->push_back(osg::Vec3(normal[0], normal[1], normal[2]));
194 // use implied normal indices. normal index = vertex index.
195 for ( i = 0; i < size; ++i ) {
196 Point3D normal = normals[ node_index[i] ];
197 nl->push_back(osg::Vec3(normal[0], normal[1], normal[2]));
202 osg::Vec4Array* cl = new osg::Vec4Array;
203 cl->push_back(osg::Vec4(1, 1, 1, 1));
205 // texture coordinates
206 size = tex_index.size();
208 osg::Vec2Array* tl = new osg::Vec2Array;
211 Point3D texcoord = texcoords[ tex_index[0] ];
212 osg::Vec2 tmp2(texcoord[0], texcoord[1]);
213 if ( tex_width > 0 ) {
214 tmp2[0] *= (1000.0 / tex_width);
216 if ( tex_height > 0 ) {
217 tmp2[1] *= (1000.0 / tex_height);
219 tl -> push_back( tmp2 );
220 } else if ( size > 1 ) {
221 for ( i = 0; i < size; ++i ) {
222 Point3D texcoord = texcoords[ tex_index[i] ];
223 osg::Vec2 tmp2(texcoord[0], texcoord[1]);
224 if ( tex_width > 0 ) {
225 tmp2[0] *= (1000.0 / tex_width);
227 if ( tex_height > 0 ) {
228 tmp2[1] *= (1000.0 / tex_height);
230 tl -> push_back( tmp2 );
235 osg::Geometry* geometry = new osg::Geometry;
236 geometry->setVertexArray(vl);
237 geometry->setNormalArray(nl);
238 geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
239 geometry->setColorArray(cl);
240 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
241 geometry->setTexCoordArray(0, tl);
242 geometry->addPrimitiveSet(new osg::DrawArrays(ty, 0, vl->size()));
243 osg::Geode* geode = new osg::Geode;
244 geode->addDrawable(geometry);
246 // lookup the state record
247 geode->setStateSet(state);
248 geode->setUserData( new SGMaterialUserData(mat) );
251 if ( coverage > 0.0 ) {
252 if ( coverage < 10000.0 ) {
253 SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is "
254 << coverage << ", pushing up to 10000");
257 SGGenRandomSurfacePoints(geometry, coverage, lights );