// // KRMeshManager.cpp // KREngine // // Copyright 2012 Kearwood Gilbert. All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // The views and conclusions contained in the software and documentation are those of the // authors and should not be interpreted as representing official policies, either expressed // or implied, of Kearwood Gilbert. // #include "KREngine-common.h" #include "KRMeshManager.h" #include "KRMesh.h" #include "KRMeshCube.h" #include "KRMeshQuad.h" #include "KRMeshSphere.h" KRMeshManager::KRMeshManager(KRContext &context) : KRContextObject(context) { m_currentVBO = NULL; m_vboMemUsed = 0; m_memoryTransferredThisFrame = 0; m_first_frame = true; m_streamerComplete = true; addModel(new KRMeshCube(context)); // FINDME - HACK! This needs to be fixed, as it currently segfaults addModel(new KRMeshQuad(context)); // FINDME - HACK! This needs to be fixed, as it currently segfaults addModel(new KRMeshSphere(context)); m_draw_call_logging_enabled = false; m_draw_call_log_used = false; // ---- Initialize stock models ---- static const GLfloat _KRENGINE_VBO_3D_CUBE_VERTEX_DATA[] = { 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0,-1.0, 1.0, -1.0,-1.0, 1.0, -1.0,-1.0,-1.0, -1.0, 1.0, 1.0, -1.0, 1.0,-1.0, 1.0, 1.0, 1.0, 1.0, 1.0,-1.0, 1.0,-1.0, 1.0, 1.0,-1.0,-1.0, -1.0,-1.0,-1.0, 1.0, 1.0,-1.0, -1.0, 1.0,-1.0 }; KRENGINE_VBO_3D_CUBE_ATTRIBS = (1 << KRMesh::KRENGINE_ATTRIB_VERTEX); KRENGINE_VBO_3D_CUBE_VERTICES.expand(sizeof(GLfloat) * 3 * 14); KRENGINE_VBO_3D_CUBE_VERTICES.lock(); memcpy(KRENGINE_VBO_3D_CUBE_VERTICES.getStart(), _KRENGINE_VBO_3D_CUBE_VERTEX_DATA, sizeof(GLfloat) * 3 * 14); KRENGINE_VBO_3D_CUBE_VERTICES.unlock(); KRENGINE_VBO_DATA_3D_CUBE_VERTICES.init(this, KRENGINE_VBO_3D_CUBE_VERTICES, KRENGINE_VBO_3D_CUBE_INDEXES, KRENGINE_VBO_3D_CUBE_ATTRIBS, false, KRVBOData::CONSTANT); static const GLfloat _KRENGINE_VBO_2D_SQUARE_VERTEX_DATA[] = { -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f }; KRENGINE_VBO_2D_SQUARE_ATTRIBS = (1 << KRMesh::KRENGINE_ATTRIB_VERTEX) | (1 << KRMesh::KRENGINE_ATTRIB_TEXUVA); KRENGINE_VBO_2D_SQUARE_VERTICES.expand(sizeof(GLfloat) * 5 * 4); KRENGINE_VBO_2D_SQUARE_VERTICES.lock(); memcpy(KRENGINE_VBO_2D_SQUARE_VERTICES.getStart(), _KRENGINE_VBO_2D_SQUARE_VERTEX_DATA, sizeof(GLfloat) * 5 * 4); KRENGINE_VBO_2D_SQUARE_VERTICES.unlock(); KRENGINE_VBO_DATA_2D_SQUARE_VERTICES.init(this, KRENGINE_VBO_2D_SQUARE_VERTICES, KRENGINE_VBO_2D_SQUARE_INDEXES, KRENGINE_VBO_2D_SQUARE_ATTRIBS, false, KRVBOData::CONSTANT); } KRMeshManager::~KRMeshManager() { for(unordered_multimap::iterator itr = m_models.begin(); itr != m_models.end(); ++itr){ delete (*itr).second; } m_models.empty(); } KRMesh *KRMeshManager::loadModel(const char *szName, KRDataBlock *pData) { KRMesh *pModel = new KRMesh(*m_pContext, szName, pData); addModel(pModel); return pModel; } void KRMeshManager::addModel(KRMesh *model) { std::string lowerName = model->getLODBaseName(); std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower); m_models.insert(std::pair(lowerName, model)); } std::vector KRMeshManager::getModel(const char *szName) { std::string lowerName = szName; std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower); std::vector matching_models; std::pair::iterator, unordered_multimap::iterator> range = m_models.equal_range(lowerName); for(unordered_multimap::iterator itr_match = range.first; itr_match != range.second; itr_match++) { matching_models.push_back(itr_match->second); } std::sort(matching_models.begin(), matching_models.end(), KRMesh::lod_sort_predicate); if(matching_models.size() == 0) { KRContext::Log(KRContext::LOG_LEVEL_INFORMATION, "Model not found: %s", lowerName.c_str()); } return matching_models; } unordered_multimap &KRMeshManager::getModels() { return m_models; } void KRMeshManager::unbindVBO() { if(m_currentVBO != NULL) { GLDEBUG(glBindBuffer(GL_ARRAY_BUFFER, 0)); GLDEBUG(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); m_currentVBO = NULL; } } void KRMeshManager::bindVBO(KRVBOData *vbo_data, float lodCoverage) { vbo_data->resetPoolExpiry(lodCoverage); bool vbo_changed = false; if(m_currentVBO == NULL) { vbo_changed = true; } else if(m_currentVBO->m_data != vbo_data->m_data) { vbo_changed = true; } bool used_vbo_data = false; if(vbo_changed) { if(m_vbosActive.find(vbo_data->m_data) != m_vbosActive.end()) { m_currentVBO = m_vbosActive[vbo_data->m_data]; } else { used_vbo_data = true; m_currentVBO = vbo_data; m_vbosActive[vbo_data->m_data] = m_currentVBO; } m_currentVBO->bind(); } if(!used_vbo_data && vbo_data->getType() == KRVBOData::TEMPORARY) { delete vbo_data; } } void KRMeshManager::startFrame(float deltaTime) { m_memoryTransferredThisFrame = 0; if(m_draw_call_log_used) { // Only log draw calls on the next frame if the draw call log was used on last frame m_draw_call_log_used = false; m_draw_call_logging_enabled = true; } m_draw_calls.clear(); if(m_first_frame) { m_first_frame = false; firstFrame(); } // TODO - Implement proper double-buffering to reduce copy operations m_streamerFenceMutex.lock(); if(m_streamerComplete) { assert(m_activeVBOs_streamer_copy.size() == 0); // The streamer should have emptied this if it really did complete const long KRENGINE_VBO_EXPIRY_FRAMES = 1; std::set expiredVBOs; for(auto itr=m_vbosActive.begin(); itr != m_vbosActive.end(); itr++) { KRVBOData *activeVBO = (*itr).second; activeVBO->_swapHandles(); if(activeVBO->getLastFrameUsed() + KRENGINE_VBO_EXPIRY_FRAMES < getContext().getCurrentFrame()) { // Expire VBO's that haven't been used in a long time switch(activeVBO->getType()) { case KRVBOData::STREAMING: activeVBO->unload(); break; case KRVBOData::TEMPORARY: delete activeVBO; break; case KRVBOData::CONSTANT: // CONSTANT VBO's are not unloaded break; } expiredVBOs.insert(activeVBO); } else { if(activeVBO->getType() == KRVBOData::STREAMING) { float priority = activeVBO->getStreamPriority(); m_activeVBOs_streamer_copy.push_back(std::pair(priority, activeVBO)); } } } for(std::set::iterator itr=expiredVBOs.begin(); itr != expiredVBOs.end(); itr++) { m_vbosActive.erase((*itr)->m_data); } if(m_activeVBOs_streamer_copy.size() > 0) { m_streamerComplete = false; } } m_streamerFenceMutex.unlock(); } void KRMeshManager::endFrame(float deltaTime) { } void KRMeshManager::firstFrame() { KRENGINE_VBO_DATA_3D_CUBE_VERTICES.load(); KRENGINE_VBO_DATA_2D_SQUARE_VERTICES.load(); getModel("__sphere")[0]->load(); getModel("__cube")[0]->load(); getModel("__quad")[0]->load(); } void KRMeshManager::doStreaming(long &memoryRemaining, long &memoryRemainingThisFrame) { // TODO - Implement proper double-buffering to reduce copy operations m_streamerFenceMutex.lock(); m_activeVBOs_streamer = std::move(m_activeVBOs_streamer_copy); m_streamerFenceMutex.unlock(); if(m_activeVBOs_streamer.size() > 0) { balanceVBOMemory(memoryRemaining, memoryRemainingThisFrame); m_streamerFenceMutex.lock(); m_streamerComplete = true; m_streamerFenceMutex.unlock(); } } void KRMeshManager::balanceVBOMemory(long &memoryRemaining, long &memoryRemainingThisFrame) { std::sort(m_activeVBOs_streamer.begin(), m_activeVBOs_streamer.end(), std::greater>()); for(auto vbo_itr = m_activeVBOs_streamer.begin(); memoryRemainingThisFrame > 0 && vbo_itr != m_activeVBOs_streamer.end(); vbo_itr++) { KRVBOData *vbo_data = (*vbo_itr).second; if(!vbo_data->isVBOLoaded()) { long vbo_size = vbo_data->getSize(); if(memoryRemainingThisFrame > vbo_size) { vbo_data->load(); memoryRemainingThisFrame -= vbo_size; } memoryRemaining -= vbo_size; } } glFinish(); } void KRMeshManager::bindVBO(KRDataBlock &data, KRDataBlock &index_data, int vertex_attrib_flags, bool static_vbo, float lodCoverage) { KRVBOData *vbo_data = new KRVBOData(this, data, index_data, vertex_attrib_flags, static_vbo, KRVBOData::TEMPORARY); vbo_data->load(); bindVBO(vbo_data, lodCoverage); } void KRMeshManager::configureAttribs(__int32_t attributes) { GLsizei data_size = (GLsizei)KRMesh::VertexSizeForAttributes(attributes); if(KRMesh::has_vertex_attribute(attributes, KRMesh::KRENGINE_ATTRIB_VERTEX_SHORT)) { GLDEBUG(glEnableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_VERTEX)); GLDEBUG(glVertexAttribPointer(KRMesh::KRENGINE_ATTRIB_VERTEX, 3, GL_SHORT, GL_TRUE, data_size, BUFFER_OFFSET(KRMesh::AttributeOffset(KRMesh::KRENGINE_ATTRIB_VERTEX_SHORT, attributes)))); } else if(KRMesh::has_vertex_attribute(attributes, KRMesh::KRENGINE_ATTRIB_VERTEX)) { GLDEBUG(glEnableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_VERTEX)); GLDEBUG(glVertexAttribPointer(KRMesh::KRENGINE_ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, data_size, BUFFER_OFFSET(KRMesh::AttributeOffset(KRMesh::KRENGINE_ATTRIB_VERTEX, attributes)))); } else { GLDEBUG(glDisableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_VERTEX)); } if(KRMesh::has_vertex_attribute(attributes, KRMesh::KRENGINE_ATTRIB_NORMAL_SHORT)) { GLDEBUG(glEnableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_NORMAL)); GLDEBUG(glVertexAttribPointer(KRMesh::KRENGINE_ATTRIB_NORMAL, 3, GL_SHORT, GL_TRUE, data_size, BUFFER_OFFSET(KRMesh::AttributeOffset(KRMesh::KRENGINE_ATTRIB_NORMAL_SHORT, attributes)))); } else if(KRMesh::has_vertex_attribute(attributes, KRMesh::KRENGINE_ATTRIB_NORMAL)) { GLDEBUG(glEnableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_NORMAL)); GLDEBUG(glVertexAttribPointer(KRMesh::KRENGINE_ATTRIB_NORMAL, 3, GL_FLOAT, GL_FALSE, data_size, BUFFER_OFFSET(KRMesh::AttributeOffset(KRMesh::KRENGINE_ATTRIB_NORMAL, attributes)))); } else { GLDEBUG(glDisableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_NORMAL)); } if(KRMesh::has_vertex_attribute(attributes, KRMesh::KRENGINE_ATTRIB_TANGENT_SHORT)) { GLDEBUG(glEnableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_TANGENT)); GLDEBUG(glVertexAttribPointer(KRMesh::KRENGINE_ATTRIB_TANGENT, 3, GL_SHORT, GL_TRUE, data_size, BUFFER_OFFSET(KRMesh::AttributeOffset(KRMesh::KRENGINE_ATTRIB_TANGENT_SHORT, attributes)))); } else if(KRMesh::has_vertex_attribute(attributes, KRMesh::KRENGINE_ATTRIB_TANGENT)) { GLDEBUG(glEnableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_TANGENT)); GLDEBUG(glVertexAttribPointer(KRMesh::KRENGINE_ATTRIB_TANGENT, 3, GL_FLOAT, GL_FALSE, data_size, BUFFER_OFFSET(KRMesh::AttributeOffset(KRMesh::KRENGINE_ATTRIB_TANGENT, attributes)))); } else { GLDEBUG(glDisableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_TANGENT)); } if(KRMesh::has_vertex_attribute(attributes, KRMesh::KRENGINE_ATTRIB_TEXUVA_SHORT)) { GLDEBUG(glEnableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_TEXUVA)); GLDEBUG(glVertexAttribPointer(KRMesh::KRENGINE_ATTRIB_TEXUVA, 2, GL_SHORT, GL_TRUE, data_size, BUFFER_OFFSET(KRMesh::AttributeOffset(KRMesh::KRENGINE_ATTRIB_TEXUVA_SHORT, attributes)))); } else if(KRMesh::has_vertex_attribute(attributes, KRMesh::KRENGINE_ATTRIB_TEXUVA)) { GLDEBUG(glEnableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_TEXUVA)); GLDEBUG(glVertexAttribPointer(KRMesh::KRENGINE_ATTRIB_TEXUVA, 2, GL_FLOAT, GL_FALSE, data_size, BUFFER_OFFSET(KRMesh::AttributeOffset(KRMesh::KRENGINE_ATTRIB_TEXUVA, attributes)))); } else { GLDEBUG(glDisableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_TEXUVA)); } if(KRMesh::has_vertex_attribute(attributes, KRMesh::KRENGINE_ATTRIB_TEXUVB_SHORT)) { GLDEBUG(glEnableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_TEXUVB)); GLDEBUG(glVertexAttribPointer(KRMesh::KRENGINE_ATTRIB_TEXUVB, 2, GL_SHORT, GL_TRUE, data_size, BUFFER_OFFSET(KRMesh::AttributeOffset(KRMesh::KRENGINE_ATTRIB_TEXUVB_SHORT, attributes)))); } else if(KRMesh::has_vertex_attribute(attributes, KRMesh::KRENGINE_ATTRIB_TEXUVB)) { GLDEBUG(glEnableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_TEXUVB)); GLDEBUG(glVertexAttribPointer(KRMesh::KRENGINE_ATTRIB_TEXUVB, 2, GL_FLOAT, GL_FALSE, data_size, BUFFER_OFFSET(KRMesh::AttributeOffset(KRMesh::KRENGINE_ATTRIB_TEXUVB, attributes)))); } else { GLDEBUG(glDisableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_TEXUVB)); } if(KRMesh::has_vertex_attribute(attributes, KRMesh::KRENGINE_ATTRIB_BONEINDEXES)) { GLDEBUG(glEnableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_BONEINDEXES)); GLDEBUG(glVertexAttribPointer(KRMesh::KRENGINE_ATTRIB_BONEINDEXES, 4, GL_UNSIGNED_BYTE, GL_FALSE, data_size, BUFFER_OFFSET(KRMesh::AttributeOffset(KRMesh::KRENGINE_ATTRIB_BONEINDEXES, attributes)))); } else { GLDEBUG(glDisableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_BONEINDEXES)); } if(KRMesh::has_vertex_attribute(attributes, KRMesh::KRENGINE_ATTRIB_BONEWEIGHTS)) { GLDEBUG(glEnableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_BONEWEIGHTS)); GLDEBUG(glVertexAttribPointer(KRMesh::KRENGINE_ATTRIB_BONEWEIGHTS, 4, GL_FLOAT, GL_FALSE, data_size, BUFFER_OFFSET(KRMesh::AttributeOffset(KRMesh::KRENGINE_ATTRIB_BONEWEIGHTS, attributes)))); } else { GLDEBUG(glDisableVertexAttribArray(KRMesh::KRENGINE_ATTRIB_BONEWEIGHTS)); } } long KRMeshManager::getMemUsed() { return m_vboMemUsed; } long KRMeshManager::getMemActive() { long mem_active = 0; for(unordered_map::iterator itr = m_vbosActive.begin(); itr != m_vbosActive.end(); itr++) { mem_active += (*itr).second->getSize(); } return mem_active; } KRDataBlock &KRMeshManager::getVolumetricLightingVertexes() { if(m_volumetricLightingVertexData.getSize() == 0) { m_volumetricLightingVertexData.expand(sizeof(VolumetricLightingVertexData) * KRENGINE_MAX_VOLUMETRIC_PLANES * 6); m_volumetricLightingVertexData.lock(); VolumetricLightingVertexData * vertex_data = (VolumetricLightingVertexData *)m_volumetricLightingVertexData.getStart(); int iVertex=0; for(int iPlane=0; iPlane < KRENGINE_MAX_VOLUMETRIC_PLANES; iPlane++) { vertex_data[iVertex].vertex.x = -1.0f; vertex_data[iVertex].vertex.y = -1.0f; vertex_data[iVertex].vertex.z = iPlane; iVertex++; vertex_data[iVertex].vertex.x = 1.0f; vertex_data[iVertex].vertex.y = -1.0f; vertex_data[iVertex].vertex.z = iPlane; iVertex++; vertex_data[iVertex].vertex.x = -1.0f; vertex_data[iVertex].vertex.y = 1.0f; vertex_data[iVertex].vertex.z = iPlane; iVertex++; vertex_data[iVertex].vertex.x = -1.0f; vertex_data[iVertex].vertex.y = 1.0f; vertex_data[iVertex].vertex.z = iPlane; iVertex++; vertex_data[iVertex].vertex.x = 1.0f; vertex_data[iVertex].vertex.y = -1.0f; vertex_data[iVertex].vertex.z = iPlane; iVertex++; vertex_data[iVertex].vertex.x = 1.0f; vertex_data[iVertex].vertex.y = 1.0f; vertex_data[iVertex].vertex.z = iPlane; iVertex++; } m_volumetricLightingVertexData.unlock(); } return m_volumetricLightingVertexData; } KRDataBlock &KRMeshManager::getRandomParticles() { if(m_randomParticleVertexData.getSize() == 0) { m_randomParticleVertexData.expand(sizeof(RandomParticleVertexData) * KRENGINE_MAX_RANDOM_PARTICLES * 3); m_randomParticleVertexData.lock(); RandomParticleVertexData *vertex_data = (RandomParticleVertexData *)m_randomParticleVertexData.getStart(); // Generate vertices for randomly placed equilateral triangles with a side length of 1 and an origin point centered so that an inscribed circle can be efficiently rendered without wasting fill float equilateral_triangle_height = sqrt(3.0f) * 0.5f; float inscribed_circle_radius = 1.0f / (2.0f * sqrt(3.0f)); int iVertex=0; for(int iParticle=0; iParticle < KRENGINE_MAX_RANDOM_PARTICLES; iParticle++) { vertex_data[iVertex].vertex.x = (float)(arc4random() % 2000) / 1000.0f - 1000.0f; vertex_data[iVertex].vertex.y = (float)(arc4random() % 2000) / 1000.0f - 1000.0f; vertex_data[iVertex].vertex.z = (float)(arc4random() % 2000) / 1000.0f - 1000.0f; vertex_data[iVertex].uva.u = -0.5f; vertex_data[iVertex].uva.v = -inscribed_circle_radius; iVertex++; vertex_data[iVertex].vertex.x = vertex_data[iVertex-1].vertex.x; vertex_data[iVertex].vertex.y = vertex_data[iVertex-1].vertex.y; vertex_data[iVertex].vertex.z = vertex_data[iVertex-1].vertex.z; vertex_data[iVertex].uva.u = 0.5f; vertex_data[iVertex].uva.v = -inscribed_circle_radius; iVertex++; vertex_data[iVertex].vertex.x = vertex_data[iVertex-1].vertex.x; vertex_data[iVertex].vertex.y = vertex_data[iVertex-1].vertex.y; vertex_data[iVertex].vertex.z = vertex_data[iVertex-1].vertex.z; vertex_data[iVertex].uva.u = 0.0f; vertex_data[iVertex].uva.v = -inscribed_circle_radius + equilateral_triangle_height; iVertex++; } m_randomParticleVertexData.unlock(); } return m_randomParticleVertexData; } long KRMeshManager::getMemoryTransferedThisFrame() { return m_memoryTransferredThisFrame; } int KRMeshManager::getActiveVBOCount() { return m_vbosActive.size(); } void KRMeshManager::log_draw_call(KRNode::RenderPass pass, const std::string &object_name, const std::string &material_name, int vertex_count) { if(m_draw_call_logging_enabled) { draw_call_info info; info.pass = pass; strncpy(info.object_name, object_name.c_str(), 256); strncpy(info.material_name, material_name.c_str(), 256); info.vertex_count = vertex_count; m_draw_calls.push_back(info); } } std::vector KRMeshManager::getDrawCalls() { m_draw_call_log_used = true; return m_draw_calls; } KRMeshManager::KRVBOData::KRVBOData() { m_is_vbo_loaded = false; m_is_vbo_ready = false; m_manager = NULL; m_type = STREAMING; m_data = NULL; m_index_data = NULL; m_vertex_attrib_flags = 0; m_vbo_handle = -1; m_vbo_handle_indexes = -1; m_vao_handle = -1; m_size = 0; m_last_frame_used = 0; m_last_frame_max_lod_coverage = 0.0f; } KRMeshManager::KRVBOData::KRVBOData(KRMeshManager *manager, KRDataBlock &data, KRDataBlock &index_data, int vertex_attrib_flags, bool static_vbo, vbo_type t) { m_is_vbo_loaded = false; m_is_vbo_ready = false; init(manager, data,index_data,vertex_attrib_flags, static_vbo, t); } void KRMeshManager::KRVBOData::init(KRMeshManager *manager, KRDataBlock &data, KRDataBlock &index_data, int vertex_attrib_flags, bool static_vbo, vbo_type t) { m_manager = manager; m_type = t; m_static_vbo = static_vbo; m_data = &data; m_index_data = &index_data; m_vertex_attrib_flags = vertex_attrib_flags; m_vbo_handle = -1; m_vbo_handle_indexes = -1; m_vao_handle = -1; m_size = m_data->getSize(); if(m_index_data != NULL) { m_size += m_index_data->getSize(); } } KRMeshManager::KRVBOData::~KRVBOData() { unload(); } void KRMeshManager::KRVBOData::load() { if(isVBOLoaded()) { return; } assert(m_vao_handle == -1); assert(m_vbo_handle == -1); assert(m_vbo_handle_indexes == -1); GLDEBUG(glGenBuffers(1, &m_vbo_handle)); if(m_index_data->getSize() > 0) { GLDEBUG(glGenBuffers(1, &m_vbo_handle_indexes)); } #if GL_OES_vertex_array_object if(m_type == CONSTANT) { GLDEBUG(glGenVertexArraysOES(1, &m_vao_handle)); GLDEBUG(glBindVertexArrayOES(m_vao_handle)); } #endif GLDEBUG(glBindBuffer(GL_ARRAY_BUFFER, m_vbo_handle)); bool use_mapbuffer = true; #if GL_OES_mapbuffer if(use_mapbuffer) { GLDEBUG(glBufferData(GL_ARRAY_BUFFER, m_data->getSize(), NULL, m_static_vbo ? GL_STATIC_DRAW : GL_DYNAMIC_DRAW)); GLDEBUG(void *map_ptr = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES)); m_data->copy(map_ptr); GLDEBUG(glUnmapBufferOES(GL_ARRAY_BUFFER)); } else #endif { m_data->lock(); GLDEBUG(glBufferData(GL_ARRAY_BUFFER, m_data->getSize(), m_data->getStart(), m_static_vbo ? GL_STATIC_DRAW : GL_DYNAMIC_DRAW)); m_data->unlock(); } configureAttribs(m_vertex_attrib_flags); if(m_index_data->getSize() == 0) { GLDEBUG(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); } else { GLDEBUG(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vbo_handle_indexes)); #if GL_OES_mapbuffer if(use_mapbuffer) { GLDEBUG(glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_index_data->getSize(), NULL, m_static_vbo ? GL_STATIC_DRAW : GL_DYNAMIC_DRAW)); GLDEBUG(void *map_ptr = glMapBufferOES(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY_OES)); m_index_data->copy(map_ptr); GLDEBUG(glUnmapBufferOES(GL_ELEMENT_ARRAY_BUFFER)); } else #endif { m_index_data->lock(); GLDEBUG(glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_index_data->getSize(), m_index_data->getStart(), m_static_vbo ? GL_STATIC_DRAW : GL_DYNAMIC_DRAW)); m_index_data->unlock(); } } m_is_vbo_loaded = true; m_manager->m_vboMemUsed += getSize(); m_manager->m_memoryTransferredThisFrame += getSize(); if(m_type == CONSTANT) { _swapHandles(); } } void KRMeshManager::KRVBOData::unload() { if(isVBOLoaded()) { m_manager->m_vboMemUsed -= getSize(); } #if GL_OES_vertex_array_object if(m_vao_handle != -1) { GLDEBUG(glDeleteVertexArraysOES(1, &m_vao_handle)); m_vao_handle = -1; } #endif if(m_vbo_handle != -1) { GLDEBUG(glDeleteBuffers(1, &m_vbo_handle)); m_vbo_handle = -1; } if(m_vbo_handle_indexes != -1) { GLDEBUG(glDeleteBuffers(1, &m_vbo_handle_indexes)); m_vbo_handle_indexes = -1; } m_is_vbo_loaded = false; m_is_vbo_ready = false; } void KRMeshManager::KRVBOData::bind() { #if GL_OES_vertex_array_object GLDEBUG(glBindVertexArrayOES(m_vao_handle)); #else GLDEBUG(glBindBuffer(GL_ARRAY_BUFFER, m_vbo_handle)); KRMeshManager::configureAttribs(m_vertex_attrib_flags); if(m_vbo_handle_indexes == -1) { GLDEBUG(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); } else { GLDEBUG(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vbo_handle_indexes)); } #endif } void KRMeshManager::KRVBOData::resetPoolExpiry(float lodCoverage) { long current_frame = m_manager->getContext().getCurrentFrame(); if(current_frame != m_last_frame_used) { m_last_frame_used = current_frame; m_last_frame_max_lod_coverage = 0.0f; m_manager->primeVBO(this); } m_last_frame_max_lod_coverage = KRMAX(lodCoverage, m_last_frame_max_lod_coverage); } float KRMeshManager::KRVBOData::getStreamPriority() { long current_frame = m_manager->getContext().getCurrentFrame(); if(current_frame > m_last_frame_used + 5) { return 1.0f - KRCLAMP((float)(current_frame - m_last_frame_used) / 60.0f, 0.0f, 1.0f); } else { return 10000.0f + m_last_frame_max_lod_coverage * 10.0f; } } void KRMeshManager::KRVBOData::_swapHandles() { if(m_is_vbo_loaded) { assert(m_vbo_handle != -1); } #if GL_OES_vertex_array_object if(m_is_vbo_loaded && m_vao_handle == -1) { GLDEBUG(glGenVertexArraysOES(1, &m_vao_handle)); GLDEBUG(glBindVertexArrayOES(m_vao_handle)); GLDEBUG(glBindBuffer(GL_ARRAY_BUFFER, m_vbo_handle)); KRMeshManager::configureAttribs(m_vertex_attrib_flags); if(m_vbo_handle_indexes == -1) { GLDEBUG(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); } else { GLDEBUG(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vbo_handle_indexes)); } } #endif m_is_vbo_ready = m_is_vbo_loaded; } void KRMeshManager::primeVBO(KRVBOData *vbo_data) { if(m_vbosActive.find(vbo_data->m_data) == m_vbosActive.end()) { m_vbosActive[vbo_data->m_data] = vbo_data; } }