diff --git a/raylib/compat.go b/raylib/compat.go new file mode 100644 index 0000000..3cd0337 --- /dev/null +++ b/raylib/compat.go @@ -0,0 +1,98 @@ +//go:build go1.21 +// +build go1.21 + +package rl + +/* +#include "raylib.h" +#include +*/ +import "C" + +import ( + "runtime" + "slices" +) + +// List of VaoIDs of meshes created by calling UploadMesh() +// Used by UnloadMesh() to determine if mesh is go-managed or C-allocated +var goManagedMeshIDs = make([]uint32, 0) + +// UploadMesh - Upload vertex data into a VAO (if supported) and VBO +func UploadMesh(mesh *Mesh, dynamic bool) { + // check if mesh has already been uploaded to prevent duplication + if mesh.VaoID != 0 { + TraceLog(LogWarning, "VAO: [ID %d] Trying to re-load an already loaded mesh", mesh.VaoID) + return + } + + pinner := runtime.Pinner{} + // Mesh pointer fields must be pinned to allow a Mesh pointer to be passed to C.UploadMesh() below + // nil checks are required because Pin() will panic if passed nil + if mesh.Vertices != nil { + pinner.Pin(mesh.Vertices) + } + if mesh.Texcoords != nil { + pinner.Pin(mesh.Texcoords) + } + if mesh.Texcoords2 != nil { + pinner.Pin(mesh.Texcoords2) + } + if mesh.Normals != nil { + pinner.Pin(mesh.Normals) + } + if mesh.Tangents != nil { + pinner.Pin(mesh.Tangents) + } + if mesh.Colors != nil { + pinner.Pin(mesh.Colors) + } + if mesh.Indices != nil { + pinner.Pin(mesh.Indices) + } + if mesh.AnimVertices != nil { + pinner.Pin(mesh.AnimVertices) + } + if mesh.AnimNormals != nil { + pinner.Pin(mesh.AnimNormals) + } + if mesh.BoneIds != nil { + pinner.Pin(mesh.BoneIds) + } + if mesh.BoneWeights != nil { + pinner.Pin(mesh.BoneWeights) + } + // VboID of a new mesh should always be nil before uploading, but including this in case a mesh happens to have it set. + if mesh.VboID != nil { + pinner.Pin(mesh.VboID) + } + + cMesh := mesh.cptr() + C.UploadMesh(cMesh, C.bool(dynamic)) + + // Add new mesh VaoID to list + goManagedMeshIDs = append(goManagedMeshIDs, mesh.VaoID) + + pinner.Unpin() +} + +// UnloadMesh - Unload mesh from memory (RAM and/or VRAM) +func UnloadMesh(mesh *Mesh) { + // Check list of go-managed mesh IDs + if slices.Contains(goManagedMeshIDs, mesh.VaoID) { + // C.UnloadMesh() only needs to read the VaoID & VboID + // passing a temporary struct with all other fields nil makes it safe for the C code to call free() + tempMesh := Mesh{ + VaoID: mesh.VaoID, + VboID: mesh.VboID, + } + cmesh := tempMesh.cptr() + C.UnloadMesh(*cmesh) + + // remove mesh VaoID from list + goManagedMeshIDs = slices.DeleteFunc(goManagedMeshIDs, func(id uint32) bool { return id == mesh.VaoID }) + } else { + cmesh := mesh.cptr() + C.UnloadMesh(*cmesh) + } +} diff --git a/raylib/compat_compat.go b/raylib/compat_compat.go new file mode 100644 index 0000000..1c362fb --- /dev/null +++ b/raylib/compat_compat.go @@ -0,0 +1,69 @@ +//go:build !go1.21 +// +build !go1.21 + +package rl + +/* +#include "raylib.h" +#include +*/ +import "C" + +import ( + "runtime" + + "golang.org/x/exp/slices" +) + +// List of VaoIDs of meshes created by calling UploadMesh() +// Used by UnloadMesh() to determine if mesh is go-managed or C-allocated +var goManagedMeshIDs = make([]uint32, 0) + +// UploadMesh - Upload vertex data into a VAO (if supported) and VBO +func UploadMesh(mesh *Mesh, dynamic bool) { + // check if mesh has already been uploaded to prevent duplication + if mesh.VaoID != 0 { + TraceLog(LogWarning, "VAO: [ID %d] Trying to re-load an already loaded mesh", mesh.VaoID) + return + } + + defer runtime.KeepAlive(mesh.Vertices) + defer runtime.KeepAlive(mesh.Texcoords) + defer runtime.KeepAlive(mesh.Texcoords2) + defer runtime.KeepAlive(mesh.Normals) + defer runtime.KeepAlive(mesh.Tangents) + defer runtime.KeepAlive(mesh.Colors) + defer runtime.KeepAlive(mesh.Indices) + defer runtime.KeepAlive(mesh.AnimVertices) + defer runtime.KeepAlive(mesh.AnimNormals) + defer runtime.KeepAlive(mesh.BoneIds) + defer runtime.KeepAlive(mesh.BoneWeights) + defer runtime.KeepAlive(mesh.VboID) + + cMesh := mesh.cptr() + C.UploadMesh(cMesh, C.bool(dynamic)) + + // Add new mesh VaoID to list + goManagedMeshIDs = append(goManagedMeshIDs, mesh.VaoID) +} + +// UnloadMesh - Unload mesh from memory (RAM and/or VRAM) +func UnloadMesh(mesh *Mesh) { + // Check list of go-managed mesh IDs + if slices.Contains(goManagedMeshIDs, mesh.VaoID) { + // C.UnloadMesh() only needs to read the VaoID & VboID + // passing a temporary struct with all other fields nil makes it safe for the C code to call free() + tempMesh := Mesh{ + VaoID: mesh.VaoID, + VboID: mesh.VboID, + } + cmesh := tempMesh.cptr() + C.UnloadMesh(*cmesh) + + // remove mesh VaoID from list + goManagedMeshIDs = slices.DeleteFunc(goManagedMeshIDs, func(id uint32) bool { return id == mesh.VaoID }) + } else { + cmesh := mesh.cptr() + C.UnloadMesh(*cmesh) + } +} diff --git a/raylib/go.mod b/raylib/go.mod index 9d3e538..4dc010b 100644 --- a/raylib/go.mod +++ b/raylib/go.mod @@ -6,3 +6,5 @@ require ( github.com/ebitengine/purego v0.7.1 golang.org/x/sys v0.20.0 ) + +require golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 diff --git a/raylib/go.sum b/raylib/go.sum index 7ea3273..2241fa5 100644 --- a/raylib/go.sum +++ b/raylib/go.sum @@ -1,4 +1,6 @@ github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA= github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/raylib/rmodels.go b/raylib/rmodels.go index dc876de..5ae017e 100644 --- a/raylib/rmodels.go +++ b/raylib/rmodels.go @@ -8,8 +8,6 @@ import "C" import ( "image/color" - "runtime" - "slices" "unsafe" ) @@ -375,68 +373,6 @@ func DrawBillboardPro(camera Camera, texture Texture2D, sourceRec Rectangle, pos C.DrawBillboardPro(*ccamera, *ctexture, *csourceRec, *cposition, *cup, *csize, *corigin, crotation, *ctint) } -// List of VaoIDs of meshes created by calling UploadMesh() -// Used by UnloadMesh() to determine if mesh is go-managed or C-allocated -var goManagedMeshIDs = make([]uint32, 0) - -// UploadMesh - Upload vertex data into a VAO (if supported) and VBO -func UploadMesh(mesh *Mesh, dynamic bool) { - // check if mesh has already been uploaded to prevent duplication - if mesh.VaoID != 0 { - TraceLog(LogWarning, "VAO: [ID %d] Trying to re-load an already loaded mesh", mesh.VaoID) - return - } - - pinner := runtime.Pinner{} - // Mesh pointer fields must be pinned to allow a Mesh pointer to be passed to C.UploadMesh() below - // nil checks are required because Pin() will panic if passed nil - if mesh.Vertices != nil { - pinner.Pin(mesh.Vertices) - } - if mesh.Texcoords != nil { - pinner.Pin(mesh.Texcoords) - } - if mesh.Texcoords2 != nil { - pinner.Pin(mesh.Texcoords2) - } - if mesh.Normals != nil { - pinner.Pin(mesh.Normals) - } - if mesh.Tangents != nil { - pinner.Pin(mesh.Tangents) - } - if mesh.Colors != nil { - pinner.Pin(mesh.Colors) - } - if mesh.Indices != nil { - pinner.Pin(mesh.Indices) - } - if mesh.AnimVertices != nil { - pinner.Pin(mesh.AnimVertices) - } - if mesh.AnimNormals != nil { - pinner.Pin(mesh.AnimNormals) - } - if mesh.BoneIds != nil { - pinner.Pin(mesh.BoneIds) - } - if mesh.BoneWeights != nil { - pinner.Pin(mesh.BoneWeights) - } - // VboID of a new mesh should always be nil before uploading, but including this in case a mesh happens to have it set. - if mesh.VboID != nil { - pinner.Pin(mesh.VboID) - } - - cMesh := mesh.cptr() - C.UploadMesh(cMesh, C.bool(dynamic)) - - // Add new mesh VaoID to list - goManagedMeshIDs = append(goManagedMeshIDs, mesh.VaoID) - - pinner.Unpin() -} - // UpdateMeshBuffer - Update mesh vertex data in GPU for a specific buffer index func UpdateMeshBuffer(mesh Mesh, index int, data []byte, offset int) { cindex := (C.int)(index) @@ -445,27 +381,6 @@ func UpdateMeshBuffer(mesh Mesh, index int, data []byte, offset int) { C.UpdateMeshBuffer(*mesh.cptr(), cindex, unsafe.Pointer(&data[0]), cdataSize, coffset) } -// UnloadMesh - Unload mesh from memory (RAM and/or VRAM) -func UnloadMesh(mesh *Mesh) { - // Check list of go-managed mesh IDs - if slices.Contains(goManagedMeshIDs, mesh.VaoID) { - // C.UnloadMesh() only needs to read the VaoID & VboID - // passing a temporary struct with all other fields nil makes it safe for the C code to call free() - tempMesh := Mesh{ - VaoID: mesh.VaoID, - VboID: mesh.VboID, - } - cmesh := tempMesh.cptr() - C.UnloadMesh(*cmesh) - - // remove mesh VaoID from list - goManagedMeshIDs = slices.DeleteFunc(goManagedMeshIDs, func(id uint32) bool { return id == mesh.VaoID }) - } else { - cmesh := mesh.cptr() - C.UnloadMesh(*cmesh) - } -} - // DrawMesh - Draw a single mesh func DrawMesh(mesh Mesh, material Material, transform Matrix) { C.DrawMesh(*mesh.cptr(), *material.cptr(), *transform.cptr())