//------------------------------------------------------------------------------ void GLMesh::Bind(GLShader* glShader) noexcept { auto vertexFormat = m_renderMesh->GetVertexFormat(); //TODO: This should be pre-calculated. GLint maxVertexAttributes = 0; glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttributes); CS_ASSERT(u32(maxVertexAttributes) >= vertexFormat.GetNumElements(), "Too many vertex elements."); glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferHandle); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBufferHandle); CS_ASSERT_NOGLERROR("An OpenGL error occurred while binding GLMesh."); for (u32 i = 0; i < vertexFormat.GetNumElements(); ++i) { glEnableVertexAttribArray(i); auto elementType = vertexFormat.GetElement(i); auto name = GLMeshUtils::GetAttributeName(elementType); auto numComponents = ChilliSource::VertexFormat::GetNumComponents(elementType); auto type = GLMeshUtils::GetGLType(ChilliSource::VertexFormat::GetDataType(elementType)); auto normalised = GLMeshUtils::IsNormalised(elementType); auto offset = reinterpret_cast<const GLvoid*>(u64(vertexFormat.GetElementOffset(i))); glShader->SetAttribute(name, numComponents, type, normalised, vertexFormat.GetSize(), offset); } for (s32 i = vertexFormat.GetNumElements(); i < maxVertexAttributes; ++i) { glDisableVertexAttribArray(i); } }
//--------------------------------------------------- //--------------------------------------------------- void TextureUnitSystem::Bind(GLenum in_type, const void* in_object, GLint in_handle, u32 in_unit) { if((s32)in_unit != m_currentTextureUnit) { m_currentTextureUnit = (s32)in_unit; glActiveTexture(GL_TEXTURE0 + in_unit); } if(m_texUnits.empty() == true) { CSRendering::RenderCapabilities* renderCapabilities = CSCore::Application::Get()->GetSystem<CSRendering::RenderCapabilities>(); CS_ASSERT(renderCapabilities, "Cannot find required system: Render Capabilities."); //Create the available texture unit slots m_texUnits.resize(renderCapabilities->GetNumTextureUnits()); std::fill(m_texUnits.begin(), m_texUnits.end(), nullptr); } //Check to make sure this texture is not already bound to this unit if(m_texUnits[in_unit] != in_object) { glBindTexture(in_type, in_handle); m_texUnits[in_unit] = in_object; } CS_ASSERT_NOGLERROR("An OpenGL error occurred while binding texture to unit."); }
//-------------------------------------------------- //-------------------------------------------------- void Texture::Destroy() { m_width = 0; m_height = 0; m_hasFilterModeChanged = true; m_hasWrapModeChanged = true; m_hasMipMaps = false; m_filterMode = FilterMode::k_bilinear; m_sWrapMode = WrapMode::k_clamp; m_tWrapMode = WrapMode::k_clamp; //If the context has already been destroyed then the cubemap has already been destroyed bool hasContext = static_cast<RenderSystem*>(CSCore::Application::Get()->GetRenderSystem())->HasContext(); if(hasContext == true && m_texHandle > 0) { Unbind(); glDeleteTextures(1, &m_texHandle); } m_texHandle = 0; #ifdef CS_TARGETPLATFORM_ANDROID m_restoreTextureDataEnabled = false; m_restorationDataSize = 0; m_restorationData.reset(); #endif CS_ASSERT_NOGLERROR("An OpenGL error occurred while destroying texture."); }
//------------------------------------------------------------------------------ void GLShader::SetUniform(const std::string& name, const ChilliSource::Matrix4& value, FailurePolicy failurePolicy) noexcept { GLint uniformHandle = GetUniformHandle(name, failurePolicy); if(uniformHandle >= 0) { glUniformMatrix4fv(uniformHandle, 1, GL_FALSE, reinterpret_cast<const GLfloat*>(&value.m)); CS_ASSERT_NOGLERROR("An OpenGL error occurred while setting uniform."); } }
//------------------------------------------------------------------------------ void GLShader::SetUniform(const std::string& name, const ChilliSource::Vector4* values, u32 numValues, FailurePolicy failurePolicy) noexcept { GLint uniformHandle = GetUniformHandle(name, failurePolicy); if(uniformHandle >= 0) { glUniform4fv(uniformHandle, numValues, reinterpret_cast<const GLfloat*>(values)); CS_ASSERT_NOGLERROR("An OpenGL error occurred while setting uniform."); } }
//------------------------------------------------------------------------------ void GLShader::SetUniform(const std::string& name, f32 value, FailurePolicy failurePolicy) noexcept { GLint uniformHandle = GetUniformHandle(name, failurePolicy); if(uniformHandle >= 0) { glUniform1f(uniformHandle, value); CS_ASSERT_NOGLERROR("An OpenGL error occurred while setting uniform."); } }
//------------------------------------------------------------------------------ void GLShader::SetAttribute(const std::string& name, GLint size, GLenum type, GLboolean isNormalised, GLsizei stride, const GLvoid* offset) noexcept { auto it = m_attributeHandles.find(name); if(it == m_attributeHandles.end()) { return; } glVertexAttribPointer(it->second, size, type, isNormalised, stride, offset); CS_ASSERT_NOGLERROR("An OpenGL error occurred while setting attribute."); }
//------------------------------------------------------------------------------ GLTextureUnitManager::GLTextureUnitManager() noexcept { s32 numTextureUnits = 0; glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numTextureUnits); m_boundTextures.reserve(numTextureUnits); for (s32 i = 0; i < numTextureUnits; ++i) { m_boundTextures.push_back(nullptr); } CS_ASSERT_NOGLERROR("An OpenGL error occurred while setting up the texture unit manager."); }
//------------------------------------------------------------------------------ GLDynamicMesh::~GLDynamicMesh() noexcept { if(!m_invalidData) { glDeleteBuffers(1, &m_vertexBufferHandle); if(m_indexBufferHandle != 0) { glDeleteBuffers(1, &m_indexBufferHandle); } CS_ASSERT_NOGLERROR("An OpenGL error occurred while deleting GLDynamicMesh."); } }
//-------------------------------------------------- /// GL makes a copy of the data so we can just /// let the incoming data delete itself //-------------------------------------------------- void Texture::Build(const Descriptor& in_desc, TextureDataUPtr in_data, bool in_mipMap, bool in_restoreTextureDataEnabled) { Destroy(); m_width = in_desc.m_width; m_height = in_desc.m_height; m_format = in_desc.m_format; m_compression = in_desc.m_compression; CS_ASSERT(m_width <= m_renderCapabilities->GetMaxTextureSize() && m_height <= m_renderCapabilities->GetMaxTextureSize(), "OpenGL does not support textures of this size on this device (" + CSCore::ToString(m_width) + ", " + CSCore::ToString(m_height) + ")"); glGenTextures(1, &m_texHandle); Bind(); u8* data = in_data.get(); switch(m_compression) { case CSCore::ImageCompression::k_none: UploadImageDataNoCompression(m_format, m_width, m_height, data); break; case CSCore::ImageCompression::k_ETC1: UploadImageDataETC1(m_format, m_width, m_height, data, in_desc.m_dataSize); break; case CSCore::ImageCompression::k_PVR2Bpp: UploadImageDataPVR2(m_format, m_width, m_height, data, in_desc.m_dataSize); break; case CSCore::ImageCompression::k_PVR4Bpp: UploadImageDataPVR4(m_format, m_width, m_height, data, in_desc.m_dataSize); break; }; if(in_mipMap == true) { glGenerateMipmap(GL_TEXTURE_2D); } m_hasMipMaps = in_mipMap; #ifdef CS_TARGETPLATFORM_ANDROID if (GetStorageLocation() == CSCore::StorageLocation::k_none && in_restoreTextureDataEnabled == true) { m_restoreTextureDataEnabled = true; m_restorationDataSize = in_desc.m_dataSize; m_restorationData = std::move(in_data); } #endif CS_ASSERT_NOGLERROR("An OpenGL error occurred while building texture."); }
//------------------------------------------------------- ChilliSource::RenderInfo RenderInfoFactory::CreateRenderInfo() noexcept { bool areHighPrecFragmentsSupported = true; bool areMapBuffersSupported = true; bool areVAOsSupported = true; bool areDepthTexturesSupported = false; bool areShadowMapsSupported = false; u32 maxTextureSize = 0; u32 maxTextureUnits = 0; u32 maxVertexAttribs = 0; #ifdef CS_OPENGLVERSION_ES s32 fragmentHighRanges[2]; s32 fragmentHighPrecision; glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, fragmentHighRanges, &fragmentHighPrecision); areHighPrecFragmentsSupported = fragmentHighPrecision != 0 && fragmentHighRanges[0] != 0 && fragmentHighRanges[1] != 0; #ifdef CS_TARGETPLATFORM_ANDROID //Check for map buffer support areMapBuffersSupported = CheckForOpenGLExtension("GL_OES_mapbuffer"); areVAOsSupported = CheckForOpenGLExtension("GL_OES_vertex_array_object"); #endif #ifdef CS_TARGETPLATFORM_RPI areVAOsSupported = false; #endif #endif #ifdef CS_OPENGLVERSION_STANDARD areDepthTexturesSupported = CheckForOpenGLExtension("GL_ARB_depth_texture"); #elif defined(CS_OPENGLVERSION_ES) areDepthTexturesSupported = CheckForOpenGLExtension("GL_OES_depth_texture"); #endif areShadowMapsSupported = (areDepthTexturesSupported && areHighPrecFragmentsSupported); glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint*)&maxTextureSize); glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, (GLint*)&maxTextureUnits); glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, (GLint*)&maxVertexAttribs); CS_ASSERT_NOGLERROR("An OpenGL error occurred while getting render capabilities."); ChilliSource::RenderInfo renderInfo(areShadowMapsSupported, areDepthTexturesSupported, areMapBuffersSupported, areVAOsSupported, areHighPrecFragmentsSupported, maxTextureSize, maxTextureUnits, maxVertexAttribs); return renderInfo; }
//------------------------------------------------------------------------------ GLuint GLTextureUnitManager::BindAdditional(const ChilliSource::RenderTexture* texture) noexcept { GLuint textureIndex = GetNextAvailableUnit(); m_boundTextures.push_back(texture); auto glTexture = static_cast<GLTexture*>(texture->GetExtraData()); CS_ASSERT(glTexture, "Cannot bind a texture which hasn't been loaded."); glActiveTexture(GL_TEXTURE0 + textureIndex); glBindTexture(GL_TEXTURE_2D, glTexture->GetHandle()); glActiveTexture(GL_TEXTURE0); CS_ASSERT_NOGLERROR("An OpenGL error occurred while binding an additional texture."); return textureIndex; }
//------------------------------------------------------- //------------------------------------------------------- void TextureUnitSystem::Unbind(const void* in_object) { for(u32 i=0; i<m_texUnits.size(); ++i) { if(m_texUnits[i] == in_object) { //Unbind the texture from this slot by binding zero. if((s32)i != m_currentTextureUnit) { m_currentTextureUnit = (s32)i; glActiveTexture(GL_TEXTURE0 + i); } glBindTexture(GL_TEXTURE_2D, 0); m_texUnits[i] = nullptr; } } CS_ASSERT_NOGLERROR("An OpenGL error occurred while unbinding texture."); }
//------------------------------------------------------------------------------ void GLDynamicMesh::Bind(GLShader* glShader, ChilliSource::PolygonType polygonType, const ChilliSource::VertexFormat& vertexFormat, ChilliSource::IndexFormat indexFormat, u32 numVertices, u32 numIndices, const u8* vertexData, u32 vertexDataSize, const u8* indexData, u32 indexDataSize) noexcept { m_polygonType = polygonType; m_vertexFormat = vertexFormat; m_indexFormat = indexFormat; m_numVertices = numVertices; m_numIndices = numIndices; glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferHandle); glBufferSubData(GL_ARRAY_BUFFER, 0, vertexDataSize, vertexData); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBufferHandle); if (m_indexBufferHandle != 0) { glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indexDataSize, indexData); } CS_ASSERT_NOGLERROR("An OpenGL error occurred while binding GLDynamicMesh."); ApplyVertexAttributes(glShader); }
//------------------------------------------------------------------------------ void GLTextureUnitManager::Bind(const std::vector<const ChilliSource::RenderTexture*>& textures) noexcept { for (u32 textureUnitIndex = 0; textureUnitIndex < u32(textures.size()); ++textureUnitIndex) { if (m_boundTextures[textureUnitIndex] != textures[textureUnitIndex]) { m_boundTextures[textureUnitIndex] = textures[textureUnitIndex]; auto glTexture = static_cast<GLTexture*>(m_boundTextures[textureUnitIndex]->GetExtraData()); CS_ASSERT(glTexture, "Cannot bind a texture which hasn't been loaded."); CS_ASSERT(!glTexture->IsDataInvalid(), "GLTextureUnitManager::Bind(): Failed to bind texture, its context is invalid!"); glActiveTexture(GL_TEXTURE0 + textureUnitIndex); glBindTexture(GL_TEXTURE_2D, glTexture->GetHandle()); } } glActiveTexture(GL_TEXTURE0); CS_ASSERT_NOGLERROR("An OpenGL error occurred while binding textures."); }
//------------------------------------------------------------------------------ GLShader::~GLShader() noexcept { if(!m_invalidData) { if(m_vertexShaderId > 0) { glDetachShader(m_programId, m_vertexShaderId); glDeleteShader(m_vertexShaderId); } if(m_fragmentShaderId > 0) { glDetachShader(m_programId, m_fragmentShaderId); glDeleteShader(m_fragmentShaderId); } if(m_programId > 0) { glDeleteProgram(m_programId); } CS_ASSERT_NOGLERROR("An OpenGL error occurred while destroying shader."); } }
//------------------------------------------------------------------------------ GLDynamicMesh::GLDynamicMesh(u32 vertexDataSize, u32 indexDataSize) noexcept : m_allocator(k_allocatorSize), m_maxVertexDataSize(vertexDataSize), m_maxIndexDataSize(indexDataSize) { glGenBuffers(1, &m_vertexBufferHandle); CS_ASSERT(m_vertexBufferHandle != 0, "Invalid vertex buffer."); if(indexDataSize > 0) { glGenBuffers(1, &m_indexBufferHandle); CS_ASSERT(m_indexBufferHandle != 0, "Invalid index buffer."); } glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferHandle); glBufferData(GL_ARRAY_BUFFER, m_maxVertexDataSize, nullptr, GL_DYNAMIC_DRAW); if(indexDataSize > 0) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBufferHandle); glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_maxIndexDataSize, nullptr, GL_DYNAMIC_DRAW); } CS_ASSERT_NOGLERROR("An OpenGL error occurred while creating GLDynamicMesh."); }
//------------------------------------------------------------------------------ void GLMesh::BuildMesh(const u8* vertexData, u32 vertexDataSize, const u8* indexData, u32 indexDataSize) noexcept { CS_ASSERT(vertexDataSize > 0 && vertexData, "Cannot build mesh with empty data"); glGenBuffers(1, &m_vertexBufferHandle); CS_ASSERT(m_vertexBufferHandle != 0, "Invalid vertex buffer."); if(indexData) { glGenBuffers(1, &m_indexBufferHandle); CS_ASSERT(m_indexBufferHandle != 0, "Invalid index buffer."); } glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferHandle); glBufferData(GL_ARRAY_BUFFER, vertexDataSize, vertexData, GL_STATIC_DRAW); if(indexData) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBufferHandle); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexDataSize, indexData, GL_STATIC_DRAW); } CS_ASSERT_NOGLERROR("An OpenGL error occurred while creating GLMesh."); }
//------------------------------------------------------------------------------ GLint GLShader::GetUniformHandle(const std::string& name, FailurePolicy failurePolicy) noexcept { GLint uniformHandle = -1; auto it = m_uniformHandles.find(name); if(it != m_uniformHandles.end()) { uniformHandle = it->second; } else { uniformHandle = glGetUniformLocation(m_programId, name.c_str()); CS_ASSERT_NOGLERROR("An OpenGL error occurred while getting uniform handle."); m_uniformHandles.insert(std::make_pair(name, uniformHandle)); } if (uniformHandle < 0 && failurePolicy == FailurePolicy::k_hard) { CS_LOG_FATAL("Cannot find shader uniform: " + name); } return uniformHandle; }
//------------------------------------------------------------------------------ void GLShader::BuildAttributeHandleMap() noexcept { static const std::array<std::string, 6> attribNames = {{ k_attributePosition, k_attributeNormal, k_attributeTexCoord, k_attributeColour, k_attributeWeights, k_attributeJointIndices }}; for(const auto& name : attribNames) { GLint handle = glGetAttribLocation(m_programId, name.c_str()); if(handle >= 0) { m_attributeHandles.insert(std::make_pair(name, handle)); } } CS_ASSERT_NOGLERROR("An OpenGL error occurred while populating attribute handles."); }
//-------------------------------------------------- //-------------------------------------------------- void Texture::UpdateRestorationData() { CS_ASSERT(GetStorageLocation() == CSCore::StorageLocation::k_none, "Cannot update restoration data on texture that was loaded from file."); if (m_restoreTextureDataEnabled == true) { Unbind(); //create an bind a new frame buffer. GLuint frameBufferHandle = 0; glGenFramebuffers(1, &frameBufferHandle); glBindFramebuffer(GL_FRAMEBUFFER, frameBufferHandle); //attach the texture glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texHandle, 0); GLuint glCheck = glCheckFramebufferStatus(GL_FRAMEBUFFER); if(glCheck != GL_FRAMEBUFFER_COMPLETE) { CS_LOG_FATAL("Framebuffer incomplete while updating texture restoration data."); } //read the data from the texture. This can only be read back in RGBA8888 format so it will need //to be converted back to the format of the texture. u32 unconvertedDataSize = GetWidth() * GetHeight() * 4; std::unique_ptr<u8[]> unconvertedData(new u8[unconvertedDataSize]); glReadPixels(0, 0, GetWidth(), GetHeight(), GL_RGBA, GL_UNSIGNED_BYTE, unconvertedData.get()); //Convert to the format of this texture CSCore::ImageFormatConverter::ImageBuffer convertedData; switch (m_format) { case CSCore::ImageFormat::k_RGBA8888: convertedData.m_size = unconvertedDataSize; convertedData.m_data = std::move(unconvertedData); break; case CSCore::ImageFormat::k_RGB888: convertedData = CSCore::ImageFormatConverter::RGBA8888ToRGB888(unconvertedData.get(), unconvertedDataSize); unconvertedData.reset(); break; case CSCore::ImageFormat::k_RGBA4444: convertedData = CSCore::ImageFormatConverter::RGBA8888ToRGBA4444(unconvertedData.get(), unconvertedDataSize); unconvertedData.reset(); break; case CSCore::ImageFormat::k_RGB565: convertedData = CSCore::ImageFormatConverter::RGBA8888ToRGB565(unconvertedData.get(), unconvertedDataSize); unconvertedData.reset(); break; default: CS_LOG_FATAL("Texture is not in a restorable format. The restorable texture data option must be disabled for this texture."); break; } m_restorationDataSize = convertedData.m_size; m_restorationData = std::move(convertedData.m_data); //clean up the frame buffer. glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &frameBufferHandle); CS_ASSERT_NOGLERROR("An OpenGL error occurred while updating texture restoration data."); } }
//------------------------------------------------------------------------------ void GLShader::Bind() noexcept { glUseProgram(m_programId); CS_ASSERT_NOGLERROR("An OpenGL error occurred while binding shader."); }