2 Commits

Author SHA1 Message Date
3d0a4f977b Comments and ClearUserData 2023-10-05 14:34:42 +04:00
5a55870493 Add funcs to set/get rigid actor user data 2023-10-05 13:25:48 +04:00
5 changed files with 68 additions and 10 deletions

28
main.go
View File

@ -2,12 +2,17 @@ package main
import (
"fmt"
"unsafe"
"github.com/bloeys/physx-go/pgo"
)
func contactHandler(cph pgo.ContactPairHeader) {
// ra1 := cph.GetRigidActors()[0]
// ra2 := cph.GetRigidActors()[1]
// fmt.Printf("Collision! User data 1: %v; User data 2: %v\n", ra1.GetUserData(), ra2.GetUserData())
// pairs := cph.GetPairs()
// for i := 0; i < len(pairs); i++ {
@ -138,6 +143,21 @@ func main() {
raycastBuffer := pgo.NewRaycastBuffer(1)
defer raycastBuffer.Release()
// Example of correct usage of user data
x := new(int64)
*x = 1095
ra.SetUserData(unsafe.Pointer(x))
z := (*int64)(ra.GetUserData())
fmt.Println("User data:", *z)
// The rigid actor might get garbage collected after this point (as its no longer used), but that will now cause
// a memory leak and a crash since the user data got pinned (i.e. GC will not move or free it) when we used SetUserData above, but can no longer be unpinned as the runtime.Pinner object will get garbage collected with the rigid actor.
//
// To solve this we have 2 options:
// 1. The pinner is unpinned before it is garbage collected by calling ClearUserData
// 2. The object holding the active pinner (the rigid actor on which SetUserData was used) must remain alive (e.g. by pinning it, putting it in file scope, in a long lived object etc)
ra.ClearUserData()
scene.SetScratchBuffer(4)
for {
scene.Collide(1 / 50.0)
@ -147,10 +167,10 @@ func main() {
scene.RaycastWithHitBuffer(pgo.NewVec3(0, 0, 0), pgo.NewVec3(0, 1, 0), 9, raycastBuffer, 1)
if raycastBuffer.HasBlock() {
block := raycastBuffer.GetBlock()
d := block.GetDistance()
pos := block.GetPos()
fmt.Printf("Raycast hit at dist (%v) and post %v\n", d, pos.String())
// block := raycastBuffer.GetBlock()
// d := block.GetDistance()
// pos := block.GetPos()
// fmt.Printf("Raycast hit at dist (%v) and post %v\n", d, pos.String())
}
// fmt.Printf("\nRaycast hit: %v\n", rHit)
// fmt.Println("Press enter...")

View File

@ -16,6 +16,7 @@ void goOnContactCallback_cgo(void* pairHeader);
*/
import "C"
import (
"runtime"
"unsafe"
"github.com/bloeys/gglm/gglm"
@ -544,7 +545,6 @@ type Quat struct {
cQ C.struct_CPxQuat
}
// CPxAPI CPxInline CSTRUCT CPxQuat NewCPxQuat(float angleRads, float x, float y, float z);
func NewQuat(angleRads, x, y, z float32) *Quat {
return &Quat{
cQ: C.NewCPxQuat(C.float(angleRads), C.float(x), C.float(y), C.float(z)),
@ -662,13 +662,48 @@ type Actor struct {
type RigidActor struct {
cRa C.struct_CPxRigidActor
pinner runtime.Pinner
}
func (ra *RigidActor) SetSimFilterData(fd *FilterData) {
C.CPxRigidActor_setSimFilterData(ra.cRa, fd.cFilterData)
}
// CPxAPI void CPxRigidActor_setSimFilterData(CSTRUCT CPxRigidActor* cra, CSTRUCT CPxFilterData* cfd);
// SetUserData sets the void* field on the rigid actor which can be used for any purpose.
// For example, it can be used to store an id or pointer that ties this rigid actor to some other object
//
// Note-1: The passed pointer will be stored in C and as such needs to be pinned, which this function will do.
// You can refer to this for notes on pinning and pointer rules: Refer to: https://pkg.go.dev/cmd/cgo#hdr-Passing_pointers
//
// Note-2: Since this RigidActor object is the one that pinned the user data, it MUST be kept alive at least until ClearUserData is used, at which point the data is unpinned and cleared.
// If this RigidActor object gets garabage collected before clear, the pinner will detect its getting collected with stuff still pinned (which is a leak) and will panic.
func (ra *RigidActor) SetUserData(userData unsafe.Pointer) {
// Note: Do NOT use interfaces here, as we need to ensure the original value
// pointed to is pinned, not the pointer to the interface (i.e. pinning the interface).
// Better avoid crazy to debug issues
// User data is a Go pointer stored in C/C++ code, and as such MUST be pinned
// before that is done, and must be unpinned when no longer stored in C/C++.
//
// Here we assume every write is of a different object, and so we always unpin before storing
// the new object.
//
// Refer to: https://pkg.go.dev/cmd/cgo#hdr-Passing_pointers
ra.pinner.Unpin()
ra.pinner.Pin(userData)
C.CPxRigidActor_set_userData(ra.cRa, userData)
}
func (ra *RigidActor) GetUserData() unsafe.Pointer {
return C.CPxRigidActor_get_userData(ra.cRa)
}
func (ra *RigidActor) ClearUserData() {
ra.pinner.Unpin()
C.CPxRigidActor_set_userData(ra.cRa, nil)
}
type RigidStatic struct {
cRs C.struct_CPxRigidStatic

View File

@ -4,7 +4,8 @@
#include "CPxFilterData.h"
#ifdef __cplusplus
extern "C" {
extern "C"
{
#endif
struct CPxRigidActor
@ -14,6 +15,8 @@ extern "C" {
// Sets the CPxFilterData on all the shapes of the actor.
CPxAPI void CPxRigidActor_setSimFilterData(CSTRUCT CPxRigidActor cra, CSTRUCT CPxFilterData cfd);
CPxAPI void CPxRigidActor_set_userData(CSTRUCT CPxRigidActor cra, void *userData);
CPxAPI void *CPxRigidActor_get_userData(CSTRUCT CPxRigidActor cra);
#ifdef __cplusplus
}