DrawCount PointArray::drawPoints(QGLShaderProgram& prog, const TransformState& transState, double quality, bool incrementalDraw) const { TransformState relativeTrans = transState.translate(offset()); relativeTrans.setUniforms(prog.programId()); //printActiveShaderAttributes(prog.programId()); std::vector<ShaderAttribute> activeAttrs = activeShaderAttributes(prog.programId()); // Figure out shader locations for each point field // TODO: attributeLocation() forces the OpenGL usage here to be // synchronous. Does this matter? (Alternative: bind them ourselves.) std::vector<const ShaderAttribute*> attributes; for (size_t i = 0; i < m_fields.size(); ++i) { const GeomField& field = m_fields[i]; if (field.spec.isArray()) { for (int j = 0; j < field.spec.count; ++j) { std::string name = tfm::format("%s[%d]", field.name, j); attributes.push_back(findAttr(name, activeAttrs)); } } else { attributes.push_back(findAttr(field.name, activeAttrs)); } } // Zero out active attributes in case they don't have associated fields GLfloat zeros[16] = {0}; for (size_t i = 0; i < activeAttrs.size(); ++i) { prog.setAttributeValue((int)i, zeros, activeAttrs[i].rows, activeAttrs[i].cols); } // Enable attributes which have associated fields for (size_t i = 0; i < attributes.size(); ++i) { if (attributes[i]) prog.enableAttributeArray(attributes[i]->location); } DrawCount drawCount; ClipBox clipBox(relativeTrans); // Draw points in each bucket, with total number drawn depending on how far // away the bucket is. Since the points are shuffled, this corresponds to // a stochastic simplification of the full point cloud. V3f relCamera = relativeTrans.cameraPos(); std::vector<const OctreeNode*> nodeStack; nodeStack.push_back(m_rootNode.get()); while (!nodeStack.empty()) { const OctreeNode* node = nodeStack.back(); nodeStack.pop_back(); if (clipBox.canCull(node->bbox)) continue; if (!node->isLeaf()) { for (int i = 0; i < 8; ++i) { OctreeNode* n = node->children[i]; if (n) nodeStack.push_back(n); } continue; } size_t idx = node->beginIndex; if (!incrementalDraw) node->nextBeginIndex = node->beginIndex; DrawCount nodeDrawCount = node->drawCount(relCamera, quality, incrementalDraw); drawCount += nodeDrawCount; idx = node->nextBeginIndex; if (nodeDrawCount.numVertices == 0) continue; for (size_t i = 0, k = 0; i < m_fields.size(); k+=m_fields[i].spec.arraySize(), ++i) { const GeomField& field = m_fields[i]; int arraySize = field.spec.arraySize(); int vecSize = field.spec.vectorSize(); for (int j = 0; j < arraySize; ++j) { const ShaderAttribute* attr = attributes[k+j]; if (!attr) continue; char* data = field.data.get() + idx*field.spec.size() + j*field.spec.elsize; if (attr->baseType == TypeSpec::Int || attr->baseType == TypeSpec::Uint) { glVertexAttribIPointer(attr->location, vecSize, glBaseType(field.spec), 0, data); } else { glVertexAttribPointer(attr->location, vecSize, glBaseType(field.spec), field.spec.fixedPoint, 0, data); } } } glDrawArrays(GL_POINTS, 0, (GLsizei)nodeDrawCount.numVertices); node->nextBeginIndex += nodeDrawCount.numVertices; } //tfm::printf("Drew %d of total points %d, quality %f\n", totDraw, m_npoints, quality); // Disable all attribute arrays - leaving these enabled seems to screw with // the OpenGL fixed function pipeline in unusual ways. for (size_t i = 0; i < attributes.size(); ++i) { if (attributes[i]) prog.disableAttributeArray(attributes[i]->location); } return drawCount; }
DrawCount PointArray::drawPoints(QGLShaderProgram& prog, const TransformState& transState, double quality, bool incrementalDraw) const { GLuint vao = getVAO("points"); glBindVertexArray(vao); GLuint vbo = getVBO("point_buffer"); glBindBuffer(GL_ARRAY_BUFFER, vbo); TransformState relativeTrans = transState.translate(offset()); relativeTrans.setUniforms(prog.programId()); //printActiveShaderAttributes(prog.programId()); std::vector<ShaderAttribute> activeAttrs = activeShaderAttributes(prog.programId()); // Figure out shader locations for each point field // TODO: attributeLocation() forces the OpenGL usage here to be // synchronous. Does this matter? (Alternative: bind them ourselves.) std::vector<const ShaderAttribute*> attributes; for (size_t i = 0; i < m_fields.size(); ++i) { const GeomField& field = m_fields[i]; if (field.spec.isArray()) { for (int j = 0; j < field.spec.count; ++j) { std::string name = tfm::format("%s[%d]", field.name, j); attributes.push_back(findAttr(name, activeAttrs)); } } else { attributes.push_back(findAttr(field.name, activeAttrs)); } } // Zero out active attributes in case they don't have associated fields GLfloat zeros[16] = {0}; for (size_t i = 0; i < activeAttrs.size(); ++i) { prog.setAttributeValue((int)i, zeros, activeAttrs[i].rows, activeAttrs[i].cols); } // Enable attributes which have associated fields for (size_t i = 0; i < attributes.size(); ++i) { if (attributes[i]) prog.enableAttributeArray(attributes[i]->location); } DrawCount drawCount; ClipBox clipBox(relativeTrans); // Draw points in each bucket, with total number drawn depending on how far // away the bucket is. Since the points are shuffled, this corresponds to // a stochastic simplification of the full point cloud. V3f relCamera = relativeTrans.cameraPos(); std::vector<const OctreeNode*> nodeStack; nodeStack.push_back(m_rootNode.get()); while (!nodeStack.empty()) { const OctreeNode* node = nodeStack.back(); nodeStack.pop_back(); if (clipBox.canCull(node->bbox)) continue; if (!node->isLeaf()) { for (int i = 0; i < 8; ++i) { OctreeNode* n = node->children[i]; if (n) nodeStack.push_back(n); } continue; } size_t idx = node->beginIndex; if (!incrementalDraw) node->nextBeginIndex = node->beginIndex; DrawCount nodeDrawCount = node->drawCount(relCamera, quality, incrementalDraw); drawCount += nodeDrawCount; idx = node->nextBeginIndex; if (nodeDrawCount.numVertices == 0) continue; if (m_fields.size() < 1) continue; long bufferSize = 0; for (size_t i = 0; i < m_fields.size(); ++i) { const GeomField &field = m_fields[i]; unsigned int arraySize = field.spec.arraySize(); unsigned int vecSize = field.spec.vectorSize(); // tfm::printfln("FIELD-NAME: %s", field.name); // tfm::printfln("AS: %i, VS: %i, FSS: %i, FSES: %i, GLBTFSS: %i", arraySize, vecSize, field.spec.size(), field.spec.elsize, sizeof(glBaseType(field.spec))); bufferSize += arraySize * vecSize * field.spec.elsize; //sizeof(glBaseType(field.spec)); } bufferSize = bufferSize * (GLsizei)nodeDrawCount.numVertices; // TODO: might be able to do something more efficient here, for example use glBufferSubData to avoid re-allocation of memory by glBufferData // INITIALIZE THE BUFFER TO FULL SIZE // tfm::printfln("INIT BUFFER: %i, BS: %i", vbo, bufferSize); glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)bufferSize, NULL, GL_STREAM_DRAW); /// ======================================================================== /// ======================================================================== GLintptr bufferOffset = 0; for (size_t i = 0, k = 0; i < m_fields.size(); k+=m_fields[i].spec.arraySize(), ++i) { const GeomField& field = m_fields[i]; int arraySize = field.spec.arraySize(); int vecSize = field.spec.vectorSize(); // TODO: should use a single data-array that isn't split into vertex / normal / color / etc. sections, but has interleaved data ? // OpenGL has a stride value in glVertexAttribPointer for exactly this purpose, which should be used for better efficiency // here we write only the current attribute data into this the buffer (e.g. all positions, then all colors) bufferSize = arraySize * vecSize * field.spec.elsize * (GLsizei)nodeDrawCount.numVertices; //sizeof(glBaseType(field.spec)) char* bufferData = field.data.get() + idx*field.spec.size(); glBufferSubData(GL_ARRAY_BUFFER, bufferOffset, bufferSize, bufferData); // tfm::printfln("UPDATE BUFFER: %i, BS: %i", vbo, bufferSize); for (int j = 0; j < arraySize; ++j) { const ShaderAttribute* attr = attributes[k+j]; if (!attr) continue; // we have to create an intermediate buffer offsets for glVertexAttribPointer, but we can still upload the whole data array earlier !? GLintptr intermediate_bufferOffset = bufferOffset + j*field.spec.elsize; if (attr->baseType == TypeSpec::Int || attr->baseType == TypeSpec::Uint) { glVertexAttribIPointer(attr->location, vecSize, glBaseType(field.spec), 0, (const GLvoid *)intermediate_bufferOffset); } else { glVertexAttribPointer(attr->location, vecSize, glBaseType(field.spec), field.spec.fixedPoint, 0, (const GLvoid *)intermediate_bufferOffset); } glEnableVertexAttribArray(attr->location); } bufferOffset += bufferSize; } glDrawArrays(GL_POINTS, 0, (GLsizei)nodeDrawCount.numVertices); node->nextBeginIndex += nodeDrawCount.numVertices; } //tfm::printf("Drew %d of total points %d, quality %f\n", totDraw, m_npoints, quality); // Disable all attribute arrays - leaving these enabled seems to screw with // the OpenGL fixed function pipeline in unusual ways. for (size_t i = 0; i < attributes.size(); ++i) { if (attributes[i]) prog.disableAttributeArray(attributes[i]->location); } glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); return drawCount; }