raylib-go icon indicating copy to clipboard operation
raylib-go copied to clipboard

Accessing OpenGL methods from within raylib-go

Open Technerder opened this issue 2 years ago • 5 comments

Is it possible to access OpenGL methods from within raylib-go? I'm trying to port some c camera code (rlFPCamera.c) and I need access to methods like rlDrawRenderBatchActive, rlMatrixMode, rlPushMatrix, and rlLoadIdentity.

Technerder avatar Jul 16 '22 01:07 Technerder

This is a duplicate of https://github.com/gen2brain/raylib-go/issues/171. If someone actually adds support I may reconsider.

gen2brain avatar Jul 16 '22 14:07 gen2brain

I'm trying to add some basic support but I'm kind of stuck on why my code isn't working. For reference I ported all of the correct functions in this commit https://github.com/Technerder/raylib-go/commit/f565649967231ee4fa0162dbd6e8058605475b46 but my test code doesn't actually seem to be working. As far as I can tell I directly ported the code almost line by line from https://github.com/raylib-extras/extras-c/tree/main/cameras/rlFPCamera, but when I try to run the example code, the game window is initialized and displays the 2D elements like the FPS counter and XYZ coordinates but I don't see the example plane grid rendering at all. Would you be able to help me understand what it is that I'm doing wrong?

Reference Testing Code:

main.go

package main

import (
	"fmt"
	"github.com/gen2brain/raylib-go/raylib"
)

type Game struct {
	ShouldRun    bool
	Camera       *FirstPersonCamera
	ScreenWidth  int
	ScreenHeight int
}

func (game *Game) HandleInput() {
	if rl.IsKeyDown(rl.KeyEscape) {
		game.ShouldRun = false
	}
	if rl.IsKeyPressed(rl.KeyF) {
		rl.ToggleFullscreen()
	}
}

func (game *Game) Render3D() {
	game.Camera.Update()
	rl.BeginDrawing()
	game.Camera.BeginMode3D()
	rl.ClearBackground(rl.RayWhite)
	rl.DrawGrid(50, 1)
	game.Camera.EndMode3D()
	rl.EndDrawing()
}

func (game *Game) Render2D() {
	rl.BeginDrawing()
	rl.DrawFPS(5, 0)
	rl.DrawText(fmt.Sprintf("X: %v", game.Camera.CameraPosition.X), 5, 20, 20, rl.Lime)
	rl.DrawText(fmt.Sprintf("Y: %v", game.Camera.CameraPosition.Y), 5, 40, 20, rl.Lime)
	rl.DrawText(fmt.Sprintf("Z: %v", game.Camera.CameraPosition.Z), 5, 60, 20, rl.Lime)
	rl.EndDrawing()
}

func (game *Game) Init() {
	rl.SetConfigFlags(rl.FlagFullscreenMode | rl.FlagVsyncHint)
	rl.SetTraceLog(rl.LogNone)
	rl.InitWindow(int32(rl.GetScreenWidth()), int32(rl.GetScreenHeight()), "Test")
	rl.SetTargetFPS(144)

	game.Camera = &FirstPersonCamera{}
	game.Camera.Init(45, rl.NewVector3(0, 2, 0))
	game.Camera.MoveSpeed.Z = 10
	game.Camera.MoveSpeed.X = 5
	game.Camera.FarPlane = 5000
	game.Camera.AllowFlight = true

	rl.SetMouseScale(0.5, 0.5)
	rl.SetCameraMoveControls(rl.KeyW, rl.KeyS, rl.KeyD, rl.KeyA, rl.KeySpace, rl.KeyLeftShift)
}

func (game *Game) Start() {
	game.ShouldRun = true
	for !rl.WindowShouldClose() && game.ShouldRun {
		game.HandleInput()
		game.Render3D()
		game.Render2D()
	}
	rl.CloseWindow()
}

func main() {
	var game Game
	game.Init()
	game.Start()
}

first_person_camera.go

package main

import "C"
import (
	rl "github.com/gen2brain/raylib-go/raylib"
	"math"
)

type FirstPersonCameraControls int64

const (
	MOVE_FRONT FirstPersonCameraControls = iota
	MOVE_BACK
	MOVE_RIGHT
	MOVE_LEFT
	MOVE_UP
	MOVE_DOWN
	TURN_LEFT
	TURN_RIGHT
	TURN_UP
	TURN_DOWN
	SPRINT
	LAST_CONTROL
)

type FirstPersonCamera struct {
	ControlKeys              []int32
	MoveSpeed                rl.Vector3
	TurnSpeed                rl.Vector2
	UseMouse                 bool
	MouseSensitivity         float32
	MinimumViewY             float32
	MaximumViewY             float32
	ViewBobbleFreq           float32
	ViewBobbleMagnitude      float32
	ViewBobbleWaverMagnitude float32
	CameraPosition           rl.Vector3
	PlayerEyesPosition       float32
	FOV                      rl.Vector2
	TargetDistance           float32
	ViewAngles               rl.Vector2
	CurrentBobble            float32
	Focused                  bool
	AllowFlight              bool
	ViewCamera               rl.Camera3D
	Forward                  rl.Vector3
	Right                    rl.Vector3
	NearPlane                float32
	FarPlane                 float32
}

func (camera *FirstPersonCamera) Init(fovY float32, position rl.Vector3) {
	camera.ControlKeys = make([]int32, LAST_CONTROL)
	camera.ControlKeys[0] = 'W'
	camera.ControlKeys[1] = 'S'
	camera.ControlKeys[2] = 'D'
	camera.ControlKeys[3] = 'A'
	camera.ControlKeys[4] = 'E'
	camera.ControlKeys[5] = 'Q'
	camera.ControlKeys[6] = rl.KeyLeft
	camera.ControlKeys[7] = rl.KeyRight
	camera.ControlKeys[8] = rl.KeyUp
	camera.ControlKeys[9] = rl.KeyDown
	camera.ControlKeys[10] = rl.KeyLeftShift

	camera.MoveSpeed = rl.NewVector3(1, 1, 1)
	camera.TurnSpeed = rl.NewVector2(90, 90)

	camera.UseMouse = true
	camera.MouseSensitivity = 600

	camera.MinimumViewY = -89.0
	camera.MaximumViewY = 89.0

	camera.ViewBobbleFreq = 0.0
	camera.ViewBobbleMagnitude = 0.02
	camera.ViewBobbleWaverMagnitude = 0.002
	camera.CurrentBobble = 0

	camera.Focused = rl.IsWindowFocused()

	camera.TargetDistance = 1
	camera.PlayerEyesPosition = 0.5
	camera.ViewAngles = rl.NewVector2(0, 0)

	camera.CameraPosition = position
	camera.FOV.Y = fovY

	camera.ViewCamera.Position = position
	camera.ViewCamera.Position.Y += camera.PlayerEyesPosition
	camera.ViewCamera.Target = rl.Vector3Add(camera.ViewCamera.Position, rl.NewVector3(0, 0, camera.TargetDistance))
	camera.ViewCamera.Up = rl.NewVector3(0.0, 1.0, 0.0)
	camera.ViewCamera.Fovy = fovY
	camera.ViewCamera.Projection = rl.CameraPerspective

	camera.AllowFlight = true
	camera.NearPlane = 0.01
	camera.FarPlane = 1000.0

	camera.ResizeView()
	camera.SetUseMouse(camera.UseMouse)
}

func (camera *FirstPersonCamera) ResizeView() {
	width := float32(rl.GetScreenWidth())
	height := float32(rl.GetScreenHeight())
	if height != 0 {
		camera.FOV.X = camera.FOV.Y * (width / height)
	}
}

func (camera *FirstPersonCamera) SetUseMouse(useMouse bool) {
	camera.UseMouse = useMouse
	if useMouse && rl.IsWindowFocused() {
		rl.DisableCursor()
	} else {
		rl.EnableCursor()
	}
}

func (camera *FirstPersonCamera) GetPosition() rl.Vector3 {
	return camera.CameraPosition
}

func (camera *FirstPersonCamera) SetPosition(pos rl.Vector3) {
	camera.CameraPosition = pos
	forward := rl.Vector3Subtract(camera.ViewCamera.Target, camera.ViewCamera.Position)
	camera.ViewCamera.Position = camera.CameraPosition
	camera.ViewCamera.Target = rl.Vector3Add(camera.CameraPosition, forward)
}

func (camera *FirstPersonCamera) GetViewRay() rl.Ray {
	return rl.NewRay(camera.CameraPosition, camera.Forward)
}

func (camera *FirstPersonCamera) BeginMode3D() {
	aspect := float32(rl.GetScreenWidth()) / float32(rl.GetScreenHeight())
	camera.SetupCamera(aspect)
}

func (camera *FirstPersonCamera) EndMode3D() {
	rl.EndMode3D()
}

func (camera *FirstPersonCamera) SetupCamera(aspect float32) {
	rl.DrawRenderBatchActive()      // rlDrawRenderBatchActive();   // Draw Buffers (Only OpenGL 3+ and ES2)
	rl.MatrixMode(rl.RL_PROJECTION) // rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
	rl.PushMatrix()                 // rlPushMatrix();              // Save previous matrix, which contains the settings for the 2d ortho projection
	rl.LoadIdentity()               // rlLoadIdentity();            // Reset current matrix (projection)
	if camera.ViewCamera.Projection == rl.CameraPerspective {
		// Setup perspective projection
		top := float32(rl.RL_CULL_DISTANCE_NEAR * math.Tan(float64(camera.ViewCamera.Fovy*0.5*rl.Deg2rad))) //double top = RL_CULL_DISTANCE_NEAR * tan(camera->ViewCamera.fovy * 0.5 * DEG2RAD);
		right := top * aspect                                                                               //double right = top * aspect;
		rl.Frustum(-right, right, -top, top, camera.NearPlane, camera.FarPlane)                             //rlFrustum(-right, right, -top, top, camera->NearPlane, camera->FarPlane);
	} else if camera.ViewCamera.Projection == rl.CameraOrthographic {
		// Setup orthographic projection
		top := camera.ViewCamera.Fovy                                         //double top = camera->ViewCamera.fovy / 2.0;
		right := top * aspect                                                 //double right = top * aspect;
		rl.Ortho(-right, right, -top, top, camera.NearPlane, camera.FarPlane) //rlOrtho(-right, right, -top, top, camera->NearPlane, camera->FarPlane);
	}
	rl.MatrixMode(rl.RL_MODELVIEW)                                                                         //rlMatrixMode(RL_MODELVIEW);         // Switch back to modelview matrix
	rl.LoadIdentity()                                                                                      //rlLoadIdentity();                   // Reset current matrix (modelview)
	matView := rl.MatrixLookAt(camera.ViewCamera.Position, camera.ViewCamera.Target, camera.ViewCamera.Up) //Matrix matView = MatrixLookAt(camera->ViewCamera.position, camera->ViewCamera.target, camera->ViewCamera.up);
	rl.MultMatrixf(rl.MatrixToFloatV(matView).V)                                                           //rlMultMatrixf(MatrixToFloatV(matView).v);      // Multiply modelview matrix by view matrix (camera)
	rl.EnableDepthTest()                                                                                   //rlEnableDepthTest();                // Enable DEPTH_TEST for 3D
}

func GetSpeedForAxis(camera *FirstPersonCamera, axis FirstPersonCameraControls, speed float32) float32 {
	key := camera.ControlKeys[axis]
	if key == -1 {
		return 0
	}
	var factor float32
	factor = 1.0
	if rl.IsKeyDown(camera.ControlKeys[SPRINT]) {
		factor = 2
	}
	if rl.IsKeyDown(camera.ControlKeys[axis]) {
		return speed * rl.GetFrameTime() * factor
	}
	return 0.0
}

func (camera *FirstPersonCamera) Update() {
	if rl.IsWindowFocused() != camera.Focused && camera.UseMouse {
		camera.Focused = rl.IsWindowFocused()
		if camera.Focused {
			rl.DisableCursor()
		} else {
			rl.EnableCursor()
		}
	}
	mousePositionDelta := rl.GetMouseDelta()
	direction := make([]float32, MOVE_DOWN+1)
	direction[0] = GetSpeedForAxis(camera, MOVE_FRONT, camera.MoveSpeed.Z)
	direction[1] = GetSpeedForAxis(camera, MOVE_BACK, camera.MoveSpeed.Z)
	direction[2] = GetSpeedForAxis(camera, MOVE_RIGHT, camera.MoveSpeed.X)
	direction[3] = GetSpeedForAxis(camera, MOVE_LEFT, camera.MoveSpeed.X)
	direction[4] = GetSpeedForAxis(camera, MOVE_UP, camera.MoveSpeed.Y)
	direction[5] = GetSpeedForAxis(camera, MOVE_DOWN, camera.MoveSpeed.Y)

	turnRotation := GetSpeedForAxis(camera, TURN_RIGHT, camera.TurnSpeed.X) - GetSpeedForAxis(camera, TURN_LEFT, camera.TurnSpeed.X)
	tiltRotation := GetSpeedForAxis(camera, TURN_UP, camera.TurnSpeed.Y) - GetSpeedForAxis(camera, TURN_DOWN, camera.TurnSpeed.Y)

	if turnRotation != 0 {
		camera.ViewAngles.X -= turnRotation * rl.Deg2rad
	} else {
		camera.ViewAngles.X += mousePositionDelta.X / -camera.MouseSensitivity
	}

	if tiltRotation != 0 {
		camera.ViewAngles.Y += tiltRotation * rl.Deg2rad
	} else if camera.UseMouse && camera.Focused {
		camera.ViewAngles.Y += mousePositionDelta.Y / -camera.MouseSensitivity
	}

	target := rl.Vector3Transform(rl.NewVector3(0, 0, 1), rl.MatrixRotateXYZ(rl.NewVector3(camera.ViewAngles.Y, -camera.ViewAngles.X, 0)))

	if camera.AllowFlight {
		camera.Forward = target
	} else {
		camera.Forward = rl.Vector3Transform(rl.NewVector3(0, 0, 1), rl.MatrixRotateXYZ(rl.NewVector3(0, -camera.ViewAngles.X, 0)))
	}

	camera.Right = rl.NewVector3(camera.Forward.Z*-1.0, 0, camera.Forward.X)

	camera.CameraPosition = rl.Vector3Add(camera.CameraPosition, rl.Vector3Scale(camera.Forward, direction[MOVE_FRONT]-direction[MOVE_BACK]))
	camera.CameraPosition = rl.Vector3Add(camera.CameraPosition, rl.Vector3Scale(camera.Right, direction[MOVE_RIGHT]-direction[MOVE_LEFT]))

	camera.CameraPosition.Y += direction[MOVE_UP] - direction[MOVE_DOWN]
	camera.ViewCamera.Position = camera.CameraPosition

	eyeOffset := camera.PlayerEyesPosition

	if camera.ViewBobbleFreq > 0 {
		swingDelta := float32(math.Max(math.Abs(float64(direction[MOVE_FRONT]-direction[MOVE_BACK])), math.Abs(float64(direction[MOVE_RIGHT]-direction[MOVE_LEFT]))))
		camera.CurrentBobble += swingDelta * camera.ViewBobbleFreq
		viewBobbleDampen := float32(8.0)
		eyeOffset -= float32(math.Sin(float64(camera.CurrentBobble/viewBobbleDampen)) * rl.FlagFullscreenMode)
		camera.ViewCamera.Up.X = float32(math.Sin(float64(camera.CurrentBobble/(viewBobbleDampen*2)))) * camera.ViewBobbleWaverMagnitude
		camera.ViewCamera.Up.Z = float32(-math.Sin(float64(camera.CurrentBobble/(viewBobbleDampen*2)))) * camera.ViewBobbleWaverMagnitude
	} else {
		camera.CurrentBobble = 0
		camera.ViewCamera.Up.X = 0
		camera.ViewCamera.Up.Z = 0
	}

	camera.ViewCamera.Position.Y += eyeOffset

	camera.ViewCamera.Target.X = camera.ViewCamera.Position.X + target.X
	camera.ViewCamera.Target.Y = camera.ViewCamera.Position.Y + target.Y
	camera.ViewCamera.Target.Z = camera.ViewCamera.Position.Z + target.Z
}

Technerder avatar Jul 19 '22 05:07 Technerder

Are there any simpler examples to test rlgl functions? Did you add all functions or just what is needed for this concrete example? Sorry, I never even tried those raylib extras.

gen2brain avatar Jul 19 '22 19:07 gen2brain

I just ported all functions needed for this one example, as its the example I was working with at the time and that worked "out of the box" in C.

Technerder avatar Jul 24 '22 18:07 Technerder

I fixed some silly bugs in f963d74 and f565649 and added a proper example to my fork here https://github.com/Technerder/raylib-go/tree/master/examples/c_extras_ports/first_person_camera, and the world actually renders now but the camera movement seems to be broken. I've double checked the reference implementation and I'm rather confused as to why mine doesn't work properly.

https://user-images.githubusercontent.com/15988124/181679745-2c80cedb-2890-41de-9aae-ee94d69506fb.mp4

Technerder avatar Jul 29 '22 04:07 Technerder

Implementation and 2d_camera_mouse_zoom example are added to the repo.

gen2brain avatar Nov 26 '22 18:11 gen2brain