framework2
3074 строки · 81.8 Кб
1#ifndef OF_MESH_H2#include "ofMesh.h"3#endif4
5#include "ofAppRunner.h"6#include "ofGraphicsBaseTypes.h"7#include "ofVectorMath.h"8#include "ofMath.h"9#include "ofMathConstants.h"10#include "ofLog.h"11#include "ofColor.h"12
13#include <unordered_map>14
15//--------------------------------------------------------------
16template<class V, class N, class C, class T>17ofMesh_<V,N,C,T>::ofMesh_(){18mode = OF_PRIMITIVE_TRIANGLES;19bVertsChanged = false;20bColorsChanged = false;21bNormalsChanged = false;22bTexCoordsChanged = false;23bIndicesChanged = false;24bFacesDirty = false;25useColors = true;26useTextures = true;27useNormals = true;28useIndices = true;29}
30
31
32//--------------------------------------------------------------
33template<class V, class N, class C, class T>34ofMesh_<V,N,C,T>::ofMesh_(ofPrimitiveMode mode, const std::vector<V>& verts){35bColorsChanged = false;36bNormalsChanged = false;37bTexCoordsChanged = false;38useColors = true;39useTextures = true;40useNormals = true;41useIndices = true;42setMode(mode);43addVertices(verts);44}
45
46
47//--------------------------------------------------------------
48template<class V, class N, class C, class T>49void ofMesh_<V,N,C,T>::clear(){50if(!vertices.empty()){51bVertsChanged = true;52vertices.clear();53}54if(!colors.empty()){55bColorsChanged = true;56colors.clear();57}58if(!normals.empty()){59bNormalsChanged = true;60normals.clear();61}62if(!texCoords.empty()){63bTexCoordsChanged = true;64texCoords.clear();65}66if(!indices.empty()){67bIndicesChanged = true;68indices.clear();69}70bFacesDirty = true;71}
72
73
74//--------------------------------------------------------------
75template<class V, class N, class C, class T>76bool ofMesh_<V,N,C,T>::haveVertsChanged(){77if(bVertsChanged){78bVertsChanged = false;79return true;80}else{81return false;82}83}
84
85
86
87//--------------------------------------------------------------
88template<class V, class N, class C, class T>89bool ofMesh_<V,N,C,T>::haveColorsChanged(){90if(bColorsChanged){91bColorsChanged = false;92return true;93}else{94return false;95}96}
97
98
99
100//--------------------------------------------------------------
101template<class V, class N, class C, class T>102bool ofMesh_<V,N,C,T>::haveNormalsChanged(){103if(bNormalsChanged){104bNormalsChanged = false;105return true;106}else{107return false;108}109}
110
111
112
113//--------------------------------------------------------------
114template<class V, class N, class C, class T>115bool ofMesh_<V,N,C,T>::haveTexCoordsChanged(){116if(bTexCoordsChanged){117bTexCoordsChanged = false;118return true;119}else{120return false;121}122}
123
124
125
126//--------------------------------------------------------------
127template<class V, class N, class C, class T>128bool ofMesh_<V,N,C,T>::haveIndicesChanged(){129if(bIndicesChanged){130bIndicesChanged = false;131return true;132}else{133return false;134}135}
136
137
138
139
140//--------------------------------------------------------------
141template<class V, class N, class C, class T>142bool ofMesh_<V,N,C,T>::hasVertices() const{143return !vertices.empty();144}
145
146
147
148//--------------------------------------------------------------
149template<class V, class N, class C, class T>150bool ofMesh_<V,N,C,T>::hasColors() const{151return !colors.empty();152}
153
154
155
156//--------------------------------------------------------------
157template<class V, class N, class C, class T>158bool ofMesh_<V,N,C,T>::hasNormals() const{159return !normals.empty();160}
161
162
163
164//--------------------------------------------------------------
165template<class V, class N, class C, class T>166bool ofMesh_<V,N,C,T>::hasTexCoords() const{167return !texCoords.empty();168}
169
170
171
172//--------------------------------------------------------------
173template<class V, class N, class C, class T>174bool ofMesh_<V,N,C,T>::hasIndices() const{175return !indices.empty();176}
177
178//ADDERS
179
180
181
182//--------------------------------------------------------------
183template<class V, class N, class C, class T>184void ofMesh_<V,N,C,T>::addVertex(const V& v){185vertices.push_back(v);186bVertsChanged = true;187bFacesDirty = true;188}
189
190
191
192//--------------------------------------------------------------
193template<class V, class N, class C, class T>194void ofMesh_<V,N,C,T>::addVertices(const std::vector<V>& verts){195vertices.insert(vertices.end(),verts.begin(),verts.end());196bVertsChanged = true;197bFacesDirty = true;198}
199
200
201
202//--------------------------------------------------------------
203template<class V, class N, class C, class T>204void ofMesh_<V,N,C,T>::addVertices(const V* verts, std::size_t amt){205vertices.insert(vertices.end(),verts,verts+amt);206bVertsChanged = true;207bFacesDirty = true;208}
209
210
211
212//--------------------------------------------------------------
213template<class V, class N, class C, class T>214void ofMesh_<V,N,C,T>::addColor(const C& c){215colors.push_back(c);216bColorsChanged = true;217bFacesDirty = true;218}
219
220
221
222//--------------------------------------------------------------
223template<class V, class N, class C, class T>224void ofMesh_<V,N,C,T>::addColors(const std::vector<C>& cols){225colors.insert(colors.end(),cols.begin(),cols.end());226bColorsChanged = true;227bFacesDirty = true;228}
229
230
231
232//--------------------------------------------------------------
233template<class V, class N, class C, class T>234void ofMesh_<V,N,C,T>::addColors(const C* cols, std::size_t amt){235colors.insert(colors.end(),cols,cols+amt);236bColorsChanged = true;237bFacesDirty = true;238}
239
240
241
242//--------------------------------------------------------------
243template<class V, class N, class C, class T>244void ofMesh_<V,N,C,T>::addNormal(const N& n){245normals.push_back(n);246bNormalsChanged = true;247bFacesDirty = true;248}
249
250
251
252//--------------------------------------------------------------
253template<class V, class N, class C, class T>254void ofMesh_<V,N,C,T>::addNormals(const std::vector<N>& norms){255normals.insert(normals.end(),norms.begin(),norms.end());256bNormalsChanged = true;257bFacesDirty = true;258}
259
260
261
262//--------------------------------------------------------------
263template<class V, class N, class C, class T>264void ofMesh_<V,N,C,T>::addNormals(const N* norms, std::size_t amt){265normals.insert(normals.end(),norms,norms+amt);266bNormalsChanged = true;267bFacesDirty = true;268}
269
270
271
272//--------------------------------------------------------------
273template<class V, class N, class C, class T>274void ofMesh_<V,N,C,T>::addTexCoord(const T& t){275//TODO: figure out if we add to all other arrays to match276texCoords.push_back(t);277bTexCoordsChanged = true;278bFacesDirty = true;279}
280
281
282
283//--------------------------------------------------------------
284template<class V, class N, class C, class T>285void ofMesh_<V,N,C,T>::addTexCoords(const std::vector<T>& tCoords){286texCoords.insert(texCoords.end(),tCoords.begin(),tCoords.end());287bTexCoordsChanged = true;288bFacesDirty = true;289}
290
291
292
293//--------------------------------------------------------------
294template<class V, class N, class C, class T>295void ofMesh_<V,N,C,T>::addTexCoords(const T* tCoords, std::size_t amt){296texCoords.insert(texCoords.end(),tCoords,tCoords+amt);297bTexCoordsChanged = true;298bFacesDirty = true;299}
300
301
302
303//--------------------------------------------------------------
304template<class V, class N, class C, class T>305ofIndexType ofMesh_<V,N,C,T>::getIndex(ofIndexType i) const{306return indices[i];307}
308
309
310
311//--------------------------------------------------------------
312template<class V, class N, class C, class T>313void ofMesh_<V,N,C,T>::addIndex(ofIndexType i){314indices.push_back(i);315bIndicesChanged = true;316bFacesDirty = true;317}
318
319
320
321//--------------------------------------------------------------
322template<class V, class N, class C, class T>323void ofMesh_<V,N,C,T>::addIndices(const std::vector<ofIndexType>& inds){324indices.insert(indices.end(),inds.begin(),inds.end());325bIndicesChanged = true;326bFacesDirty = true;327}
328
329
330
331//--------------------------------------------------------------
332template<class V, class N, class C, class T>333void ofMesh_<V,N,C,T>::addIndices(const ofIndexType* inds, std::size_t amt){334indices.insert(indices.end(),inds,inds+amt);335bIndicesChanged = true;336bFacesDirty = true;337}
338
339
340
341//--------------------------------------------------------------
342template<class V, class N, class C, class T>343void ofMesh_<V,N,C,T>::addTriangle(ofIndexType index1, ofIndexType index2, ofIndexType index3) {344addIndex(index1);345addIndex(index2);346addIndex(index3);347}
348
349//REMOVERS
350
351
352//--------------------------------------------------------------
353template<class V, class N, class C, class T>354void ofMesh_<V,N,C,T>::removeVertex(ofIndexType index){355if(index >= vertices.size()){356ofLogError("ofMesh") << "removeVertex(): ignoring out of range index " << index << ", number of vertices is" << vertices.size();357}else{358vertices.erase(vertices.begin() + index);359bVertsChanged = true;360bFacesDirty = true;361}362}
363
364template<class V, class N, class C, class T>365void ofMesh_<V,N,C,T>::removeVertices(ofIndexType startIndex, ofIndexType endIndex){366if(startIndex >= vertices.size() || endIndex > vertices.size()){367ofLogError("ofMesh") << "removeVertex(): ignoring out of range startIndex " << startIndex << " endIndex " << endIndex << ", number of vertices is" << vertices.size();368}else{369vertices.erase(vertices.begin() + startIndex, vertices.begin() + endIndex);370bVertsChanged = true;371bFacesDirty = true;372}373}
374
375
376
377//--------------------------------------------------------------
378template<class V, class N, class C, class T>379void ofMesh_<V,N,C,T>::removeNormal(ofIndexType index){380if(index >= normals.size()){381ofLogError("ofMesh") << "removeNormal(): ignoring out of range index " << index << ", number of normals is" << normals.size();382}else{383normals.erase(normals.begin() + index);384bNormalsChanged = true;385bFacesDirty = true;386}387}
388
389template<class V, class N, class C, class T>390void ofMesh_<V,N,C,T>::removeNormals(ofIndexType startIndex, ofIndexType endIndex){391if(startIndex >= normals.size() || endIndex > normals.size()){392ofLogError("ofMesh") << "removeNormal(): ignoring out of range beginIndex " << startIndex << " endIndex " << endIndex << ", number of normals is" << normals.size();393}else{394normals.erase(normals.begin() + startIndex, normals.begin() + endIndex);395bNormalsChanged = true;396bFacesDirty = true;397}398}
399
400
401//--------------------------------------------------------------
402template<class V, class N, class C, class T>403void ofMesh_<V,N,C,T>::removeColor(ofIndexType index){404if(index >= colors.size()){405ofLogError("ofMesh") << "removeColor(): ignoring out of range index " << index << ", number of colors is" << colors.size();406}else{407colors.erase(colors.begin() + index);408bColorsChanged = true;409bFacesDirty = true;410}411}
412
413template<class V, class N, class C, class T>414void ofMesh_<V,N,C,T>::removeColors(ofIndexType startIndex, ofIndexType endIndex){415if(startIndex >= colors.size() || endIndex > colors.size()){416ofLogError("ofMesh") << "removeColor(): ignoring out of range startIndex " << startIndex << " endIndex " << endIndex << ", number of colors is" << colors.size();417}else{418colors.erase(colors.begin() + startIndex, colors.begin() + endIndex);419bColorsChanged = true;420bFacesDirty = true;421}422}
423
424
425
426//--------------------------------------------------------------
427template<class V, class N, class C, class T>428void ofMesh_<V,N,C,T>::removeTexCoord(ofIndexType index){429if(index >= texCoords.size()){430ofLogError("ofMesh") << "removeTexCoord(): ignoring out of range index " << index << ", number of tex coords is" << texCoords.size();431}else{432texCoords.erase(texCoords.begin() + index);433bTexCoordsChanged = true;434bFacesDirty = true;435}436}
437
438template<class V, class N, class C, class T>439void ofMesh_<V,N,C,T>::removeTexCoords(ofIndexType startIndex, ofIndexType endIndex){440if(startIndex >= texCoords.size() || endIndex >= texCoords.size()){441ofLogError("ofMesh") << "removeTexCoord(): ignoring out of range startIndex " << startIndex << " endIndex " << endIndex << ", number of tex coords is" << texCoords.size();442}else{443texCoords.erase(texCoords.begin() + startIndex, texCoords.begin() + endIndex);444bTexCoordsChanged = true;445bFacesDirty = true;446}447}
448
449
450
451
452//--------------------------------------------------------------
453template<class V, class N, class C, class T>454void ofMesh_<V,N,C,T>::removeIndex(ofIndexType index){455if(index >= indices.size()){456ofLogError("ofMesh") << "removeIndex(): ignoring out of range index " << index << ", number of indices is" << indices.size();457}else{458indices.erase(indices.begin() + index);459bIndicesChanged = true;460bFacesDirty = true;461}462}
463
464template<class V, class N, class C, class T>465void ofMesh_<V,N,C,T>::removeIndices(ofIndexType startIndex, ofIndexType endIndex){466if(startIndex >= indices.size() || endIndex > indices.size()){467ofLogError("ofMesh") << "removeIndex(): ignoring out of range startIndex " << startIndex << " endIndex " << endIndex << ", number of indices is" << indices.size();;468}else{469indices.erase(indices.begin() + startIndex, indices.begin() + endIndex);470bIndicesChanged = true;471bFacesDirty = true;472}473}
474
475
476
477//GETTERS
478
479
480//--------------------------------------------------------------
481template<class V, class N, class C, class T>482ofPrimitiveMode ofMesh_<V,N,C,T>::getMode() const{483return mode;484}
485
486
487
488//--------------------------------------------------------------
489template<class V, class N, class C, class T>490V ofMesh_<V,N,C,T>::getVertex(ofIndexType i) const{491return vertices[i];492}
493
494
495
496//--------------------------------------------------------------
497template<class V, class N, class C, class T>498N ofMesh_<V,N,C,T>::getNormal(ofIndexType i) const{499return normals[i];500}
501
502
503
504//--------------------------------------------------------------
505template<class V, class N, class C, class T>506C ofMesh_<V,N,C,T>::getColor(ofIndexType i) const{507return colors[i];508}
509
510
511
512//--------------------------------------------------------------
513template<class V, class N, class C, class T>514T ofMesh_<V,N,C,T>::getTexCoord(ofIndexType i) const{515return texCoords[i];516}
517
518
519
520//--------------------------------------------------------------
521template<class V, class N, class C, class T>522std::size_t ofMesh_<V,N,C,T>::getNumVertices() const{523return vertices.size();524}
525
526
527
528//--------------------------------------------------------------
529template<class V, class N, class C, class T>530std::size_t ofMesh_<V,N,C,T>::getNumColors() const{531return colors.size();532}
533
534
535
536//--------------------------------------------------------------
537template<class V, class N, class C, class T>538std::size_t ofMesh_<V,N,C,T>::getNumNormals() const{539return normals.size();540}
541
542
543
544//--------------------------------------------------------------
545template<class V, class N, class C, class T>546std::size_t ofMesh_<V,N,C,T>::getNumTexCoords() const{547return texCoords.size();548}
549
550
551
552//--------------------------------------------------------------
553template<class V, class N, class C, class T>554std::size_t ofMesh_<V,N,C,T>::getNumIndices() const{555return indices.size();556}
557
558/*
559
560
561//--------------------------------------------------------------
562template<class V, class N, class C, class T>
563int ofPrimitive::getNumIndicesSolid(){
564return indicesSolid.size();
565}
566
567
568
569//--------------------------------------------------------------
570template<class V, class N, class C, class T>
571int ofPrimitive::getNumIndicesWire(){
572return indicesWire.size();
573}
574*/
575
576
577
578//--------------------------------------------------------------
579template<class V, class N, class C, class T>580V* ofMesh_<V,N,C,T>::getVerticesPointer(){581return vertices.data();582}
583
584
585
586//--------------------------------------------------------------
587template<class V, class N, class C, class T>588C* ofMesh_<V,N,C,T>::getColorsPointer(){589return colors.data();590}
591
592
593
594//--------------------------------------------------------------
595template<class V, class N, class C, class T>596N* ofMesh_<V,N,C,T>::getNormalsPointer(){597return normals.data();598}
599
600
601
602//--------------------------------------------------------------
603template<class V, class N, class C, class T>604T* ofMesh_<V,N,C,T>::getTexCoordsPointer(){605return texCoords.data();606}
607
608
609
610//--------------------------------------------------------------
611template<class V, class N, class C, class T>612ofIndexType* ofMesh_<V,N,C,T>::getIndexPointer(){613return indices.data();614}
615
616
617
618
619//--------------------------------------------------------------
620template<class V, class N, class C, class T>621const V* ofMesh_<V,N,C,T>::getVerticesPointer() const{622return vertices.data();623}
624
625
626
627//--------------------------------------------------------------
628template<class V, class N, class C, class T>629const C* ofMesh_<V,N,C,T>::getColorsPointer() const{630return colors.data();631}
632
633
634
635//--------------------------------------------------------------
636template<class V, class N, class C, class T>637const N* ofMesh_<V,N,C,T>::getNormalsPointer() const{638return normals.data();639}
640
641
642
643//--------------------------------------------------------------
644template<class V, class N, class C, class T>645const T* ofMesh_<V,N,C,T>::getTexCoordsPointer() const{646return texCoords.data();647}
648
649
650
651//--------------------------------------------------------------
652template<class V, class N, class C, class T>653const ofIndexType * ofMesh_<V,N,C,T>::getIndexPointer() const{654return indices.data();655}
656
657//--------------------------------------------------------------
658template<class V, class N, class C, class T>659std::vector<V> & ofMesh_<V,N,C,T>::getVertices(){660bVertsChanged = true;661bFacesDirty = true;662return vertices;663}
664
665//--------------------------------------------------------------
666template<class V, class N, class C, class T>667std::vector<C> & ofMesh_<V,N,C,T>::getColors(){668bColorsChanged = true;669bFacesDirty = true;670return colors;671}
672
673//--------------------------------------------------------------
674template<class V, class N, class C, class T>675std::vector<N> & ofMesh_<V,N,C,T>::getNormals(){676bNormalsChanged = true;677bFacesDirty = true;678return normals;679}
680
681//--------------------------------------------------------------
682template<class V, class N, class C, class T>683std::vector<T> & ofMesh_<V,N,C,T>::getTexCoords(){684bTexCoordsChanged = true;685bFacesDirty = true;686return texCoords;687}
688
689//--------------------------------------------------------------
690template<class V, class N, class C, class T>691std::vector<ofIndexType> & ofMesh_<V,N,C,T>::getIndices(){692bIndicesChanged = true;693bFacesDirty = true;694return indices;695}
696
697//--------------------------------------------------------------
698template<class V, class N, class C, class T>699const std::vector<V> & ofMesh_<V,N,C,T>::getVertices() const{700return vertices;701}
702
703//--------------------------------------------------------------
704template<class V, class N, class C, class T>705const std::vector<C> & ofMesh_<V,N,C,T>::getColors() const{706return colors;707}
708
709//--------------------------------------------------------------
710template<class V, class N, class C, class T>711const std::vector<N> & ofMesh_<V,N,C,T>::getNormals() const{712return normals;713}
714
715//--------------------------------------------------------------
716template<class V, class N, class C, class T>717const std::vector<T> & ofMesh_<V,N,C,T>::getTexCoords() const{718return texCoords;719}
720
721//--------------------------------------------------------------
722template<class V, class N, class C, class T>723const std::vector<ofIndexType> & ofMesh_<V,N,C,T>::getIndices() const{724return indices;725}
726
727/*
728
729
730//--------------------------------------------------------------
731template<class V, class N, class C, class T>
732GLuint* ofPrimitive::getSolidIndexPointer(){
733return &indicesSolid[0];
734}
735
736
737
738//--------------------------------------------------------------
739template<class V, class N, class C, class T>
740GLuint* ofPrimitive::getWireIndexPointer(){
741return &indicesWire[0];
742}
743*/
744
745/*
746
747
748//--------------------------------------------------------------
749template<class V, class N, class C, class T>
750std::vector<int>& ofPrimitive::getFace(int faceNum){
751switch(mode){
752//GL_QUADS
753indices[faceNum*4+0];
754indices[faceNum*4+1];
755indices[faceNum*4+2];
756indices[faceNum*4+3];
757
758//GL_TRIANGLES
759indices[faceNum*3+0];
760indices[faceNum*3+1];
761indices[faceNum*3+2];
762
763//GL_TRIANGLE_FAN
764// 1 element per fan
765indices[0];
766indices[faceNum+1];
767indices[faceNum+2];
768
769//GL_TRIANGLE_STRIP
770// 1 element per strip
771indices[faceNum+0];
772indices[faceNum+1];
773indices[faceNum+2];
774default:break;
775}
776}
777*/
778
779
780
781
782//--------------------------------------------------------------
783template<class V, class N, class C, class T>784V ofMesh_<V,N,C,T>::getCentroid() const {785if(vertices.size() == 0) {786ofLogWarning("ofMesh") << "getCentroid(): mesh has no vertices, returning glm::vec3(0, 0, 0)";787return glm::vec3(0, 0, 0);788}789
790V sum;791for(ofIndexType i = 0; i < vertices.size(); i++) {792sum += vertices[i];793}794sum /= vertices.size();795return sum;796}
797
798//SETTERS
799
800
801//--------------------------------------------------------------
802template<class V, class N, class C, class T>803void ofMesh_<V,N,C,T>::setMode(ofPrimitiveMode m){804bIndicesChanged = true;805mode = m;806}
807
808
809
810//--------------------------------------------------------------
811template<class V, class N, class C, class T>812void ofMesh_<V,N,C,T>::setVertex(ofIndexType index, const V& v){813vertices[index] = v;814bVertsChanged = true;815bIndicesChanged = true;816bFacesDirty = true;817}
818
819
820
821//--------------------------------------------------------------
822template<class V, class N, class C, class T>823void ofMesh_<V,N,C,T>::setNormal(ofIndexType index, const N& n){824normals[index] = n;825bNormalsChanged = true;826bFacesDirty = true;827}
828
829
830
831//--------------------------------------------------------------
832template<class V, class N, class C, class T>833void ofMesh_<V,N,C,T>::setColor(ofIndexType index, const C& c){834colors[index] = c;835bColorsChanged = true;836bFacesDirty = true;837}
838
839
840
841//--------------------------------------------------------------
842template<class V, class N, class C, class T>843void ofMesh_<V,N,C,T>::setTexCoord(ofIndexType index, const T& t){844texCoords[index] = t;845bTexCoordsChanged = true;846bFacesDirty = true;847}
848
849
850
851//--------------------------------------------------------------
852template<class V, class N, class C, class T>853void ofMesh_<V,N,C,T>::setIndex(ofIndexType index, ofIndexType val){854indices[index] = val;855bIndicesChanged = true;856bFacesDirty = true;857}
858
859
860
861//--------------------------------------------------------------
862template<class V, class N, class C, class T>863void ofMesh_<V,N,C,T>::setupIndicesAuto(){864bIndicesChanged = true;865bFacesDirty = true;866indices.resize(vertices.size());867for(ofIndexType i = 0; i < vertices.size();i++){868indices[i]=i;869}870}
871
872
873
874
875
876//--------------------------------------------------------------
877template<class V, class N, class C, class T>878void ofMesh_<V,N,C,T>::clearVertices(){879vertices.clear();880bVertsChanged=true;881}
882
883
884
885//--------------------------------------------------------------
886template<class V, class N, class C, class T>887void ofMesh_<V,N,C,T>::clearNormals(){888normals.clear();889bNormalsChanged=true;890bFacesDirty = true;891}
892
893
894
895//--------------------------------------------------------------
896template<class V, class N, class C, class T>897void ofMesh_<V,N,C,T>::clearColors(){898colors.clear();899bColorsChanged=true;900bFacesDirty = true;901}
902
903
904
905//--------------------------------------------------------------
906template<class V, class N, class C, class T>907void ofMesh_<V,N,C,T>::clearTexCoords(){908texCoords.clear();909bTexCoordsChanged=true;910bFacesDirty = true;911}
912
913
914
915//--------------------------------------------------------------
916template<class V, class N, class C, class T>917void ofMesh_<V,N,C,T>::clearIndices(){918indices.clear();919bIndicesChanged = true;920bFacesDirty = true;921}
922
923
924
925//--------------------------------------------------------------
926template<class V, class N, class C, class T>927void ofMesh_<V,N,C,T>::drawVertices() const{928draw(OF_MESH_POINTS);929}
930
931
932
933//--------------------------------------------------------------
934template<class V, class N, class C, class T>935void ofMesh_<V,N,C,T>::drawWireframe() const{936draw(OF_MESH_WIREFRAME);937}
938
939
940
941//--------------------------------------------------------------
942template<class V, class N, class C, class T>943void ofMesh_<V,N,C,T>::drawFaces() const{944draw(OF_MESH_FILL);945}
946
947
948
949//--------------------------------------------------------------
950template<class V, class N, class C, class T>951void ofMesh_<V,N,C,T>::draw() const{952draw(OF_MESH_FILL);953}
954
955
956
957//--------------------------------------------------------------
958template<class V, class N, class C, class T>959void ofMesh_<V,N,C,T>::draw(ofPolyRenderMode renderType) const{960if(getNumVertices()==0) return;961ofGetCurrentRenderer()->draw(*this,renderType,useColors,useTextures,useNormals);962}
963
964
965
966//--------------------------------------------------------------
967template<class V, class N, class C, class T>968void ofMesh_<V,N,C,T>::enableColors(){969useColors = true;970}
971
972
973
974//--------------------------------------------------------------
975template<class V, class N, class C, class T>976void ofMesh_<V,N,C,T>::enableTextures(){977useTextures = true;978}
979
980
981
982//--------------------------------------------------------------
983template<class V, class N, class C, class T>984void ofMesh_<V,N,C,T>::enableNormals(){985useNormals = true;986}
987
988
989
990//--------------------------------------------------------------
991template<class V, class N, class C, class T>992void ofMesh_<V,N,C,T>::enableIndices(){993useIndices = true;994}
995
996
997
998//--------------------------------------------------------------
999template<class V, class N, class C, class T>1000void ofMesh_<V,N,C,T>::disableColors(){1001useColors = false;1002}
1003
1004
1005
1006//--------------------------------------------------------------
1007template<class V, class N, class C, class T>1008void ofMesh_<V,N,C,T>::disableTextures(){1009useTextures = false;1010}
1011
1012
1013
1014//--------------------------------------------------------------
1015template<class V, class N, class C, class T>1016void ofMesh_<V,N,C,T>::disableNormals(){1017useNormals = false;1018}
1019
1020
1021
1022//--------------------------------------------------------------
1023template<class V, class N, class C, class T>1024void ofMesh_<V,N,C,T>::disableIndices(){1025useIndices = false;1026}
1027
1028
1029
1030//--------------------------------------------------------------
1031template<class V, class N, class C, class T>1032bool ofMesh_<V,N,C,T>::usingColors() const{1033return useColors;1034}
1035
1036
1037
1038//--------------------------------------------------------------
1039template<class V, class N, class C, class T>1040bool ofMesh_<V,N,C,T>::usingTextures() const{1041return useTextures;1042}
1043
1044
1045
1046//--------------------------------------------------------------
1047template<class V, class N, class C, class T>1048bool ofMesh_<V,N,C,T>::usingNormals() const{1049return useNormals;1050}
1051
1052
1053
1054//--------------------------------------------------------------
1055template<class V, class N, class C, class T>1056bool ofMesh_<V,N,C,T>::usingIndices() const{1057return useIndices;1058}
1059
1060
1061
1062
1063//--------------------------------------------------------------
1064template<class V, class N, class C, class T>1065void ofMesh_<V,N,C,T>::append(const ofMesh_<V,N,C,T> & mesh){1066ofIndexType prevNumVertices = static_cast<ofIndexType>(vertices.size());1067if(mesh.getNumVertices()){1068vertices.insert(vertices.end(),mesh.getVertices().begin(),mesh.getVertices().end());1069}1070if(mesh.getNumTexCoords()){1071texCoords.insert(texCoords.end(),mesh.getTexCoords().begin(),mesh.getTexCoords().end());1072}1073if(mesh.getNumColors()){1074colors.insert(colors.end(),mesh.getColors().begin(),mesh.getColors().end());1075}1076if(mesh.getNumNormals()){1077normals.insert(normals.end(),mesh.getNormals().begin(),mesh.getNormals().end());1078}1079if(mesh.getNumIndices()){1080for(auto index: mesh.getIndices()){1081indices.push_back(index+prevNumVertices);1082}1083}1084}
1085
1086
1087
1088
1089//--------------------------------------------------------------
1090template<class V, class N, class C, class T>1091void ofMesh_<V,N,C,T>::load(const of::filesystem::path& path){1092ofFile is = {path, ofFile::ReadOnly};1093auto & data = *this;1094
1095std::string error;1096ofBuffer buffer(is);1097auto backup = data;1098
1099int orderVertices=-1;1100int orderIndices=-1;1101
1102ofIndexType vertexCoordsFound=0;1103ofIndexType colorCompsFound=0;1104ofIndexType texCoordsFound=0;1105ofIndexType normalsCoordsFound=0;1106
1107ofIndexType currentVertex = 0;1108ofIndexType currentFace = 0;1109
1110bool colorTypeIsUChar = false; /// flag to distinguish between uchar (more common) and float (less common) color format in ply file1111
1112enum State{1113Header,1114VertexDef,1115FaceDef,1116Vertices,1117Normals,1118Faces
1119};1120
1121
1122enum Attribute {1123Position,1124Color,1125Normal,1126TexCoord,1127};1128
1129std::vector<Attribute> meshDefinition;1130
1131data.clear();1132State state = Header;1133
1134int lineNum = 0;1135ofBuffer::Lines lines = buffer.getLines();1136ofBuffer::Line line = lines.begin();1137lineNum++;1138if(*line!="ply"){1139error = "wrong format, expecting 'ply'";1140goto clean;1141}1142
1143line++;1144lineNum++;1145if(*line!="format ascii 1.0"){1146error = "wrong format, expecting 'format ascii 1.0'";1147goto clean;1148}1149
1150for(;line != lines.end(); ++line){1151lineNum++;1152std::string lineStr = *line;1153if(lineStr.find("comment")==0 || lineStr.empty()){1154continue;1155}1156
1157if((state==Header || state==FaceDef) && lineStr.find("element vertex")==0){1158state = VertexDef;1159orderVertices = std::max(orderIndices, 0)+1;1160data.getVertices().resize(ofTo<size_t>(lineStr.substr(15)));1161continue;1162}1163
1164if((state==Header || state==VertexDef) && lineStr.find("element face")==0){1165state = FaceDef;1166orderIndices = std::max(orderVertices, 0)+1;1167data.getIndices().resize(ofTo<size_t>(lineStr.substr(13))*3);1168continue;1169}1170
1171if(state==VertexDef && (lineStr.find("property float x")==0 || lineStr.find("property float y")==0 || lineStr.find("property float z")==01172|| lineStr.find("property double x")==0 || lineStr.find("property double y")==0 || lineStr.find("property double z")==0)){1173meshDefinition.push_back(Position);1174vertexCoordsFound++;1175continue;1176}1177
1178if(state==VertexDef && (lineStr.find("property float r")==0 || lineStr.find("property float g")==0 || lineStr.find("property float b")==0 || lineStr.find("property float a")==0)){1179colorCompsFound++;1180meshDefinition.push_back(Color);1181data.getColors().resize(data.getVertices().size());1182continue;1183}1184
1185if(state==VertexDef && (lineStr.find("property uchar red")==0 || lineStr.find("property uchar green")==0 || lineStr.find("property uchar blue")==0 || lineStr.find("property uchar alpha")==0)){1186colorTypeIsUChar = true;1187colorCompsFound++;1188meshDefinition.push_back(Color);1189data.getColors().resize(data.getVertices().size());1190continue;1191}1192
1193if(state==VertexDef && (lineStr.find("property float u")==0 || lineStr.find("property float v")==0|| lineStr.find("property float s")==0 || lineStr.find("property float t")==0)){1194texCoordsFound++;1195meshDefinition.push_back(TexCoord);1196data.getTexCoords().resize(data.getVertices().size());1197continue;1198}1199
1200if(state==VertexDef && (lineStr.find("property float nx")==0 || lineStr.find("property float ny")==0 || lineStr.find("property float nz")==0)){1201normalsCoordsFound++;1202meshDefinition.push_back(Normal);1203if (normalsCoordsFound==3) data.getNormals().resize(data.getVertices().size());1204continue;1205}1206
1207if(state==FaceDef && lineStr.find("property list")!=0 && lineStr!="end_header"){1208error = "wrong face definition";1209goto clean;1210}1211
1212if(lineStr=="end_header"){1213if(data.hasColors() && colorCompsFound!=3 && colorCompsFound!=4){1214error = "data has color coordiantes but not correct number of components. Found " + ofToString(colorCompsFound) + " expecting 3 or 4";1215goto clean;1216}1217if(data.hasNormals() && normalsCoordsFound!=3){1218error = "data has normal coordiantes but not correct number of components. Found " + ofToString(normalsCoordsFound) + " expecting 3";1219goto clean;1220}1221if(!data.hasVertices()){1222ofLogWarning("ofMesh") << "load(): mesh loaded from \"" << path << "\" has no vertices";1223}1224if(orderVertices==-1) orderVertices=9999;1225if(orderIndices==-1) orderIndices=9999;1226
1227if(orderVertices < orderIndices){1228state = Vertices;1229}else {1230state = Faces;1231}1232continue;1233}1234
1235if(state==Vertices){1236if(data.getNumVertices()<=currentVertex){1237error = "found more vertices: " + ofToString(currentVertex+1) + " than specified in header: " + ofToString(data.getNumVertices());1238goto clean;1239}1240std::stringstream sline(lineStr);1241
1242// read in a line of vertex elements1243// and split it into attributes,1244// based attribute order specified in file header1245ofIndexType vAttr = 0;1246ofIndexType nAttr = 0;1247ofIndexType tAttr = 0;1248ofIndexType cAttr = 0;1249for(auto s:meshDefinition){1250switch (s) {1251case Position:1252sline >> *(&data.getVertices()[currentVertex].x + (vAttr++)%vertexCoordsFound);1253break;1254case Color:1255if (colorTypeIsUChar){1256int c = 0;1257sline >> c;1258*(&data.getColors()[currentVertex].r + (cAttr++)%colorCompsFound) = c/255.f;1259} else {1260sline >> *(&data.getColors()[currentVertex].r + (cAttr++)%colorCompsFound);1261}1262break;1263case Normal:1264sline >> *(&data.getNormals()[currentVertex].x + (nAttr++)%normalsCoordsFound);1265break;1266case TexCoord:1267sline >> *(&data.getTexCoords()[currentVertex].x + (tAttr++)%texCoordsFound);1268break;1269default:1270break;1271}1272}1273if (vAttr != vertexCoordsFound || cAttr!= colorCompsFound || nAttr!=normalsCoordsFound || tAttr!=texCoordsFound){1274error = "attribute data does not match definition in header";1275goto clean;1276}1277
1278currentVertex++;1279if(currentVertex==data.getNumVertices()){1280if(orderVertices<orderIndices){1281state = Faces;1282}else{1283state = Vertices;1284}1285}1286continue;1287}1288
1289if(state==Faces){1290if(data.getNumIndices()/3<currentFace){1291error = "found more faces than specified in header";1292goto clean;1293}1294std::stringstream sline(lineStr);1295int numV;1296sline >> numV;1297if(numV!=3){1298error = "face not a triangle";1299goto clean;1300}1301ofIndexType i;1302sline >> i;1303data.getIndices()[currentFace*3] = i;1304sline >> i;1305data.getIndices()[currentFace*3+1] = i;1306sline >> i;1307data.getIndices()[currentFace*3+2] = i;1308
1309currentFace++;1310if(currentFace==data.getNumIndices()/3){1311if(orderVertices<orderIndices){1312state = Vertices;1313}else{1314state = Faces;1315}1316}1317continue;1318}1319}1320
1321
1322return;1323clean:1324ofLogError("ofMesh") << "load(): " << lineNum << ":" << error;1325ofLogError("ofMesh") << "load(): \"" << *line << "\"";1326data = backup;1327}
1328
1329//--------------------------------------------------------------
1330template<class V, class N, class C, class T>1331void ofMesh_<V,N,C,T>::save(const of::filesystem::path& path, bool useBinary) const{1332ofFile os(path, ofFile::WriteOnly);1333const auto & data = *this;1334
1335os << "ply" << std::endl;1336if(useBinary) {1337os << "format binary_little_endian 1.0" << std::endl;1338} else {1339os << "format ascii 1.0" << std::endl;1340}1341
1342if(data.getNumVertices()){1343os << "element vertex " << data.getNumVertices() << std::endl;1344os << "property float x" << std::endl;1345os << "property float y" << std::endl;1346os << "property float z" << std::endl;1347if(data.getNumColors()){1348os << "property uchar red" << std::endl;1349os << "property uchar green" << std::endl;1350os << "property uchar blue" << std::endl;1351os << "property uchar alpha" << std::endl;1352}1353if(data.getNumTexCoords()){1354os << "property float u" << std::endl;1355os << "property float v" << std::endl;1356}1357if(data.getNumNormals()){1358os << "property float nx" << std::endl;1359os << "property float ny" << std::endl;1360os << "property float nz" << std::endl;1361}1362}1363
1364uint8_t faceSize = 3;1365if(data.getNumIndices()){1366os << "element face " << data.getNumIndices() / faceSize << std::endl;1367os << "property list uchar int vertex_indices" << std::endl;1368} else if(data.getMode() == OF_PRIMITIVE_TRIANGLES) {1369os << "element face " << data.getNumVertices() / faceSize << std::endl;1370os << "property list uchar int vertex_indices" << std::endl;1371} else if(data.getMode() == OF_PRIMITIVE_TRIANGLE_STRIP && data.getNumVertices() >= 4) {1372os << "element face " << data.getNumVertices() - 2 << std::endl;1373os << "property list uchar int vertex_indices" << std::endl;1374}1375
1376os << "end_header" << std::endl;1377
1378for(std::size_t i = 0; i < data.getNumVertices(); i++){1379if(useBinary) {1380os.write((char*) &data.getVertices()[i], sizeof(V));1381} else {1382os << data.getVertex(i).x << " " << data.getVertex(i).y << " " << data.getVertex(i).z;1383}1384if(data.getNumColors()){1385// VCG lib / MeshLab don't support float colors, so we have to cast1386ofColor cur = data.getColors()[i];1387if(useBinary) {1388os.write((char*) &cur, sizeof(ofColor));1389} else {1390os << " " << (int) cur.r << " " << (int) cur.g << " " << (int) cur.b << " " << (int) cur.a;1391}1392}1393if(data.getNumTexCoords()){1394if(useBinary) {1395os.write((char*) &data.getTexCoords()[i], sizeof(T));1396} else {1397os << " " << data.getTexCoord(i).x << " " << data.getTexCoord(i).y;1398}1399}1400if(data.getNumNormals()){1401if(useBinary) {1402os.write((char*) &data.getNormals()[i], sizeof(V));1403} else {1404os << " " << data.getNormal(i).x << " " << data.getNormal(i).y << " " << data.getNormal(i).z;1405}1406}1407if(!useBinary) {1408os << std::endl;1409}1410}1411
1412if(data.getNumIndices()) {1413for(uint32_t i = 0; i < data.getNumIndices(); i += faceSize) {1414if(useBinary) {1415os.write((char*) &faceSize, sizeof(unsigned char));1416os.write((char*)&data.getIndices()[i], faceSize);1417} else {1418os << (std::size_t) faceSize << " " << data.getIndex(i) << " " << data.getIndex(i+1) << " " << data.getIndex(i+2) << std::endl;1419}1420}1421} else if(data.getMode() == OF_PRIMITIVE_TRIANGLES) {1422for(uint32_t i = 0; i < data.getNumVertices(); i += faceSize) {1423uint32_t indices[] = {i, i + 1, i + 2};1424if(useBinary) {1425os.write((char*) &faceSize, sizeof(unsigned char));1426os.write((char*) indices, sizeof(indices));1427} else {1428os << (std::size_t) faceSize << " " << indices[0] << " " << indices[1] << " " << indices[2] << std::endl;1429}1430}1431} else if(data.getMode() == OF_PRIMITIVE_TRIANGLE_STRIP && data.getNumVertices() >= 4) {1432for(uint32_t i = 0; i < data.getNumVertices() - 2; i += 2) {1433uint32_t indices1[] = {i, i + 1, i + 2};1434uint32_t indices2[] = {i + 1, i + 3, i + 2};1435if(useBinary) {1436os.write((char*) &faceSize, sizeof(unsigned char));1437os.write((char*) indices1, sizeof(indices1));1438os.write((char*) &faceSize, sizeof(unsigned char));1439os.write((char*) indices2, sizeof(indices2));1440} else {1441os << (std::size_t) faceSize << " " << indices1[0] << " " << indices1[1] << " " << indices1[2] << std::endl;1442os << (std::size_t) faceSize << " " << indices2[0] << " " << indices2[1] << " " << indices2[2] << std::endl;1443}1444}1445}1446
1447//TODO: add index generation for other OF_PRIMITIVE cases1448}
1449
1450
1451//--------------------------------------------------------------
1452template<class V, class N, class C, class T>1453void ofMesh_<V,N,C,T>::setColorForIndices( ofIndexType startIndex, ofIndexType endIndex, C color ) {1454if(!hasColors()) {1455// no colors for vertices, so we must set them here //1456getColors().resize( getNumVertices() );1457}1458
1459for(ofIndexType i = startIndex; i < endIndex; i++) {1460setColor( getIndex(i), color);1461}1462}
1463
1464
1465//--------------------------------------------------------------
1466template<class V, class N, class C, class T>1467ofMesh_<V,N,C,T> ofMesh_<V,N,C,T>::getMeshForIndices( ofIndexType startIndex, ofIndexType endIndex ) const {1468ofIndexType startVertIndex = 0;1469ofIndexType endVertIndex = 0;1470
1471if(startIndex >= getNumIndices() ) {1472startVertIndex = 0;1473} else {1474startVertIndex = getIndex( startIndex );1475}1476
1477if(endIndex >= getNumIndices() ) {1478// set to the total, because the vector assign does not include the last element //1479endVertIndex = getNumVertices();1480} else {1481endVertIndex = getIndex( endIndex );1482}1483return getMeshForIndices(startIndex, endIndex, startVertIndex, endVertIndex );1484}
1485
1486
1487//--------------------------------------------------------------
1488template<class V, class N, class C, class T>1489ofMesh_<V,N,C,T> ofMesh_<V,N,C,T>::getMeshForIndices( ofIndexType startIndex, ofIndexType endIndex, ofIndexType startVertIndex, ofIndexType endVertIndex ) const{1490
1491ofMesh_<V,N,C,T> mesh;1492mesh.setMode( getMode() );1493
1494mesh.getVertices().assign( getVertices().begin()+startVertIndex, getVertices().begin()+endVertIndex );1495
1496if( hasColors() ) {1497std::vector<ofFloatColor> colors;1498mesh.getColors().assign( getColors().begin()+startVertIndex, getColors().begin()+endVertIndex );1499if( usingColors()) mesh.enableColors();1500else mesh.disableColors();1501}1502
1503if( hasTexCoords() ) {1504mesh.getTexCoords().assign( getTexCoords().begin()+startVertIndex, getTexCoords().begin()+endVertIndex );1505if( usingTextures() ) mesh.enableTextures();1506else mesh.disableTextures();1507}1508
1509if( hasNormals() ) {1510mesh.getNormals().assign( getNormals().begin()+startVertIndex, getNormals().begin()+endVertIndex );1511if( usingNormals() ) mesh.enableNormals();1512else mesh.disableNormals();1513}1514
1515ofIndexType offsetIndex = getIndex(startIndex);1516bool bFoundLessThanZero = false;1517for(ofIndexType i = startIndex; i < endIndex; i++) {1518ofIndexType index;1519if(getIndex(i)<offsetIndex){1520index = 0;1521bFoundLessThanZero = true;1522}else{1523index = getIndex(i) - offsetIndex;1524}1525mesh.addIndex( index );1526}1527
1528if(bFoundLessThanZero) {1529ofLogWarning( "ofMesh :: getMeshForIndices : found some indices less than 0, setting them to 0" );1530}1531
1532return mesh;1533}
1534
1535
1536//--------------------------------------------------------------
1537template<class V, class N, class C, class T>1538void ofMesh_<V,N,C,T>::mergeDuplicateVertices() {1539
1540std::vector<V> verts = getVertices();1541std::vector<ofIndexType> indices = getIndices();1542
1543//get indexes to share single point - TODO: try j < i1544for(ofIndexType i = 0; i < indices.size(); i++) {1545for(ofIndexType j = 0; j < indices.size(); j++ ) {1546if(i==j) continue;1547
1548ofIndexType i1 = indices[i];1549ofIndexType i2 = indices[j];1550const V & v1 = verts[ i1 ];1551const V & v2 = verts[ i2 ];1552
1553if( v1 == v2 && i1 != i2) {1554indices[j] = i1;1555break;1556}1557}1558}1559
1560//indices array now has list of unique points we need1561//but we need to delete the old points we're not using and that means the index values will change1562//so we are going to create a new list of points and new indexes - we will use a map to map old index values to the new ones1563std::vector <V> newPoints;1564std::vector <ofIndexType> newIndexes;1565std::unordered_map <ofIndexType, bool> ptCreated;1566std::unordered_map <ofIndexType, ofIndexType> oldIndexNewIndex;1567
1568std::vector<ofFloatColor> newColors;1569std::vector<ofFloatColor>& colors = getColors();1570std::vector<T> newTCoords;1571std::vector<T>& tcoords = getTexCoords();1572std::vector<N> newNormals;1573std::vector<N>& normals = getNormals();1574
1575for(ofIndexType i = 0; i < indices.size(); i++){1576ptCreated[i] = false;1577}1578
1579for(ofIndexType i = 0; i < indices.size(); i++){1580ofIndexType index = indices[i];1581const auto & p = verts[ index ];1582
1583if( ptCreated[index] == false ){1584oldIndexNewIndex[index] = newPoints.size();1585newPoints.push_back( p );1586if(hasColors()) {1587newColors.push_back(colors[index]);1588}1589if(hasTexCoords()) {1590newTCoords.push_back(tcoords[index]);1591}1592if(hasNormals()) {1593newNormals.push_back(normals[index]);1594}1595
1596ptCreated[index] = true;1597}1598
1599//ofLogNotice("ofMesh") << "[" << i << "]: old " << index << " --> " << oldIndexNewIndex[index];1600newIndexes.push_back( oldIndexNewIndex[index] );1601}1602
1603verts.clear();1604verts = newPoints;1605
1606indices.clear();1607indices = newIndexes;1608
1609clearIndices();1610addIndices(indices);1611clearVertices();1612addVertices( verts );1613
1614if(hasColors()) {1615clearColors();1616addColors( newColors );1617}1618
1619if(hasTexCoords()) {1620clearTexCoords();1621addTexCoords( newTCoords );1622}1623
1624if(hasNormals()) {1625clearNormals();1626addNormals( newNormals );1627}1628
1629}
1630
1631
1632//--------------------------------------------------------------
1633template<class V, class N, class C, class T>1634ofMeshFace_<V,N,C,T> ofMesh_<V,N,C,T>::getFace(ofIndexType faceId) const{1635const std::vector<ofMeshFace_<V,N,C,T>> & faces = getUniqueFaces();1636if(faces.size()>faceId){1637return faces[faceId];1638}else{1639ofLogError() << "couldn't find face " << faceId;1640return ofMeshFace_<V,N,C,T>();1641}1642}
1643
1644
1645//--------------------------------------------------------------
1646template<class V, class N, class C, class T>1647const std::vector<ofMeshFace_<V,N,C,T>> & ofMesh_<V,N,C,T>::getUniqueFaces() const{1648if(bFacesDirty){1649// if we are doing triangles, we have to use a vert and normal for each triangle1650// that way we can calculate face normals and use getFaceNormal();1651faces.resize( indices.size()/3 );1652
1653int index = 0;1654int triindex = 0;1655
1656bool bHasColors = hasColors();1657bool bHasNormals = hasNormals();1658bool bHasTexcoords = hasTexCoords();1659
1660if( getMode() == OF_PRIMITIVE_TRIANGLES) {1661for(std::size_t j = 0; j < indices.size(); j += 3) {1662ofMeshFace_<V,N,C,T> & tri = faces[triindex];1663for(std::size_t k = 0; k < 3; k++) {1664index = indices[j+k];1665tri.setVertex( k, vertices[index] );1666if(bHasNormals)1667tri.setNormal(k, normals[index] );1668if(bHasTexcoords)1669tri.setTexCoord(k, texCoords[index] );1670if(bHasColors)1671tri.setColor(k, colors[index] );1672}1673triindex++;1674}1675
1676} else {1677ofLogWarning("ofMesh") << "getUniqueFaces(): only works with primitive mode OF_PRIMITIVE_TRIANGLES";1678}1679
1680bFacesDirty = false;1681}1682
1683return faces;1684
1685}
1686
1687
1688//--------------------------------------------------------------
1689template<class V, class N, class C, class T>1690std::vector<N> ofMesh_<V,N,C,T>::getFaceNormals( bool perVertex ) const{1691// default for ofPrimitiveBase is vertex normals //1692std::vector<N> faceNormals;1693
1694if( hasVertices() ) {1695if(vertices.size() > 3 && indices.size() > 3) {1696if(perVertex){1697faceNormals.resize(indices.size()*3);1698}else{1699faceNormals.resize(indices.size());1700}1701ofMeshFace_<V,N,C,T> face;1702N n;1703for(ofIndexType i = 0; i < indices.size(); i+=3) {1704face.setVertex( 0, vertices[indices[i+0]] );1705face.setVertex( 1, vertices[indices[i+1]] );1706face.setVertex( 2, vertices[indices[i+2]] );1707
1708n = face.getFaceNormal();1709
1710faceNormals[i]=n;1711if(perVertex) {1712faceNormals[i+1]=n;1713faceNormals[i+2]=n;1714}1715}1716}1717}1718
1719return faceNormals;1720}
1721
1722
1723//--------------------------------------------------------------
1724template<class V, class N, class C, class T>1725void ofMesh_<V,N,C,T>::setFromTriangles( const std::vector<ofMeshFace_<V,N,C,T>>& tris, bool bUseFaceNormal ) {1726if(tris.empty()) {1727ofLogWarning("ofMesh") << "setFromTriangles(): ignoring empty tris vector";1728return;1729}1730
1731typename std::vector<ofMeshFace_<V,N,C,T>>::const_iterator it;1732
1733vertices.resize(tris.size()*3 );1734it = tris.begin();1735// if the first tri has data, assume the rest do as well //1736if(it->hasNormals()){1737normals.resize(tris.size()*3);1738}else{1739normals.clear();1740}1741if(it->hasColors()){1742colors.resize(tris.size()*3);1743}else{1744colors.clear();1745}1746if(it->hasTexcoords()){1747texCoords.resize(tris.size()*3);1748}else{1749texCoords.clear();1750}1751
1752int i = 0;1753for(it = tris.begin(); it != tris.end(); it++) {1754for(std::size_t k = 0; k < 3; k++) {1755vertices[i] = it->getVertex(k);1756if(it->hasTexcoords())1757texCoords[i] = it->getTexCoord(k);1758if(it->hasColors())1759colors[i] = it->getColor(k);1760if(bUseFaceNormal)1761normals[i] = it->getFaceNormal();1762else if(it->hasNormals())1763normals[i] = it->getNormal(k);1764i++;1765}1766}1767
1768setupIndicesAuto();1769bVertsChanged = true;1770bIndicesChanged = true;1771bNormalsChanged = true;1772bColorsChanged = true;1773bTexCoordsChanged = true;1774
1775bFacesDirty = false;1776faces = tris;1777}
1778
1779
1780//--------------------------------------------------------------
1781template<class V, class N, class C, class T>1782void ofMesh_<V,N,C,T>::smoothNormals( float angle ) {1783
1784if( getMode() == OF_PRIMITIVE_TRIANGLES) {1785std::vector<ofMeshFace_<V,N,C,T>> triangles = getUniqueFaces();1786std::vector<V> verts;1787for(ofIndexType i = 0; i < triangles.size(); i++) {1788for(ofIndexType j = 0; j < 3; j++) {1789verts.push_back( triangles[i].getVertex(j) );1790}1791}1792
1793std::unordered_map<int, int> removeIds;1794
1795float epsilon = .01f;1796for(ofIndexType i = 0; i < verts.size()-1; i++) {1797for(ofIndexType j = i+1; j < verts.size(); j++) {1798if(i != j) {1799const auto& v1 = toGlm(verts[i]);1800const auto& v2 = toGlm(verts[j]);1801if( glm::distance(v1, v2) <= epsilon ) {1802// average the location //1803verts[i] = (v1+v2)/2.f;1804verts[j] = verts[i];1805removeIds[j] = 1;1806}1807}1808}1809}1810
1811// string of vertex in 3d space to triangle index //1812std::unordered_map<std::string, std::vector<int> > vertHash;1813
1814//ofLogNotice("ofMesh") << "smoothNormals(): num verts = " << verts.size() << " tris size = " << triangles.size();1815
1816std::string xStr, yStr, zStr;1817
1818for(ofIndexType i = 0; i < verts.size(); i++ ) {1819xStr = "x"+ofToString(verts[i].x==-0?0:verts[i].x);1820yStr = "y"+ofToString(verts[i].y==-0?0:verts[i].y);1821zStr = "z"+ofToString(verts[i].z==-0?0:verts[i].z);1822std::string vstring = xStr+yStr+zStr;1823if(vertHash.find(vstring) == vertHash.end()) {1824for(ofIndexType j = 0; j < triangles.size(); j++) {1825for(ofIndexType k = 0; k < 3; k++) {1826if(verts[i].x == triangles[j].getVertex(k).x) {1827if(verts[i].y == triangles[j].getVertex(k).y) {1828if(verts[i].z == triangles[j].getVertex(k).z) {1829vertHash[vstring].push_back( j );1830}1831}1832}1833}1834}1835}1836}1837
1838// for( std::unordered_map<std::string, std::vector<int> >::iterator it = vertHash.begin(); it != vertHash.end(); ++it) {
1839// //for( std::unordered_map<std::string, int >::iterator it = vertHash.begin(); it != vertHash.end(); ++it) {
1840// ofLogNotice("ofMesh") << "smoothNormals(): " << it->first << " num = " << it->second.size();
1841// }
1842
1843V vert;1844N normal;1845float angleCos = cos(ofDegToRad(angle));1846float numNormals=0;1847
1848for(ofIndexType j = 0; j < triangles.size(); j++) {1849for(ofIndexType k = 0; k < 3; k++) {1850vert = triangles[j].getVertex(k);1851xStr = "x"+ofToString(vert.x==-0?0:vert.x);1852yStr = "y"+ofToString(vert.y==-0?0:vert.y);1853zStr = "z"+ofToString(vert.z==-0?0:vert.z);1854
1855std::string vstring = xStr+yStr+zStr;1856numNormals=0;1857normal = {0.f,0.f,0.f};1858if(vertHash.find(vstring) != vertHash.end()) {1859for(ofIndexType i = 0; i < vertHash[vstring].size(); i++) {1860auto f1 = triangles[j].getFaceNormal();1861auto f2 = triangles[vertHash[vstring][i]].getFaceNormal();1862if(glm::dot(toGlm(f1), toGlm(f2)) >= angleCos ) {1863normal += f2;1864numNormals+=1.f;1865}1866}1867//normal /= (float)vertHash[vstring].size();1868normal /= numNormals;1869
1870triangles[j].setNormal(k, normal);1871}1872}1873}1874
1875//ofLogNotice("ofMesh") << "smoothNormals(): setting from triangles ";1876setFromTriangles( triangles );1877
1878}1879}
1880
1881//--------------------------------------------------------------
1882template<class V, class N, class C, class T>1883void ofMesh_<V,N,C,T>::flatNormals() {1884if( getMode() == OF_PRIMITIVE_TRIANGLES) {1885
1886// get copy original mesh data1887auto indices = getIndices();1888auto verts = getVertices();1889auto texCoords = getTexCoords();1890auto colors = getColors();1891
1892// remove all data to start from scratch1893clear();1894
1895// add mesh data back, duplicating vertices and recalculating normals1896N normal;1897for(ofIndexType i = 0; i < indices.size(); i++) {1898ofIndexType indexCurr = indices[i];1899
1900if(i % 3 == 0) {1901ofIndexType indexNext1 = indices[i + 1];1902ofIndexType indexNext2 = indices[i + 2];1903auto e1 = verts[indexCurr] - verts[indexNext1];1904auto e2 = verts[indexNext2] - verts[indexNext1];1905normal = glm::normalize(glm::cross(e1, e2));1906}1907
1908addIndex(i);1909addNormal(normal);1910
1911if(indexCurr < texCoords.size()) {1912addTexCoord(texCoords[indexCurr]);1913}1914
1915if(indexCurr < verts.size()) {1916addVertex(verts[indexCurr]);1917}1918
1919if(indexCurr < colors.size()) {1920addColor(colors[indexCurr]);1921}1922}1923}1924}
1925
1926// PLANE MESH //
1927
1928
1929//--------------------------------------------------------------
1930template<class V, class N, class C, class T>1931ofMesh_<V,N,C,T> ofMesh_<V,N,C,T>::plane(float width, float height, int columns, int rows, ofPrimitiveMode mode ) {1932ofMesh_<V,N,C,T> mesh;1933
1934if(mode != OF_PRIMITIVE_TRIANGLE_STRIP && mode != OF_PRIMITIVE_TRIANGLES) {1935ofLogWarning("ofMesh") << "ofGetPlaneMesh(): primtive mode " << mode << " not supported, setting to OF_PRIMITIVE_TRIANGLES";1936mode = OF_PRIMITIVE_TRIANGLES;1937}1938
1939mesh.setMode(mode);1940
1941V vert;1942N normal(0, 0, 1); // always facing forward //1943T texcoord;1944
1945// the origin of the plane is at the center //1946float halfW = width * 0.5f;1947float halfH = height * 0.5f;1948
1949// add the vertexes //1950for(int iy = 0; iy != rows; iy++) {1951for(int ix = 0; ix != columns; ix++) {1952
1953// normalized tex coords //1954texcoord.x = ((float)ix/((float)columns-1));1955texcoord.y = 1.f - ((float)iy/((float)rows-1));1956
1957vert.x = texcoord.x * width - halfW;1958vert.y = -(texcoord.y-1) * height - halfH;1959
1960mesh.addVertex(vert);1961mesh.addTexCoord(texcoord);1962mesh.addNormal(normal);1963}1964}1965if(mode == OF_PRIMITIVE_TRIANGLE_STRIP) {1966for(int y = 0; y < rows-1; y++) {1967// even rows //1968if((y&1)==0) {1969for(int x = 0; x < columns; x++) {1970mesh.addIndex( (y) * columns + x );1971mesh.addIndex( (y+1) * columns + x);1972}1973} else {1974for(int x = columns-1; x >0; x--) {1975mesh.addIndex( (y+1) * columns + x );1976mesh.addIndex( y * columns + x-1 );1977}1978}1979}1980
1981if(rows%2!=0) mesh.addIndex(mesh.getNumVertices()-columns);1982} else {1983// Triangles //1984for(int y = 0; y < rows-1; y++) {1985for(int x = 0; x < columns-1; x++) {1986// first triangle //1987mesh.addIndex((y)*columns + x);1988mesh.addIndex((y)*columns + x+1);1989mesh.addIndex((y+1)*columns + x);1990
1991// second triangle //1992mesh.addIndex((y)*columns + x+1);1993mesh.addIndex((y+1)*columns + x+1);1994mesh.addIndex((y+1)*columns + x);1995}1996}1997}1998
1999return mesh;2000}
2001
2002
2003
2004//--------------------------------------------------------------
2005template<class V, class N, class C, class T>2006ofMesh_<V,N,C,T> ofMesh_<V,N,C,T>::sphere( float radius, int res, ofPrimitiveMode mode ) {2007
2008ofMesh_<V,N,C,T> mesh;2009
2010float doubleRes = res*2.f;2011float polarInc = glm::pi<float>()/(res); // ringAngle2012float azimInc = glm::two_pi<float>()/(doubleRes); // segAngle //2013
2014if(mode != OF_PRIMITIVE_TRIANGLE_STRIP && mode != OF_PRIMITIVE_TRIANGLES) {2015mode = OF_PRIMITIVE_TRIANGLE_STRIP;2016}2017mesh.setMode(mode);2018
2019V vert;2020T tcoord;2021
2022for(float i = 0; i < res+1; i++) {2023
2024float tr = sin( glm::pi<float>()-i * polarInc );2025float ny = cos( glm::pi<float>()-i * polarInc );2026
2027tcoord.y = 1.f - (i / res);2028
2029for(float j = 0; j <= doubleRes; j++) {2030
2031float nx = tr * sin(j * azimInc);2032float nz = tr * cos(j * azimInc);2033
2034tcoord.x = j / (doubleRes);2035
2036vert = {nx, ny, nz};2037mesh.addNormal(vert);2038vert *= radius;2039mesh.addVertex(vert);2040mesh.addTexCoord(tcoord);2041}2042}2043
2044int nr = doubleRes+1;2045if(mode == OF_PRIMITIVE_TRIANGLES) {2046
2047ofIndexType index1, index2, index3;2048
2049for(float iy = 0; iy < res; iy++) {2050for(float ix = 0; ix < doubleRes; ix++) {2051
2052// first tri //2053if(iy > 0) {2054index1 = (iy+0) * (nr) + (ix+0);2055index2 = (iy+0) * (nr) + (ix+1);2056index3 = (iy+1) * (nr) + (ix+0);2057
2058mesh.addIndex(index1);2059mesh.addIndex(index3);2060mesh.addIndex(index2);2061}2062
2063if(iy < res-1 ) {2064// second tri //2065index1 = (iy+0) * (nr) + (ix+1);2066index2 = (iy+1) * (nr) + (ix+1);2067index3 = (iy+1) * (nr) + (ix+0);2068
2069mesh.addIndex(index1);2070mesh.addIndex(index3);2071mesh.addIndex(index2);2072
2073}2074}2075}2076
2077} else {2078for(int y = 0; y < res; y++) {2079for(int x = 0; x <= doubleRes; x++) {2080mesh.addIndex( (y)*nr + x );2081mesh.addIndex( (y+1)*nr + x );2082}2083}2084}2085
2086
2087return mesh;2088}
2089
2090/*
2091-----------------------------------------------------------------------------
2092This source file is part of ogre-procedural
2093
2094For the latest info, see http://code.google.com/p/ogre-procedural/
2095
2096Copyright (c) 2010 Michael Broutin
2097
2098Permission is hereby granted, free of charge, to any person obtaining a copy
2099of this software and associated documentation files (the "Software"), to deal
2100in the Software without restriction, including without limitation the rights
2101to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2102copies of the Software, and to permit persons to whom the Software is
2103furnished to do so, subject to the following conditions:
2104
2105The above copyright notice and this permission notice shall be included in
2106all copies or substantial portions of the Software.
2107
2108THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2109IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2110FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2111AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2112LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2113OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2114THE SOFTWARE.
2115-----------------------------------------------------------------------------
2116*/
2117// http://code.google.com/p/ogre-procedural/source/browse/library/src/ProceduralIcoSphereGenerator.cpp
2118
2119
2120//--------------------------------------------------------------
2121template<class V, class N, class C, class T>2122ofMesh_<V,N,C,T> ofMesh_<V,N,C,T>::icosahedron(float radius) {2123auto mesh = icosphere(radius, 0);2124mesh.flatNormals();2125return mesh;2126}
2127
2128// based on code by Michael Broutin for the ogre-procedural library //
2129// http://code.google.com/p/ogre-procedural/source/browse/library/src/ProceduralIcoSphereGenerator.cpp
2130// For the latest info, see http://code.google.com/p/ogre-procedural/ //
2131
2132//--------------------------------------------------------------
2133template<class V, class N, class C, class T>2134ofMesh_<V,N,C,T> ofMesh_<V,N,C,T>::icosphere(float radius, std::size_t iterations) {2135ofMesh_<V,N,C,T> sphere;2136
2137/// Step 1 : Generate icosahedron2138const float sqrt5 = sqrt(5.0f);2139const float phi = (1.0f + sqrt5) * 0.5f;2140const float invnorm = 1/sqrt(phi*phi+1);2141
2142sphere.addVertex(invnorm * V(-1, phi, 0));//02143sphere.addVertex(invnorm * V( 1, phi, 0));//12144sphere.addVertex(invnorm * V(0, 1, -phi));//22145sphere.addVertex(invnorm * V(0, 1, phi));//32146sphere.addVertex(invnorm * V(-phi,0, -1));//42147sphere.addVertex(invnorm * V(-phi,0, 1));//52148sphere.addVertex(invnorm * V( phi,0, -1));//62149sphere.addVertex(invnorm * V( phi,0, 1));//72150sphere.addVertex(invnorm * V(0, -1, -phi));//82151sphere.addVertex(invnorm * V(0, -1, phi));//92152sphere.addVertex(invnorm * V(-1, -phi,0));//102153sphere.addVertex(invnorm * V( 1, -phi,0));//112154
2155ofIndexType firstFaces[] = {21560,1,2,21570,3,1,21580,4,5,21591,7,6,21601,6,2,21611,3,7,21620,2,4,21630,5,3,21642,6,8,21652,8,4,21663,5,9,21673,9,7,216811,6,7,216910,5,4,217010,4,8,217110,9,5,217211,8,6,217311,7,9,217410,8,11,217510,11,92176};2177
2178for(ofIndexType i = 0; i < 60; i+=3) {2179sphere.addTriangle(firstFaces[i], firstFaces[i+1], firstFaces[i+2]);2180}2181
2182auto& vertices = sphere.getVertices();2183auto& faces = sphere.getIndices();2184
2185ofIndexType size = faces.size();2186
2187/// Step 2 : tessellate2188for (ofIndexType iteration = 0; iteration < iterations; iteration++)2189{2190size*=4;2191std::vector<ofIndexType> newFaces;2192for (ofIndexType i=0; i<size/12; i++)2193{2194auto i1 = faces[i*3];2195auto i2 = faces[i*3+1];2196auto i3 = faces[i*3+2];2197auto i12 = vertices.size();2198auto i23 = i12+1;2199auto i13 = i12+2;2200auto v1 = vertices[i1];2201auto v2 = vertices[i2];2202auto v3 = vertices[i3];2203//make 1 vertice at the center of each edge and project it onto the sphere2204vertices.push_back(glm::normalize(toGlm(v1+v2)));2205vertices.push_back(glm::normalize(toGlm(v2+v3)));2206vertices.push_back(glm::normalize(toGlm(v1+v3)));2207//now recreate indices2208newFaces.push_back(i1);2209newFaces.push_back(i12);2210newFaces.push_back(i13);2211newFaces.push_back(i2);2212newFaces.push_back(i23);2213newFaces.push_back(i12);2214newFaces.push_back(i3);2215newFaces.push_back(i13);2216newFaces.push_back(i23);2217newFaces.push_back(i12);2218newFaces.push_back(i23);2219newFaces.push_back(i13);2220}2221faces.swap(newFaces);2222}2223
2224/// Step 3 : generate texcoords2225std::vector<T> texCoords;2226for (ofIndexType i=0;i<vertices.size();i++)2227{2228const auto& vec = vertices[i];2229float u, v;2230float r0 = sqrtf(vec.x*vec.x+vec.z*vec.z);2231float alpha;2232alpha = atan2f(vec.z,vec.x);2233u = alpha/glm::two_pi<float>()+.5f;2234v = atan2f(vec.y, r0)/glm::pi<float>() + .5f;2235// reverse the u coord, so the default is texture mapped left to2236// right on the outside of a sphere2237// reverse the v coord, so that texture origin is at top left2238texCoords.push_back(T(1.0-u,1.f-v));2239}2240
2241/// Step 4 : fix texcoords2242// find vertices to split2243std::vector<ofIndexType> indexToSplit;2244
2245for (ofIndexType i=0;i<faces.size()/3;i++)2246{2247T& t0 = texCoords[faces[i*3+0]];2248T& t1 = texCoords[faces[i*3+1]];2249T& t2 = texCoords[faces[i*3+2]];2250
2251if (std::abs(t2.x-t0.x)>0.5)2252{2253if (t0.x<0.5)2254indexToSplit.push_back(faces[i*3]);2255else2256indexToSplit.push_back(faces[i*3+2]);2257}2258if (std::abs(t1.x-t0.x)>0.5)2259{2260if (t0.x<0.5)2261indexToSplit.push_back(faces[i*3]);2262else2263indexToSplit.push_back(faces[i*3+1]);2264}2265if (std::abs(t2.x-t1.x)>0.5)2266{2267if (t1.x<0.5)2268indexToSplit.push_back(faces[i*3+1]);2269else2270indexToSplit.push_back(faces[i*3+2]);2271}2272}2273
2274//split vertices2275for (ofIndexType i=0;i<indexToSplit.size();i++)2276{2277ofIndexType index = indexToSplit[i];2278//duplicate vertex2279V v = vertices[index];2280T t = texCoords[index] + T(1.f, 0.f);2281vertices.push_back(v);2282texCoords.push_back(t);2283ofIndexType newIndex = vertices.size()-1;2284//reassign indices2285for (ofIndexType j=0;j<faces.size();j++)2286{2287if (faces[j]==index)2288{2289ofIndexType index1 = faces[(j+1)%3+(j/3)*3];2290ofIndexType index2 = faces[(j+2)%3+(j/3)*3];2291if ((texCoords[index1].x>0.5) || (texCoords[index2].x>0.5))2292{2293faces[j] = newIndex;2294}2295}2296}2297}2298
2299// tig: flip face(=triangle) winding order, so that we are consistent with all other ofPrimitives.2300// i wish there was a more elegant way to do this, but anything happening before "split vertices"2301// makes things very, very complicated.2302
2303for (ofIndexType i = 0; i < faces.size(); i+=3) {2304std::swap(faces[i+1], faces[i+2]);2305}2306
2307sphere.addNormals( vertices );2308sphere.addTexCoords( texCoords );2309
2310for(ofIndexType i = 0; i < vertices.size(); i++ ) {2311vertices[i] *= radius;2312}2313
2314return sphere;2315}
2316/*
2317-----------------------------------------------------------------------------
2318// END OGRE
2319-----------------------------------------------------------------------------
2320*/
2321
2322
2323
2324// Cylinder Mesh
2325
2326//--------------------------------------------------------------
2327template<class V, class N, class C, class T>2328ofMesh_<V,N,C,T> ofMesh_<V,N,C,T>::cylinder( float radius, float height, int radiusSegments, int heightSegments, int numCapSegments, bool bCapped, ofPrimitiveMode mode ) {2329ofMesh_<V,N,C,T> mesh;2330if(mode != OF_PRIMITIVE_TRIANGLE_STRIP && mode != OF_PRIMITIVE_TRIANGLES) {2331mode = OF_PRIMITIVE_TRIANGLE_STRIP;2332}2333mesh.setMode(mode);2334
2335radiusSegments = radiusSegments+1;2336int capSegs = numCapSegments;2337capSegs = capSegs+1;2338heightSegments = heightSegments+1;2339if(heightSegments < 2) heightSegments = 2;2340if( capSegs < 2 ) bCapped = false;2341if(!bCapped) capSegs=1;2342
2343float angleIncRadius = -1 * (glm::two_pi<float>()/((float)radiusSegments-1.f));2344float heightInc = height/((float)heightSegments-1.f);2345float halfH = height*.5f;2346
2347float newRad;2348V vert;2349T tcoord;2350N normal;2351glm::vec3 up(0,1,0);2352
2353std::size_t vertOffset = 0;2354
2355float maxTexY = heightSegments-1.f;2356if(capSegs > 0) {2357maxTexY += (capSegs*2)-2.f;2358}2359float maxTexYNormalized = (capSegs-1.f) / maxTexY;2360
2361// add the top cap //2362if(bCapped && capSegs > 0) {2363normal = {0.f, -1.f, 0.f};2364for(int iy = 0; iy < capSegs; iy++) {2365for(int ix = 0; ix < radiusSegments; ix++) {2366newRad = ofMap((float)iy, 0, capSegs-1, 0.0, radius);2367vert.x = cos((float)ix*angleIncRadius) * newRad;2368vert.z = sin((float)ix*angleIncRadius) * newRad;2369vert.y = -halfH;2370
2371tcoord.x = (float)ix/((float)radiusSegments-1.f);2372tcoord.y = 1.f - ofMap(iy, 0, capSegs-1, 0, maxTexYNormalized);2373
2374mesh.addTexCoord( tcoord );2375mesh.addVertex( vert );2376mesh.addNormal( normal );2377}2378}2379
2380if(mode == OF_PRIMITIVE_TRIANGLES) {2381for(int y = 0; y < capSegs-1; y++) {2382for(int x = 0; x < radiusSegments-1; x++) {2383if(y > 0) {2384// first triangle //2385mesh.addIndex( (y)*radiusSegments + x + vertOffset );2386mesh.addIndex( (y)*radiusSegments + x+1 + vertOffset);2387mesh.addIndex( (y+1)*radiusSegments + x + vertOffset);2388}2389
2390// second triangle //2391mesh.addIndex( (y)*radiusSegments + x+1 + vertOffset);2392mesh.addIndex( (y+1)*radiusSegments + x+1 + vertOffset);2393mesh.addIndex( (y+1)*radiusSegments + x + vertOffset);2394}2395}2396} else {2397for(int y = 0; y < capSegs-1; y++) {2398for(int x = 0; x < radiusSegments; x++) {2399mesh.addIndex( (y)*radiusSegments + x + vertOffset );2400mesh.addIndex( (y+1)*radiusSegments + x + vertOffset);2401}2402}2403}2404
2405vertOffset = mesh.getNumVertices();2406
2407}2408
2409//maxTexY = heightSegments-1.f + capSegs-1.f;2410float minTexYNormalized = 0;2411if(bCapped) minTexYNormalized = maxTexYNormalized;2412maxTexYNormalized = 1.f;2413if(bCapped) maxTexYNormalized = (heightSegments) / maxTexY;2414
2415// cylinder vertices //2416for(int iy = 0; iy < heightSegments; iy++) {2417normal = {1.f, 0.f, 0.f};2418for(int ix = 0; ix < radiusSegments; ix++) {2419
2420//newRad = ofMap((float)iy, 0, heightSegments-1, 0.0, radius);2421vert.x = cos(ix*angleIncRadius) * radius;2422vert.y = heightInc*float(iy) - halfH;2423vert.z = sin(ix*angleIncRadius) * radius;2424
2425tcoord.x = float(ix)/(float(radiusSegments)-1.f);2426tcoord.y = 1.f - ofMap(iy, 0, heightSegments-1, minTexYNormalized, maxTexYNormalized );2427
2428mesh.addTexCoord( tcoord );2429mesh.addVertex( vert );2430mesh.addNormal( normal );2431
2432normal = glm::rotate(toGlm(normal), -angleIncRadius, up);2433
2434}2435}2436
2437if(mode == OF_PRIMITIVE_TRIANGLES) {2438for(int y = 0; y < heightSegments-1; y++) {2439for(int x = 0; x < radiusSegments-1; x++) {2440// first triangle //2441mesh.addIndex( (y)*radiusSegments + x + vertOffset);2442mesh.addIndex( (y)*radiusSegments + x+1 + vertOffset );2443mesh.addIndex( (y+1)*radiusSegments + x + vertOffset );2444
2445// second triangle //2446mesh.addIndex( (y)*radiusSegments + x+1 + vertOffset );2447mesh.addIndex( (y+1)*radiusSegments + x+1 + vertOffset );2448mesh.addIndex( (y+1)*radiusSegments + x + vertOffset );2449}2450}2451} else {2452for(int y = 0; y < heightSegments-1; y++) {2453for(int x = 0; x < radiusSegments; x++) {2454mesh.addIndex( (y)*radiusSegments + x + vertOffset );2455mesh.addIndex( (y+1)*radiusSegments + x + vertOffset );2456}2457}2458}2459
2460vertOffset = mesh.getNumVertices();2461
2462// add the bottom cap2463if(bCapped && capSegs > 0) {2464minTexYNormalized = maxTexYNormalized;2465maxTexYNormalized = 1.f;2466
2467normal = {0.f, 1.f, 0.f};2468for(int iy = 0; iy < capSegs; iy++) {2469for(int ix = 0; ix < radiusSegments; ix++) {2470newRad = ofMap((float)iy, 0, capSegs-1, radius, 0.0);2471vert.x = cos((float)ix*angleIncRadius) * newRad;2472vert.z = sin((float)ix*angleIncRadius) * newRad;2473vert.y = halfH;2474
2475tcoord.x = (float)ix/((float)radiusSegments-1.f);2476tcoord.y = 1.f - ofMap(iy, 0, capSegs-1, minTexYNormalized, maxTexYNormalized);2477
2478mesh.addTexCoord( tcoord );2479mesh.addVertex( vert );2480mesh.addNormal( normal );2481}2482}2483
2484if(mode == OF_PRIMITIVE_TRIANGLES) {2485for(int y = 0; y < capSegs-1; y++) {2486for(int x = 0; x < radiusSegments-1; x++) {2487// first triangle //2488mesh.addIndex( (y)*radiusSegments + x + vertOffset );2489mesh.addIndex( (y)*radiusSegments + x+1 + vertOffset);2490mesh.addIndex( (y+1)*radiusSegments + x + vertOffset);2491
2492if(y < capSegs -1 && capSegs > 2) {2493// second triangle //2494mesh.addIndex( (y)*radiusSegments + x+1 + vertOffset);2495mesh.addIndex( (y+1)*radiusSegments + x+1 + vertOffset);2496mesh.addIndex( (y+1)*radiusSegments + x + vertOffset);2497}2498}2499}2500} else {2501for(int y = 0; y < capSegs-1; y++) {2502for(int x = 0; x < radiusSegments; x++) {2503mesh.addIndex( (y)*radiusSegments + x + vertOffset );2504mesh.addIndex( (y+1)*radiusSegments + x + vertOffset);2505}2506}2507}2508
2509vertOffset = mesh.getNumVertices();2510
2511}2512
2513return mesh;2514}
2515
2516// Cone Mesh //
2517
2518
2519//--------------------------------------------------------------
2520template<class V, class N, class C, class T>2521ofMesh_<V,N,C,T> ofMesh_<V,N,C,T>::cone( float radius, float height, int radiusSegments, int heightSegments, int capSegments, ofPrimitiveMode mode ) {2522ofMesh_<V,N,C,T> mesh;2523if(mode != OF_PRIMITIVE_TRIANGLE_STRIP && mode != OF_PRIMITIVE_TRIANGLES) {2524mode = OF_PRIMITIVE_TRIANGLE_STRIP;2525}2526mesh.setMode(mode);2527
2528radiusSegments = radiusSegments+1;2529capSegments = capSegments+1;2530heightSegments = heightSegments+1;2531if(heightSegments < 2) heightSegments = 2;2532int capSegs = capSegments;2533if( capSegs < 2 ) {2534capSegs = 0;2535}2536
2537
2538float angleIncRadius = -1.f * ((glm::two_pi<float>()/((float)radiusSegments-1.f)));2539float heightInc = height/((float)heightSegments-1);2540float halfH = height*.5f;2541
2542float newRad;2543V vert;2544N normal;2545T tcoord;2546glm::vec3 up(0,1,0);2547
2548std::size_t vertOffset = 0;2549
2550float maxTexY = heightSegments-1.f;2551if(capSegs > 0) {2552maxTexY += capSegs-1.f;2553}2554
2555V startVec(0, -halfH-1.f, 0);2556
2557// cone vertices //2558for(int iy = 0; iy < heightSegments; iy++) {2559for(int ix = 0; ix < radiusSegments; ix++) {2560
2561newRad = ofMap((float)iy, 0, heightSegments-1, 0.0, radius);2562vert.x = cos((float)ix*angleIncRadius) * newRad;2563vert.y = heightInc*((float)iy) - halfH;2564vert.z = sin((float)ix*angleIncRadius) * newRad;2565
2566tcoord.x = (float)ix/((float)radiusSegments-1.f);2567tcoord.y = 1.f - (float)iy/((float)maxTexY);2568
2569mesh.addTexCoord( tcoord );2570mesh.addVertex( vert );2571
2572if(iy == 0) {2573newRad = 1.f;2574vert.x = cos((float)ix*angleIncRadius) * newRad;2575vert.y = heightInc*((float)iy) - halfH;2576vert.z = sin((float)ix*angleIncRadius) * newRad;2577}2578
2579auto diff = toGlm(vert - startVec);2580auto crossed = glm::cross(up, toGlm(vert));2581normal = glm::cross(crossed, diff);2582mesh.addNormal( glm::normalize(toGlm(normal)) );2583
2584}2585}2586
2587if(mode == OF_PRIMITIVE_TRIANGLES) {2588for(int y = 0; y < heightSegments-1; y++) {2589for(int x = 0; x < radiusSegments-1; x++) {2590if(y > 0){2591// first triangle //2592mesh.addIndex( (y)*radiusSegments + x );2593mesh.addIndex( (y)*radiusSegments + x+1 );2594mesh.addIndex( (y+1)*radiusSegments + x );2595}2596
2597// second triangle //2598mesh.addIndex( (y)*radiusSegments + x+1 );2599mesh.addIndex( (y+1)*radiusSegments + x+1 );2600mesh.addIndex( (y+1)*radiusSegments + x );2601}2602}2603} else {2604for(int y = 0; y < heightSegments-1; y++) {2605for(int x = 0; x < radiusSegments; x++) {2606mesh.addIndex( (y)*radiusSegments + x );2607mesh.addIndex( (y+1)*radiusSegments + x );2608}2609}2610}2611
2612vertOffset = mesh.getNumVertices();2613float maxTexYNormalized = (heightSegments-1.f) / maxTexY;2614
2615// add the cap //2616normal= {0.f,1.f,0.f};2617for(int iy = 0; iy < capSegs; iy++) {2618for(int ix = 0; ix < radiusSegments; ix++) {2619newRad = ofMap((float)iy, 0, capSegs-1, radius, 0.0);2620vert.x = cos((float)ix*angleIncRadius) * newRad;2621vert.z = sin((float)ix*angleIncRadius) * newRad;2622vert.y = halfH;2623
2624tcoord.x = (float)ix/((float)radiusSegments-1.f);2625tcoord.y = 1.f - ofMap(iy, 0, capSegs-1, maxTexYNormalized, 1.f);2626
2627mesh.addTexCoord( tcoord );2628mesh.addVertex( vert );2629mesh.addNormal( normal );2630}2631}2632
2633if(mode == OF_PRIMITIVE_TRIANGLES) {2634if( capSegs > 0 ) {2635for(int y = 0; y < capSegs-1; y++) {2636for(int x = 0; x < radiusSegments-1; x++) {2637// first triangle //2638mesh.addIndex( (y)*radiusSegments + x + vertOffset );2639mesh.addIndex( (y)*radiusSegments + x+1 + vertOffset);2640mesh.addIndex( (y+1)*radiusSegments + x + vertOffset);2641
2642if(y < capSegs-1) {2643// second triangle //2644mesh.addIndex( (y)*radiusSegments + x+1 + vertOffset);2645mesh.addIndex( (y+1)*radiusSegments + x+1 + vertOffset);2646mesh.addIndex( (y+1)*radiusSegments + x + vertOffset);2647}2648}2649}2650}2651} else {2652if(capSegs > 0 ) {2653for(int y = 0; y < capSegs-1; y++) {2654for(int x = 0; x < radiusSegments; x++) {2655mesh.addIndex( (y)*radiusSegments + x + vertOffset );2656mesh.addIndex( (y+1)*radiusSegments + x + vertOffset);2657}2658}2659}2660}2661
2662return mesh;2663}
2664
2665
2666// Box Mesh //
2667
2668
2669//--------------------------------------------------------------
2670template<class V, class N, class C, class T>2671ofMesh_<V,N,C,T> ofMesh_<V,N,C,T>::box( float width, float height, float depth, int resX, int resY, int resZ ) {2672// mesh only available as triangles //2673ofMesh_<V,N,C,T> mesh;2674mesh.setMode( OF_PRIMITIVE_TRIANGLES );2675
2676resX = resX + 1;2677resY = resY + 1;2678resZ = resZ + 1;2679
2680if( resX < 2 ) resX = 0;2681if( resY < 2 ) resY = 0;2682if( resZ < 2 ) resZ = 0;2683
2684// halves //2685float halfW = width * .5f;2686float halfH = height * .5f;2687float halfD = depth * .5f;2688
2689V vert;2690T texcoord;2691N normal;2692std::size_t vertOffset = 0;2693
2694// TRIANGLES //2695
2696// Front Face //2697normal = {0.f, 0.f, 1.f};2698// add the vertexes //2699for(int iy = 0; iy < resY; iy++) {2700for(int ix = 0; ix < resX; ix++) {2701
2702// normalized tex coords //2703texcoord.x = ((float)ix/((float)resX-1.f));2704texcoord.y = 1.f - ((float)iy/((float)resY-1.f));2705
2706vert.x = texcoord.x * width - halfW;2707vert.y = -(texcoord.y-1.f) * height - halfH;2708vert.z = halfD;2709
2710mesh.addVertex(vert);2711mesh.addTexCoord(texcoord);2712mesh.addNormal(normal);2713}2714}2715
2716for(int y = 0; y < resY-1; y++) {2717for(int x = 0; x < resX-1; x++) {2718// first triangle //2719mesh.addIndex((y)*resX + x + vertOffset);2720mesh.addIndex((y+1)*resX + x + vertOffset);2721mesh.addIndex((y)*resX + x+1 + vertOffset);2722
2723// second triangle //2724mesh.addIndex((y)*resX + x+1 + vertOffset);2725mesh.addIndex((y+1)*resX + x + vertOffset);2726mesh.addIndex((y+1)*resX + x+1 + vertOffset);2727}2728}2729
2730vertOffset = mesh.getNumVertices();2731
2732
2733// Right Side Face //2734normal = {1.f, 0.f, 0.f};2735// add the vertexes //2736for(int iy = 0; iy < resY; iy++) {2737for(int ix = 0; ix < resZ; ix++) {2738
2739// normalized tex coords //2740texcoord.x = ((float)ix/((float)resZ-1.f));2741texcoord.y = 1.f - ((float)iy/((float)resY-1.f));2742
2743//vert.x = texcoord.x * width - halfW;2744vert.x = halfW;2745vert.y = -(texcoord.y-1.f) * height - halfH;2746vert.z = texcoord.x * -depth + halfD;2747
2748mesh.addVertex(vert);2749mesh.addTexCoord(texcoord);2750mesh.addNormal(normal);2751}2752}2753
2754for(int y = 0; y < resY-1; y++) {2755for(int x = 0; x < resZ-1; x++) {2756// first triangle //2757mesh.addIndex((y)*resZ + x + vertOffset);2758mesh.addIndex((y+1)*resZ + x + vertOffset);2759mesh.addIndex((y)*resZ + x+1 + vertOffset);2760
2761// second triangle //2762mesh.addIndex((y)*resZ + x+1 + vertOffset);2763mesh.addIndex((y+1)*resZ + x + vertOffset);2764mesh.addIndex((y+1)*resZ + x+1 + vertOffset);2765}2766}2767
2768vertOffset = mesh.getNumVertices();2769
2770// Left Side Face //2771normal = {-1.f, 0.f, 0.f};2772// add the vertexes //2773for(int iy = 0; iy < resY; iy++) {2774for(int ix = 0; ix < resZ; ix++) {2775
2776// normalized tex coords //2777texcoord.x = ((float)ix/((float)resZ-1.f));2778texcoord.y = 1.f-((float)iy/((float)resY-1.f));2779
2780//vert.x = texcoord.x * width - halfW;2781vert.x = -halfW;2782vert.y = -(texcoord.y-1.f) * height - halfH;2783vert.z = texcoord.x * depth - halfD;2784
2785mesh.addVertex(vert);2786mesh.addTexCoord(texcoord);2787mesh.addNormal(normal);2788}2789}2790
2791for(int y = 0; y < resY-1; y++) {2792for(int x = 0; x < resZ-1; x++) {2793// first triangle //2794mesh.addIndex((y)*resZ + x + vertOffset);2795mesh.addIndex((y+1)*resZ + x + vertOffset);2796mesh.addIndex((y)*resZ + x+1 + vertOffset);2797
2798// second triangle //2799mesh.addIndex((y)*resZ + x+1 + vertOffset);2800mesh.addIndex((y+1)*resZ + x + vertOffset);2801mesh.addIndex((y+1)*resZ + x+1 + vertOffset);2802}2803}2804
2805vertOffset = mesh.getNumVertices();2806
2807
2808// Back Face //2809normal = {0.f, 0.f, -1.f};2810// add the vertexes //2811for(int iy = 0; iy < resY; iy++) {2812for(int ix = 0; ix < resX; ix++) {2813
2814// normalized tex coords //2815texcoord.x = ((float)ix/((float)resX-1.f));2816texcoord.y = 1.f-((float)iy/((float)resY-1.f));2817
2818vert.x = texcoord.x * -width + halfW;2819vert.y = -(texcoord.y-1.f) * height - halfH;2820vert.z = -halfD;2821
2822mesh.addVertex(vert);2823mesh.addTexCoord(texcoord);2824mesh.addNormal(normal);2825}2826}2827
2828for(int y = 0; y < resY-1; y++) {2829for(int x = 0; x < resX-1; x++) {2830// first triangle //2831mesh.addIndex((y)*resX + x + vertOffset);2832mesh.addIndex((y+1)*resX + x + vertOffset);2833mesh.addIndex((y)*resX + x+1 + vertOffset);2834
2835// second triangle //2836mesh.addIndex((y)*resX + x+1 + vertOffset);2837mesh.addIndex((y+1)*resX + x + vertOffset);2838mesh.addIndex((y+1)*resX + x+1 + vertOffset);2839}2840}2841
2842vertOffset = mesh.getNumVertices();2843
2844
2845// Top Face //2846normal = {0.f, -1.f, 0.f};2847// add the vertexes //2848for(int iy = 0; iy < resZ; iy++) {2849for(int ix = 0; ix < resX; ix++) {2850
2851// normalized tex coords //2852texcoord.x = ((float)ix/((float)resX-1.f));2853texcoord.y = 1.f-((float)iy/((float)resZ-1.f));2854
2855vert.x = texcoord.x * width - halfW;2856//vert.y = -(texcoord.y-1.f) * height - halfH;2857vert.y = -halfH;2858vert.z = texcoord.y * depth - halfD;2859
2860mesh.addVertex(vert);2861mesh.addTexCoord(texcoord);2862mesh.addNormal(normal);2863}2864}2865
2866for(int y = 0; y < resZ-1; y++) {2867for(int x = 0; x < resX-1; x++) {2868// first triangle //2869mesh.addIndex((y)*resX + x + vertOffset);2870mesh.addIndex((y)*resX + x+1 + vertOffset);2871mesh.addIndex((y+1)*resX + x + vertOffset);2872
2873// second triangle //2874mesh.addIndex((y)*resX + x+1 + vertOffset);2875mesh.addIndex((y+1)*resX + x+1 + vertOffset);2876mesh.addIndex((y+1)*resX + x + vertOffset);2877}2878}2879
2880vertOffset = mesh.getNumVertices();2881
2882
2883// Bottom Face //2884normal = {0.f, 1.f, 0.f};2885// add the vertexes //2886for(int iy = 0; iy < resZ; iy++) {2887for(int ix = 0; ix < resX; ix++) {2888
2889// normalized tex coords //2890texcoord.x = ((float)ix/((float)resX-1.f));2891texcoord.y = 1.f-((float)iy/((float)resZ-1.f));2892
2893vert.x = texcoord.x * width - halfW;2894//vert.y = -(texcoord.y-1.f) * height - halfH;2895vert.y = halfH;2896vert.z = texcoord.y * -depth + halfD;2897
2898mesh.addVertex(vert);2899mesh.addTexCoord(texcoord);2900mesh.addNormal(normal);2901}2902}2903
2904for(int y = 0; y < resZ-1; y++) {2905for(int x = 0; x < resX-1; x++) {2906// first triangle //2907mesh.addIndex((y)*resX + x + vertOffset);2908mesh.addIndex((y)*resX + x+1 + vertOffset);2909mesh.addIndex((y+1)*resX + x + vertOffset);2910
2911// second triangle //2912mesh.addIndex((y)*resX + x+1 + vertOffset);2913mesh.addIndex((y+1)*resX + x+1 + vertOffset);2914mesh.addIndex((y+1)*resX + x + vertOffset);2915}2916}2917
2918return mesh;2919}
2920
2921
2922
2923
2924//--------------------------------------------------------------
2925/// Returns an ofMesh representing an XYZ coordinate system.
2926template<class V, class N, class C, class T>2927ofMesh_<V,N,C,T> ofMesh_<V,N,C,T>::axis( float size ) {2928ofMesh_<V,N,C,T> mesh;2929
2930// mesh only available as wireframe //2931mesh.setMode(OF_PRIMITIVE_LINES);2932
2933V vertices[6] = {2934V(0,0,0),2935V(size,0,0),2936V(0,0,0),2937V(0,size,0),2938V(0,0,0),2939V(0,0,size),2940};2941C colors[6] = {2942C::red,2943C::red,2944C::green,2945C::green,2946C::blue,2947C::blue,2948};2949
2950mesh.addVertices(vertices, 6);2951mesh.addColors(colors, 6);2952
2953return mesh;2954}
2955
2956
2957
2958//--------------------------------------------------------------
2959template<class V, class N, class C, class T>2960ofMeshFace_<V,N,C,T>::ofMeshFace_()2961:bHasNormals(false)2962,bHasColors(false)2963,bHasTexcoords(false)2964,bFaceNormalDirty(false)2965{
2966}
2967
2968//--------------------------------------------------------------
2969template<class V, class N, class C, class T>2970const N & ofMeshFace_<V,N,C,T>::getFaceNormal() const{2971if(bFaceNormalDirty) calculateFaceNormal();2972return faceNormal;2973}
2974
2975//--------------------------------------------------------------
2976template<class V, class N, class C, class T>2977void ofMeshFace_<V,N,C,T>::calculateFaceNormal() const{2978glm::vec3 u, v;2979
2980u = toGlm(vertices[1]-vertices[0]);2981v = toGlm(vertices[2]-vertices[0]);2982
2983faceNormal = glm::cross(u, v);2984faceNormal = glm::normalize(toGlm(faceNormal));2985bFaceNormalDirty = false;2986}
2987
2988//--------------------------------------------------------------
2989template<class V, class N, class C, class T>2990void ofMeshFace_<V,N,C,T>::setVertex( ofIndexType index, const V& v ) {2991vertices[index] = v;2992bFaceNormalDirty = true;2993}
2994
2995//--------------------------------------------------------------
2996template<class V, class N, class C, class T>2997const V& ofMeshFace_<V,N,C,T>::getVertex( ofIndexType index ) const{2998return vertices[index];2999}
3000
3001//--------------------------------------------------------------
3002template<class V, class N, class C, class T>3003void ofMeshFace_<V,N,C,T>::setNormal( ofIndexType index, const N& n ) {3004normals[index] = n;3005bHasNormals = true;3006}
3007
3008//--------------------------------------------------------------
3009template<class V, class N, class C, class T>3010const N& ofMeshFace_<V,N,C,T>::getNormal( ofIndexType index ) const{3011return normals[ index ];3012}
3013
3014//--------------------------------------------------------------
3015template<class V, class N, class C, class T>3016void ofMeshFace_<V,N,C,T>::setColor( ofIndexType index, const C& color ) {3017colors[index] = color;3018bHasColors = true;3019}
3020
3021//--------------------------------------------------------------
3022template<class V, class N, class C, class T>3023const C& ofMeshFace_<V,N,C,T>::getColor( ofIndexType index) const{3024return colors[index];3025}
3026
3027//--------------------------------------------------------------
3028template<class V, class N, class C, class T>3029void ofMeshFace_<V,N,C,T>::setTexCoord( ofIndexType index, const T& tCoord ) {3030texCoords[index] = tCoord;3031bHasTexcoords = true;3032}
3033
3034//--------------------------------------------------------------
3035template<class V, class N, class C, class T>3036const T& ofMeshFace_<V,N,C,T>::getTexCoord( ofIndexType index ) const{3037return texCoords[index];3038}
3039
3040//--------------------------------------------------------------
3041template<class V, class N, class C, class T>3042void ofMeshFace_<V,N,C,T>::setHasColors( bool bColors ) {3043bHasColors = bColors;3044}
3045
3046//--------------------------------------------------------------
3047template<class V, class N, class C, class T>3048void ofMeshFace_<V,N,C,T>::setHasNormals( bool bNormals ) {3049bHasNormals = bNormals;3050}
3051
3052//--------------------------------------------------------------
3053template<class V, class N, class C, class T>3054void ofMeshFace_<V,N,C,T>::setHasTexcoords( bool bTexcoords ) {3055bHasTexcoords = bTexcoords;3056}
3057
3058//--------------------------------------------------------------
3059template<class V, class N, class C, class T>3060bool ofMeshFace_<V,N,C,T>::hasColors() const{3061return bHasColors;3062}
3063
3064//--------------------------------------------------------------
3065template<class V, class N, class C, class T>3066bool ofMeshFace_<V,N,C,T>::hasNormals() const{3067return bHasNormals;3068}
3069
3070//--------------------------------------------------------------
3071template<class V, class N, class C, class T>3072bool ofMeshFace_<V,N,C,T>::hasTexcoords() const{3073return bHasTexcoords;3074}
3075