Panda3D
|
00001 // Filename: meshDrawer.cxx 00002 // Created by: treeform (19dec08) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "meshDrawer.h" 00016 00017 #include "geomVertexFormat.h" 00018 #include "geomVertexArrayFormat.h" 00019 #include "geomVertexData.h" 00020 #include "geomVertexWriter.h" 00021 #include "geomVertexRewriter.h" 00022 #include "camera.h" 00023 #include "boundingSphere.h" 00024 #include "geomTristrips.h" 00025 #include "geomTriangles.h" 00026 #include "geom.h" 00027 #include "geomNode.h" 00028 #include "pnmPainter.h" 00029 #include "pnmBrush.h" 00030 #include "lvecBase4.h" 00031 #include "lvector3.h" 00032 #include "pandaNode.h" 00033 00034 TypeHandle MeshDrawer::_type_handle; 00035 00036 PN_stdfloat randFloat() { 00037 return ((PN_stdfloat) rand() / (PN_stdfloat) 0x7fffffff); 00038 } 00039 00040 //////////////////////////////////////////////////////////////////// 00041 // Function: MeshDrawer::generator 00042 // Access: Private 00043 // Description: Creates a system with a given budget. 00044 //////////////////////////////////////////////////////////////////// 00045 void MeshDrawer::generator(int budget) { 00046 // create enough triangles for budget: 00047 _vdata = new GeomVertexData(_root.get_name(), GeomVertexFormat::get_v3n3c4t2(), Geom::UH_static);//UH_dynamic); 00048 GeomVertexWriter *tvertex = new GeomVertexWriter(_vdata, "vertex"); 00049 GeomVertexWriter *tnormal = new GeomVertexWriter(_vdata, "normal"); 00050 GeomVertexWriter *tuv = new GeomVertexWriter(_vdata, "texcoord"); 00051 GeomVertexWriter *tcolor = new GeomVertexWriter(_vdata, "color"); 00052 _prim = new GeomTriangles(Geom::UH_static); 00053 00054 // iterate and fill _up a geom with random data so that it will 00055 // not be optimized out by panda3d system 00056 for(int i = 0; i < budget; i++) { 00057 for( int vert = 0; vert < 3; vert++) { 00058 LVector3 vec3 = LVector3(randFloat()+1000,randFloat(),randFloat())*.001; 00059 LVector4 vec4 = LVector4(1,1,1,randFloat()); 00060 LVector2 vec2 = LVector2(0,randFloat()); 00061 tvertex->add_data3(vec3); 00062 tcolor->add_data4(vec4); 00063 tuv->add_data2(vec2); 00064 tnormal->add_data3(vec3); 00065 } 00066 _prim->add_vertices(i * 3, i * 3 + 1, i * 3 + 2); 00067 } 00068 // create our node and attach it to this node path 00069 _prim->close_primitive(); 00070 _geom = new Geom(_vdata); 00071 _geom->add_primitive(_prim); 00072 _geomnode = new GeomNode("__MeshDrawer_GeomNode"); 00073 _geomnode->add_geom(_geom); 00074 _root.attach_new_node(_geomnode); 00075 _last_clear_index = budget; 00076 00077 delete tvertex; 00078 delete tnormal; 00079 delete tuv; 00080 delete tcolor; 00081 } 00082 00083 //////////////////////////////////////////////////////////////////// 00084 // Function: MeshDrawer::begin 00085 // Access: Published 00086 // Description: Pass the current camera node and the root node. 00087 // Passing the camera is required to generate 00088 // bill boards that face it. 00089 //////////////////////////////////////////////////////////////////// 00090 void MeshDrawer::begin(NodePath camera, NodePath render) { 00091 // sanity check 00092 assert(render.get_error_type() == NodePath::ET_ok); 00093 assert(camera.get_error_type() == NodePath::ET_ok); 00094 00095 // remember our arguments 00096 _camera = camera; 00097 _render = render; 00098 00099 // compute some help vectors 00100 _eyePos = camera.get_pos(); 00101 _up = _render.get_relative_vector(camera, LVector3(0, 0, 1)); 00102 _right = _render.get_relative_vector(camera, LVector3(1, 0, 0)); 00103 _b1 = - _right - _up; 00104 _b2 = _right - _up; 00105 _b3 = _right + _up; 00106 _b4 = - _right + _up; 00107 00108 // recreate our rewriters 00109 if (_vertex != NULL) delete _vertex; 00110 if (_normal != NULL) delete _normal; 00111 if (_uv != NULL) delete _uv; 00112 if (_color != NULL) delete _color; 00113 _vertex = new GeomVertexRewriter(_vdata, "vertex"); 00114 _uv = new GeomVertexRewriter(_vdata, "texcoord"); 00115 _normal = new GeomVertexRewriter(_vdata, "normal"); 00116 _color = new GeomVertexRewriter(_vdata, "color"); 00117 _dprim = _prim->decompose(); 00118 00119 // reseta our clearning indexes 00120 _start_clear_index = 0; 00121 _end_clear_index = _budget; 00122 _clear_index = _start_clear_index; 00123 00124 } 00125 00126 //////////////////////////////////////////////////////////////////// 00127 // Function: MeshDrawer::end 00128 // Access: Published 00129 // Description: Finish the drawing and clearing off the remaining 00130 // vertexes. 00131 //////////////////////////////////////////////////////////////////// 00132 void MeshDrawer::end() { 00133 00134 // clear the unused triangles at the end of the buffer 00135 for(int i = _clear_index ; i < _last_clear_index; i ++ ) { 00136 _vertex->add_data3(0,0,0); 00137 _vertex->add_data3(0,0,0); 00138 _vertex->add_data3(0,0,0); 00139 } 00140 // don't clear more then you have too 00141 _last_clear_index = _clear_index; 00142 00143 // delete the re writers 00144 delete _vertex; _vertex = NULL; 00145 delete _uv; _uv = NULL; 00146 delete _normal; _normal = NULL; 00147 delete _color; _color = NULL; 00148 00149 } 00150 00151 //////////////////////////////////////////////////////////////////// 00152 // Function: MeshDrawer::particle 00153 // Access: Published 00154 // Description: Draws a particle that is sort of like a bill board 00155 // but has an extra rotation component. 00156 // Frame contains u,v,u-size,v-size quadruple. 00157 //////////////////////////////////////////////////////////////////// 00158 void MeshDrawer::particle(const LVector3 &pos, const LVector4 &frame, PN_stdfloat size, 00159 const LVector4 &color, PN_stdfloat rotation) { 00160 00161 rotation = rotation / 57.29578; 00162 00163 LVector3 v1 = pos + _b1*size*sin(rotation) + _b2*size*cos(rotation); 00164 LVector3 v2 = pos + _b2*size*sin(rotation) + _b3*size*cos(rotation); 00165 LVector3 v3 = pos + _b3*size*sin(rotation) + _b4*size*cos(rotation); 00166 LVector3 v4 = pos + _b4*size*sin(rotation) + _b1*size*cos(rotation); 00167 00168 PN_stdfloat u = frame.get_x(); 00169 PN_stdfloat v = frame.get_y(); 00170 PN_stdfloat us = frame.get_z(); 00171 PN_stdfloat vs = frame.get_w(); 00172 00173 tri( 00174 v1, color, LVector2(u,v), 00175 v2, color, LVector2(u+us,v), 00176 v3, color, LVector2(u+us,v+vs)); 00177 tri( 00178 v3, color, LVector2(u+us,v+vs), 00179 v4, color, LVector2(u,v+vs), 00180 v1, color, LVector2(u,v)); 00181 } 00182 00183 //////////////////////////////////////////////////////////////////// 00184 // Function: MeshDrawer::blended_particle 00185 // Access: Published 00186 // Description: Works just like particle but accepts 2 frames and 00187 // a blend (from 0 to 1) component between them 00188 // Frame contains u,v,u-size,v-size quadruple. 00189 //////////////////////////////////////////////////////////////////// 00190 void MeshDrawer::blended_particle(const LVector3 &pos, const LVector4 &frame1, 00191 const LVector4 &frame2, PN_stdfloat blend, PN_stdfloat size, const LVector4 &color, PN_stdfloat rotation) { 00192 00193 LVector4 c2 = color; 00194 PN_stdfloat original_w = c2.get_w(); 00195 c2.set_w((1.f-blend)*original_w); 00196 particle(pos,frame1,size,c2,rotation); 00197 c2.set_w(blend*original_w); 00198 particle(pos,frame2,size,c2,rotation); 00199 00200 } 00201 00202 //////////////////////////////////////////////////////////////////// 00203 // Function: MeshDrawer::billboard 00204 // Access: Published 00205 // Description: Draws a billboard - particle with no rotation. 00206 // Billboards always face the camera. 00207 // Frame contains u,v,u-size,v-size quadruple. 00208 //////////////////////////////////////////////////////////////////// 00209 void MeshDrawer::billboard(const LVector3 &pos, const LVector4 &frame, PN_stdfloat size, 00210 const LVector4 &_color) { 00211 00212 LVector3 v1 = pos + _b1*size; 00213 LVector3 v2 = pos + _b2*size; 00214 LVector3 v3 = pos + _b3*size; 00215 LVector3 v4 = pos + _b4*size; 00216 00217 PN_stdfloat u = frame.get_x(); 00218 PN_stdfloat v = frame.get_y(); 00219 PN_stdfloat us = frame.get_z(); 00220 PN_stdfloat vs = frame.get_w(); 00221 00222 tri( 00223 v1, _color, LVector2(u,v), 00224 v2, _color, LVector2(u+us,v), 00225 v3, _color, LVector2(u+us,v+vs)); 00226 tri( 00227 v3, _color, LVector2(u+us,v+vs), 00228 v4, _color, LVector2(u,v+vs), 00229 v1, _color, LVector2(u,v)); 00230 } 00231 00232 00233 //////////////////////////////////////////////////////////////////// 00234 // Function: MeshDrawer::segment 00235 // Access: Published 00236 // Description: Draws a segment a line with a thickness. That has 00237 // billboarding effect. 00238 // Frame contains u,v,u-size,v-size quadruple. 00239 //////////////////////////////////////////////////////////////////// 00240 void MeshDrawer::segment(const LVector3 &start, const LVector3 &stop, const LVector4 &frame, 00241 PN_stdfloat thickness, const LVector4 &color) { 00242 link_segment(start, frame, thickness, color); 00243 link_segment(stop, frame, thickness, color); 00244 link_segment_end(frame, color); 00245 } 00246 //////////////////////////////////////////////////////////////////// 00247 // Function: MeshDrawer::cross_segment 00248 // Access: Published 00249 // Description: Draws a segment a line with a thickness. This 00250 // segment does not use the bill boarding behavior 00251 // and instead draws 2 planes in a cross. 00252 // Stars at start and ends at stop. 00253 // Frame contains u,v,u-size,v-size quadruple. 00254 //////////////////////////////////////////////////////////////////// 00255 void MeshDrawer::cross_segment(const LVector3 &start, const LVector3 &stop, const LVector4 &frame, 00256 PN_stdfloat thickness, const LVector4 &color) { 00257 00258 PN_stdfloat u = frame.get_x(); 00259 PN_stdfloat v = frame.get_y(); 00260 PN_stdfloat us = frame.get_z(); 00261 PN_stdfloat vs = frame.get_w(); 00262 00263 LVector3 v1 = start - _up*thickness; 00264 LVector3 v2 = stop - _up*thickness; 00265 LVector3 v3 = stop + _up*thickness; 00266 LVector3 v4 = start + _up*thickness; 00267 00268 tri(v1, color, LVector2(u,v), 00269 v2, color, LVector2(u+us,v), 00270 v3, color, LVector2(u+us,v+vs)); 00271 tri(v3, color, LVector2(u+us,v+vs), 00272 v4, color, LVector2(u,v+vs), 00273 v1, color, LVector2(u,v)); 00274 00275 v1 = start - _right*thickness; 00276 v2 = stop - _right*thickness; 00277 v3 = stop + _right*thickness; 00278 v4 = start + _right*thickness; 00279 00280 tri(v1, color, LVector2(u,v), 00281 v2, color, LVector2(u+us,v), 00282 v3, color, LVector2(u+us,v+vs)); 00283 tri(v3, color, LVector2(u+us,v+vs), 00284 v4, color, LVector2(u,v+vs), 00285 v1, color, LVector2(u,v)); 00286 00287 } 00288 00289 00290 00291 00292 //////////////////////////////////////////////////////////////////// 00293 // Function: MeshDrawer::uneven_segment 00294 // Access: Published 00295 // Description: Draws a segment a line with different thickness 00296 // and color on both sides. 00297 // Stars at start and ends at stop. 00298 // Frame contains u,v,u-size,v-size quadruple. 00299 //////////////////////////////////////////////////////////////////// 00300 void MeshDrawer::uneven_segment(const LVector3 &start, const LVector3 &stop, 00301 const LVector4 &frame, PN_stdfloat thickness_start, const LVector4 &color_start, 00302 PN_stdfloat thickness_stop, const LVector4 &color_stop) { 00303 00304 PN_stdfloat u = frame.get_x(); 00305 PN_stdfloat v = frame.get_y(); 00306 PN_stdfloat us = frame.get_z(); 00307 PN_stdfloat vs = frame.get_w(); 00308 00309 LVector3 v1 = start - _up*thickness_start; 00310 LVector3 v2 = stop - _up*thickness_stop; 00311 LVector3 v3 = stop + _up*thickness_stop; 00312 LVector3 v4 = start + _up*thickness_start; 00313 00314 tri(v1, color_start, LVector2(u,v), 00315 v2, color_stop, LVector2(u+us,v), 00316 v3, color_stop, LVector2(u+us,v+vs)); 00317 tri(v3, color_stop, LVector2(u+us,v+vs), 00318 v4, color_start, LVector2(u,v+vs), 00319 v1, color_start, LVector2(u,v)); 00320 00321 v1 = start - _right*thickness_start; 00322 v2 = stop - _right*thickness_stop; 00323 v3 = stop + _right*thickness_stop; 00324 v4 = start + _right*thickness_start; 00325 00326 tri(v1, color_start, LVector2(u,v), 00327 v2, color_stop, LVector2(u+us,v), 00328 v3, color_stop, LVector2(u+us,v+vs)); 00329 tri(v3, color_stop, LVector2(u+us,v+vs), 00330 v4, color_start, LVector2(u,v+vs), 00331 v1, color_start, LVector2(u,v)); 00332 } 00333 00334 //////////////////////////////////////////////////////////////////// 00335 // Function: MeshDrawer::explosion 00336 // Access: Published 00337 // Description: Draws number of particles in a sphere like emitter. 00338 // Frame contains u,v,u-size,v-size quadruple. 00339 //////////////////////////////////////////////////////////////////// 00340 void MeshDrawer::explosion( 00341 const LVector3 &pos, const LVector4 &frame, PN_stdfloat size, const LVector4 &_color, 00342 int seed, int number, PN_stdfloat distance) { 00343 srand(seed); 00344 LVector3 relative_pos; 00345 for(int i = 0; i < number; i++) { 00346 relative_pos = LVector3(randFloat()-.5f,randFloat()-.5f,randFloat()-.5f); 00347 relative_pos.normalize(); 00348 relative_pos *= randFloat()*distance; 00349 particle(relative_pos+pos,frame,size,_color,randFloat()*360.0f); 00350 } 00351 } 00352 00353 //////////////////////////////////////////////////////////////////// 00354 // Function: MeshDrawer::stream 00355 // Access: Published 00356 // Description: Draws a number of particles in a big line with a 00357 // shift dictated by the offset. 00358 // Frame contains u,v,u-size,v-size quadruple. 00359 //////////////////////////////////////////////////////////////////// 00360 void MeshDrawer::stream(const LVector3 &start, const LVector3 &stop, const LVector4 &frame, PN_stdfloat size, const LVector4 &_color, 00361 int number, PN_stdfloat offset) { 00362 00363 offset = offset-floor(offset); 00364 LVector3 relative_pos = stop; 00365 LVector3 vec = stop - start; 00366 PN_stdfloat distance = vec.length(); 00367 for(int i = 0; i < number; i++) { 00368 relative_pos = stop + vec * ((i-offset)*(distance/PN_stdfloat(number))); 00369 billboard(relative_pos,frame,size,_color); 00370 } 00371 } 00372 00373 00374 00375 //////////////////////////////////////////////////////////////////// 00376 // Function: MeshDrawer::geometry 00377 // Access: Published 00378 // Description: Draws the geometry that is inside this node path into 00379 // the MeshDrawer object. This performs a similar 00380 // functions as RigidBodyCombiner but for very 00381 // dynamic situations that share the same texture 00382 // like physcal chunks of explosions. 00383 // It can be a little slow 00384 //////////////////////////////////////////////////////////////////// 00385 void MeshDrawer::geometry(NodePath draw_node) { 00386 assert(_render.get_error_type() == NodePath::ET_ok); 00387 00388 LVector4 color = LVector4(1,1,1,1); 00389 LVector3 vec[3]; 00390 LVector2 uv[3]; 00391 00392 // process node 00393 NodePathCollection geom_collection = draw_node.find_all_matches("**/+GeomNode"); 00394 for(int i=0; i < geom_collection.get_num_paths(); i++ ) { 00395 NodePath current_node_path = geom_collection.get_path(i); 00396 PT(GeomNode) geomNode = DCAST(GeomNode, current_node_path.node()); 00397 00398 // process geom node 00399 for(int j=0; j<geomNode->get_num_geoms(); j++) { 00400 CPT(Geom) geom = geomNode->get_geom(j); 00401 CPT(GeomVertexData) v_data = geom->get_vertex_data(); 00402 GeomVertexReader *prim_vertex_reader = new GeomVertexReader(v_data, "vertex"); 00403 GeomVertexReader *prim_uv_reader = new GeomVertexReader(v_data, "texcoord"); 00404 for(int k=0; k <geom->get_num_primitives(); k++) { 00405 CPT(GeomPrimitive) prim1 = geom->get_primitive(k); 00406 CPT(GeomPrimitive) _prim = prim1->decompose(); 00407 00408 // process primitive 00409 for(int p=0; p < _prim->get_num_primitives();p++) { 00410 int s = _prim->get_primitive_start(p); 00411 int e = _prim->get_primitive_end(p); 00412 int indx_over = 0; 00413 00414 // process polygon 00415 for(int idx=s; idx<e; idx++) { 00416 int vidx = _prim->get_vertex(idx); 00417 prim_vertex_reader->set_row_unsafe(vidx); 00418 prim_uv_reader->set_row_unsafe(vidx); 00419 vec[indx_over] = _render.get_relative_point( 00420 current_node_path,prim_vertex_reader->get_data3()); 00421 uv[indx_over] = prim_uv_reader->get_data2(); 00422 indx_over++; 00423 if (indx_over > 2) break; 00424 } 00425 00426 // draw polygon 00427 tri(vec[0],color,uv[0], 00428 vec[1],color,uv[1], 00429 vec[2],color,uv[2]); 00430 } 00431 // if we are over budget just quit 00432 if( _clear_index > _end_clear_index) return; 00433 } 00434 // delete our reders 00435 delete prim_vertex_reader; 00436 delete prim_uv_reader; 00437 } 00438 } 00439 } 00440 00441 00442 00443 //////////////////////////////////////////////////////////////////// 00444 // Function: MeshDrawer::link_segment 00445 // Access: Published 00446 // Description: Stars or continues linked segment. 00447 // Control position, frame, thickness and color with 00448 // parameters. 00449 // Frame contains u,v,u-size,v-size quadruple. 00450 //////////////////////////////////////////////////////////////////// 00451 void MeshDrawer:: 00452 link_segment(const LVector3 &pos, const LVector4 &frame, 00453 PN_stdfloat thickness, const LVector4 &color) { 00454 assert(_render.get_error_type() == NodePath::ET_ok); 00455 assert(_camera.get_error_type() == NodePath::ET_ok); 00456 /* 00457 * X 00458 * ---X 00459 * ===0---X 00460 * ===0===0---X 00461 * ===0===0===O---X 00462 * ===0===0===0===End 00463 * 00464 * first call marks position X 00465 * second call moves position and promises to draw segment 00466 * it can't draw it yet because next segment might bend it 00467 * third call finally draws segment 00468 * and the chain continues till 00469 * link_segment_end to flush the linking segments is called. 00470 */ 00471 00472 // mark 1st position 00473 if(_at_start==0) { 00474 _last_pos = pos; 00475 _last_thickness = thickness; 00476 _last_color = color; 00477 _at_start=1; 00478 return; 00479 } 00480 00481 LVector3 start = _last_pos; 00482 LVector3 stop = pos; 00483 00484 LVector3 cam_start3d = _camera.get_relative_point(_render, start); 00485 LPoint2 cam_start2d = LVector2(); 00486 LVector3 cam_stop3d = _camera.get_relative_point(_render, stop); 00487 LPoint2 cam_stop2d = LVector2(); 00488 00489 PT(Camera) camera = DCAST(Camera, _camera.node()); 00490 PT(Lens) lens = camera->get_lens(); 00491 00492 lens->project(cam_start3d, cam_start2d); 00493 lens->project(cam_stop3d, cam_stop2d); 00494 00495 LVector2 dif = cam_stop2d - cam_start2d; 00496 PN_stdfloat rotation = atan2(dif.get_x(),dif.get_y()); 00497 00498 LVector3 now_v1 = start + _b1*(PN_stdfloat)(thickness*sin(rotation)) + _b2*(PN_stdfloat)(thickness*cos(rotation)); 00499 LVector3 now_v4 = start + _b4*(PN_stdfloat)(thickness*sin(rotation)) + _b1*(PN_stdfloat)(thickness*cos(rotation)); 00500 LVector3 now_v2 = stop + _b2*(PN_stdfloat)(thickness*sin(rotation)) + _b3*(PN_stdfloat)(thickness*cos(rotation)); 00501 LVector3 now_v3 = stop + _b3*(PN_stdfloat)(thickness*sin(rotation)) + _b4*(PN_stdfloat)(thickness*cos(rotation)); 00502 00503 // mark the segment we going to draw 00504 // we need to draw it when we know what the next segment looks like 00505 // because it can bend it a little 00506 if(_at_start==1) { 00507 _last_v1 = now_v1; 00508 _last_v2 = now_v2; 00509 _last_v3 = now_v3; 00510 _last_v4 = now_v4; 00511 _at_start = 2; 00512 return; 00513 } 00514 00515 // draw the last segment a little bent 00516 LVector3 v1 = _last_v1; 00517 LVector3 v2 = (_last_v2+now_v1)/2.0f; 00518 LVector3 v3 = (_last_v3+now_v4)/2.0f; 00519 LVector3 v4 = _last_v4; 00520 00521 // compute this frame 00522 PN_stdfloat u = frame.get_x(); 00523 PN_stdfloat v = frame.get_y(); 00524 PN_stdfloat us = frame.get_z(); 00525 PN_stdfloat vs = frame.get_w(); 00526 00527 tri(v1, _last_color, LVector2(u,v), 00528 v2, color, LVector2(u+us,v), 00529 v3, color, LVector2(u+us,v+vs)); 00530 tri(v3, color, LVector2(u+us,v+vs), 00531 v4, _last_color, LVector2(u,v+vs), 00532 v1, _last_color, LVector2(u,v)); 00533 00534 // save this segment 00535 _last_v1 = v2; 00536 _last_v2 = now_v2; 00537 _last_v3 = now_v3; 00538 _last_v4 = v3; 00539 00540 // make this position 00541 _last_pos = pos; 00542 _last_thickness = thickness; 00543 _last_color = color; 00544 } 00545 00546 //////////////////////////////////////////////////////////////////// 00547 // Function: MeshDrawer::link_segment_end 00548 // Access: Published 00549 // Description: Finish drawing linked segments, needs at least 00550 // two calls to link_segment before it can end 00551 // the linked segment. 00552 // Frame contains u,v,u-size,v-size quadruple. 00553 //////////////////////////////////////////////////////////////////// 00554 void MeshDrawer::link_segment_end(const LVector4 &frame, const LVector4 &color) 00555 { 00556 PN_stdfloat u = frame.get_x(); 00557 PN_stdfloat v = frame.get_y(); 00558 PN_stdfloat us = frame.get_z(); 00559 PN_stdfloat vs = frame.get_w(); 00560 00561 tri(_last_v1, _last_color, LVector2(u,v), 00562 _last_v2, color, LVector2(u+us,v), 00563 _last_v3, color, LVector2(u+us,v+vs)); 00564 tri(_last_v3, color, LVector2(u+us,v+vs), 00565 _last_v4, _last_color, LVector2(u,v+vs), 00566 _last_v1, _last_color, LVector2(u,v)); 00567 00568 _at_start = 0; 00569 }