func (r *Renderer) LoadMesh(m *gfx.Mesh, done chan *gfx.Mesh) { // Lock the mesh until we are done loading it. m.Lock() if m.Loaded && !m.HasChanged() { // Mesh is already loaded and has not changed, signal completion and // return after unlocking. m.Unlock() select { case done <- m: default: } return } f := func() { // Find the native mesh, creating a new one if none exists. var native *nativeMesh if !m.Loaded { native = new(nativeMesh) native.r = r } else { native = m.NativeMesh.(*nativeMesh) } // Determine usage hint. usageHint := gl.STATIC_DRAW if m.Dynamic { usageHint = gl.DYNAMIC_DRAW } // Update Indices VBO. if !m.Loaded || m.IndicesChanged { if len(m.Indices) == 0 { // Delete indices VBO. r.deleteVBO(&native.indices) } else { if native.indices == 0 { // Create indices VBO. native.indices = r.createVBO() } // Update indices VBO. r.updateVBO( usageHint, unsafe.Sizeof(m.Indices[0]), len(m.Indices), unsafe.Pointer(&m.Indices[0]), native.indices, ) native.indicesCount = uint32(len(m.Indices)) } m.IndicesChanged = false } // Update Vertices VBO. if !m.Loaded || m.VerticesChanged { if len(m.Vertices) == 0 { // Delete vertices VBO. r.deleteVBO(&native.vertices) native.verticesCount = 0 } else { if native.vertices == 0 { // Create vertices VBO. native.vertices = r.createVBO() } // Update vertices VBO. r.updateVBO( usageHint, unsafe.Sizeof(m.Vertices[0]), len(m.Vertices), unsafe.Pointer(&m.Vertices[0]), native.vertices, ) native.verticesCount = uint32(len(m.Vertices)) } m.VerticesChanged = false } // Update Colors VBO. if !m.Loaded || m.ColorsChanged { if len(m.Colors) == 0 { // Delete colors VBO. r.deleteVBO(&native.colors) } else { if native.colors == 0 { // Create colors VBO. native.colors = r.createVBO() } // Update colors VBO. r.updateVBO( usageHint, unsafe.Sizeof(m.Colors[0]), len(m.Colors), unsafe.Pointer(&m.Colors[0]), native.colors, ) } m.ColorsChanged = false } // Update Bary VBO. if !m.Loaded || m.BaryChanged { if len(m.Bary) == 0 { // Delete bary VBO. r.deleteVBO(&native.bary) } else { if native.bary == 0 { // Create bary VBO. native.bary = r.createVBO() } // Update bary VBO. r.updateVBO( usageHint, unsafe.Sizeof(m.Bary[0]), len(m.Bary), unsafe.Pointer(&m.Bary[0]), native.bary, ) } m.BaryChanged = false } // Any texture coordinate sets that were removed should have their // VBO's deleted. deletedMax := len(m.TexCoords) if deletedMax > len(native.texCoords) { deletedMax = len(native.texCoords) } deleted := native.texCoords[:deletedMax] native.texCoords = native.texCoords[:deletedMax] for _, vbo := range deleted { r.deleteVBO(&vbo) } // Any texture coordinate sets that were added should have VBO's // created. added := m.TexCoords[len(native.texCoords):] toUpdate := m.TexCoords for _, set := range added { vbo := r.createVBO() native.texCoords = append(native.texCoords, vbo) // Update the VBO. r.updateVBO( usageHint, unsafe.Sizeof(set.Slice[0]), len(set.Slice), unsafe.Pointer(&set.Slice[0]), vbo, ) } // And finally, any texture coordinate sets that were changed need to // have their VBO's updated. for index, set := range toUpdate { if set.Changed { // Update the VBO. r.updateVBO( usageHint, unsafe.Sizeof(set.Slice[0]), len(set.Slice), unsafe.Pointer(&set.Slice[0]), native.texCoords[index], ) } } // Ensure no buffer is active when we leave (so that OpenGL state is untouched). r.loader.BindBuffer(gl.ARRAY_BUFFER, 0) // Flush and execute OpenGL commands. r.loader.Flush() r.loader.Execute() // If the mesh is not loaded, then we need to assign the native mesh // and create a finalizer to free the native mesh later. if !m.Loaded { // Assign the native mesh. m.NativeMesh = native // Attach a finalizer to the mesh that will later free it. runtime.SetFinalizer(native, finalizeMesh) } // Set the mesh to loaded, clear any data slices if they are not wanted. m.Loaded = true m.ClearData() // Unlock, signal completion, and return. m.Unlock() select { case done <- m: default: } return } select { case r.LoaderExec <- f: default: go func() { r.LoaderExec <- f }() } }