mirror of
https://github.com/bloeys/nmage.git
synced 2025-12-29 13:28:20 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 841a6e989c | |||
| 94942e55a1 | |||
| 15087ac542 | |||
| f16407629a | |||
| 592208d5c9 | |||
| fd74d58ad3 | |||
| 4c2fca48b3 | |||
| 50c2ab650f | |||
| 8e96cf7050 | |||
| 56e10049e9 | |||
| 2bfba880a9 | |||
| ffc9b6aa7c |
3
.gitignore
vendored
3
.gitignore
vendored
@ -14,4 +14,5 @@
|
|||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
vendor/
|
vendor/
|
||||||
.vscode/
|
.vscode/
|
||||||
imgui.ini
|
imgui.ini
|
||||||
|
*~
|
||||||
27
assets/assets.go
Executable file
27
assets/assets.go
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
package assets
|
||||||
|
|
||||||
|
var (
|
||||||
|
Textures map[uint32]Texture = make(map[uint32]Texture)
|
||||||
|
TexturePaths map[string]uint32 = make(map[string]uint32)
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetTexture(t Texture) {
|
||||||
|
|
||||||
|
if _, ok := TexturePaths[t.Path]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Loaded texture:", t.Path)
|
||||||
|
Textures[t.TexID] = t
|
||||||
|
TexturePaths[t.Path] = t.TexID
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTexture(texID uint32) (Texture, bool) {
|
||||||
|
tex, ok := Textures[texID]
|
||||||
|
return tex, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTexturePath(path string) (Texture, bool) {
|
||||||
|
tex, ok := Textures[TexturePaths[path]]
|
||||||
|
return tex, ok
|
||||||
|
}
|
||||||
85
assets/textures.go
Executable file
85
assets/textures.go
Executable file
@ -0,0 +1,85 @@
|
|||||||
|
package assets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"os"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/go-gl/gl/v4.1-core/gl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ColorFormat int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ColorFormat_RGBA8 ColorFormat = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
type Texture struct {
|
||||||
|
Path string
|
||||||
|
TexID uint32
|
||||||
|
Width int32
|
||||||
|
Height int32
|
||||||
|
Pixels []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadPNGTexture(file string) (Texture, error) {
|
||||||
|
|
||||||
|
if tex, ok := GetTexturePath(file); ok {
|
||||||
|
return tex, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//Load from disk
|
||||||
|
fileBytes, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return Texture{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
img, err := png.Decode(bytes.NewReader(fileBytes))
|
||||||
|
if err != nil {
|
||||||
|
return Texture{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tex := Texture{
|
||||||
|
Path: file,
|
||||||
|
Width: int32(img.Bounds().Dx()),
|
||||||
|
Height: int32(img.Bounds().Dy()),
|
||||||
|
Pixels: make([]byte, img.Bounds().Dx()*img.Bounds().Dy()*4),
|
||||||
|
}
|
||||||
|
|
||||||
|
//NOTE: Load bottom left to top right because this is the texture coordinate system used by OpenGL
|
||||||
|
//NOTE: We only support 8-bit channels (32-bit colors) for now
|
||||||
|
i := 0
|
||||||
|
for y := img.Bounds().Dy() - 1; y >= 0; y-- {
|
||||||
|
for x := 0; x < img.Bounds().Dx(); x++ {
|
||||||
|
|
||||||
|
c := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA)
|
||||||
|
|
||||||
|
tex.Pixels[i] = c.R
|
||||||
|
tex.Pixels[i+1] = c.G
|
||||||
|
tex.Pixels[i+2] = c.B
|
||||||
|
tex.Pixels[i+3] = c.A
|
||||||
|
|
||||||
|
i += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Prepare opengl stuff
|
||||||
|
gl.GenTextures(1, &tex.TexID)
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, tex.TexID)
|
||||||
|
|
||||||
|
// set the texture wrapping/filtering options (on the currently bound texture object)
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||||
|
|
||||||
|
// load and generate the texture
|
||||||
|
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, tex.Width, tex.Height, 0, gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(&tex.Pixels[0]))
|
||||||
|
gl.GenerateMipmap(gl.TEXTURE_2D)
|
||||||
|
|
||||||
|
SetTexture(tex)
|
||||||
|
|
||||||
|
return tex, nil
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
//go:build debug
|
//go:build !release
|
||||||
|
|
||||||
package consts
|
package consts
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
//go:build !debug
|
//go:build release
|
||||||
|
|
||||||
package consts
|
package consts
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
package engine
|
package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/bloeys/nmage/input"
|
||||||
"github.com/bloeys/nmage/timing"
|
"github.com/bloeys/nmage/timing"
|
||||||
"github.com/go-gl/gl/v4.1-core/gl"
|
"github.com/go-gl/gl/v4.1-core/gl"
|
||||||
|
"github.com/inkyblackness/imgui-go/v4"
|
||||||
"github.com/veandco/go-sdl2/sdl"
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -11,6 +13,72 @@ type Window struct {
|
|||||||
GlCtx sdl.GLContext
|
GlCtx sdl.GLContext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Window) handleInputs() {
|
||||||
|
|
||||||
|
input.EventLoopStart()
|
||||||
|
imIO := imgui.CurrentIO()
|
||||||
|
|
||||||
|
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
|
||||||
|
|
||||||
|
switch e := event.(type) {
|
||||||
|
|
||||||
|
case *sdl.MouseWheelEvent:
|
||||||
|
|
||||||
|
input.HandleMouseWheelEvent(e)
|
||||||
|
|
||||||
|
xDelta, yDelta := input.GetMouseWheelMotion()
|
||||||
|
imIO.AddMouseWheelDelta(float32(xDelta), float32(yDelta))
|
||||||
|
|
||||||
|
case *sdl.KeyboardEvent:
|
||||||
|
input.HandleKeyboardEvent(e)
|
||||||
|
|
||||||
|
if e.Type == sdl.KEYDOWN {
|
||||||
|
imIO.KeyPress(int(e.Keysym.Scancode))
|
||||||
|
} else if e.Type == sdl.KEYUP {
|
||||||
|
imIO.KeyRelease(int(e.Keysym.Scancode))
|
||||||
|
}
|
||||||
|
|
||||||
|
case *sdl.TextInputEvent:
|
||||||
|
imIO.AddInputCharacters(string(e.Text[:]))
|
||||||
|
|
||||||
|
case *sdl.MouseButtonEvent:
|
||||||
|
input.HandleMouseBtnEvent(e)
|
||||||
|
|
||||||
|
case *sdl.MouseMotionEvent:
|
||||||
|
input.HandleMouseMotionEvent(e)
|
||||||
|
|
||||||
|
case *sdl.WindowEvent:
|
||||||
|
if e.Event == sdl.WINDOWEVENT_SIZE_CHANGED {
|
||||||
|
w.handleWindowResize()
|
||||||
|
}
|
||||||
|
|
||||||
|
case *sdl.QuitEvent:
|
||||||
|
input.HandleQuitEvent(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
|
||||||
|
x, y, _ := sdl.GetMouseState()
|
||||||
|
imIO.SetMousePosition(imgui.Vec2{X: float32(x), Y: float32(y)})
|
||||||
|
|
||||||
|
imIO.SetMouseButtonDown(0, input.MouseDown(sdl.BUTTON_LEFT))
|
||||||
|
imIO.SetMouseButtonDown(1, input.MouseDown(sdl.BUTTON_RIGHT))
|
||||||
|
imIO.SetMouseButtonDown(2, input.MouseDown(sdl.BUTTON_MIDDLE))
|
||||||
|
|
||||||
|
imIO.KeyShift(sdl.SCANCODE_LSHIFT, sdl.SCANCODE_RSHIFT)
|
||||||
|
imIO.KeyCtrl(sdl.SCANCODE_LCTRL, sdl.SCANCODE_RCTRL)
|
||||||
|
imIO.KeyAlt(sdl.SCANCODE_LALT, sdl.SCANCODE_RALT)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Window) handleWindowResize() {
|
||||||
|
|
||||||
|
fbWidth, fbHeight := w.SDLWin.GLGetDrawableSize()
|
||||||
|
if fbWidth <= 0 || fbHeight <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gl.Viewport(0, 0, fbWidth, fbHeight)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Window) Destroy() error {
|
func (w *Window) Destroy() error {
|
||||||
return w.SDLWin.Destroy()
|
return w.SDLWin.Destroy()
|
||||||
}
|
}
|
||||||
|
|||||||
53
engine/game.go
Executable file
53
engine/game.go
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/bloeys/nmage/timing"
|
||||||
|
nmageimgui "github.com/bloeys/nmage/ui/imgui"
|
||||||
|
"github.com/go-gl/gl/v4.1-core/gl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Game interface {
|
||||||
|
Init()
|
||||||
|
|
||||||
|
FrameStart()
|
||||||
|
Update()
|
||||||
|
Render()
|
||||||
|
FrameEnd()
|
||||||
|
ShouldRun() bool
|
||||||
|
|
||||||
|
GetWindow() *Window
|
||||||
|
GetImGUI() nmageimgui.ImguiInfo
|
||||||
|
|
||||||
|
Deinit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(g Game) {
|
||||||
|
|
||||||
|
g.Init()
|
||||||
|
|
||||||
|
w := g.GetWindow()
|
||||||
|
ui := g.GetImGUI()
|
||||||
|
for g.ShouldRun() {
|
||||||
|
|
||||||
|
width, height := w.SDLWin.GetSize()
|
||||||
|
fbWidth, fbHeight := w.SDLWin.GLGetDrawableSize()
|
||||||
|
|
||||||
|
timing.FrameStarted()
|
||||||
|
w.handleInputs()
|
||||||
|
ui.FrameStart(float32(width), float32(height))
|
||||||
|
|
||||||
|
g.FrameStart()
|
||||||
|
|
||||||
|
g.Update()
|
||||||
|
|
||||||
|
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||||
|
g.Render()
|
||||||
|
ui.Render(float32(width), float32(height), fbWidth, fbHeight)
|
||||||
|
w.SDLWin.GLSwap()
|
||||||
|
|
||||||
|
g.FrameEnd()
|
||||||
|
timing.FrameEnded()
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Deinit()
|
||||||
|
}
|
||||||
@ -31,10 +31,11 @@ type mouseWheelState struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
keyMap = make(map[sdl.Keycode]*keyState)
|
keyMap = make(map[sdl.Keycode]*keyState)
|
||||||
mouseBtnMap = make(map[int]*mouseBtnState)
|
mouseBtnMap = make(map[int]*mouseBtnState)
|
||||||
mouseMotion = mouseMotionState{}
|
mouseMotion = mouseMotionState{}
|
||||||
mouseWheel = mouseWheelState{}
|
mouseWheel = mouseWheelState{}
|
||||||
|
quitRequested bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func EventLoopStart() {
|
func EventLoopStart() {
|
||||||
@ -55,6 +56,16 @@ func EventLoopStart() {
|
|||||||
|
|
||||||
mouseWheel.XDelta = 0
|
mouseWheel.XDelta = 0
|
||||||
mouseWheel.YDelta = 0
|
mouseWheel.YDelta = 0
|
||||||
|
|
||||||
|
quitRequested = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleQuitEvent(e *sdl.QuitEvent) {
|
||||||
|
quitRequested = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsQuitClicked() bool {
|
||||||
|
return quitRequested
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleKeyboardEvent(e *sdl.KeyboardEvent) {
|
func HandleKeyboardEvent(e *sdl.KeyboardEvent) {
|
||||||
|
|||||||
446
main.go
446
main.go
@ -6,88 +6,76 @@ import (
|
|||||||
|
|
||||||
"github.com/bloeys/assimp-go/asig"
|
"github.com/bloeys/assimp-go/asig"
|
||||||
"github.com/bloeys/gglm/gglm"
|
"github.com/bloeys/gglm/gglm"
|
||||||
|
"github.com/bloeys/nmage/assets"
|
||||||
"github.com/bloeys/nmage/engine"
|
"github.com/bloeys/nmage/engine"
|
||||||
"github.com/bloeys/nmage/input"
|
"github.com/bloeys/nmage/input"
|
||||||
"github.com/bloeys/nmage/logging"
|
"github.com/bloeys/nmage/logging"
|
||||||
"github.com/bloeys/nmage/materials"
|
"github.com/bloeys/nmage/materials"
|
||||||
"github.com/bloeys/nmage/meshes"
|
"github.com/bloeys/nmage/meshes"
|
||||||
"github.com/bloeys/nmage/timing"
|
"github.com/bloeys/nmage/timing"
|
||||||
|
nmageimgui "github.com/bloeys/nmage/ui/imgui"
|
||||||
"github.com/go-gl/gl/v4.1-core/gl"
|
"github.com/go-gl/gl/v4.1-core/gl"
|
||||||
"github.com/inkyblackness/imgui-go/v4"
|
"github.com/inkyblackness/imgui-go/v4"
|
||||||
"github.com/veandco/go-sdl2/sdl"
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
)
|
)
|
||||||
|
|
||||||
//TODO: Tasks:
|
//TODO: Tasks:
|
||||||
//Engine loop
|
|
||||||
//Proper rendering setup
|
//Proper rendering setup
|
||||||
//Flesh out the material system
|
//Entities and components
|
||||||
//Object
|
//Camera class
|
||||||
//Abstract UI
|
|
||||||
//Textures
|
|
||||||
//Audio
|
//Audio
|
||||||
|
//Flesh out the material system
|
||||||
|
|
||||||
//Low Priority:
|
//Low Priority:
|
||||||
// Proper Asset loading
|
// Abstract keys enum away from sdl
|
||||||
|
// Abstract UI
|
||||||
type ImguiInfo struct {
|
// Proper Asset loading
|
||||||
imCtx *imgui.Context
|
|
||||||
|
|
||||||
vaoID uint32
|
|
||||||
vboID uint32
|
|
||||||
indexBufID uint32
|
|
||||||
texID uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
winWidth int32 = 1280
|
|
||||||
winHeight int32 = 720
|
|
||||||
|
|
||||||
isRunning bool = true
|
isRunning bool = true
|
||||||
window *engine.Window
|
window *engine.Window
|
||||||
|
|
||||||
simpleMat *materials.Material
|
simpleMat *materials.Material
|
||||||
imguiMat *materials.Material
|
|
||||||
cubeMesh *meshes.Mesh
|
cubeMesh *meshes.Mesh
|
||||||
|
|
||||||
modelMat = gglm.NewTrMatId()
|
modelMat = gglm.NewTrMatId()
|
||||||
projMat = &gglm.Mat4{}
|
projMat = &gglm.Mat4{}
|
||||||
|
|
||||||
imguiInfo *ImguiInfo
|
|
||||||
|
|
||||||
camPos = gglm.NewVec3(0, 0, -10)
|
camPos = gglm.NewVec3(0, 0, -10)
|
||||||
camForward = gglm.NewVec3(0, 0, 1)
|
camForward = gglm.NewVec3(0, 0, 1)
|
||||||
|
|
||||||
|
lightPos1 = gglm.NewVec3(2, 2, 0)
|
||||||
|
lightColor1 = gglm.NewVec3(1, 1, 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
type OurGame struct {
|
||||||
|
Win *engine.Window
|
||||||
|
ImGUIInfo nmageimgui.ImguiInfo
|
||||||
|
Quitting bool
|
||||||
|
}
|
||||||
|
|
||||||
runtime.LockOSThread()
|
func (g *OurGame) Init() {
|
||||||
|
|
||||||
//Init engine
|
|
||||||
err := engine.Init()
|
|
||||||
if err != nil {
|
|
||||||
logging.ErrLog.Fatalln("Failed to init nMage. Err:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Create window
|
|
||||||
window, err = engine.CreateOpenGLWindowCentered("nMage", winWidth, winHeight, engine.WindowFlags_RESIZABLE)
|
|
||||||
if err != nil {
|
|
||||||
logging.ErrLog.Fatalln("Failed to create window. Err: ", err)
|
|
||||||
}
|
|
||||||
defer window.Destroy()
|
|
||||||
|
|
||||||
engine.SetVSync(false)
|
|
||||||
|
|
||||||
//Create materials
|
//Create materials
|
||||||
simpleMat = materials.NewMaterial("Simple Mat", "./res/shaders/simple")
|
simpleMat = materials.NewMaterial("Simple Mat", "./res/shaders/simple")
|
||||||
imguiMat = materials.NewMaterial("ImGUI Mat", "./res/shaders/imgui")
|
|
||||||
|
|
||||||
//Load meshes
|
//Load meshes
|
||||||
cubeMesh, err = meshes.NewMesh("Cube", "./res/models/color-cube.fbx", asig.PostProcess(0))
|
var err error
|
||||||
|
cubeMesh, err = meshes.NewMesh("Cube", "./res/models/tex-cube.fbx", asig.PostProcess(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.ErrLog.Fatalln("Failed to load cube mesh. Err: ", err)
|
logging.ErrLog.Fatalln("Failed to load cube mesh. Err: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
initImGUI()
|
//Load textures
|
||||||
|
tex, err := assets.LoadPNGTexture("./res/textures/Low poly planet.png")
|
||||||
|
if err != nil {
|
||||||
|
logging.ErrLog.Fatalln("Failed to load texture. Err: ", err)
|
||||||
|
}
|
||||||
|
cubeMesh.AddTexture(tex)
|
||||||
|
|
||||||
|
//Set mesh textures on material
|
||||||
|
for _, v := range cubeMesh.TextureIDs {
|
||||||
|
simpleMat.AddTextureID(v)
|
||||||
|
}
|
||||||
|
|
||||||
//Enable vertex attributes
|
//Enable vertex attributes
|
||||||
simpleMat.SetAttribute(cubeMesh.Buf)
|
simpleMat.SetAttribute(cubeMesh.Buf)
|
||||||
@ -104,182 +92,28 @@ func main() {
|
|||||||
updateViewMat()
|
updateViewMat()
|
||||||
|
|
||||||
//Perspective/Depth
|
//Perspective/Depth
|
||||||
projMat := gglm.Perspective(45*gglm.Deg2Rad, float32(winWidth)/float32(winHeight), 0.1, 500)
|
projMat := gglm.Perspective(45*gglm.Deg2Rad, float32(1280)/float32(720), 0.1, 500)
|
||||||
simpleMat.SetUnifMat4("projMat", projMat)
|
simpleMat.SetUnifMat4("projMat", projMat)
|
||||||
|
|
||||||
//Lights
|
//Lights
|
||||||
simpleMat.SetUnifVec3("lightPos1", &lightPos1)
|
simpleMat.SetUnifVec3("lightPos1", lightPos1)
|
||||||
simpleMat.SetUnifVec3("lightColor1", &lightColor1)
|
simpleMat.SetUnifVec3("lightColor1", lightColor1)
|
||||||
|
|
||||||
//Game loop
|
|
||||||
for isRunning {
|
|
||||||
|
|
||||||
timing.FrameStarted()
|
|
||||||
|
|
||||||
handleInputs()
|
|
||||||
runGameLogic()
|
|
||||||
|
|
||||||
draw()
|
|
||||||
|
|
||||||
timing.FrameEnded()
|
|
||||||
|
|
||||||
window.SDLWin.SetTitle(fmt.Sprintf("FPS: %.0f; Elapsed: %v", 1/timing.DT(), timing.ElapsedTime()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateViewMat() {
|
func (g *OurGame) FrameStart() {
|
||||||
targetPos := camPos.Clone().Add(camForward)
|
|
||||||
viewMat := gglm.LookAt(camPos, targetPos, gglm.NewVec3(0, 1, 0))
|
|
||||||
simpleMat.SetUnifMat4("viewMat", &viewMat.Mat4)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initImGUI() {
|
func (g *OurGame) Update() {
|
||||||
|
|
||||||
imguiInfo = &ImguiInfo{
|
if input.IsQuitClicked() {
|
||||||
imCtx: imgui.CreateContext(nil),
|
g.Quitting = true
|
||||||
}
|
}
|
||||||
|
|
||||||
imIO := imgui.CurrentIO()
|
winWidth, winHeight := g.Win.SDLWin.GetSize()
|
||||||
imIO.SetBackendFlags(imIO.GetBackendFlags() | imgui.BackendFlagsRendererHasVtxOffset)
|
|
||||||
|
|
||||||
gl.GenVertexArrays(1, &imguiInfo.vaoID)
|
|
||||||
gl.GenBuffers(1, &imguiInfo.vboID)
|
|
||||||
gl.GenBuffers(1, &imguiInfo.indexBufID)
|
|
||||||
gl.GenTextures(1, &imguiInfo.texID)
|
|
||||||
|
|
||||||
// Upload font to gpu
|
|
||||||
gl.BindTexture(gl.TEXTURE_2D, imguiInfo.texID)
|
|
||||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
|
|
||||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
|
||||||
gl.PixelStorei(gl.UNPACK_ROW_LENGTH, 0)
|
|
||||||
|
|
||||||
image := imIO.Fonts().TextureDataAlpha8()
|
|
||||||
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RED, int32(image.Width), int32(image.Height), 0, gl.RED, gl.UNSIGNED_BYTE, image.Pixels)
|
|
||||||
|
|
||||||
// Store our identifier
|
|
||||||
imIO.Fonts().SetTextureID(imgui.TextureID(imguiInfo.texID))
|
|
||||||
|
|
||||||
//Shader attributes
|
|
||||||
imguiMat.Bind()
|
|
||||||
imguiMat.EnableAttribute("Position")
|
|
||||||
imguiMat.EnableAttribute("UV")
|
|
||||||
imguiMat.EnableAttribute("Color")
|
|
||||||
imguiMat.UnBind()
|
|
||||||
|
|
||||||
//Init imgui input mapping
|
|
||||||
keys := map[int]int{
|
|
||||||
imgui.KeyTab: sdl.SCANCODE_TAB,
|
|
||||||
imgui.KeyLeftArrow: sdl.SCANCODE_LEFT,
|
|
||||||
imgui.KeyRightArrow: sdl.SCANCODE_RIGHT,
|
|
||||||
imgui.KeyUpArrow: sdl.SCANCODE_UP,
|
|
||||||
imgui.KeyDownArrow: sdl.SCANCODE_DOWN,
|
|
||||||
imgui.KeyPageUp: sdl.SCANCODE_PAGEUP,
|
|
||||||
imgui.KeyPageDown: sdl.SCANCODE_PAGEDOWN,
|
|
||||||
imgui.KeyHome: sdl.SCANCODE_HOME,
|
|
||||||
imgui.KeyEnd: sdl.SCANCODE_END,
|
|
||||||
imgui.KeyInsert: sdl.SCANCODE_INSERT,
|
|
||||||
imgui.KeyDelete: sdl.SCANCODE_DELETE,
|
|
||||||
imgui.KeyBackspace: sdl.SCANCODE_BACKSPACE,
|
|
||||||
imgui.KeySpace: sdl.SCANCODE_BACKSPACE,
|
|
||||||
imgui.KeyEnter: sdl.SCANCODE_RETURN,
|
|
||||||
imgui.KeyEscape: sdl.SCANCODE_ESCAPE,
|
|
||||||
imgui.KeyA: sdl.SCANCODE_A,
|
|
||||||
imgui.KeyC: sdl.SCANCODE_C,
|
|
||||||
imgui.KeyV: sdl.SCANCODE_V,
|
|
||||||
imgui.KeyX: sdl.SCANCODE_X,
|
|
||||||
imgui.KeyY: sdl.SCANCODE_Y,
|
|
||||||
imgui.KeyZ: sdl.SCANCODE_Z,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
|
|
||||||
for imguiKey, nativeKey := range keys {
|
|
||||||
imIO.KeyMap(imguiKey, nativeKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleInputs() {
|
|
||||||
|
|
||||||
input.EventLoopStart()
|
|
||||||
imIO := imgui.CurrentIO()
|
|
||||||
|
|
||||||
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
|
|
||||||
|
|
||||||
switch e := event.(type) {
|
|
||||||
|
|
||||||
case *sdl.MouseWheelEvent:
|
|
||||||
|
|
||||||
input.HandleMouseWheelEvent(e)
|
|
||||||
|
|
||||||
xDelta, yDelta := input.GetMouseWheelMotion()
|
|
||||||
imIO.AddMouseWheelDelta(float32(xDelta), float32(yDelta))
|
|
||||||
|
|
||||||
case *sdl.KeyboardEvent:
|
|
||||||
input.HandleKeyboardEvent(e)
|
|
||||||
|
|
||||||
if e.Type == sdl.KEYDOWN {
|
|
||||||
imIO.KeyPress(int(e.Keysym.Scancode))
|
|
||||||
} else if e.Type == sdl.KEYUP {
|
|
||||||
imIO.KeyRelease(int(e.Keysym.Scancode))
|
|
||||||
}
|
|
||||||
|
|
||||||
case *sdl.TextInputEvent:
|
|
||||||
imIO.AddInputCharacters(string(e.Text[:]))
|
|
||||||
|
|
||||||
case *sdl.MouseButtonEvent:
|
|
||||||
input.HandleMouseBtnEvent(e)
|
|
||||||
|
|
||||||
case *sdl.MouseMotionEvent:
|
|
||||||
input.HandleMouseMotionEvent(e)
|
|
||||||
|
|
||||||
case *sdl.QuitEvent:
|
|
||||||
isRunning = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currWinWidth, currWinHeight := window.SDLWin.GetSize()
|
|
||||||
if winWidth != currWinWidth || winHeight != currWinHeight {
|
|
||||||
handleWindowResize(currWinWidth, currWinHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
|
|
||||||
x, y, _ := sdl.GetMouseState()
|
|
||||||
imIO.SetMousePosition(imgui.Vec2{X: float32(x), Y: float32(y)})
|
|
||||||
|
|
||||||
imIO.SetMouseButtonDown(0, input.MouseDown(sdl.BUTTON_LEFT))
|
|
||||||
imIO.SetMouseButtonDown(1, input.MouseDown(sdl.BUTTON_RIGHT))
|
|
||||||
imIO.SetMouseButtonDown(2, input.MouseDown(sdl.BUTTON_MIDDLE))
|
|
||||||
|
|
||||||
imIO.KeyShift(sdl.SCANCODE_LSHIFT, sdl.SCANCODE_RSHIFT)
|
|
||||||
imIO.KeyCtrl(sdl.SCANCODE_LCTRL, sdl.SCANCODE_RCTRL)
|
|
||||||
imIO.KeyAlt(sdl.SCANCODE_LALT, sdl.SCANCODE_RALT)
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleWindowResize(newWinWidth, newWinHeight int32) {
|
|
||||||
|
|
||||||
winWidth = newWinWidth
|
|
||||||
winHeight = newWinHeight
|
|
||||||
|
|
||||||
fbWidth, fbHeight := window.SDLWin.GLGetDrawableSize()
|
|
||||||
if fbWidth <= 0 || fbHeight <= 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
gl.Viewport(0, 0, fbWidth, fbHeight)
|
|
||||||
|
|
||||||
projMat = gglm.Perspective(45*gglm.Deg2Rad, float32(winWidth)/float32(winHeight), 0.1, 20)
|
projMat = gglm.Perspective(45*gglm.Deg2Rad, float32(winWidth)/float32(winHeight), 0.1, 20)
|
||||||
simpleMat.SetUnifMat4("projMat", projMat)
|
simpleMat.SetUnifMat4("projMat", projMat)
|
||||||
}
|
|
||||||
|
|
||||||
var time uint64 = 0
|
|
||||||
var name string = ""
|
|
||||||
|
|
||||||
var ambientColor gglm.Vec3 = *gglm.NewVec3(1, 1, 1)
|
|
||||||
var ambientColorStrength float32 = 0.1
|
|
||||||
|
|
||||||
var lightPos1 gglm.Vec3 = *gglm.NewVec3(2, 2, 0)
|
|
||||||
var lightColor1 gglm.Vec3 = *gglm.NewVec3(1, 1, 1)
|
|
||||||
|
|
||||||
func runGameLogic() {
|
|
||||||
|
|
||||||
|
//Camera movement
|
||||||
var camSpeed float32 = 15
|
var camSpeed float32 = 15
|
||||||
if input.KeyDown(sdl.K_w) {
|
if input.KeyDown(sdl.K_w) {
|
||||||
camPos.Data[1] += camSpeed * timing.DT()
|
camPos.Data[1] += camSpeed * timing.DT()
|
||||||
@ -306,53 +140,26 @@ func runGameLogic() {
|
|||||||
updateViewMat()
|
updateViewMat()
|
||||||
}
|
}
|
||||||
|
|
||||||
modelMat.Rotate(10*timing.DT()*gglm.Deg2Rad, gglm.NewVec3(1, 1, 1).Normalize())
|
//Rotating cubes
|
||||||
simpleMat.SetUnifMat4("modelMat", &modelMat.Mat4)
|
if input.KeyDown(sdl.K_SPACE) {
|
||||||
|
modelMat.Rotate(10*timing.DT()*gglm.Deg2Rad, gglm.NewVec3(1, 1, 1).Normalize())
|
||||||
//ImGUI
|
simpleMat.SetUnifMat4("modelMat", &modelMat.Mat4)
|
||||||
imIO := imgui.CurrentIO()
|
|
||||||
imIO.SetDisplaySize(imgui.Vec2{X: float32(winWidth), Y: float32(winHeight)})
|
|
||||||
|
|
||||||
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
|
|
||||||
frequency := sdl.GetPerformanceFrequency()
|
|
||||||
currentTime := sdl.GetPerformanceCounter()
|
|
||||||
if time > 0 {
|
|
||||||
imIO.SetDeltaTime(float32(currentTime-time) / float32(frequency))
|
|
||||||
} else {
|
|
||||||
imIO.SetDeltaTime(1.0 / 60.0)
|
|
||||||
}
|
|
||||||
time = currentTime
|
|
||||||
|
|
||||||
imgui.NewFrame()
|
|
||||||
|
|
||||||
if imgui.SliderFloat3("Ambient Color", &ambientColor.Data, 0, 1) {
|
|
||||||
simpleMat.SetUnifVec3("ambientLightColor", &ambientColor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if imgui.SliderFloat("Ambient Color Strength", &ambientColorStrength, 0, 1) {
|
imgui.DragFloat3("Cam Pos", &camPos.Data)
|
||||||
simpleMat.SetUnifFloat32("ambientStrength", ambientColorStrength)
|
|
||||||
}
|
|
||||||
|
|
||||||
if imgui.SliderFloat3("Light Pos 1", &lightPos1.Data, -10, 10) {
|
|
||||||
simpleMat.SetUnifVec3("lightPos1", &lightPos1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if imgui.SliderFloat3("Light Color 1", &lightColor1.Data, 0, 1) {
|
|
||||||
simpleMat.SetUnifVec3("lightColor1", &lightColor1)
|
|
||||||
}
|
|
||||||
|
|
||||||
imgui.Render()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func draw() {
|
var dtAccum float32 = 0
|
||||||
|
var lastElapsedTime uint64 = 0
|
||||||
|
var framesSinceLastFPSUpdate uint = 0
|
||||||
|
|
||||||
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
func (g *OurGame) Render() {
|
||||||
|
|
||||||
simpleMat.Bind()
|
simpleMat.Bind()
|
||||||
cubeMesh.Buf.Bind()
|
cubeMesh.Buf.Bind()
|
||||||
tempModelMat := modelMat.Clone()
|
tempModelMat := modelMat.Clone()
|
||||||
|
|
||||||
rowSize := 10
|
rowSize := 100
|
||||||
for y := 0; y < rowSize; y++ {
|
for y := 0; y < rowSize; y++ {
|
||||||
for x := 0; x < rowSize; x++ {
|
for x := 0; x < rowSize; x++ {
|
||||||
simpleMat.SetUnifMat4("modelMat", &tempModelMat.Translate(gglm.NewVec3(-1, 0, 0)).Mat4)
|
simpleMat.SetUnifMat4("modelMat", &tempModelMat.Translate(gglm.NewVec3(-1, 0, 0)).Mat4)
|
||||||
@ -360,95 +167,72 @@ func draw() {
|
|||||||
}
|
}
|
||||||
simpleMat.SetUnifMat4("modelMat", &tempModelMat.Translate(gglm.NewVec3(float32(rowSize), -1, 0)).Mat4)
|
simpleMat.SetUnifMat4("modelMat", &tempModelMat.Translate(gglm.NewVec3(float32(rowSize), -1, 0)).Mat4)
|
||||||
}
|
}
|
||||||
|
|
||||||
simpleMat.SetUnifMat4("modelMat", &modelMat.Mat4)
|
simpleMat.SetUnifMat4("modelMat", &modelMat.Mat4)
|
||||||
window.SDLWin.GLSwap()
|
|
||||||
|
dtAccum += timing.DT()
|
||||||
|
framesSinceLastFPSUpdate++
|
||||||
|
if timing.ElapsedTime() > lastElapsedTime {
|
||||||
|
|
||||||
|
avgDT := dtAccum / float32(framesSinceLastFPSUpdate)
|
||||||
|
g.GetWindow().SDLWin.SetTitle(fmt.Sprint("nMage (", 1/avgDT, " fps)"))
|
||||||
|
|
||||||
|
dtAccum = 0
|
||||||
|
framesSinceLastFPSUpdate = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
lastElapsedTime = timing.ElapsedTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
func drawUI() {
|
func (g *OurGame) FrameEnd() {
|
||||||
|
|
||||||
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
}
|
||||||
fbWidth, fbHeight := window.SDLWin.GLGetDrawableSize()
|
|
||||||
if fbWidth <= 0 || fbHeight <= 0 {
|
func (g *OurGame) ShouldRun() bool {
|
||||||
return
|
return !g.Quitting
|
||||||
}
|
}
|
||||||
|
|
||||||
drawData := imgui.RenderedDrawData()
|
func (g *OurGame) GetWindow() *engine.Window {
|
||||||
drawData.ScaleClipRects(imgui.Vec2{
|
return g.Win
|
||||||
X: float32(fbWidth) / float32(winWidth),
|
}
|
||||||
Y: float32(fbHeight) / float32(winHeight),
|
|
||||||
})
|
func (g *OurGame) GetImGUI() nmageimgui.ImguiInfo {
|
||||||
|
return g.ImGUIInfo
|
||||||
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
|
}
|
||||||
gl.Enable(gl.BLEND)
|
|
||||||
gl.BlendEquation(gl.FUNC_ADD)
|
func (g *OurGame) Deinit() {
|
||||||
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
|
g.Win.Destroy()
|
||||||
gl.Disable(gl.CULL_FACE)
|
}
|
||||||
gl.Disable(gl.DEPTH_TEST)
|
|
||||||
gl.Enable(gl.SCISSOR_TEST)
|
func main() {
|
||||||
gl.PolygonMode(gl.FRONT_AND_BACK, gl.FILL)
|
|
||||||
|
runtime.LockOSThread()
|
||||||
// Setup viewport, orthographic projection matrix
|
|
||||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right).
|
//Init engine
|
||||||
// DisplayMin is typically (0,0) for single viewport apps.
|
err := engine.Init()
|
||||||
|
if err != nil {
|
||||||
imguiMat.Bind()
|
logging.ErrLog.Fatalln("Failed to init nMage. Err:", err)
|
||||||
|
}
|
||||||
gl.Uniform1i(gl.GetUniformLocation(imguiMat.ShaderProg.ID, gl.Str("Texture\x00")), 0)
|
|
||||||
|
//Create window
|
||||||
//PERF: only update the ortho matrix on window resize
|
window, err = engine.CreateOpenGLWindowCentered("nMage", 1280, 720, engine.WindowFlags_RESIZABLE)
|
||||||
orthoMat := gglm.Ortho(0, float32(winWidth), 0, float32(winHeight), 0, 20)
|
if err != nil {
|
||||||
imguiMat.SetUnifMat4("ProjMtx", &orthoMat.Mat4)
|
logging.ErrLog.Fatalln("Failed to create window. Err: ", err)
|
||||||
gl.BindSampler(0, 0) // Rely on combined texture/sampler state.
|
}
|
||||||
|
defer window.Destroy()
|
||||||
// Recreate the VAO every time
|
|
||||||
// (This is to easily allow multiple GL contexts. VAO are not shared among GL contexts, and
|
engine.SetVSync(false)
|
||||||
// we don't track creation/deletion of windows so we don't have an obvious key to use to cache them.)
|
|
||||||
gl.BindVertexArray(imguiInfo.vaoID)
|
game := &OurGame{
|
||||||
gl.BindBuffer(gl.ARRAY_BUFFER, imguiInfo.vboID)
|
Win: window,
|
||||||
|
ImGUIInfo: nmageimgui.NewImGUI(),
|
||||||
vertexSize, vertexOffsetPos, vertexOffsetUv, vertexOffsetCol := imgui.VertexBufferLayout()
|
}
|
||||||
imguiMat.EnableAttribute("Position")
|
|
||||||
imguiMat.EnableAttribute("UV")
|
engine.Run(game)
|
||||||
imguiMat.EnableAttribute("Color")
|
return
|
||||||
gl.VertexAttribPointerWithOffset(uint32(imguiMat.GetAttribLoc("Position")), 2, gl.FLOAT, false, int32(vertexSize), uintptr(vertexOffsetPos))
|
}
|
||||||
gl.VertexAttribPointerWithOffset(uint32(imguiMat.GetAttribLoc("UV")), 2, gl.FLOAT, false, int32(vertexSize), uintptr(vertexOffsetUv))
|
|
||||||
gl.VertexAttribPointerWithOffset(uint32(imguiMat.GetAttribLoc("Color")), 4, gl.UNSIGNED_BYTE, true, int32(vertexSize), uintptr(vertexOffsetCol))
|
func updateViewMat() {
|
||||||
|
targetPos := camPos.Clone().Add(camForward)
|
||||||
indexSize := imgui.IndexBufferLayout()
|
viewMat := gglm.LookAt(camPos, targetPos, gglm.NewVec3(0, 1, 0))
|
||||||
drawType := gl.UNSIGNED_SHORT
|
simpleMat.SetUnifMat4("viewMat", &viewMat.Mat4)
|
||||||
if indexSize == 4 {
|
|
||||||
drawType = gl.UNSIGNED_INT
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw
|
|
||||||
for _, list := range drawData.CommandLists() {
|
|
||||||
|
|
||||||
vertexBuffer, vertexBufferSize := list.VertexBuffer()
|
|
||||||
gl.BindBuffer(gl.ARRAY_BUFFER, imguiInfo.vboID)
|
|
||||||
gl.BufferData(gl.ARRAY_BUFFER, vertexBufferSize, vertexBuffer, gl.STREAM_DRAW)
|
|
||||||
|
|
||||||
indexBuffer, indexBufferSize := list.IndexBuffer()
|
|
||||||
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, imguiInfo.indexBufID)
|
|
||||||
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, indexBufferSize, indexBuffer, gl.STREAM_DRAW)
|
|
||||||
|
|
||||||
for _, cmd := range list.Commands() {
|
|
||||||
if cmd.HasUserCallback() {
|
|
||||||
cmd.CallUserCallback(list)
|
|
||||||
} else {
|
|
||||||
|
|
||||||
gl.BindTexture(gl.TEXTURE_2D, imguiInfo.texID)
|
|
||||||
clipRect := cmd.ClipRect()
|
|
||||||
gl.Scissor(int32(clipRect.X), int32(fbHeight)-int32(clipRect.W), int32(clipRect.Z-clipRect.X), int32(clipRect.W-clipRect.Y))
|
|
||||||
|
|
||||||
gl.DrawElementsBaseVertex(gl.TRIANGLES, int32(cmd.ElementCount()), uint32(drawType), gl.PtrOffset(cmd.IndexOffset()*indexSize), int32(cmd.VertexOffset()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Reset gl state
|
|
||||||
gl.Disable(gl.BLEND)
|
|
||||||
gl.Disable(gl.SCISSOR_TEST)
|
|
||||||
gl.Enable(gl.CULL_FACE)
|
|
||||||
gl.Enable(gl.DEPTH_TEST)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package materials
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bloeys/gglm/gglm"
|
"github.com/bloeys/gglm/gglm"
|
||||||
|
"github.com/bloeys/nmage/asserts"
|
||||||
"github.com/bloeys/nmage/buffers"
|
"github.com/bloeys/nmage/buffers"
|
||||||
"github.com/bloeys/nmage/logging"
|
"github.com/bloeys/nmage/logging"
|
||||||
"github.com/bloeys/nmage/shaders"
|
"github.com/bloeys/nmage/shaders"
|
||||||
@ -11,18 +12,54 @@ import (
|
|||||||
type Material struct {
|
type Material struct {
|
||||||
Name string
|
Name string
|
||||||
ShaderProg shaders.ShaderProgram
|
ShaderProg shaders.ShaderProgram
|
||||||
|
TexIDs []uint32
|
||||||
|
|
||||||
|
UnifLocs map[string]int32
|
||||||
|
AttribLocs map[string]int32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Material) Bind() {
|
func (m *Material) Bind() {
|
||||||
|
|
||||||
gl.UseProgram(m.ShaderProg.ID)
|
gl.UseProgram(m.ShaderProg.ID)
|
||||||
|
for i, v := range m.TexIDs {
|
||||||
|
gl.ActiveTexture(gl.TEXTURE0 + uint32(i))
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Material) UnBind() {
|
func (m *Material) UnBind() {
|
||||||
|
|
||||||
gl.UseProgram(0)
|
gl.UseProgram(0)
|
||||||
|
for i := range m.TexIDs {
|
||||||
|
gl.ActiveTexture(gl.TEXTURE0 + uint32(i))
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Material) GetAttribLoc(attribName string) int32 {
|
func (m *Material) GetAttribLoc(attribName string) int32 {
|
||||||
return gl.GetAttribLocation(m.ShaderProg.ID, gl.Str(attribName+"\x00"))
|
|
||||||
|
loc, ok := m.AttribLocs[attribName]
|
||||||
|
if ok {
|
||||||
|
return loc
|
||||||
|
}
|
||||||
|
|
||||||
|
loc = gl.GetAttribLocation(m.ShaderProg.ID, gl.Str(attribName+"\x00"))
|
||||||
|
asserts.T(loc != -1, "Attribute '"+attribName+"' doesn't exist on material "+m.Name)
|
||||||
|
m.AttribLocs[attribName] = loc
|
||||||
|
return loc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Material) GetUnifLoc(uniformName string) int32 {
|
||||||
|
|
||||||
|
loc, ok := m.UnifLocs[uniformName]
|
||||||
|
if ok {
|
||||||
|
return loc
|
||||||
|
}
|
||||||
|
|
||||||
|
loc = gl.GetUniformLocation(m.ShaderProg.ID, gl.Str(uniformName+"\x00"))
|
||||||
|
asserts.T(loc != -1, "Uniform '"+uniformName+"' doesn't exist on material "+m.Name)
|
||||||
|
m.UnifLocs[uniformName] = loc
|
||||||
|
return loc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Material) SetAttribute(bufObj buffers.Buffer) {
|
func (m *Material) SetAttribute(bufObj buffers.Buffer) {
|
||||||
@ -42,6 +79,10 @@ func (m *Material) SetAttribute(bufObj buffers.Buffer) {
|
|||||||
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Material) AddTextureID(texID uint32) {
|
||||||
|
m.TexIDs = append(m.TexIDs, texID)
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Material) EnableAttribute(attribName string) {
|
func (m *Material) EnableAttribute(attribName string) {
|
||||||
gl.EnableVertexAttribArray(uint32(m.GetAttribLoc(attribName)))
|
gl.EnableVertexAttribArray(uint32(m.GetAttribLoc(attribName)))
|
||||||
}
|
}
|
||||||
@ -50,39 +91,36 @@ func (m *Material) DisableAttribute(attribName string) {
|
|||||||
gl.DisableVertexAttribArray(uint32(m.GetAttribLoc(attribName)))
|
gl.DisableVertexAttribArray(uint32(m.GetAttribLoc(attribName)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Material) SetUnifInt32(uniformName string, val int32) {
|
||||||
|
gl.ProgramUniform1i(m.ShaderProg.ID, m.GetUnifLoc(uniformName), val)
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Material) SetUnifFloat32(uniformName string, val float32) {
|
func (m *Material) SetUnifFloat32(uniformName string, val float32) {
|
||||||
loc := gl.GetUniformLocation(m.ShaderProg.ID, gl.Str(uniformName+"\x00"))
|
gl.ProgramUniform1f(m.ShaderProg.ID, m.GetUnifLoc(uniformName), val)
|
||||||
gl.ProgramUniform1f(m.ShaderProg.ID, loc, val)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Material) SetUnifVec2(uniformName string, vec2 *gglm.Vec2) {
|
func (m *Material) SetUnifVec2(uniformName string, vec2 *gglm.Vec2) {
|
||||||
loc := gl.GetUniformLocation(m.ShaderProg.ID, gl.Str(uniformName+"\x00"))
|
gl.ProgramUniform2fv(m.ShaderProg.ID, m.GetUnifLoc(uniformName), 1, &vec2.Data[0])
|
||||||
gl.ProgramUniform2fv(m.ShaderProg.ID, loc, 1, &vec2.Data[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Material) SetUnifVec3(uniformName string, vec3 *gglm.Vec3) {
|
func (m *Material) SetUnifVec3(uniformName string, vec3 *gglm.Vec3) {
|
||||||
loc := gl.GetUniformLocation(m.ShaderProg.ID, gl.Str(uniformName+"\x00"))
|
gl.ProgramUniform3fv(m.ShaderProg.ID, m.GetUnifLoc(uniformName), 1, &vec3.Data[0])
|
||||||
gl.ProgramUniform3fv(m.ShaderProg.ID, loc, 1, &vec3.Data[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Material) SetUnifVec4(uniformName string, vec4 *gglm.Vec4) {
|
func (m *Material) SetUnifVec4(uniformName string, vec4 *gglm.Vec4) {
|
||||||
loc := gl.GetUniformLocation(m.ShaderProg.ID, gl.Str(uniformName+"\x00"))
|
gl.ProgramUniform4fv(m.ShaderProg.ID, m.GetUnifLoc(uniformName), 1, &vec4.Data[0])
|
||||||
gl.ProgramUniform4fv(m.ShaderProg.ID, loc, 1, &vec4.Data[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Material) SetUnifMat2(uniformName string, mat2 *gglm.Mat2) {
|
func (m *Material) SetUnifMat2(uniformName string, mat2 *gglm.Mat2) {
|
||||||
loc := gl.GetUniformLocation(m.ShaderProg.ID, gl.Str(uniformName+"\x00"))
|
gl.ProgramUniformMatrix2fv(m.ShaderProg.ID, m.GetUnifLoc(uniformName), 1, false, &mat2.Data[0][0])
|
||||||
gl.ProgramUniformMatrix2fv(m.ShaderProg.ID, loc, 1, false, &mat2.Data[0][0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Material) SetUnifMat3(uniformName string, mat3 *gglm.Mat3) {
|
func (m *Material) SetUnifMat3(uniformName string, mat3 *gglm.Mat3) {
|
||||||
loc := gl.GetUniformLocation(m.ShaderProg.ID, gl.Str(uniformName+"\x00"))
|
gl.ProgramUniformMatrix3fv(m.ShaderProg.ID, m.GetUnifLoc(uniformName), 1, false, &mat3.Data[0][0])
|
||||||
gl.ProgramUniformMatrix3fv(m.ShaderProg.ID, loc, 1, false, &mat3.Data[0][0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Material) SetUnifMat4(uniformName string, mat4 *gglm.Mat4) {
|
func (m *Material) SetUnifMat4(uniformName string, mat4 *gglm.Mat4) {
|
||||||
loc := gl.GetUniformLocation(m.ShaderProg.ID, gl.Str(uniformName+"\x00"))
|
gl.ProgramUniformMatrix4fv(m.ShaderProg.ID, m.GetUnifLoc(uniformName), 1, false, &mat4.Data[0][0])
|
||||||
gl.ProgramUniformMatrix4fv(m.ShaderProg.ID, loc, 1, false, &mat4.Data[0][0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Material) Delete() {
|
func (m *Material) Delete() {
|
||||||
@ -110,5 +148,5 @@ func NewMaterial(matName, shaderPath string) *Material {
|
|||||||
shdrProg.AttachShader(fragShader)
|
shdrProg.AttachShader(fragShader)
|
||||||
shdrProg.Link()
|
shdrProg.Link()
|
||||||
|
|
||||||
return &Material{Name: matName, ShaderProg: shdrProg}
|
return &Material{Name: matName, ShaderProg: shdrProg, UnifLocs: make(map[string]int32), AttribLocs: make(map[string]int32)}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,12 +7,18 @@ import (
|
|||||||
"github.com/bloeys/assimp-go/asig"
|
"github.com/bloeys/assimp-go/asig"
|
||||||
"github.com/bloeys/gglm/gglm"
|
"github.com/bloeys/gglm/gglm"
|
||||||
"github.com/bloeys/nmage/asserts"
|
"github.com/bloeys/nmage/asserts"
|
||||||
|
"github.com/bloeys/nmage/assets"
|
||||||
"github.com/bloeys/nmage/buffers"
|
"github.com/bloeys/nmage/buffers"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mesh struct {
|
type Mesh struct {
|
||||||
Name string
|
Name string
|
||||||
Buf buffers.Buffer
|
TextureIDs []uint32
|
||||||
|
Buf buffers.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mesh) AddTexture(tex assets.Texture) {
|
||||||
|
m.TextureIDs = append(m.TextureIDs, tex.TexID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh, error) {
|
func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh, error) {
|
||||||
@ -31,41 +37,55 @@ func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh,
|
|||||||
sceneMesh := scene.Meshes[0]
|
sceneMesh := scene.Meshes[0]
|
||||||
mesh.Buf = buffers.NewBuffer()
|
mesh.Buf = buffers.NewBuffer()
|
||||||
|
|
||||||
layoutToUse := []buffers.Element{{ElementType: buffers.DataTypeVec3}, {ElementType: buffers.DataTypeVec3}}
|
asserts.T(len(sceneMesh.TexCoords[0]) > 0, "Mesh has no UV0")
|
||||||
if len(sceneMesh.ColorSets) > 0 {
|
layoutToUse := []buffers.Element{{ElementType: buffers.DataTypeVec3}, {ElementType: buffers.DataTypeVec3}, {ElementType: buffers.DataTypeVec2}}
|
||||||
|
|
||||||
|
if len(sceneMesh.ColorSets) > 0 && len(sceneMesh.ColorSets[0]) > 0 {
|
||||||
layoutToUse = append(layoutToUse, buffers.Element{ElementType: buffers.DataTypeVec4})
|
layoutToUse = append(layoutToUse, buffers.Element{ElementType: buffers.DataTypeVec4})
|
||||||
}
|
}
|
||||||
mesh.Buf.SetLayout(layoutToUse...)
|
mesh.Buf.SetLayout(layoutToUse...)
|
||||||
|
|
||||||
var values []float32
|
var values []float32
|
||||||
if len(sceneMesh.ColorSets) > 0 {
|
arrs := []arrToInterleave{{V3s: sceneMesh.Vertices}, {V3s: sceneMesh.Normals}, {V2s: v3sToV2s(sceneMesh.TexCoords[0])}}
|
||||||
values = interleave(
|
|
||||||
arrToInterleave{V3s: sceneMesh.Vertices},
|
if len(sceneMesh.ColorSets) > 0 && len(sceneMesh.ColorSets[0]) > 0 {
|
||||||
arrToInterleave{V3s: sceneMesh.Normals},
|
arrs = append(arrs, arrToInterleave{V4s: sceneMesh.ColorSets[0]})
|
||||||
arrToInterleave{V4s: sceneMesh.ColorSets[0]},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
values = interleave(
|
|
||||||
arrToInterleave{V3s: sceneMesh.Vertices},
|
|
||||||
arrToInterleave{V3s: sceneMesh.Normals},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
values = interleave(arrs...)
|
||||||
|
|
||||||
mesh.Buf.SetData(values)
|
mesh.Buf.SetData(values)
|
||||||
mesh.Buf.SetIndexBufData(flattenFaces(sceneMesh.Faces))
|
mesh.Buf.SetIndexBufData(flattenFaces(sceneMesh.Faces))
|
||||||
return mesh, nil
|
return mesh, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func v3sToV2s(v3s []gglm.Vec3) []gglm.Vec2 {
|
||||||
|
|
||||||
|
v2s := make([]gglm.Vec2, len(v3s))
|
||||||
|
for i := 0; i < len(v3s); i++ {
|
||||||
|
v2s[i] = gglm.Vec2{
|
||||||
|
Data: [2]float32{v3s[i].X(), v3s[i].Y()},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return v2s
|
||||||
|
}
|
||||||
|
|
||||||
type arrToInterleave struct {
|
type arrToInterleave struct {
|
||||||
|
V2s []gglm.Vec2
|
||||||
V3s []gglm.Vec3
|
V3s []gglm.Vec3
|
||||||
V4s []gglm.Vec4
|
V4s []gglm.Vec4
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *arrToInterleave) get(i int) []float32 {
|
func (a *arrToInterleave) get(i int) []float32 {
|
||||||
|
|
||||||
|
asserts.T(len(a.V2s) == 0 || len(a.V3s) == 0, "One array should be set in arrToInterleave, but both arrays are set")
|
||||||
|
asserts.T(len(a.V2s) == 0 || len(a.V4s) == 0, "One array should be set in arrToInterleave, but both arrays are set")
|
||||||
asserts.T(len(a.V3s) == 0 || len(a.V4s) == 0, "One array should be set in arrToInterleave, but both arrays are set")
|
asserts.T(len(a.V3s) == 0 || len(a.V4s) == 0, "One array should be set in arrToInterleave, but both arrays are set")
|
||||||
|
|
||||||
if len(a.V3s) > 0 {
|
if len(a.V2s) > 0 {
|
||||||
|
return a.V2s[i].Data[:]
|
||||||
|
} else if len(a.V3s) > 0 {
|
||||||
return a.V3s[i].Data[:]
|
return a.V3s[i].Data[:]
|
||||||
} else {
|
} else {
|
||||||
return a.V4s[i].Data[:]
|
return a.V4s[i].Data[:]
|
||||||
@ -75,21 +95,26 @@ func (a *arrToInterleave) get(i int) []float32 {
|
|||||||
func interleave(arrs ...arrToInterleave) []float32 {
|
func interleave(arrs ...arrToInterleave) []float32 {
|
||||||
|
|
||||||
asserts.T(len(arrs) > 0, "No input sent to interleave")
|
asserts.T(len(arrs) > 0, "No input sent to interleave")
|
||||||
asserts.T(len(arrs[0].V3s) > 0 || len(arrs[0].V4s) > 0, "Interleave arrays are empty")
|
asserts.T(len(arrs[0].V2s) > 0 || len(arrs[0].V3s) > 0 || len(arrs[0].V4s) > 0, "Interleave arrays are empty")
|
||||||
|
|
||||||
elementCount := 0
|
elementCount := 0
|
||||||
if len(arrs[0].V3s) > 0 {
|
if len(arrs[0].V2s) > 0 {
|
||||||
|
elementCount = len(arrs[0].V2s)
|
||||||
|
} else if len(arrs[0].V3s) > 0 {
|
||||||
elementCount = len(arrs[0].V3s)
|
elementCount = len(arrs[0].V3s)
|
||||||
} else {
|
} else {
|
||||||
elementCount = len(arrs[0].V4s)
|
elementCount = len(arrs[0].V4s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Calculate final size of the float buffer
|
||||||
totalSize := 0
|
totalSize := 0
|
||||||
for i := 0; i < len(arrs); i++ {
|
for i := 0; i < len(arrs); i++ {
|
||||||
|
|
||||||
asserts.T(len(arrs[i].V3s) == elementCount || len(arrs[i].V4s) == elementCount, "Mesh vertex data given to interleave is not the same length")
|
asserts.T(len(arrs[i].V2s) == elementCount || len(arrs[i].V3s) == elementCount || len(arrs[i].V4s) == elementCount, "Mesh vertex data given to interleave is not the same length")
|
||||||
|
|
||||||
if len(arrs[i].V3s) > 0 {
|
if len(arrs[i].V2s) > 0 {
|
||||||
|
totalSize += len(arrs[i].V2s) * 2
|
||||||
|
} else if len(arrs[i].V3s) > 0 {
|
||||||
totalSize += len(arrs[i].V3s) * 3
|
totalSize += len(arrs[i].V3s) * 3
|
||||||
} else {
|
} else {
|
||||||
totalSize += len(arrs[i].V4s) * 4
|
totalSize += len(arrs[i].V4s) * 4
|
||||||
|
|||||||
BIN
res/models/tex-cube.fbx
Executable file
BIN
res/models/tex-cube.fbx
Executable file
Binary file not shown.
@ -1,23 +1,26 @@
|
|||||||
#version 410
|
#version 410
|
||||||
|
|
||||||
in vec3 vertColor;
|
|
||||||
in vec3 vertNormal;
|
|
||||||
in vec3 fragPos;
|
|
||||||
|
|
||||||
out vec4 fragColor;
|
|
||||||
|
|
||||||
uniform float ambientStrength = 0.1;
|
uniform float ambientStrength = 0.1;
|
||||||
uniform vec3 ambientLightColor = vec3(1, 1, 1);
|
uniform vec3 ambientLightColor = vec3(1, 1, 1);
|
||||||
|
|
||||||
uniform vec3 lightPos1;
|
uniform vec3 lightPos1;
|
||||||
uniform vec3 lightColor1;
|
uniform vec3 lightColor1;
|
||||||
|
|
||||||
|
uniform sampler2D diffTex;
|
||||||
|
|
||||||
|
in vec3 vertColor;
|
||||||
|
in vec3 vertNormal;
|
||||||
|
in vec2 vertUV0;
|
||||||
|
in vec3 fragPos;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
// vec3 norm = normalize(Normal);
|
|
||||||
vec3 lightDir = normalize(lightPos1 - fragPos);
|
vec3 lightDir = normalize(lightPos1 - fragPos);
|
||||||
float diffStrength = max(0.0, dot(normalize(vertNormal), lightDir));
|
float diffStrength = max(0.0, dot(normalize(vertNormal), lightDir));
|
||||||
|
|
||||||
vec3 finalAmbientColor = ambientLightColor * ambientStrength;
|
vec3 finalAmbientColor = ambientLightColor * ambientStrength;
|
||||||
fragColor = vec4(vertColor * (finalAmbientColor + diffStrength*lightColor1), 1.0);
|
vec4 texColor = texture(diffTex, vertUV0);
|
||||||
|
fragColor = vec4(texColor.rgb * vertColor * (finalAmbientColor + diffStrength*lightColor1) , texColor.a);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
layout(location=0) in vec3 vertPosIn;
|
layout(location=0) in vec3 vertPosIn;
|
||||||
layout(location=1) in vec3 vertNormalIn;
|
layout(location=1) in vec3 vertNormalIn;
|
||||||
layout(location=2) in vec3 vertColorIn;
|
layout(location=2) in vec2 vertUV0In;
|
||||||
|
layout(location=3) in vec3 vertColorIn;
|
||||||
|
|
||||||
out vec3 vertColor;
|
|
||||||
out vec3 vertNormal;
|
out vec3 vertNormal;
|
||||||
|
out vec2 vertUV0;
|
||||||
|
out vec3 vertColor;
|
||||||
out vec3 fragPos;
|
out vec3 fragPos;
|
||||||
|
|
||||||
//MVP = Model View Projection
|
//MVP = Model View Projection
|
||||||
@ -15,8 +17,9 @@ uniform mat4 projMat;
|
|||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vertColor = vertColorIn;
|
|
||||||
vertNormal = mat3(transpose(inverse(modelMat))) * vertNormalIn;
|
vertNormal = mat3(transpose(inverse(modelMat))) * vertNormalIn;
|
||||||
|
vertUV0 = vertUV0In;
|
||||||
|
vertColor = vertColorIn;
|
||||||
fragPos = vec3(modelMat * vec4(vertPosIn, 1.0));
|
fragPos = vec3(modelMat * vec4(vertPosIn, 1.0));
|
||||||
|
|
||||||
gl_Position = projMat * viewMat * modelMat * vec4(vertPosIn, 1.0);
|
gl_Position = projMat * viewMat * modelMat * vec4(vertPosIn, 1.0);
|
||||||
|
|||||||
BIN
res/textures/Low poly planet.png
Executable file
BIN
res/textures/Low poly planet.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 468 KiB |
@ -20,7 +20,7 @@ func FrameEnded() {
|
|||||||
dt = float32(time.Since(frameStart).Seconds())
|
dt = float32(time.Since(frameStart).Seconds())
|
||||||
}
|
}
|
||||||
|
|
||||||
//DT is frame deltatime in milliseconds
|
//DT is frame deltatime in seconds
|
||||||
func DT() float32 {
|
func DT() float32 {
|
||||||
return dt
|
return dt
|
||||||
}
|
}
|
||||||
|
|||||||
202
ui/imgui/imgui.go
Executable file
202
ui/imgui/imgui.go
Executable file
@ -0,0 +1,202 @@
|
|||||||
|
package nmageimgui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/bloeys/gglm/gglm"
|
||||||
|
"github.com/bloeys/nmage/asserts"
|
||||||
|
"github.com/bloeys/nmage/materials"
|
||||||
|
"github.com/bloeys/nmage/timing"
|
||||||
|
"github.com/go-gl/gl/v4.1-core/gl"
|
||||||
|
"github.com/inkyblackness/imgui-go/v4"
|
||||||
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ImguiInfo struct {
|
||||||
|
ImCtx *imgui.Context
|
||||||
|
|
||||||
|
Mat *materials.Material
|
||||||
|
vaoID uint32
|
||||||
|
vboID uint32
|
||||||
|
indexBufID uint32
|
||||||
|
texID uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ImguiInfo) FrameStart(winWidth, winHeight float32) {
|
||||||
|
|
||||||
|
if err := i.ImCtx.SetCurrent(); err != nil {
|
||||||
|
asserts.T(false, "Setting imgui ctx as current failed. Err: "+err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
imIO := imgui.CurrentIO()
|
||||||
|
imIO.SetDisplaySize(imgui.Vec2{X: float32(winWidth), Y: float32(winHeight)})
|
||||||
|
|
||||||
|
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
|
||||||
|
frequency := sdl.GetPerformanceFrequency()
|
||||||
|
currentTime := sdl.GetPerformanceCounter()
|
||||||
|
if timing.ElapsedTime() > 0 {
|
||||||
|
imIO.SetDeltaTime(float32(currentTime-timing.ElapsedTime()) / float32(frequency))
|
||||||
|
} else {
|
||||||
|
imIO.SetDeltaTime(1.0 / 60.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
imgui.NewFrame()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ImguiInfo) Render(winWidth, winHeight float32, fbWidth, fbHeight int32) {
|
||||||
|
|
||||||
|
if err := i.ImCtx.SetCurrent(); err != nil {
|
||||||
|
asserts.T(false, "Setting imgui ctx as current failed. Err: "+err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
imgui.Render()
|
||||||
|
|
||||||
|
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
||||||
|
if fbWidth <= 0 || fbHeight <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
drawData := imgui.RenderedDrawData()
|
||||||
|
drawData.ScaleClipRects(imgui.Vec2{
|
||||||
|
X: float32(fbWidth) / float32(winWidth),
|
||||||
|
Y: float32(fbHeight) / float32(winHeight),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
|
||||||
|
gl.Enable(gl.BLEND)
|
||||||
|
gl.BlendEquation(gl.FUNC_ADD)
|
||||||
|
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
|
||||||
|
gl.Disable(gl.CULL_FACE)
|
||||||
|
gl.Disable(gl.DEPTH_TEST)
|
||||||
|
gl.Enable(gl.SCISSOR_TEST)
|
||||||
|
gl.PolygonMode(gl.FRONT_AND_BACK, gl.FILL)
|
||||||
|
|
||||||
|
// Setup viewport, orthographic projection matrix
|
||||||
|
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right).
|
||||||
|
// DisplayMin is typically (0,0) for single viewport apps.
|
||||||
|
|
||||||
|
i.Mat.Bind()
|
||||||
|
|
||||||
|
gl.Uniform1i(gl.GetUniformLocation(i.Mat.ShaderProg.ID, gl.Str("Texture\x00")), 0)
|
||||||
|
|
||||||
|
//PERF: only update the ortho matrix on window resize
|
||||||
|
orthoMat := gglm.Ortho(0, float32(winWidth), 0, float32(winHeight), 0, 20)
|
||||||
|
i.Mat.SetUnifMat4("ProjMtx", &orthoMat.Mat4)
|
||||||
|
gl.BindSampler(0, 0) // Rely on combined texture/sampler state.
|
||||||
|
|
||||||
|
// Recreate the VAO every time
|
||||||
|
// (This is to easily allow multiple GL contexts. VAO are not shared among GL contexts, and
|
||||||
|
// we don't track creation/deletion of windows so we don't have an obvious key to use to cache them.)
|
||||||
|
gl.BindVertexArray(i.vaoID)
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, i.vboID)
|
||||||
|
|
||||||
|
vertexSize, vertexOffsetPos, vertexOffsetUv, vertexOffsetCol := imgui.VertexBufferLayout()
|
||||||
|
i.Mat.EnableAttribute("Position")
|
||||||
|
i.Mat.EnableAttribute("UV")
|
||||||
|
i.Mat.EnableAttribute("Color")
|
||||||
|
gl.VertexAttribPointerWithOffset(uint32(i.Mat.GetAttribLoc("Position")), 2, gl.FLOAT, false, int32(vertexSize), uintptr(vertexOffsetPos))
|
||||||
|
gl.VertexAttribPointerWithOffset(uint32(i.Mat.GetAttribLoc("UV")), 2, gl.FLOAT, false, int32(vertexSize), uintptr(vertexOffsetUv))
|
||||||
|
gl.VertexAttribPointerWithOffset(uint32(i.Mat.GetAttribLoc("Color")), 4, gl.UNSIGNED_BYTE, true, int32(vertexSize), uintptr(vertexOffsetCol))
|
||||||
|
|
||||||
|
indexSize := imgui.IndexBufferLayout()
|
||||||
|
drawType := gl.UNSIGNED_SHORT
|
||||||
|
if indexSize == 4 {
|
||||||
|
drawType = gl.UNSIGNED_INT
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
for _, list := range drawData.CommandLists() {
|
||||||
|
|
||||||
|
vertexBuffer, vertexBufferSize := list.VertexBuffer()
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, i.vboID)
|
||||||
|
gl.BufferData(gl.ARRAY_BUFFER, vertexBufferSize, vertexBuffer, gl.STREAM_DRAW)
|
||||||
|
|
||||||
|
indexBuffer, indexBufferSize := list.IndexBuffer()
|
||||||
|
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, i.indexBufID)
|
||||||
|
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, indexBufferSize, indexBuffer, gl.STREAM_DRAW)
|
||||||
|
|
||||||
|
for _, cmd := range list.Commands() {
|
||||||
|
if cmd.HasUserCallback() {
|
||||||
|
cmd.CallUserCallback(list)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, i.texID)
|
||||||
|
clipRect := cmd.ClipRect()
|
||||||
|
gl.Scissor(int32(clipRect.X), int32(fbHeight)-int32(clipRect.W), int32(clipRect.Z-clipRect.X), int32(clipRect.W-clipRect.Y))
|
||||||
|
|
||||||
|
gl.DrawElementsBaseVertex(gl.TRIANGLES, int32(cmd.ElementCount()), uint32(drawType), gl.PtrOffset(cmd.IndexOffset()*indexSize), int32(cmd.VertexOffset()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Reset gl state
|
||||||
|
gl.Disable(gl.BLEND)
|
||||||
|
gl.Disable(gl.SCISSOR_TEST)
|
||||||
|
gl.Enable(gl.CULL_FACE)
|
||||||
|
gl.Enable(gl.DEPTH_TEST)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewImGUI() ImguiInfo {
|
||||||
|
|
||||||
|
imguiInfo := ImguiInfo{
|
||||||
|
ImCtx: imgui.CreateContext(nil),
|
||||||
|
Mat: materials.NewMaterial("ImGUI Mat", "./res/shaders/imgui"),
|
||||||
|
}
|
||||||
|
|
||||||
|
imIO := imgui.CurrentIO()
|
||||||
|
imIO.SetBackendFlags(imIO.GetBackendFlags() | imgui.BackendFlagsRendererHasVtxOffset)
|
||||||
|
|
||||||
|
gl.GenVertexArrays(1, &imguiInfo.vaoID)
|
||||||
|
gl.GenBuffers(1, &imguiInfo.vboID)
|
||||||
|
gl.GenBuffers(1, &imguiInfo.indexBufID)
|
||||||
|
gl.GenTextures(1, &imguiInfo.texID)
|
||||||
|
|
||||||
|
// Upload font to gpu
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, imguiInfo.texID)
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||||
|
gl.PixelStorei(gl.UNPACK_ROW_LENGTH, 0)
|
||||||
|
|
||||||
|
image := imIO.Fonts().TextureDataAlpha8()
|
||||||
|
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RED, int32(image.Width), int32(image.Height), 0, gl.RED, gl.UNSIGNED_BYTE, image.Pixels)
|
||||||
|
|
||||||
|
// Store our identifier
|
||||||
|
imIO.Fonts().SetTextureID(imgui.TextureID(imguiInfo.texID))
|
||||||
|
|
||||||
|
//Shader attributes
|
||||||
|
imguiInfo.Mat.Bind()
|
||||||
|
imguiInfo.Mat.EnableAttribute("Position")
|
||||||
|
imguiInfo.Mat.EnableAttribute("UV")
|
||||||
|
imguiInfo.Mat.EnableAttribute("Color")
|
||||||
|
imguiInfo.Mat.UnBind()
|
||||||
|
|
||||||
|
//Init imgui input mapping
|
||||||
|
keys := map[int]int{
|
||||||
|
imgui.KeyTab: sdl.SCANCODE_TAB,
|
||||||
|
imgui.KeyLeftArrow: sdl.SCANCODE_LEFT,
|
||||||
|
imgui.KeyRightArrow: sdl.SCANCODE_RIGHT,
|
||||||
|
imgui.KeyUpArrow: sdl.SCANCODE_UP,
|
||||||
|
imgui.KeyDownArrow: sdl.SCANCODE_DOWN,
|
||||||
|
imgui.KeyPageUp: sdl.SCANCODE_PAGEUP,
|
||||||
|
imgui.KeyPageDown: sdl.SCANCODE_PAGEDOWN,
|
||||||
|
imgui.KeyHome: sdl.SCANCODE_HOME,
|
||||||
|
imgui.KeyEnd: sdl.SCANCODE_END,
|
||||||
|
imgui.KeyInsert: sdl.SCANCODE_INSERT,
|
||||||
|
imgui.KeyDelete: sdl.SCANCODE_DELETE,
|
||||||
|
imgui.KeyBackspace: sdl.SCANCODE_BACKSPACE,
|
||||||
|
imgui.KeySpace: sdl.SCANCODE_BACKSPACE,
|
||||||
|
imgui.KeyEnter: sdl.SCANCODE_RETURN,
|
||||||
|
imgui.KeyEscape: sdl.SCANCODE_ESCAPE,
|
||||||
|
imgui.KeyA: sdl.SCANCODE_A,
|
||||||
|
imgui.KeyC: sdl.SCANCODE_C,
|
||||||
|
imgui.KeyV: sdl.SCANCODE_V,
|
||||||
|
imgui.KeyX: sdl.SCANCODE_X,
|
||||||
|
imgui.KeyY: sdl.SCANCODE_Y,
|
||||||
|
imgui.KeyZ: sdl.SCANCODE_Z,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
|
||||||
|
for imguiKey, nativeKey := range keys {
|
||||||
|
imIO.KeyMap(imguiKey, nativeKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
return imguiInfo
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user