/*************************************************************************** * Copyright (c) 2011 Werner Mayer * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include "PreCompiled.h" #ifndef FC_OS_WIN32 #define GL_GLEXT_PROTOTYPES #endif #ifndef _PreComp_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif #include "SoBrepFaceSet.h" #include #include #include #ifdef FC_OS_WIN32 #include #include #include #else #ifdef FC_OS_MACOSX #include #include #else #include #include #endif #endif // Should come after glext.h to avoid warnings #include using namespace PartGui; SO_NODE_SOURCE(SoBrepFaceSet); #define PRIVATE(p) ((p)->pimpl) class SoBrepFaceSet::VBO { public: struct Buffer { uint32_t myvbo[2]; }; static SbBool vboAvailable; SbBool updateVbo; SbBool vboLoaded; uint32_t indice_array; std::map vbomap; VBO() { SoContextHandler::addContextDestructionCallback(context_destruction_cb, this); //SoBase::staticDataLock(); static bool init = false; if (!init) { std::string ext = (const char*)(glGetString(GL_EXTENSIONS)); vboAvailable = (ext.find("GL_ARB_vertex_buffer_object") != std::string::npos); init = true; } //SoBase::staticDataUnlock(); updateVbo = false; vboLoaded = false; indice_array = 0; } ~VBO() { SoContextHandler::removeContextDestructionCallback(context_destruction_cb, this); // schedule delete for all allocated GL resources std::map::iterator it; for (it = vbomap.begin(); it != vbomap.end(); ++it) { void * ptr0 = (void*) ((uintptr_t) it->second.myvbo[0]); SoGLCacheContextElement::scheduleDeleteCallback(it->first, VBO::vbo_delete, ptr0); void * ptr1 = (void*) ((uintptr_t) it->second.myvbo[1]); SoGLCacheContextElement::scheduleDeleteCallback(it->first, VBO::vbo_delete, ptr1); } } static void context_destruction_cb(uint32_t context, void * userdata) { Buffer buffer; VBO * self = static_cast(userdata); std::map::iterator it = self->vbomap.find(context); if (it != self->vbomap.end()) { #ifdef FC_OS_WIN32 const cc_glglue * glue = cc_glglue_instance((int) context); PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)cc_glglue_getprocaddress(glue, "glDeleteBuffersARB"); #endif //cc_glglue_glDeleteBuffers(glue, buffer.size(), buffer.data()); buffer = it->second; glDeleteBuffersARB(2, buffer.myvbo); self->vbomap.erase(it); } } static void vbo_delete(void * closure, uint32_t contextid) { const cc_glglue * glue = cc_glglue_instance((int) contextid); GLuint id = (GLuint) ((uintptr_t) closure); cc_glglue_glDeleteBuffers(glue, 1, &id); } }; SbBool SoBrepFaceSet::VBO::vboAvailable = false; void SoBrepFaceSet::initClass() { SO_NODE_INIT_CLASS(SoBrepFaceSet, SoIndexedFaceSet, "IndexedFaceSet"); } SoBrepFaceSet::SoBrepFaceSet() { SO_NODE_CONSTRUCTOR(SoBrepFaceSet); SO_NODE_ADD_FIELD(partIndex, (-1)); SO_NODE_ADD_FIELD(highlightIndex, (-1)); SO_NODE_ADD_FIELD(selectionIndex, (-1)); selectionIndex.setNum(0); pimpl.reset(new VBO); } SoBrepFaceSet::~SoBrepFaceSet() { } void SoBrepFaceSet::doAction(SoAction* action) { if (action->getTypeId() == Gui::SoHighlightElementAction::getClassTypeId()) { Gui::SoHighlightElementAction* hlaction = static_cast(action); if (!hlaction->isHighlighted()) { this->highlightIndex = -1; return; } const SoDetail* detail = hlaction->getElement(); if (detail) { if (detail->isOfType(SoFaceDetail::getClassTypeId())) { int index = static_cast(detail)->getPartIndex(); this->highlightIndex.setValue(index); this->highlightColor = hlaction->getColor(); } else { this->highlightIndex = -1; return; } } } else if (action->getTypeId() == Gui::SoSelectionElementAction::getClassTypeId()) { Gui::SoSelectionElementAction* selaction = static_cast(action); this->selectionColor = selaction->getColor(); if (selaction->getType() == Gui::SoSelectionElementAction::All) { int num = this->partIndex.getNum(); this->selectionIndex.setNum(num); int32_t* v = this->selectionIndex.startEditing(); for (int i=0; iselectionIndex.finishEditing(); PRIVATE(this)->updateVbo = true; return; } else if (selaction->getType() == Gui::SoSelectionElementAction::None) { this->selectionIndex.setNum(0); PRIVATE(this)->updateVbo = true; return; } const SoDetail* detail = selaction->getElement(); if (detail) { if (!detail->isOfType(SoFaceDetail::getClassTypeId())) { return; } int index = static_cast(detail)->getPartIndex(); switch (selaction->getType()) { case Gui::SoSelectionElementAction::Append: { if (this->selectionIndex.find(index) < 0) { int start = this->selectionIndex.getNum(); this->selectionIndex.set1Value(start, index); } } break; case Gui::SoSelectionElementAction::Remove: { int start = this->selectionIndex.find(index); if (start >= 0) this->selectionIndex.deleteValues(start,1); } break; default: break; } } } else if (action->getTypeId() == Gui::SoVRMLAction::getClassTypeId()) { // update the materialIndex field to match with the number of triangles if needed SoState * state = action->getState(); Binding mbind = this->findMaterialBinding(state); if (mbind == PER_PART) { const SoLazyElement* mat = SoLazyElement::getInstance(state); int numColor = 1; int numParts = partIndex.getNum(); if (mat) { numColor = mat->getNumDiffuse(); if (numColor == numParts) { int count = 0; const int32_t * indices = this->partIndex.getValues(0); for (int i=0; imaterialIndex.setNum(count); int32_t * matind = this->materialIndex.startEditing(); int32_t k = 0; for (int i=0; imaterialIndex.finishEditing(); } } } } // The recommended way to set 'updateVbo' is to reimplement the method 'notify' // but the base class made this method private so that we can't override it. // So, the alternative way is to write a custom SoAction class. else if (action->getTypeId() == Gui::SoUpdateVBOAction::getClassTypeId()) { PRIVATE(this)->updateVbo = true; } inherited::doAction(action); } #ifdef RENDER_GLARRAYS void SoBrepFaceSet::GLRender(SoGLRenderAction *action) { SoState * state = action->getState(); SoMaterialBundle mb(action); Binding mbind = this->findMaterialBinding(state); SoTextureCoordinateBundle tb(action, true, false); SbBool doTextures = tb.needCoordinates(); int32_t hl_idx = this->highlightIndex.getValue(); int32_t num_selected = this->selectionIndex.getNum(); if (this->coordIndex.getNum() < 3) return; if (num_selected > 0) renderSelection(action); if (hl_idx >= 0) renderHighlight(action); // When setting transparency shouldGLRender() handles the rendering and returns false. // Therefore generatePrimitives() needs to be re-implemented to handle the materials // correctly. if (!this->shouldGLRender(action)) return; #ifdef RENDER_GLARRAYS if (!doTextures && index_array.size() && hl_idx < 0 && num_selected <= 0) { if (mbind == 0) { mb.sendFirst(); // only one material -> apply it! renderSimpleArray(); return; } else if (mbind == 1) { renderColoredArray(&mb); return; } } #endif Binding nbind = this->findNormalBinding(state); const SoCoordinateElement * coords; const SbVec3f * normals; const int32_t * cindices; int numindices; const int32_t * nindices; const int32_t * tindices; const int32_t * mindices; const int32_t * pindices; int numparts; SbBool normalCacheUsed; SbBool sendNormals = !mb.isColorOnly() || tb.isFunction(); this->getVertexData(state, coords, normals, cindices, nindices, tindices, mindices, numindices, sendNormals, normalCacheUsed); mb.sendFirst(); // make sure we have the correct material // just in case someone forgot if (!mindices) mindices = cindices; if (!nindices) nindices = cindices; pindices = this->partIndex.getValues(0); numparts = this->partIndex.getNum(); renderShape(state, vboAvailable, static_cast(coords), cindices, numindices, pindices, numparts, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0); // Disable caching for this node SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE); // Workaround for #0000433 //#if !defined(FC_OS_WIN32) if (hl_idx >= 0) renderHighlight(action); if (num_selected > 0) renderSelection(action); //#endif if(normalCacheUsed) this->readUnlockNormalCache(); } //**************************************************************************** // renderSimpleArray: normal and coord from vertex_array; // no texture, color, highlight or selection but highet possible speed; // all vertices written in one go! // void SoBrepFaceSet::renderSimpleArray() { int cnt = index_array.size(); if (cnt == 0) return; glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); #if 0 glInterleavedArrays(GL_N3F_V3F, 0, vertex_array.data()); glDrawElements(GL_TRIANGLES, cnt, GL_UNSIGNED_INT, index_array.data()); #else glInterleavedArrays(GL_N3F_V3F, 0, &(vertex_array[0])); glDrawElements(GL_TRIANGLES, cnt, GL_UNSIGNED_INT, &(index_array[0])); #endif glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); } //**************************************************************************** // renderColoredArray: normal and coord from vertex_array; // no texture, highlight or selection but color / material array. // needs to iterate over parts (i.e. geometry faces) // void SoBrepFaceSet::renderColoredArray(SoMaterialBundle *const materials) { int num_parts = partIndex.getNum(); int cnt = index_array.size(); if (cnt == 0) return; glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); #if 0 glInterleavedArrays(GL_N3F_V3F, 0, vertex_array.data()); const int32_t* ptr = index_array.data(); #else glInterleavedArrays(GL_N3F_V3F, 0, &(vertex_array[0])); const int32_t* ptr = &(index_array[0]); #endif for (int part_id = 0; part_id < num_parts; part_id++) { int tris = partIndex[part_id]; if (tris > 0) { materials->send(part_id, true); glDrawElements(GL_TRIANGLES, 3 * tris, GL_UNSIGNED_INT, ptr); ptr += 3 * tris; } } glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); } #else void SoBrepFaceSet::GLRender(SoGLRenderAction *action) { if (this->coordIndex.getNum() < 3) return; if (this->selectionIndex.getNum() > 0) renderSelection(action); if (this->highlightIndex.getValue() >= 0) renderHighlight(action); // When setting transparency shouldGLRender() handles the rendering and returns false. // Therefore generatePrimitives() needs to be re-implemented to handle the materials // correctly. if (!this->shouldGLRender(action)) return; SoState * state = action->getState(); Binding mbind = this->findMaterialBinding(state); Binding nbind = this->findNormalBinding(state); const SoCoordinateElement * coords; const SbVec3f * normals; const int32_t * cindices; int numindices; const int32_t * nindices; const int32_t * tindices; const int32_t * mindices; const int32_t * pindices; int numparts; SbBool doTextures; SbBool normalCacheUsed; SoMaterialBundle mb(action); SoTextureCoordinateBundle tb(action, true, false); doTextures = tb.needCoordinates(); SbBool sendNormals = !mb.isColorOnly() || tb.isFunction(); this->getVertexData(state, coords, normals, cindices, nindices, tindices, mindices, numindices, sendNormals, normalCacheUsed); mb.sendFirst(); // make sure we have the correct material // just in case someone forgot if (!mindices) mindices = cindices; if (!nindices) nindices = cindices; pindices = this->partIndex.getValues(0); numparts = this->partIndex.getNum(); SbBool hasVBO = PRIVATE(this)->vboAvailable; if (hasVBO) { // get the VBO status of the viewer Gui::SoGLVBOActivatedElement::get(state, hasVBO); // //if (SoGLVBOElement::shouldCreateVBO(state, numindices)) { // this->startVertexArray(action, coords, normals, false, false); //} } renderShape(action, hasVBO, static_cast(coords), cindices, numindices, pindices, numparts, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0); // Disable caching for this node SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE); if (normalCacheUsed) this->readUnlockNormalCache(); // Workaround for #0000433 //#if !defined(FC_OS_WIN32) if (this->highlightIndex.getValue() >= 0) renderHighlight(action); if (this->selectionIndex.getNum() > 0) renderSelection(action); //#endif } #endif void SoBrepFaceSet::GLRenderBelowPath(SoGLRenderAction * action) { inherited::GLRenderBelowPath(action); } // this macro actually makes the code below more readable :-) #define DO_VERTEX(idx) \ if (mbind == PER_VERTEX) { \ pointDetail.setMaterialIndex(matnr); \ vertex.setMaterialIndex(matnr++); \ } \ else if (mbind == PER_VERTEX_INDEXED) { \ pointDetail.setMaterialIndex(*mindices); \ vertex.setMaterialIndex(*mindices++); \ } \ if (nbind == PER_VERTEX) { \ pointDetail.setNormalIndex(normnr); \ currnormal = &normals[normnr++]; \ vertex.setNormal(*currnormal); \ } \ else if (nbind == PER_VERTEX_INDEXED) { \ pointDetail.setNormalIndex(*nindices); \ currnormal = &normals[*nindices++]; \ vertex.setNormal(*currnormal); \ } \ if (tb.isFunction()) { \ vertex.setTextureCoords(tb.get(coords->get3(idx), *currnormal)); \ if (tb.needIndices()) pointDetail.setTextureCoordIndex(tindices ? *tindices++ : texidx++); \ } \ else if (tbind != NONE) { \ pointDetail.setTextureCoordIndex(tindices ? *tindices : texidx); \ vertex.setTextureCoords(tb.get(tindices ? *tindices++ : texidx++)); \ } \ vertex.setPoint(coords->get3(idx)); \ pointDetail.setCoordinateIndex(idx); \ this->shapeVertex(&vertex); void SoBrepFaceSet::generatePrimitives(SoAction * action) { //TODO #if 0 inherited::generatePrimitives(action); #else //This is highly experimental!!! if (this->coordIndex.getNum() < 3) return; SoState * state = action->getState(); if (this->vertexProperty.getValue()) { state->push(); this->vertexProperty.getValue()->doAction(action); } Binding mbind = this->findMaterialBinding(state); Binding nbind = this->findNormalBinding(state); const SoCoordinateElement * coords; const SbVec3f * normals; const int32_t * cindices; int numindices; const int32_t * nindices; const int32_t * tindices; const int32_t * mindices; SbBool doTextures; SbBool sendNormals; SbBool normalCacheUsed; sendNormals = true; // always generate normals this->getVertexData(state, coords, normals, cindices, nindices, tindices, mindices, numindices, sendNormals, normalCacheUsed); SoTextureCoordinateBundle tb(action, false, false); doTextures = tb.needCoordinates(); if (!sendNormals) nbind = OVERALL; else if (normalCacheUsed && nbind == PER_VERTEX) { nbind = PER_VERTEX_INDEXED; } else if (normalCacheUsed && nbind == PER_FACE_INDEXED) { nbind = PER_FACE; } if (this->getNodeType() == SoNode::VRML1) { // For VRML1, PER_VERTEX means per vertex in shape, not PER_VERTEX // on the state. if (mbind == PER_VERTEX) { mbind = PER_VERTEX_INDEXED; mindices = cindices; } if (nbind == PER_VERTEX) { nbind = PER_VERTEX_INDEXED; nindices = cindices; } } Binding tbind = NONE; if (doTextures) { if (tb.isFunction() && !tb.needIndices()) { tbind = NONE; tindices = NULL; } // FIXME: just call inherited::areTexCoordsIndexed() instead of // the if-check? 20020110 mortene. else if (SoTextureCoordinateBindingElement::get(state) == SoTextureCoordinateBindingElement::PER_VERTEX) { tbind = PER_VERTEX; tindices = NULL; } else { tbind = PER_VERTEX_INDEXED; if (tindices == NULL) tindices = cindices; } } if (nbind == PER_VERTEX_INDEXED && nindices == NULL) { nindices = cindices; } if (mbind == PER_VERTEX_INDEXED && mindices == NULL) { mindices = cindices; } int texidx = 0; TriangleShape mode = POLYGON; TriangleShape newmode; const int32_t *viptr = cindices; const int32_t *viendptr = viptr + numindices; const int32_t *piptr = this->partIndex.getValues(0); int num_partindices = this->partIndex.getNum(); const int32_t *piendptr = piptr + num_partindices; int32_t v1, v2, v3, v4, v5 = 0, pi; // v5 init unnecessary, but kills a compiler warning. SoPrimitiveVertex vertex; SoPointDetail pointDetail; SoFaceDetail faceDetail; vertex.setDetail(&pointDetail); SbVec3f dummynormal(0,0,1); const SbVec3f *currnormal = &dummynormal; if (normals) currnormal = normals; vertex.setNormal(*currnormal); int matnr = 0; int normnr = 0; int trinr = 0; pi = piptr < piendptr ? *piptr++ : -1; while (pi == 0) { // It may happen that a part has no triangles pi = piptr < piendptr ? *piptr++ : -1; if (mbind == PER_PART) matnr++; else if (mbind == PER_PART_INDEXED) mindices++; } while (viptr + 2 < viendptr) { v1 = *viptr++; v2 = *viptr++; v3 = *viptr++; if (v1 < 0 || v2 < 0 || v3 < 0) { break; } v4 = viptr < viendptr ? *viptr++ : -1; if (v4 < 0) newmode = TRIANGLES; else { v5 = viptr < viendptr ? *viptr++ : -1; if (v5 < 0) newmode = QUADS; else newmode = POLYGON; } if (newmode != mode) { if (mode != POLYGON) this->endShape(); mode = newmode; this->beginShape(action, mode, &faceDetail); } else if (mode == POLYGON) this->beginShape(action, POLYGON, &faceDetail); // vertex 1 can't use DO_VERTEX if (mbind == PER_PART) { if (trinr == 0) { pointDetail.setMaterialIndex(matnr); vertex.setMaterialIndex(matnr++); } } else if (mbind == PER_PART_INDEXED) { if (trinr == 0) { pointDetail.setMaterialIndex(*mindices); vertex.setMaterialIndex(*mindices++); } } else if (mbind == PER_VERTEX || mbind == PER_FACE) { pointDetail.setMaterialIndex(matnr); vertex.setMaterialIndex(matnr++); } else if (mbind == PER_VERTEX_INDEXED || mbind == PER_FACE_INDEXED) { pointDetail.setMaterialIndex(*mindices); vertex.setMaterialIndex(*mindices++); } if (nbind == PER_VERTEX || nbind == PER_FACE) { pointDetail.setNormalIndex(normnr); currnormal = &normals[normnr++]; vertex.setNormal(*currnormal); } else if (nbind == PER_FACE_INDEXED || nbind == PER_VERTEX_INDEXED) { pointDetail.setNormalIndex(*nindices); currnormal = &normals[*nindices++]; vertex.setNormal(*currnormal); } if (tb.isFunction()) { vertex.setTextureCoords(tb.get(coords->get3(v1), *currnormal)); if (tb.needIndices()) pointDetail.setTextureCoordIndex(tindices ? *tindices++ : texidx++); } else if (tbind != NONE) { pointDetail.setTextureCoordIndex(tindices ? *tindices : texidx); vertex.setTextureCoords(tb.get(tindices ? *tindices++ : texidx++)); } pointDetail.setCoordinateIndex(v1); vertex.setPoint(coords->get3(v1)); this->shapeVertex(&vertex); DO_VERTEX(v2); DO_VERTEX(v3); if (mode != TRIANGLES) { DO_VERTEX(v4); if (mode == POLYGON) { DO_VERTEX(v5); v1 = viptr < viendptr ? *viptr++ : -1; while (v1 >= 0) { DO_VERTEX(v1); v1 = viptr < viendptr ? *viptr++ : -1; } this->endShape(); } } faceDetail.incFaceIndex(); if (mbind == PER_VERTEX_INDEXED) { mindices++; } if (nbind == PER_VERTEX_INDEXED) { nindices++; } if (tindices) tindices++; trinr++; if (pi == trinr) { pi = piptr < piendptr ? *piptr++ : -1; while (pi == 0) { // It may happen that a part has no triangles pi = piptr < piendptr ? *piptr++ : -1; if (mbind == PER_PART) matnr++; else if (mbind == PER_PART_INDEXED) mindices++; } trinr = 0; } } if (mode != POLYGON) this->endShape(); if (normalCacheUsed) { this->readUnlockNormalCache(); } if (this->vertexProperty.getValue()) { state->pop(); } #endif } #undef DO_VERTEX void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action) { SoState * state = action->getState(); state->push(); SoLazyElement::setEmissive(state, &this->highlightColor); SoOverrideElement::setEmissiveColorOverride(state, this, true); #if 0 // disables shading effect // sendNormals will be false SoLazyElement::setDiffuse(state, this,1, &this->highlightColor,&this->colorpacker); SoOverrideElement::setDiffuseColorOverride(state, this, true); SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR); #endif Binding mbind = this->findMaterialBinding(state); Binding nbind = this->findNormalBinding(state); const SoCoordinateElement * coords; const SbVec3f * normals; const int32_t * cindices; int numindices; const int32_t * nindices; const int32_t * tindices; const int32_t * mindices; const int32_t * pindices; SbBool doTextures; SbBool normalCacheUsed; SoMaterialBundle mb(action); SoTextureCoordinateBundle tb(action, true, false); doTextures = tb.needCoordinates(); SbBool sendNormals = !mb.isColorOnly() || tb.isFunction(); this->getVertexData(state, coords, normals, cindices, nindices, tindices, mindices, numindices, sendNormals, normalCacheUsed); mb.sendFirst(); // make sure we have the correct material int32_t id = this->highlightIndex.getValue(); if (id >= this->partIndex.getNum()) { SoDebugError::postWarning("SoBrepFaceSet::renderHighlight", "highlightIndex out of range"); } else { // just in case someone forgot if (!mindices) mindices = cindices; if (!nindices) nindices = cindices; pindices = this->partIndex.getValues(0); // coords int length = (int)pindices[id]*4; int start=0; for (int i=0;i(coords), &(cindices[start]), length, &(pindices[id]), 1, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0); } state->pop(); if (normalCacheUsed) this->readUnlockNormalCache(); } void SoBrepFaceSet::renderSelection(SoGLRenderAction *action) { int numSelected = this->selectionIndex.getNum(); const int32_t* selected = this->selectionIndex.getValues(0); if (numSelected == 0) return; SoState * state = action->getState(); state->push(); SoLazyElement::setEmissive(state, &this->selectionColor); SoOverrideElement::setEmissiveColorOverride(state, this, true); #if 0 // disables shading effect SoLazyElement::setDiffuse(state, this,1, &this->selectionColor,&this->colorpacker); SoOverrideElement::setDiffuseColorOverride(state, this, true); SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR); #endif Binding mbind = this->findMaterialBinding(state); Binding nbind = this->findNormalBinding(state); const SoCoordinateElement * coords; const SbVec3f * normals; const int32_t * cindices; int numindices; const int32_t * nindices; const int32_t * tindices; const int32_t * mindices; const int32_t * pindices; SbBool doTextures; SbBool normalCacheUsed; SoMaterialBundle mb(action); SoTextureCoordinateBundle tb(action, true, false); doTextures = tb.needCoordinates(); SbBool sendNormals = !mb.isColorOnly() || tb.isFunction(); this->getVertexData(state, coords, normals, cindices, nindices, tindices, mindices, numindices, sendNormals, normalCacheUsed); mb.sendFirst(); // make sure we have the correct material // just in case someone forgot if (!mindices) mindices = cindices; if (!nindices) nindices = cindices; pindices = this->partIndex.getValues(0); // materials mbind = OVERALL; doTextures = false; for (int i=0; i= this->partIndex.getNum()) { SoDebugError::postWarning("SoBrepFaceSet::renderSelection", "selectionIndex out of range"); break; } // coords int length = (int)pindices[id]*4; int start=0; for (int j=0;j(coords), &(cindices[start]), length, &(pindices[id]), 1, normals_s, nindices_s, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0); } state->pop(); if (normalCacheUsed) this->readUnlockNormalCache(); } void SoBrepFaceSet::renderShape(SoGLRenderAction * action, SbBool hasVBO, const SoGLCoordinateElement * const vertexlist, const int32_t *vertexindices, int num_indices, const int32_t *partindices, int num_partindices, const SbVec3f *normals, const int32_t *normalindices, SoMaterialBundle *const materials, const int32_t *matindices, SoTextureCoordinateBundle * const texcoords, const int32_t *texindices, const int nbind, const int mbind, const int texture) { int texidx = 0; const SbVec3f * coords3d = NULL; SbVec3f * cur_coords3d = NULL; SbColor mycolor1,mycolor2,mycolor3; coords3d = vertexlist->getArrayPtr3(); cur_coords3d = ( SbVec3f *)coords3d; const int32_t *viptr = vertexindices; const int32_t *viendptr = viptr + num_indices; const int32_t *piptr = partindices; const int32_t *piendptr = piptr + num_partindices; int32_t v1, v2, v3, v4, pi; SbVec3f dummynormal(0,0,1); int numverts = vertexlist->getNum(); const SbVec3f *currnormal = &dummynormal; if (normals) currnormal = normals; int matnr = 0; int trinr = 0; // Can we use vertex buffer objects? if (hasVBO) { float * vertex_array = NULL; GLuint * index_array = NULL; SbVec3f *mynormal1,*mynormal2,*mynormal3; int indice=0; uint32_t RGBA,R,G,B,A; float Rf,Gf,Bf,Af; VBO::Buffer buf; uint32_t contextId = action->getCacheContext(); std::map::iterator it = PRIVATE(this)->vbomap.find(contextId); if (it == PRIVATE(this)->vbomap.end()) { #ifdef FC_OS_WIN32 const cc_glglue * glue = cc_glglue_instance(action->getCacheContext()); PFNGLGENBUFFERSPROC glGenBuffersARB = (PFNGLGENBUFFERSPROC)cc_glglue_getprocaddress(glue, "glGenBuffersARB"); #endif glGenBuffersARB(2, buf.myvbo); PRIVATE(this)->vbomap[contextId] = buf; } else { buf = it->second; } // vbo loaded is defining if we must pre-load data into the VBO. When the variable is set to 0 // it means that the VBO has not been initialized // updateVbo is tracking the need to update the content of the VBO which act as a buffer within // the graphic card // TODO FINISHING THE COLOR SUPPORT ! if (!PRIVATE(this)->vboLoaded || PRIVATE(this)->updateVbo) { #ifdef FC_OS_WIN32 const cc_glglue * glue = cc_glglue_instance(action->getCacheContext()); #endif if (PRIVATE(this)->updateVbo && PRIVATE(this)->vboLoaded) { // TODO // We must remember the buffer size ... If it has to be extended we must // take care of that #ifdef FC_OS_WIN32 PFNGLBINDBUFFERARBPROC glBindBufferARB = (PFNGLBINDBUFFERARBPROC) cc_glglue_getprocaddress(glue, "glBindBufferARB"); PFNGLMAPBUFFERARBPROC glMapBufferARB = (PFNGLMAPBUFFERARBPROC) cc_glglue_getprocaddress(glue, "glMapBufferARB"); #endif glBindBufferARB(GL_ARRAY_BUFFER_ARB, buf.myvbo[0]); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, buf.myvbo[1]); vertex_array=(float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); index_array=(GLuint *)glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); PRIVATE(this)->indice_array=0; } else { // We are allocating local buffer to transfer initial VBO content vertex_array = ( float * ) malloc ( sizeof(float) * num_indices *10 ); index_array = ( GLuint *) malloc ( sizeof(GLuint) * num_indices *3 ); } // Get the initial colors SoState * state = action->getState(); mycolor1=SoLazyElement::getDiffuse(state,0); mycolor2=SoLazyElement::getDiffuse(state,0); mycolor3=SoLazyElement::getDiffuse(state,0); pi = piptr < piendptr ? *piptr++ : -1; while (pi == 0) { // It may happen that a part has no triangles pi = piptr < piendptr ? *piptr++ : -1; if (mbind == PER_PART) matnr++; else if (mbind == PER_PART_INDEXED) matindices++; } while (viptr + 2 < viendptr) { v1 = *viptr++; v2 = *viptr++; v3 = *viptr++; // This test is for robustness upon buggy data sets if (v1 < 0 || v2 < 0 || v3 < 0 || v1 >= numverts || v2 >= numverts || v3 >= numverts) { break; } v4 = viptr < viendptr ? *viptr++ : -1; (void)v4; if (mbind == PER_PART) { if (trinr == 0) { materials->send(matnr++, true); mycolor1=SoLazyElement::getDiffuse(state,matnr-1); mycolor2=mycolor1; mycolor3=mycolor1; } } else if (mbind == PER_PART_INDEXED) { if (trinr == 0) materials->send(*matindices++, true); } else if (mbind == PER_VERTEX || mbind == PER_FACE) { materials->send(matnr++, true); } else if (mbind == PER_VERTEX_INDEXED || mbind == PER_FACE_INDEXED) { materials->send(*matindices++, true); } if (normals) { if (nbind == PER_VERTEX || nbind == PER_FACE) { currnormal = normals++; mynormal1=(SbVec3f *)currnormal; } else if (nbind == PER_VERTEX_INDEXED || nbind == PER_FACE_INDEXED) { currnormal = &normals[*normalindices++]; mynormal1 =(SbVec3f *) currnormal; } } if (mbind == PER_VERTEX) materials->send(matnr++, true); else if (mbind == PER_VERTEX_INDEXED) materials->send(*matindices++, true); if (normals) { if (nbind == PER_VERTEX) { currnormal = normals++; mynormal2 = (SbVec3f *)currnormal; } else if (nbind == PER_VERTEX_INDEXED) { currnormal = &normals[*normalindices++]; mynormal2 = (SbVec3f *)currnormal; } } if (mbind == PER_VERTEX) materials->send(matnr++, true); else if (mbind == PER_VERTEX_INDEXED) materials->send(*matindices++, true); if (normals) { if (nbind == PER_VERTEX) { currnormal = normals++; mynormal3 =(SbVec3f *)currnormal; } else if (nbind == PER_VERTEX_INDEXED) { currnormal = &normals[*normalindices++]; mynormal3 = (SbVec3f *)currnormal; } } if (nbind == PER_VERTEX_INDEXED) normalindices++; /* We building the Vertex dataset there and push it to a VBO */ /* The Vertex array shall contain per element vertex_coordinates[3], normal_coordinates[3], color_value[3] (RGBA format) */ index_array[PRIVATE(this)->indice_array] = PRIVATE(this)->indice_array; index_array[PRIVATE(this)->indice_array+1] = PRIVATE(this)->indice_array + 1; index_array[PRIVATE(this)->indice_array+2] = PRIVATE(this)->indice_array + 2; PRIVATE(this)->indice_array += 3; ((SbVec3f *)(cur_coords3d+v1 ))->getValue(vertex_array[indice+0], vertex_array[indice+1], vertex_array[indice+2]); ((SbVec3f *)(mynormal1))->getValue(vertex_array[indice+3], vertex_array[indice+4], vertex_array[indice+5]); /* We decode the Vertex1 color */ RGBA = mycolor1.getPackedValue(); R = ( RGBA & 0xFF000000 ) >> 24 ; G = ( RGBA & 0xFF0000 ) >> 16; B = ( RGBA & 0xFF00 ) >> 8; A = ( RGBA & 0xFF ); Rf = (((float )R) / 255.0); Gf = (((float )G) / 255.0); Bf = (((float )B) / 255.0); Af = (((float )A) / 255.0); vertex_array[indice+6] = Rf; vertex_array[indice+7] = Gf; vertex_array[indice+8] = Bf; vertex_array[indice+9] = Af; indice+=10; ((SbVec3f *)(cur_coords3d+v2))->getValue(vertex_array[indice+0], vertex_array[indice+1], vertex_array[indice+2]); ((SbVec3f *)(mynormal2))->getValue(vertex_array[indice+3], vertex_array[indice+4], vertex_array[indice+5]); RGBA = mycolor2.getPackedValue(); R = ( RGBA & 0xFF000000 ) >> 24 ; G = ( RGBA & 0xFF0000 ) >> 16; B = ( RGBA & 0xFF00 ) >> 8; A = ( RGBA & 0xFF ); Rf = (((float )R) / 255.0); Gf = (((float )G) / 255.0); Bf = (((float )B) / 255.0); Af = (((float )A) / 255.0); vertex_array[indice+6] = Rf; vertex_array[indice+7] = Gf; vertex_array[indice+8] = Bf; vertex_array[indice+9] = Af; indice+=10; ((SbVec3f *)(cur_coords3d+v3))->getValue(vertex_array[indice+0], vertex_array[indice+1], vertex_array[indice+2]); ((SbVec3f *)(mynormal3))->getValue(vertex_array[indice+3], vertex_array[indice+4], vertex_array[indice+5]); RGBA = mycolor3.getPackedValue(); R = ( RGBA & 0xFF000000 ) >> 24 ; G = ( RGBA & 0xFF0000 ) >> 16; B = ( RGBA & 0xFF00 ) >> 8; A = ( RGBA & 0xFF ); Rf = (((float )R) / 255.0); Gf = (((float )G) / 255.0); Bf = (((float )B) / 255.0); Af = (((float )A) / 255.0); vertex_array[indice+6] = Rf; vertex_array[indice+7] = Gf; vertex_array[indice+8] = Bf; vertex_array[indice+9] = Af; indice+=10; /* ============================================================ */ trinr++; if (pi == trinr) { pi = piptr < piendptr ? *piptr++ : -1; while (pi == 0) { // It may happen that a part has no triangles pi = piptr < piendptr ? *piptr++ : -1; if (mbind == PER_PART) matnr++; else if (mbind == PER_PART_INDEXED) matindices++; } trinr = 0; } } if (!PRIVATE(this)->updateVbo || !PRIVATE(this)->vboLoaded) { // Push the content to the VBO #ifdef FC_OS_WIN32 PFNGLBINDBUFFERARBPROC glBindBufferARB = (PFNGLBINDBUFFERARBPROC)cc_glglue_getprocaddress(glue, "glBindBufferARB"); PFNGLBUFFERDATAARBPROC glBufferDataARB = (PFNGLBUFFERDATAARBPROC)cc_glglue_getprocaddress(glue, "glBufferDataARB"); #endif glBindBufferARB(GL_ARRAY_BUFFER_ARB, buf.myvbo[0]); glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(float) * indice , vertex_array, GL_DYNAMIC_DRAW_ARB); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, buf.myvbo[1]); glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(GLuint) * PRIVATE(this)->indice_array , &index_array[0], GL_DYNAMIC_DRAW_ARB); glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); PRIVATE(this)->vboLoaded = true; PRIVATE(this)->updateVbo = false; free(vertex_array); free(index_array); } else { #ifdef FC_OS_WIN32 PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)cc_glglue_getprocaddress(glue, "glUnmapBufferARB"); #endif glUnmapBufferARB(GL_ARRAY_BUFFER_ARB); glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB); PRIVATE(this)->updateVbo = false; } } // This is the VBO rendering code #ifdef FC_OS_WIN32 const cc_glglue * glue = cc_glglue_instance(action->getCacheContext()); PFNGLBINDBUFFERARBPROC glBindBufferARB = (PFNGLBINDBUFFERARBPROC)cc_glglue_getprocaddress(glue, "glBindBufferARB"); #endif glBindBufferARB(GL_ARRAY_BUFFER_ARB, buf.myvbo[0]); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, buf.myvbo[1]); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glVertexPointer(3,GL_FLOAT,10*sizeof(GLfloat),0); glNormalPointer(GL_FLOAT,10*sizeof(GLfloat),(GLvoid *)(3*sizeof(GLfloat))); glColorPointer(4,GL_FLOAT,10*sizeof(GLfloat),(GLvoid *)(6*sizeof(GLfloat))); glDrawElements(GL_TRIANGLES, PRIVATE(this)->indice_array, GL_UNSIGNED_INT, (void *)0); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); // The data is within the VBO we can clear it at application level return; } // Legacy code without VBO support pi = piptr < piendptr ? *piptr++ : -1; while (pi == 0) { // It may happen that a part has no triangles pi = piptr < piendptr ? *piptr++ : -1; if (mbind == PER_PART) matnr++; else if (mbind == PER_PART_INDEXED) matindices++; } glBegin(GL_TRIANGLES); while (viptr + 2 < viendptr) { v1 = *viptr++; v2 = *viptr++; v3 = *viptr++; if (v1 < 0 || v2 < 0 || v3 < 0 || v1 >= numverts || v2 >= numverts || v3 >= numverts) { break; } v4 = viptr < viendptr ? *viptr++ : -1; (void)v4; /* vertex 1 *********************************************************/ if (mbind == PER_PART) { if (trinr == 0) materials->send(matnr++, true); } else if (mbind == PER_PART_INDEXED) { if (trinr == 0) materials->send(*matindices++, true); } else if (mbind == PER_VERTEX || mbind == PER_FACE) { materials->send(matnr++, true); } else if (mbind == PER_VERTEX_INDEXED || mbind == PER_FACE_INDEXED) { materials->send(*matindices++, true); } if (normals) { if (nbind == PER_VERTEX || nbind == PER_FACE) { currnormal = normals++; glNormal3fv((const GLfloat*)currnormal); } else if (nbind == PER_VERTEX_INDEXED || nbind == PER_FACE_INDEXED) { currnormal = &normals[*normalindices++]; glNormal3fv((const GLfloat*)currnormal); } } if (texture) { texcoords->send(texindices ? *texindices++ : texidx++, vertexlist->get3(v1), *currnormal); } glVertex3fv((const GLfloat*) (coords3d + v1)); /* vertex 2 *********************************************************/ if (mbind == PER_VERTEX) materials->send(matnr++, true); else if (mbind == PER_VERTEX_INDEXED) materials->send(*matindices++, true); if (normals) { if (nbind == PER_VERTEX) { currnormal = normals++; glNormal3fv((const GLfloat*)currnormal); } else if (nbind == PER_VERTEX_INDEXED) { currnormal = &normals[*normalindices++]; glNormal3fv((const GLfloat*)currnormal); } } if (texture) { texcoords->send(texindices ? *texindices++ : texidx++, vertexlist->get3(v2), *currnormal); } glVertex3fv((const GLfloat*) (coords3d + v2)); /* vertex 3 *********************************************************/ if (mbind == PER_VERTEX) materials->send(matnr++, true); else if (mbind == PER_VERTEX_INDEXED) materials->send(*matindices++, true); if (normals) { if (nbind == PER_VERTEX) { currnormal = normals++; glNormal3fv((const GLfloat*)currnormal); } else if (nbind == PER_VERTEX_INDEXED) { currnormal = &normals[*normalindices++]; glNormal3fv((const GLfloat*)currnormal); } } if (texture) { texcoords->send(texindices ? *texindices++ : texidx++, vertexlist->get3(v3), *currnormal); } glVertex3fv((const GLfloat*) (coords3d + v3)); if (mbind == PER_VERTEX_INDEXED) matindices++; if (nbind == PER_VERTEX_INDEXED) normalindices++; if (texture && texindices) { texindices++; } trinr++; if (pi == trinr) { pi = piptr < piendptr ? *piptr++ : -1; while (pi == 0) { // It may happen that a part has no triangles pi = piptr < piendptr ? *piptr++ : -1; if (mbind == PER_PART) matnr++; else if (mbind == PER_PART_INDEXED) matindices++; } trinr = 0; } } glEnd(); } SoDetail * SoBrepFaceSet::createTriangleDetail(SoRayPickAction * action, const SoPrimitiveVertex * v1, const SoPrimitiveVertex * v2, const SoPrimitiveVertex * v3, SoPickedPoint * pp) { SoDetail* detail = inherited::createTriangleDetail(action, v1, v2, v3, pp); const int32_t * indices = this->partIndex.getValues(0); int num = this->partIndex.getNum(); if (indices) { SoFaceDetail* face_detail = static_cast(detail); int index = face_detail->getFaceIndex(); int count = 0; for (int i=0; isetPartIndex(i); break; } } } return detail; } SoBrepFaceSet::Binding SoBrepFaceSet::findMaterialBinding(SoState * const state) const { Binding binding = OVERALL; SoMaterialBindingElement::Binding matbind = SoMaterialBindingElement::get(state); switch (matbind) { case SoMaterialBindingElement::OVERALL: binding = OVERALL; break; case SoMaterialBindingElement::PER_VERTEX: binding = PER_VERTEX; break; case SoMaterialBindingElement::PER_VERTEX_INDEXED: binding = PER_VERTEX_INDEXED; break; case SoMaterialBindingElement::PER_PART: binding = PER_PART; break; case SoMaterialBindingElement::PER_FACE: binding = PER_FACE; break; case SoMaterialBindingElement::PER_PART_INDEXED: binding = PER_PART_INDEXED; break; case SoMaterialBindingElement::PER_FACE_INDEXED: binding = PER_FACE_INDEXED; break; default: break; } return binding; } SoBrepFaceSet::Binding SoBrepFaceSet::findNormalBinding(SoState * const state) const { Binding binding = PER_VERTEX_INDEXED; SoNormalBindingElement::Binding normbind = (SoNormalBindingElement::Binding) SoNormalBindingElement::get(state); switch (normbind) { case SoNormalBindingElement::OVERALL: binding = OVERALL; break; case SoNormalBindingElement::PER_VERTEX: binding = PER_VERTEX; break; case SoNormalBindingElement::PER_VERTEX_INDEXED: binding = PER_VERTEX_INDEXED; break; case SoNormalBindingElement::PER_PART: binding = PER_PART; break; case SoNormalBindingElement::PER_FACE: binding = PER_FACE; break; case SoNormalBindingElement::PER_PART_INDEXED: binding = PER_PART_INDEXED; break; case SoNormalBindingElement::PER_FACE_INDEXED: binding = PER_FACE_INDEXED; break; default: break; } return binding; } #undef PRIVATE