Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b44b00d7e2 | |||
| 70dccd757e | |||
| d7cd5bfc8d | |||
| 3b8e5c06de | |||
| 8e9dbee002 | |||
| 7b2fb19618 | |||
| 9651f77348 | |||
| a16654107b | |||
| 855cbfaba3 | |||
| b025afe1b4 | |||
| 84cd8c28c8 | |||
| 7d5e3e2d82 | |||
| 23a6689346 | |||
| 36488ead04 | |||
| de77d5464e | |||
| 305982deca | |||
| 653a315631 | |||
| c971324b5a | |||
| b5a2479c16 | |||
| 6f54aecb5f | |||
| 7a25aea6ba | |||
| 3071b52c85 |
@ -1,22 +1,28 @@
|
|||||||
name: build-nmage
|
name: build-nmage
|
||||||
|
|
||||||
on:
|
on:
|
||||||
create:
|
create:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-nmage-macos:
|
build-nmage-macos:
|
||||||
runs-on: macos-10.15
|
runs-on: macos-12
|
||||||
steps:
|
steps:
|
||||||
- name: Install golang 1.17
|
|
||||||
uses: actions/setup-go@v2
|
- name: Install golang 1.18
|
||||||
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '^1.17'
|
go-version: '^1.18'
|
||||||
|
|
||||||
- name: Install assimp-go dylib
|
- name: Install assimp-go dylib
|
||||||
run: sudo mkdir -p /usr/local/lib && sudo wget https://github.com/bloeys/assimp-go/releases/download/v0.4.2/libassimp_darwin_amd64.dylib -O /usr/local/lib/libassimp.5.dylib
|
run: sudo mkdir -p /usr/local/lib && sudo wget https://github.com/bloeys/assimp-go/releases/download/v0.4.2/libassimp_darwin_amd64.dylib -O /usr/local/lib/libassimp.5.dylib
|
||||||
|
|
||||||
- name: Install SDL2
|
- name: Install SDL2
|
||||||
run: brew install sdl2{,_image,_mixer,_ttf,_gfx} pkg-config
|
run: brew install sdl2{,_image,_mixer,_ttf,_gfx} pkg-config
|
||||||
|
|
||||||
- name: Clone nmage
|
- name: Clone nmage
|
||||||
run: git clone https://github.com/bloeys/nmage
|
run: git clone https://github.com/bloeys/nmage
|
||||||
|
|
||||||
- name: build nmage
|
- name: build nmage
|
||||||
working-directory: nmage
|
working-directory: nmage
|
||||||
run: go build .
|
run: go build .
|
||||||
@ -1,6 +1,6 @@
|
|||||||
# nMage
|
# nMage
|
||||||
|
|
||||||
[](https://github.com/bloeys/nmage/actions/workflows/run-nmage.yml)
|
[](https://github.com/bloeys/nmage/actions/workflows/build-nmage.yml)
|
||||||
|
|
||||||
nMage is a (hopefully!) high performance 3D Game Engine written in Go being developed [live](https://twitch.tv/bloeys), with recordings posted on [YouTube](https://www.youtube.com/channel/UCCf4qyNGPVwpj1HYFGahs_A).
|
nMage is a (hopefully!) high performance 3D Game Engine written in Go being developed [live](https://twitch.tv/bloeys), with recordings posted on [YouTube](https://www.youtube.com/channel/UCCf4qyNGPVwpj1HYFGahs_A).
|
||||||
|
|
||||||
|
|||||||
@ -5,8 +5,9 @@ import (
|
|||||||
"github.com/bloeys/nmage/logging"
|
"github.com/bloeys/nmage/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
func T(check bool, msg string) {
|
func T(check bool, msg string, args ...any) {
|
||||||
|
|
||||||
if consts.Debug && !check {
|
if consts.Debug && !check {
|
||||||
logging.ErrLog.Panicln("Assert failed:", msg)
|
logging.ErrLog.Panicf("Assert failed: "+msg, args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
package assets
|
package assets
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Textures map[uint32]Texture = make(map[uint32]Texture)
|
Textures = make(map[uint32]Texture)
|
||||||
TexturePaths map[string]uint32 = make(map[string]uint32)
|
TexturePaths = make(map[string]uint32)
|
||||||
)
|
)
|
||||||
|
|
||||||
func AddTextureToCache(t Texture) {
|
func AddTextureToCache(t Texture) {
|
||||||
|
|||||||
@ -2,10 +2,15 @@ package assets
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"image/jpeg"
|
||||||
"image/png"
|
"image/png"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/go-gl/gl/v4.1-core/gl"
|
"github.com/go-gl/gl/v4.1-core/gl"
|
||||||
@ -30,6 +35,18 @@ type TextureLoadOptions struct {
|
|||||||
TryLoadFromCache bool
|
TryLoadFromCache bool
|
||||||
WriteToCache bool
|
WriteToCache bool
|
||||||
GenMipMaps bool
|
GenMipMaps bool
|
||||||
|
KeepPixelsInMem bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cubemap struct {
|
||||||
|
// These only exists for textures loaded from disk
|
||||||
|
RightPath string
|
||||||
|
LeftPath string
|
||||||
|
TopPath string
|
||||||
|
BotPath string
|
||||||
|
FrontPath string
|
||||||
|
BackPath string
|
||||||
|
TexID uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadTexturePNG(file string, loadOptions *TextureLoadOptions) (Texture, error) {
|
func LoadTexturePNG(file string, loadOptions *TextureLoadOptions) (Texture, error) {
|
||||||
@ -56,28 +73,10 @@ func LoadTexturePNG(file string, loadOptions *TextureLoadOptions) (Texture, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
tex := Texture{
|
tex := Texture{
|
||||||
Path: file,
|
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
|
tex.Pixels, tex.Width, tex.Height = pixelsFromNrgbaPng(img)
|
||||||
//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
|
//Prepare opengl stuff
|
||||||
gl.GenTextures(1, &tex.TexID)
|
gl.GenTextures(1, &tex.TexID)
|
||||||
@ -100,37 +99,21 @@ func LoadTexturePNG(file string, loadOptions *TextureLoadOptions) (Texture, erro
|
|||||||
AddTextureToCache(tex)
|
AddTextureToCache(tex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !loadOptions.KeepPixelsInMem {
|
||||||
|
tex.Pixels = nil
|
||||||
|
}
|
||||||
|
|
||||||
return tex, nil
|
return tex, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadTextureInMemImg(img image.Image, loadOptions *TextureLoadOptions) (Texture, error) {
|
func LoadTextureInMemPngImg(img image.Image, loadOptions *TextureLoadOptions) (Texture, error) {
|
||||||
|
|
||||||
if loadOptions == nil {
|
if loadOptions == nil {
|
||||||
loadOptions = &TextureLoadOptions{}
|
loadOptions = &TextureLoadOptions{}
|
||||||
}
|
}
|
||||||
|
|
||||||
tex := Texture{
|
tex := Texture{}
|
||||||
Width: int32(img.Bounds().Dx()),
|
tex.Pixels, tex.Width, tex.Height = pixelsFromNrgbaPng(img)
|
||||||
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
|
//Prepare opengl stuff
|
||||||
gl.GenTextures(1, &tex.TexID)
|
gl.GenTextures(1, &tex.TexID)
|
||||||
@ -153,5 +136,173 @@ func LoadTextureInMemImg(img image.Image, loadOptions *TextureLoadOptions) (Text
|
|||||||
AddTextureToCache(tex)
|
AddTextureToCache(tex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !loadOptions.KeepPixelsInMem {
|
||||||
|
tex.Pixels = nil
|
||||||
|
}
|
||||||
|
|
||||||
return tex, nil
|
return tex, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LoadTextureJpeg(file string, loadOptions *TextureLoadOptions) (Texture, error) {
|
||||||
|
|
||||||
|
if loadOptions == nil {
|
||||||
|
loadOptions = &TextureLoadOptions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if loadOptions.TryLoadFromCache {
|
||||||
|
if tex, ok := GetTextureFromCachePath(file); ok {
|
||||||
|
return tex, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Load from disk
|
||||||
|
fileBytes, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return Texture{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
img, err := jpeg.Decode(bytes.NewReader(fileBytes))
|
||||||
|
if err != nil {
|
||||||
|
return Texture{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tex := Texture{
|
||||||
|
Path: file,
|
||||||
|
}
|
||||||
|
|
||||||
|
tex.Pixels, tex.Width, tex.Height = pixelsFromNrgbaPng(img)
|
||||||
|
|
||||||
|
//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)
|
||||||
|
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]))
|
||||||
|
|
||||||
|
if loadOptions.GenMipMaps {
|
||||||
|
gl.GenerateMipmap(tex.TexID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if loadOptions.WriteToCache {
|
||||||
|
AddTextureToCache(tex)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !loadOptions.KeepPixelsInMem {
|
||||||
|
tex.Pixels = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return tex, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pixelsFromNrgbaPng(img image.Image) (pixels []byte, width, height int32) {
|
||||||
|
|
||||||
|
//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
|
||||||
|
width, height = int32(img.Bounds().Dx()), int32(img.Bounds().Dy())
|
||||||
|
pixels = make([]byte, img.Bounds().Dx()*img.Bounds().Dy()*4)
|
||||||
|
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)
|
||||||
|
|
||||||
|
pixels[i] = c.R
|
||||||
|
pixels[i+1] = c.G
|
||||||
|
pixels[i+2] = c.B
|
||||||
|
pixels[i+3] = c.A
|
||||||
|
|
||||||
|
i += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pixels, width, height
|
||||||
|
}
|
||||||
|
|
||||||
|
func pixelsFromNrgbaJpg(img image.Image) (pixels []byte, width, height int32) {
|
||||||
|
|
||||||
|
//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
|
||||||
|
width, height = int32(img.Bounds().Dx()), int32(img.Bounds().Dy())
|
||||||
|
pixels = make([]byte, img.Bounds().Dx()*img.Bounds().Dy()*4)
|
||||||
|
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)
|
||||||
|
|
||||||
|
pixels[i] = c.R
|
||||||
|
pixels[i+1] = c.G
|
||||||
|
pixels[i+2] = c.B
|
||||||
|
pixels[i+3] = c.A
|
||||||
|
|
||||||
|
i += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pixels, width, height
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadCubemapTextures(rightTex, leftTex, topTex, botTex, frontTex, backTex string) (Cubemap, error) {
|
||||||
|
|
||||||
|
var imgDecoder func(r io.Reader) (image.Image, error)
|
||||||
|
var pixelDecoder func(image.Image) ([]byte, int32, int32)
|
||||||
|
ext := strings.ToLower(path.Ext(rightTex))
|
||||||
|
if ext == ".jpg" || ext == ".jpeg" {
|
||||||
|
imgDecoder = jpeg.Decode
|
||||||
|
pixelDecoder = pixelsFromNrgbaJpg
|
||||||
|
} else if ext == ".png" {
|
||||||
|
imgDecoder = png.Decode
|
||||||
|
pixelDecoder = pixelsFromNrgbaPng
|
||||||
|
} else {
|
||||||
|
return Cubemap{}, fmt.Errorf("unknown image extension: %s. Expected one of: .jpg, .jpeg, .png", ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmap := Cubemap{
|
||||||
|
RightPath: rightTex,
|
||||||
|
LeftPath: leftTex,
|
||||||
|
TopPath: topTex,
|
||||||
|
BotPath: botTex,
|
||||||
|
FrontPath: frontTex,
|
||||||
|
BackPath: backTex,
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.GenTextures(1, &cmap.TexID)
|
||||||
|
gl.BindTexture(gl.TEXTURE_CUBE_MAP, cmap.TexID)
|
||||||
|
|
||||||
|
// The order here matters
|
||||||
|
texturePaths := []string{rightTex, leftTex, topTex, botTex, frontTex, backTex}
|
||||||
|
for i := uint32(0); i < uint32(len(texturePaths)); i++ {
|
||||||
|
|
||||||
|
fPath := texturePaths[i]
|
||||||
|
|
||||||
|
//Load from disk
|
||||||
|
fileBytes, err := os.ReadFile(fPath)
|
||||||
|
if err != nil {
|
||||||
|
return Cubemap{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
img, err := imgDecoder(bytes.NewReader(fileBytes))
|
||||||
|
if err != nil {
|
||||||
|
return Cubemap{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pixels, width, height := pixelDecoder(img)
|
||||||
|
|
||||||
|
gl.TexImage2D(uint32(gl.TEXTURE_CUBE_MAP_POSITIVE_X)+i, 0, gl.RGBA8, int32(width), int32(height), 0, gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(&pixels[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the texture wrapping/filtering options (on the currently bound texture object)
|
||||||
|
gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
|
||||||
|
gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||||
|
gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||||
|
gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
||||||
|
gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE)
|
||||||
|
|
||||||
|
return cmap, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -7,12 +7,15 @@ import (
|
|||||||
|
|
||||||
type Buffer struct {
|
type Buffer struct {
|
||||||
VAOID uint32
|
VAOID uint32
|
||||||
//BufID is the ID of the VBO
|
// BufID is the ID of the VBO
|
||||||
BufID uint32
|
BufID uint32
|
||||||
//IndexBufID is the ID of the index/element buffer
|
// IndexBufID is the ID of the index/element buffer
|
||||||
IndexBufID uint32
|
IndexBufID uint32
|
||||||
|
// IndexBufCount is the number of elements in the index buffer
|
||||||
|
// Updated on SetIndexBufData
|
||||||
IndexBufCount int32
|
IndexBufCount int32
|
||||||
Stride int32
|
// IndexBufCount int32
|
||||||
|
Stride int32
|
||||||
|
|
||||||
layout []Element
|
layout []Element
|
||||||
}
|
}
|
||||||
@ -80,8 +83,8 @@ func (b *Buffer) GetLayout() []Element {
|
|||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
//SetLayout updates the layout object and the corresponding vertex attributes.
|
// SetLayout updates the layout object and the corresponding vertex attributes.
|
||||||
//Vertex attributes are also enabled.
|
// Vertex attributes are also enabled.
|
||||||
func (b *Buffer) SetLayout(layout ...Element) {
|
func (b *Buffer) SetLayout(layout ...Element) {
|
||||||
|
|
||||||
b.layout = layout
|
b.layout = layout
|
||||||
@ -101,7 +104,7 @@ func (b *Buffer) SetLayout(layout ...Element) {
|
|||||||
|
|
||||||
for i := 0; i < len(layout); i++ {
|
for i := 0; i < len(layout); i++ {
|
||||||
gl.EnableVertexAttribArray(uint32(i))
|
gl.EnableVertexAttribArray(uint32(i))
|
||||||
gl.VertexAttribPointer(uint32(i), layout[i].ElementType.CompCount(), layout[i].ElementType.GLType(), false, b.Stride, gl.PtrOffset(layout[i].Offset))
|
gl.VertexAttribPointerWithOffset(uint32(i), layout[i].ElementType.CompCount(), layout[i].ElementType.GLType(), false, b.Stride, uintptr(layout[i].Offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
b.UnBind()
|
b.UnBind()
|
||||||
|
|||||||
@ -112,7 +112,7 @@ func Init() error {
|
|||||||
|
|
||||||
func initSDL() error {
|
func initSDL() error {
|
||||||
|
|
||||||
err := sdl.Init(sdl.INIT_EVERYTHING)
|
err := sdl.Init(sdl.INIT_TIMER | sdl.INIT_VIDEO)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,19 +23,26 @@ type Game interface {
|
|||||||
func Run(g Game, w *Window, ui nmageimgui.ImguiInfo) {
|
func Run(g Game, w *Window, ui nmageimgui.ImguiInfo) {
|
||||||
|
|
||||||
isRunning = true
|
isRunning = true
|
||||||
|
|
||||||
|
// Run init with an active Imgui frame to allow init full imgui access
|
||||||
|
timing.FrameStarted()
|
||||||
|
w.handleInputs()
|
||||||
|
|
||||||
|
width, height := w.SDLWin.GetSize()
|
||||||
|
ui.FrameStart(float32(width), float32(height))
|
||||||
|
|
||||||
g.Init()
|
g.Init()
|
||||||
|
|
||||||
//Simulate an imgui frame during init so any imgui calls are allowed within init
|
fbWidth, fbHeight := w.SDLWin.GLGetDrawableSize()
|
||||||
tempWidth, tempHeight := w.SDLWin.GetSize()
|
ui.Render(float32(width), float32(height), fbWidth, fbHeight)
|
||||||
tempFBWidth, tempFBHeight := w.SDLWin.GLGetDrawableSize()
|
|
||||||
ui.FrameStart(float32(tempWidth), float32(tempHeight))
|
timing.FrameEnded()
|
||||||
ui.Render(float32(tempWidth), float32(tempHeight), tempFBWidth, tempFBHeight)
|
|
||||||
|
|
||||||
for isRunning {
|
for isRunning {
|
||||||
|
|
||||||
//PERF: Cache these
|
//PERF: Cache these
|
||||||
width, height := w.SDLWin.GetSize()
|
width, height = w.SDLWin.GetSize()
|
||||||
fbWidth, fbHeight := w.SDLWin.GLGetDrawableSize()
|
fbWidth, fbHeight = w.SDLWin.GLGetDrawableSize()
|
||||||
|
|
||||||
timing.FrameStarted()
|
timing.FrameStarted()
|
||||||
w.handleInputs()
|
w.handleInputs()
|
||||||
@ -43,7 +50,7 @@ func Run(g Game, w *Window, ui nmageimgui.ImguiInfo) {
|
|||||||
|
|
||||||
g.Update()
|
g.Update()
|
||||||
|
|
||||||
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)
|
||||||
g.Render()
|
g.Render()
|
||||||
ui.Render(float32(width), float32(height), fbWidth, fbHeight)
|
ui.Render(float32(width), float32(height), fbWidth, fbHeight)
|
||||||
w.SDLWin.GLSwap()
|
w.SDLWin.GLSwap()
|
||||||
|
|||||||
27
entity/base_comp.go
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
package entity
|
||||||
|
|
||||||
|
import "github.com/bloeys/nmage/assert"
|
||||||
|
|
||||||
|
var _ Comp = &BaseComp{}
|
||||||
|
|
||||||
|
type BaseComp struct {
|
||||||
|
Entity *Entity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseComp) base() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseComp) Init(parent *Entity) {
|
||||||
|
assert.T(parent != nil, "Component was initialized with a nil parent. That is not allowed.")
|
||||||
|
b.Entity = parent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseComp) Name() string {
|
||||||
|
return "Base Component"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseComp) Update() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseComp) Destroy() {
|
||||||
|
}
|
||||||
62
entity/comp.go
Executable file
@ -0,0 +1,62 @@
|
|||||||
|
package entity
|
||||||
|
|
||||||
|
import "github.com/bloeys/nmage/assert"
|
||||||
|
|
||||||
|
type Comp interface {
|
||||||
|
// This ensures that implementors of the Comp interface
|
||||||
|
// always embed BaseComp
|
||||||
|
base()
|
||||||
|
|
||||||
|
Name() string
|
||||||
|
Init(parent *Entity)
|
||||||
|
Update()
|
||||||
|
Destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddComp[T Comp](e *Entity, c T) {
|
||||||
|
|
||||||
|
assert.T(!HasComp[T](e), "Entity with id '%v' already has component of type '%T'", e.ID, c)
|
||||||
|
|
||||||
|
e.Comps = append(e.Comps, c)
|
||||||
|
c.Init(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HasComp[T Comp](e *Entity) bool {
|
||||||
|
|
||||||
|
for i := 0; i < len(e.Comps); i++ {
|
||||||
|
|
||||||
|
_, ok := e.Comps[i].(T)
|
||||||
|
if ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetComp[T Comp](e *Entity) (out T) {
|
||||||
|
|
||||||
|
for i := 0; i < len(e.Comps); i++ {
|
||||||
|
|
||||||
|
comp, ok := e.Comps[i].(T)
|
||||||
|
if ok {
|
||||||
|
return comp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DestroyComp calls Destroy on the component and then removes it from the entities component list
|
||||||
|
func DestroyComp[T Comp](e *Entity) {
|
||||||
|
|
||||||
|
for i := 0; i < len(e.Comps); i++ {
|
||||||
|
|
||||||
|
comp, ok := e.Comps[i].(T)
|
||||||
|
if ok {
|
||||||
|
comp.Destroy()
|
||||||
|
e.Comps = append(e.Comps[:i], e.Comps[i+1:]...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,9 +3,8 @@ package entity
|
|||||||
type EntityFlag byte
|
type EntityFlag byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
EntityFlag_Unknown EntityFlag = 0
|
EntityFlag_None EntityFlag = 0
|
||||||
EntityFlag_Dead EntityFlag = 1 << (iota - 1)
|
EntityFlag_Alive EntityFlag = 1 << (iota - 1)
|
||||||
EntityFlag_Alive
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -14,64 +13,37 @@ const (
|
|||||||
IndexBitMask = 0x00_00_FFFF_FFFF_FFFF
|
IndexBitMask = 0x00_00_FFFF_FFFF_FFFF
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type EntityHandle uint64
|
||||||
|
|
||||||
type Entity struct {
|
type Entity struct {
|
||||||
|
|
||||||
// Byte 1: Generation; Byte 2: Flags; Bytes 3-8: Index
|
// Byte 1: Generation; Byte 2: Flags; Bytes 3-8: Index
|
||||||
ID uint64
|
ID EntityHandle
|
||||||
Comps []Comp
|
Comps []Comp
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGeneration(id uint64) byte {
|
func (e *Entity) HasFlag(ef EntityFlag) bool {
|
||||||
|
return GetFlags(e.ID)&ef > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Entity) UpdateAllComps() {
|
||||||
|
for i := 0; i < len(e.Comps); i++ {
|
||||||
|
e.Comps[i].Update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGeneration(id EntityHandle) byte {
|
||||||
return byte(id >> GenerationShiftBits)
|
return byte(id >> GenerationShiftBits)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFlags(id uint64) byte {
|
func GetFlags(id EntityHandle) EntityFlag {
|
||||||
return byte(id >> FlagsShiftBits)
|
return EntityFlag(id >> FlagsShiftBits)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetIndex(id uint64) uint64 {
|
func GetIndex(id EntityHandle) uint64 {
|
||||||
return id & IndexBitMask
|
return uint64(id & IndexBitMask)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Entity) HasFlag(ef EntityFlag) bool {
|
func NewEntityId(generation byte, flags EntityFlag, index uint64) EntityHandle {
|
||||||
return GetFlags(e.ID)&byte(ef) > 0
|
return EntityHandle(index | (uint64(generation) << GenerationShiftBits) | (uint64(flags) << FlagsShiftBits))
|
||||||
}
|
|
||||||
|
|
||||||
func NewEntityId(generation, flags byte, index uint64) uint64 {
|
|
||||||
return index | (uint64(generation) << GenerationShiftBits) | (uint64(flags) << FlagsShiftBits)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Comp interface {
|
|
||||||
Name() string
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddComp(e *Entity, c Comp) {
|
|
||||||
e.Comps = append(e.Comps, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetComp[T Comp](e *Entity) (out T) {
|
|
||||||
|
|
||||||
for i := 0; i < len(e.Comps); i++ {
|
|
||||||
|
|
||||||
comp, ok := e.Comps[i].(T)
|
|
||||||
if ok {
|
|
||||||
return comp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAllCompOfType[T Comp](e *Entity) (out []T) {
|
|
||||||
|
|
||||||
out = []T{}
|
|
||||||
for i := 0; i < len(e.Comps); i++ {
|
|
||||||
|
|
||||||
comp, ok := e.Comps[i].(T)
|
|
||||||
if ok {
|
|
||||||
out = append(out, comp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,12 @@ import (
|
|||||||
"github.com/bloeys/nmage/assert"
|
"github.com/bloeys/nmage/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// The number of slots required to be in the free list before the free list
|
||||||
|
// is used for creating new entries
|
||||||
|
FreeListUsageThreshold uint32 = 20
|
||||||
|
)
|
||||||
|
|
||||||
type freeListitem struct {
|
type freeListitem struct {
|
||||||
EntityIndex uint64
|
EntityIndex uint64
|
||||||
nextFree *freeListitem
|
nextFree *freeListitem
|
||||||
@ -12,7 +18,9 @@ type freeListitem struct {
|
|||||||
type Registry struct {
|
type Registry struct {
|
||||||
EntityCount uint64
|
EntityCount uint64
|
||||||
Entities []Entity
|
Entities []Entity
|
||||||
FreeList *freeListitem
|
|
||||||
|
FreeList *freeListitem
|
||||||
|
FreeListSize uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registry) NewEntity() *Entity {
|
func (r *Registry) NewEntity() *Entity {
|
||||||
@ -22,17 +30,18 @@ func (r *Registry) NewEntity() *Entity {
|
|||||||
entityToUseIndex := uint64(0)
|
entityToUseIndex := uint64(0)
|
||||||
var entityToUse *Entity = nil
|
var entityToUse *Entity = nil
|
||||||
|
|
||||||
if r.FreeList != nil {
|
if r.FreeList != nil && r.FreeListSize > FreeListUsageThreshold {
|
||||||
|
|
||||||
entityToUseIndex = r.FreeList.EntityIndex
|
entityToUseIndex = r.FreeList.EntityIndex
|
||||||
entityToUse = &r.Entities[entityToUseIndex]
|
entityToUse = &r.Entities[entityToUseIndex]
|
||||||
r.FreeList = r.FreeList.nextFree
|
r.FreeList = r.FreeList.nextFree
|
||||||
|
r.FreeListSize--
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
for i := 0; i < len(r.Entities); i++ {
|
for i := 0; i < len(r.Entities); i++ {
|
||||||
|
|
||||||
e := &r.Entities[i]
|
e := &r.Entities[i]
|
||||||
if GetFlags(e.ID) != byte(EntityFlag_Unknown) && !e.HasFlag(EntityFlag_Dead) {
|
if e.HasFlag(EntityFlag_Alive) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,12 +56,12 @@ func (r *Registry) NewEntity() *Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r.EntityCount++
|
r.EntityCount++
|
||||||
entityToUse.ID = NewEntityId(GetGeneration(entityToUse.ID)+1, byte(EntityFlag_Alive), entityToUseIndex)
|
entityToUse.ID = NewEntityId(GetGeneration(entityToUse.ID)+1, EntityFlag_Alive, entityToUseIndex)
|
||||||
assert.T(entityToUse.ID != 0, "Entity ID must not be zero")
|
assert.T(entityToUse.ID != 0, "Entity ID must not be zero")
|
||||||
return entityToUse
|
return entityToUse
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registry) GetEntity(id uint64) *Entity {
|
func (r *Registry) GetEntity(id EntityHandle) *Entity {
|
||||||
|
|
||||||
index := GetIndex(id)
|
index := GetIndex(id)
|
||||||
gen := GetGeneration(id)
|
gen := GetGeneration(id)
|
||||||
@ -67,23 +76,29 @@ func (r *Registry) GetEntity(id uint64) *Entity {
|
|||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registry) FreeEntity(id uint64) {
|
// FreeEntity calls Destroy on all the entities components, resets the component list, resets the entity flags, then ads this entity to the free list
|
||||||
|
func (r *Registry) FreeEntity(id EntityHandle) {
|
||||||
|
|
||||||
e := r.GetEntity(id)
|
e := r.GetEntity(id)
|
||||||
if e == nil {
|
if e == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(e.Comps); i++ {
|
||||||
|
e.Comps[i].Destroy()
|
||||||
|
}
|
||||||
|
|
||||||
r.EntityCount--
|
r.EntityCount--
|
||||||
eIndex := GetIndex(e.ID)
|
eIndex := GetIndex(e.ID)
|
||||||
|
|
||||||
e.Comps = []Comp{}
|
e.Comps = []Comp{}
|
||||||
e.ID = NewEntityId(GetGeneration(e.ID), byte(EntityFlag_Dead), eIndex)
|
e.ID = NewEntityId(GetGeneration(e.ID), EntityFlag_None, eIndex)
|
||||||
|
|
||||||
r.FreeList = &freeListitem{
|
r.FreeList = &freeListitem{
|
||||||
EntityIndex: eIndex,
|
EntityIndex: eIndex,
|
||||||
nextFree: r.FreeList,
|
nextFree: r.FreeList,
|
||||||
}
|
}
|
||||||
|
r.FreeListSize++
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRegistry(size uint32) *Registry {
|
func NewRegistry(size uint32) *Registry {
|
||||||
|
|||||||
2
go.mod
@ -8,6 +8,6 @@ require github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bloeys/assimp-go v0.4.4
|
github.com/bloeys/assimp-go v0.4.4
|
||||||
github.com/bloeys/gglm v0.42.0
|
github.com/bloeys/gglm v0.43.0
|
||||||
github.com/inkyblackness/imgui-go/v4 v4.6.0
|
github.com/inkyblackness/imgui-go/v4 v4.6.0
|
||||||
)
|
)
|
||||||
|
|||||||
4
go.sum
@ -1,7 +1,7 @@
|
|||||||
github.com/bloeys/assimp-go v0.4.4 h1:Yn5e/RpE0Oes0YMBy8O7KkwAO4R/RpgrZPJCt08dVIU=
|
github.com/bloeys/assimp-go v0.4.4 h1:Yn5e/RpE0Oes0YMBy8O7KkwAO4R/RpgrZPJCt08dVIU=
|
||||||
github.com/bloeys/assimp-go v0.4.4/go.mod h1:my3yRxT7CfOztmvi+0svmwbaqw0KFrxaHxncoyaEIP0=
|
github.com/bloeys/assimp-go v0.4.4/go.mod h1:my3yRxT7CfOztmvi+0svmwbaqw0KFrxaHxncoyaEIP0=
|
||||||
github.com/bloeys/gglm v0.42.0 h1:UAUFGTaZv3dpZ0YSIQVum3bdeCZgNmx965VLnD2v11k=
|
github.com/bloeys/gglm v0.43.0 h1:ZpOghR3PHfpkigTDh+FqxLsF0gN8CD6s/bWoei6LyxI=
|
||||||
github.com/bloeys/gglm v0.42.0/go.mod h1:qwJQ0WzV191wAMwlGicbfbChbKoSedMk7gFFX6GnyOk=
|
github.com/bloeys/gglm v0.43.0/go.mod h1:qwJQ0WzV191wAMwlGicbfbChbKoSedMk7gFFX6GnyOk=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk=
|
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk=
|
||||||
|
|||||||
175
main.go
@ -3,7 +3,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"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/assets"
|
||||||
"github.com/bloeys/nmage/camera"
|
"github.com/bloeys/nmage/camera"
|
||||||
@ -17,14 +16,12 @@ import (
|
|||||||
"github.com/bloeys/nmage/renderer/rend3dgl"
|
"github.com/bloeys/nmage/renderer/rend3dgl"
|
||||||
"github.com/bloeys/nmage/timing"
|
"github.com/bloeys/nmage/timing"
|
||||||
nmageimgui "github.com/bloeys/nmage/ui/imgui"
|
nmageimgui "github.com/bloeys/nmage/ui/imgui"
|
||||||
|
"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:
|
// @Todo:
|
||||||
// Complete entity registry (e.g. HasEntity, GetEntity, Generational Indices etc...)
|
|
||||||
// Helper functions to update active entities
|
|
||||||
|
|
||||||
// Integrate physx
|
// Integrate physx
|
||||||
// Create VAO struct independent from VBO to support multi-VBO use cases (e.g. instancing)
|
// Create VAO struct independent from VBO to support multi-VBO use cases (e.g. instancing)
|
||||||
// Renderer batching
|
// Renderer batching
|
||||||
@ -48,12 +45,21 @@ var (
|
|||||||
cam *camera.Camera
|
cam *camera.Camera
|
||||||
|
|
||||||
simpleMat *materials.Material
|
simpleMat *materials.Material
|
||||||
cubeMesh *meshes.Mesh
|
skyboxMat *materials.Material
|
||||||
|
|
||||||
|
chairMesh *meshes.Mesh
|
||||||
|
cubeMesh *meshes.Mesh
|
||||||
|
skyboxMesh *meshes.Mesh
|
||||||
|
|
||||||
cubeModelMat = gglm.NewTrMatId()
|
cubeModelMat = gglm.NewTrMatId()
|
||||||
|
|
||||||
lightPos1 = gglm.NewVec3(-2, 0, 2)
|
lightPos1 = gglm.NewVec3(-2, 0, 2)
|
||||||
lightColor1 = gglm.NewVec3(1, 1, 1)
|
lightColor1 = gglm.NewVec3(1, 1, 1)
|
||||||
|
|
||||||
|
debugDepthMat *materials.Material
|
||||||
|
debugDrawDepthBuffer bool
|
||||||
|
|
||||||
|
skyboxCmap assets.Cubemap
|
||||||
)
|
)
|
||||||
|
|
||||||
type OurGame struct {
|
type OurGame struct {
|
||||||
@ -62,12 +68,14 @@ type OurGame struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TransformComp struct {
|
type TransformComp struct {
|
||||||
|
entity.BaseComp
|
||||||
|
|
||||||
Pos *gglm.Vec3
|
Pos *gglm.Vec3
|
||||||
Rot *gglm.Quat
|
Rot *gglm.Quat
|
||||||
Scale *gglm.Vec3
|
Scale *gglm.Vec3
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t TransformComp) Name() string {
|
func (t *TransformComp) Name() string {
|
||||||
return "Transform Component"
|
return "Transform Component"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,23 +85,15 @@ func Test() {
|
|||||||
e1 := lvl.Registry.NewEntity()
|
e1 := lvl.Registry.NewEntity()
|
||||||
|
|
||||||
trComp := entity.GetComp[*TransformComp](e1)
|
trComp := entity.GetComp[*TransformComp](e1)
|
||||||
fmt.Println("Got comp 1:", trComp)
|
fmt.Println("Get comp before adding any:", trComp)
|
||||||
|
|
||||||
e1.Comps = append(e1.Comps, &TransformComp{
|
entity.AddComp(e1, &TransformComp{
|
||||||
Pos: gglm.NewVec3(0, 0, 0),
|
Pos: gglm.NewVec3(0, 0, 0),
|
||||||
Rot: gglm.NewQuatEulerXYZ(0, 0, 0),
|
Rot: gglm.NewQuatEulerXYZ(0, 0, 0),
|
||||||
Scale: gglm.NewVec3(0, 0, 0),
|
Scale: gglm.NewVec3(0, 0, 0),
|
||||||
}, &TransformComp{
|
|
||||||
Pos: gglm.NewVec3(0, 0, 0),
|
|
||||||
Rot: gglm.NewQuatEulerXYZ(0, 0, 0),
|
|
||||||
Scale: gglm.NewVec3(1, 1, 1),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
trComp = entity.GetComp[*TransformComp](e1)
|
trComp = entity.GetComp[*TransformComp](e1)
|
||||||
fmt.Println("Got comp 2:", trComp)
|
fmt.Println("Get transform comp:", trComp)
|
||||||
|
|
||||||
trComps := entity.GetAllCompOfType[*TransformComp](e1)
|
|
||||||
fmt.Printf("Got comp 3: %+v, %+v\n", trComps[0], trComps[1])
|
|
||||||
|
|
||||||
fmt.Printf("Entity: %+v\n", e1)
|
fmt.Printf("Entity: %+v\n", e1)
|
||||||
fmt.Printf("Entity: %+v\n", lvl.Registry.NewEntity())
|
fmt.Printf("Entity: %+v\n", lvl.Registry.NewEntity())
|
||||||
@ -142,38 +142,60 @@ func (g *OurGame) handleWindowEvents(e sdl.Event) {
|
|||||||
cam.Update()
|
cam.Update()
|
||||||
|
|
||||||
simpleMat.SetUnifMat4("projMat", &cam.ProjMat)
|
simpleMat.SetUnifMat4("projMat", &cam.ProjMat)
|
||||||
|
debugDepthMat.SetUnifMat4("projMat", &cam.ProjMat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *OurGame) Init() {
|
func (g *OurGame) Init() {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
//Create materials
|
//Create materials
|
||||||
simpleMat = materials.NewMaterial("Simple Mat", "./res/shaders/simple.glsl")
|
simpleMat = materials.NewMaterial("Simple mat", "./res/shaders/simple.glsl")
|
||||||
|
debugDepthMat = materials.NewMaterial("Debug depth mat", "./res/shaders/debug-depth.glsl")
|
||||||
|
skyboxMat = materials.NewMaterial("Skybox mat", "./res/shaders/skybox.glsl")
|
||||||
|
|
||||||
//Load meshes
|
//Load meshes
|
||||||
var err error
|
cubeMesh, err = meshes.NewMesh("Cube", "./res/models/tex-cube.fbx", 0)
|
||||||
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 mesh. Err: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
chairMesh, err = meshes.NewMesh("Chair", "./res/models/chair.fbx", 0)
|
||||||
|
if err != nil {
|
||||||
|
logging.ErrLog.Fatalln("Failed to load mesh. Err: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
skyboxMesh, err = meshes.NewMesh("Skybox", "./res/models/skybox-cube.obj", 0)
|
||||||
|
if err != nil {
|
||||||
|
logging.ErrLog.Fatalln("Failed to load mesh. Err: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Load textures
|
//Load textures
|
||||||
tex, err := assets.LoadTexturePNG("./res/textures/Low poly planet.png", nil)
|
tex, err := assets.LoadTexturePNG("./res/textures/pallete-endesga-64-1x.png", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.ErrLog.Fatalln("Failed to load texture. Err: ", err)
|
logging.ErrLog.Fatalln("Failed to load texture. Err: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Configure material
|
skyboxCmap, err = assets.LoadCubemapTextures(
|
||||||
|
"./res/textures/sb-right.jpg", "./res/textures/sb-left.jpg",
|
||||||
|
"./res/textures/sb-top.jpg", "./res/textures/sb-bottom.jpg",
|
||||||
|
"./res/textures/sb-front.jpg", "./res/textures/sb-back.jpg",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
logging.ErrLog.Fatalln("Failed to load cubemap. Err: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure materials
|
||||||
simpleMat.DiffuseTex = tex.TexID
|
simpleMat.DiffuseTex = tex.TexID
|
||||||
|
|
||||||
//Movement, scale and rotation
|
//Movement, scale and rotation
|
||||||
translationMat := gglm.NewTranslationMat(gglm.NewVec3(0, 0, 0))
|
translationMat := gglm.NewTranslationMat(gglm.NewVec3(0, 0, 0))
|
||||||
scaleMat := gglm.NewScaleMat(gglm.NewVec3(0.25, 0.25, 0.25))
|
scaleMat := gglm.NewScaleMat(gglm.NewVec3(1, 1, 1))
|
||||||
rotMat := gglm.NewRotMat(gglm.NewQuatEuler(gglm.NewVec3(0, 0, 0).AsRad()))
|
rotMat := gglm.NewRotMat(gglm.NewQuatEuler(gglm.NewVec3(-90, -90, 0).AsRad()))
|
||||||
|
|
||||||
cubeModelMat.Mul(translationMat.Mul(rotMat.Mul(scaleMat)))
|
cubeModelMat.Mul(translationMat.Mul(rotMat.Mul(scaleMat)))
|
||||||
simpleMat.SetUnifMat4("modelMat", &cubeModelMat.Mat4)
|
|
||||||
|
|
||||||
// Camera
|
// Camera
|
||||||
winWidth, winHeight := g.Win.SDLWin.GetSize()
|
winWidth, winHeight := g.Win.SDLWin.GetSize()
|
||||||
@ -181,11 +203,12 @@ func (g *OurGame) Init() {
|
|||||||
gglm.NewVec3(0, 0, 10),
|
gglm.NewVec3(0, 0, 10),
|
||||||
gglm.NewVec3(0, 0, -1),
|
gglm.NewVec3(0, 0, -1),
|
||||||
gglm.NewVec3(0, 1, 0),
|
gglm.NewVec3(0, 1, 0),
|
||||||
0.1, 20,
|
0.1, 200,
|
||||||
45*gglm.Deg2Rad,
|
45*gglm.Deg2Rad,
|
||||||
float32(winWidth)/float32(winHeight),
|
float32(winWidth)/float32(winHeight),
|
||||||
)
|
)
|
||||||
simpleMat.SetUnifMat4("projMat", &cam.ProjMat)
|
simpleMat.SetUnifMat4("projMat", &cam.ProjMat)
|
||||||
|
debugDepthMat.SetUnifMat4("projMat", &cam.ProjMat)
|
||||||
|
|
||||||
updateViewMat()
|
updateViewMat()
|
||||||
|
|
||||||
@ -194,21 +217,6 @@ func (g *OurGame) Init() {
|
|||||||
simpleMat.SetUnifVec3("lightColor1", lightColor1)
|
simpleMat.SetUnifVec3("lightColor1", lightColor1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO: Add this to gglm
|
|
||||||
// func vecRotByQuat(v *gglm.Vec3, q *gglm.Quat) *gglm.Vec3 {
|
|
||||||
|
|
||||||
// // Reference: https://gamedev.stackexchange.com/questions/28395/rotating-vector3-by-a-quaternion
|
|
||||||
// qVec := gglm.NewVec3(q.X(), q.Y(), q.Z())
|
|
||||||
|
|
||||||
// rotatedVec := qVec.Clone().Scale(2 * gglm.DotVec3(v, qVec))
|
|
||||||
|
|
||||||
// t1 := q.W()*q.W() - gglm.DotVec3(qVec, qVec)
|
|
||||||
// rotatedVec.Add(v.Clone().Scale(t1))
|
|
||||||
|
|
||||||
// rotatedVec.Add(gglm.Cross(qVec, v).Scale(2 * q.W()))
|
|
||||||
// return rotatedVec
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (g *OurGame) Update() {
|
func (g *OurGame) Update() {
|
||||||
|
|
||||||
if input.IsQuitClicked() || input.KeyClicked(sdl.K_ESCAPE) {
|
if input.IsQuitClicked() || input.KeyClicked(sdl.K_ESCAPE) {
|
||||||
@ -221,7 +229,6 @@ func (g *OurGame) Update() {
|
|||||||
//Rotating cubes
|
//Rotating cubes
|
||||||
if input.KeyDown(sdl.K_SPACE) {
|
if input.KeyDown(sdl.K_SPACE) {
|
||||||
cubeModelMat.Rotate(10*timing.DT()*gglm.Deg2Rad, gglm.NewVec3(1, 1, 1).Normalize())
|
cubeModelMat.Rotate(10*timing.DT()*gglm.Deg2Rad, gglm.NewVec3(1, 1, 1).Normalize())
|
||||||
simpleMat.SetUnifMat4("modelMat", &cubeModelMat.Mat4)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if imgui.DragFloat3("Cam Pos", &cam.Pos.Data) {
|
if imgui.DragFloat3("Cam Pos", &cam.Pos.Data) {
|
||||||
@ -231,15 +238,25 @@ func (g *OurGame) Update() {
|
|||||||
updateViewMat()
|
updateViewMat()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if imgui.DragFloat3("Light Pos 1", &lightPos1.Data) {
|
||||||
|
simpleMat.SetUnifVec3("lightPos1", lightPos1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if imgui.DragFloat3("Light Color 1", &lightColor1.Data) {
|
||||||
|
simpleMat.SetUnifVec3("lightColor1", lightColor1)
|
||||||
|
}
|
||||||
|
|
||||||
if input.KeyClicked(sdl.K_F4) {
|
if input.KeyClicked(sdl.K_F4) {
|
||||||
fmt.Printf("Pos: %s; Forward: %s; |Forward|: %f\n", cam.Pos.String(), cam.Forward.String(), cam.Forward.Mag())
|
fmt.Printf("Pos: %s; Forward: %s; |Forward|: %f\n", cam.Pos.String(), cam.Forward.String(), cam.Forward.Mag())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g.Win.SDLWin.SetTitle(fmt.Sprint("nMage (", timing.GetAvgFPS(), " fps)"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *OurGame) updateCameraLookAround() {
|
func (g *OurGame) updateCameraLookAround() {
|
||||||
|
|
||||||
mouseX, mouseY := input.GetMouseMotion()
|
mouseX, mouseY := input.GetMouseMotion()
|
||||||
if mouseX == 0 && mouseY == 0 {
|
if (mouseX == 0 && mouseY == 0) || !input.MouseDown(sdl.BUTTON_RIGHT) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,21 +283,26 @@ func (g *OurGame) updateCameraPos() {
|
|||||||
|
|
||||||
update := false
|
update := false
|
||||||
|
|
||||||
|
var camSpeedScale float32 = 1.0
|
||||||
|
if input.KeyDown(sdl.K_LSHIFT) {
|
||||||
|
camSpeedScale = 2
|
||||||
|
}
|
||||||
|
|
||||||
// Forward and backward
|
// Forward and backward
|
||||||
if input.KeyDown(sdl.K_w) {
|
if input.KeyDown(sdl.K_w) {
|
||||||
cam.Pos.Add(cam.Forward.Clone().Scale(camSpeed * timing.DT()))
|
cam.Pos.Add(cam.Forward.Clone().Scale(camSpeed * camSpeedScale * timing.DT()))
|
||||||
update = true
|
update = true
|
||||||
} else if input.KeyDown(sdl.K_s) {
|
} else if input.KeyDown(sdl.K_s) {
|
||||||
cam.Pos.Add(cam.Forward.Clone().Scale(-camSpeed * timing.DT()))
|
cam.Pos.Add(cam.Forward.Clone().Scale(-camSpeed * camSpeedScale * timing.DT()))
|
||||||
update = true
|
update = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Left and right
|
// Left and right
|
||||||
if input.KeyDown(sdl.K_d) {
|
if input.KeyDown(sdl.K_d) {
|
||||||
cam.Pos.Add(gglm.Cross(&cam.Forward, &cam.WorldUp).Normalize().Scale(camSpeed * timing.DT()))
|
cam.Pos.Add(gglm.Cross(&cam.Forward, &cam.WorldUp).Normalize().Scale(camSpeed * camSpeedScale * timing.DT()))
|
||||||
update = true
|
update = true
|
||||||
} else if input.KeyDown(sdl.K_a) {
|
} else if input.KeyDown(sdl.K_a) {
|
||||||
cam.Pos.Add(gglm.Cross(&cam.Forward, &cam.WorldUp).Normalize().Scale(-camSpeed * timing.DT()))
|
cam.Pos.Add(gglm.Cross(&cam.Forward, &cam.WorldUp).Normalize().Scale(-camSpeed * camSpeedScale * timing.DT()))
|
||||||
update = true
|
update = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,18 +313,54 @@ func (g *OurGame) updateCameraPos() {
|
|||||||
|
|
||||||
func (g *OurGame) Render() {
|
func (g *OurGame) Render() {
|
||||||
|
|
||||||
tempModelMat := cubeModelMat.Clone()
|
matToUse := simpleMat
|
||||||
|
imgui.Checkbox("Debug depth buffer", &debugDrawDepthBuffer)
|
||||||
rowSize := 100
|
if debugDrawDepthBuffer {
|
||||||
for y := 0; y < rowSize; y++ {
|
matToUse = debugDepthMat
|
||||||
for x := 0; x < rowSize; x++ {
|
|
||||||
tempModelMat.Translate(gglm.NewVec3(-1, 0, 0))
|
|
||||||
window.Rend.Draw(cubeMesh, tempModelMat, simpleMat)
|
|
||||||
}
|
|
||||||
tempModelMat.Translate(gglm.NewVec3(float32(rowSize), -1, 0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g.Win.SDLWin.SetTitle(fmt.Sprint("nMage (", timing.GetAvgFPS(), " fps)"))
|
tempModelMatrix := cubeModelMat.Clone()
|
||||||
|
window.Rend.Draw(chairMesh, tempModelMatrix, matToUse)
|
||||||
|
|
||||||
|
rowSize := 1
|
||||||
|
for y := 0; y < rowSize; y++ {
|
||||||
|
for x := 0; x < rowSize; x++ {
|
||||||
|
tempModelMatrix.Translate(gglm.NewVec3(-6, 0, 0))
|
||||||
|
window.Rend.Draw(cubeMesh, tempModelMatrix, matToUse)
|
||||||
|
}
|
||||||
|
tempModelMatrix.Translate(gglm.NewVec3(float32(rowSize), -1, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
g.DrawSkybox()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *OurGame) DrawSkybox() {
|
||||||
|
|
||||||
|
gl.Disable(gl.CULL_FACE)
|
||||||
|
gl.DepthFunc(gl.LEQUAL)
|
||||||
|
skyboxMesh.Buf.Bind()
|
||||||
|
skyboxMat.Bind()
|
||||||
|
gl.ActiveTexture(gl.TEXTURE0)
|
||||||
|
gl.BindTexture(gl.TEXTURE_CUBE_MAP, skyboxCmap.TexID)
|
||||||
|
|
||||||
|
viewMat := cam.ViewMat.Clone()
|
||||||
|
viewMat.Set(0, 3, 0)
|
||||||
|
viewMat.Set(1, 3, 0)
|
||||||
|
viewMat.Set(2, 3, 0)
|
||||||
|
viewMat.Set(3, 0, 0)
|
||||||
|
viewMat.Set(3, 1, 0)
|
||||||
|
viewMat.Set(3, 2, 0)
|
||||||
|
viewMat.Set(3, 3, 0)
|
||||||
|
|
||||||
|
skyboxMat.SetUnifMat4("viewMat", viewMat)
|
||||||
|
skyboxMat.SetUnifMat4("projMat", &cam.ProjMat)
|
||||||
|
// window.Rend.Draw(cubeMesh, gglm.NewTrMatId(), skyboxMat)
|
||||||
|
for i := 0; i < len(skyboxMesh.SubMeshes); i++ {
|
||||||
|
gl.DrawElementsBaseVertexWithOffset(gl.TRIANGLES, skyboxMesh.SubMeshes[i].IndexCount, gl.UNSIGNED_INT, uintptr(skyboxMesh.SubMeshes[i].BaseIndex), skyboxMesh.SubMeshes[i].BaseVertex)
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.DepthFunc(gl.LESS)
|
||||||
|
gl.Enable(gl.CULL_FACE)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *OurGame) FrameEnd() {
|
func (g *OurGame) FrameEnd() {
|
||||||
@ -315,4 +373,5 @@ func (g *OurGame) DeInit() {
|
|||||||
func updateViewMat() {
|
func updateViewMat() {
|
||||||
cam.Update()
|
cam.Update()
|
||||||
simpleMat.SetUnifMat4("viewMat", &cam.ViewMat)
|
simpleMat.SetUnifMat4("viewMat", &cam.ViewMat)
|
||||||
|
debugDepthMat.SetUnifMat4("viewMat", &cam.ViewMat)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,9 +10,16 @@ import (
|
|||||||
"github.com/bloeys/nmage/buffers"
|
"github.com/bloeys/nmage/buffers"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SubMesh struct {
|
||||||
|
BaseVertex int32
|
||||||
|
BaseIndex uint32
|
||||||
|
IndexCount int32
|
||||||
|
}
|
||||||
|
|
||||||
type Mesh struct {
|
type Mesh struct {
|
||||||
Name string
|
Name string
|
||||||
Buf buffers.Buffer
|
Buf buffers.Buffer
|
||||||
|
SubMeshes []SubMesh
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh, error) {
|
func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh, error) {
|
||||||
@ -27,32 +34,68 @@ func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh,
|
|||||||
return nil, errors.New("No meshes found in file: " + modelPath)
|
return nil, errors.New("No meshes found in file: " + modelPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh := &Mesh{Name: name}
|
mesh := &Mesh{
|
||||||
sceneMesh := scene.Meshes[0]
|
Name: name,
|
||||||
mesh.Buf = buffers.NewBuffer()
|
Buf: buffers.NewBuffer(),
|
||||||
|
SubMeshes: make([]SubMesh, 0, 1),
|
||||||
if len(sceneMesh.TexCoords[0]) == 0 {
|
|
||||||
sceneMesh.TexCoords[0] = make([]gglm.Vec3, len(sceneMesh.Vertices))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
layoutToUse := []buffers.Element{{ElementType: buffers.DataTypeVec3}, {ElementType: buffers.DataTypeVec3}, {ElementType: buffers.DataTypeVec2}}
|
// Initial sizes assuming one submesh that has vertex pos+normals+texCoords, and 3 indices per face
|
||||||
|
var vertexBufData []float32 = make([]float32, 0, len(scene.Meshes[0].Vertices)*3*3*2)
|
||||||
|
var indexBufData []uint32 = make([]uint32, 0, len(scene.Meshes[0].Faces)*3)
|
||||||
|
|
||||||
if len(sceneMesh.ColorSets) > 0 && len(sceneMesh.ColorSets[0]) > 0 {
|
for i := 0; i < len(scene.Meshes); i++ {
|
||||||
layoutToUse = append(layoutToUse, buffers.Element{ElementType: buffers.DataTypeVec4})
|
|
||||||
}
|
|
||||||
mesh.Buf.SetLayout(layoutToUse...)
|
|
||||||
|
|
||||||
var values []float32
|
sceneMesh := scene.Meshes[i]
|
||||||
arrs := []arrToInterleave{{V3s: sceneMesh.Vertices}, {V3s: sceneMesh.Normals}, {V2s: v3sToV2s(sceneMesh.TexCoords[0])}}
|
|
||||||
|
|
||||||
if len(sceneMesh.ColorSets) > 0 && len(sceneMesh.ColorSets[0]) > 0 {
|
if len(sceneMesh.TexCoords[0]) == 0 {
|
||||||
arrs = append(arrs, arrToInterleave{V4s: sceneMesh.ColorSets[0]})
|
sceneMesh.TexCoords[0] = make([]gglm.Vec3, len(sceneMesh.Vertices))
|
||||||
|
println("Zeroing tex coords for submesh", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
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})
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
mesh.Buf.SetLayout(layoutToUse...)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// @NOTE: Require that all submeshes have the same vertex buffer layout
|
||||||
|
firstSubmeshLayout := mesh.Buf.GetLayout()
|
||||||
|
assert.T(len(firstSubmeshLayout) == len(layoutToUse), fmt.Sprintf("Vertex layout of submesh %d does not equal vertex layout of the first submesh. Original layout: %v; This layout: %v", i, firstSubmeshLayout, layoutToUse))
|
||||||
|
|
||||||
|
for i := 0; i < len(firstSubmeshLayout); i++ {
|
||||||
|
if firstSubmeshLayout[i].ElementType != layoutToUse[i].ElementType {
|
||||||
|
panic(fmt.Sprintf("Vertex layout of submesh %d does not equal vertex layout of the first submesh. Original layout: %v; This layout: %v", i, firstSubmeshLayout, layoutToUse))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arrs := []arrToInterleave{{V3s: sceneMesh.Vertices}, {V3s: sceneMesh.Normals}, {V2s: v3sToV2s(sceneMesh.TexCoords[0])}}
|
||||||
|
if len(sceneMesh.ColorSets) > 0 && len(sceneMesh.ColorSets[0]) > 0 {
|
||||||
|
arrs = append(arrs, arrToInterleave{V4s: sceneMesh.ColorSets[0]})
|
||||||
|
}
|
||||||
|
|
||||||
|
indices := flattenFaces(sceneMesh.Faces)
|
||||||
|
mesh.SubMeshes = append(mesh.SubMeshes, SubMesh{
|
||||||
|
|
||||||
|
// Index of the vertex to start from (e.g. if index buffer says use vertex 5, and BaseVertex=3, the vertex used will be vertex 8)
|
||||||
|
BaseVertex: int32(len(vertexBufData)*4) / mesh.Buf.Stride,
|
||||||
|
// Which index (in the index buffer) to start from
|
||||||
|
BaseIndex: uint32(len(indexBufData)),
|
||||||
|
// How many indices in this submesh
|
||||||
|
IndexCount: int32(len(indices)),
|
||||||
|
})
|
||||||
|
|
||||||
|
vertexBufData = append(vertexBufData, interleave(arrs...)...)
|
||||||
|
indexBufData = append(indexBufData, indices...)
|
||||||
}
|
}
|
||||||
|
|
||||||
values = interleave(arrs...)
|
// fmt.Printf("!!! Vertex count: %d; Submeshes: %+v\n", len(vertexBufData)*4/int(mesh.Buf.Stride), mesh.SubMeshes)
|
||||||
|
mesh.Buf.SetData(vertexBufData)
|
||||||
mesh.Buf.SetData(values)
|
mesh.Buf.SetIndexBufData(indexBufData)
|
||||||
mesh.Buf.SetIndexBufData(flattenFaces(sceneMesh.Faces))
|
|
||||||
return mesh, nil
|
return mesh, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -28,7 +28,10 @@ func (r3d *Rend3DGL) Draw(mesh *meshes.Mesh, trMat *gglm.TrMat, mat *materials.M
|
|||||||
}
|
}
|
||||||
|
|
||||||
mat.SetUnifMat4("modelMat", &trMat.Mat4)
|
mat.SetUnifMat4("modelMat", &trMat.Mat4)
|
||||||
gl.DrawElements(gl.TRIANGLES, mesh.Buf.IndexBufCount, gl.UNSIGNED_INT, gl.PtrOffset(0))
|
|
||||||
|
for i := 0; i < len(mesh.SubMeshes); i++ {
|
||||||
|
gl.DrawElementsBaseVertexWithOffset(gl.TRIANGLES, mesh.SubMeshes[i].IndexCount, gl.UNSIGNED_INT, uintptr(mesh.SubMeshes[i].BaseIndex), mesh.SubMeshes[i].BaseVertex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r3d *Rend3DGL) FrameEnd() {
|
func (r3d *Rend3DGL) FrameEnd() {
|
||||||
|
|||||||
BIN
res/models/chair.fbx
Executable file
@ -1,46 +0,0 @@
|
|||||||
# Blender v2.92.0 OBJ File: ''
|
|
||||||
# www.blender.org
|
|
||||||
mtllib obj.mtl
|
|
||||||
o Cube
|
|
||||||
v 2.275618 1.000000 0.349413
|
|
||||||
v 3.520138 -1.000000 0.102233
|
|
||||||
v 2.275618 1.000000 0.752820
|
|
||||||
v 3.520138 -1.000000 1.000000
|
|
||||||
v 0.244520 1.000000 0.349413
|
|
||||||
v -1.000000 -1.000000 0.102233
|
|
||||||
v 0.244520 1.000000 0.752820
|
|
||||||
v -1.000000 -1.000000 1.000000
|
|
||||||
vt 0.806168 0.568832
|
|
||||||
vt 0.693832 0.681168
|
|
||||||
vt 0.693832 0.568832
|
|
||||||
vt 0.375000 1.000000
|
|
||||||
vt 0.375000 0.750000
|
|
||||||
vt 0.375000 0.000000
|
|
||||||
vt 0.625000 0.250000
|
|
||||||
vt 0.375000 0.250000
|
|
||||||
vt 0.375000 0.500000
|
|
||||||
vt 0.125000 0.750000
|
|
||||||
vt 0.125000 0.500000
|
|
||||||
vt 0.806168 0.681168
|
|
||||||
vt 0.625000 0.931168
|
|
||||||
vt 0.625000 0.068832
|
|
||||||
vn 0.0000 1.0000 0.0000
|
|
||||||
vn 0.0000 0.1227 0.9924
|
|
||||||
vn -0.8490 0.5283 0.0000
|
|
||||||
vn 0.0000 -1.0000 0.0000
|
|
||||||
vn 0.8490 0.5283 0.0000
|
|
||||||
vn 0.0000 0.1227 -0.9924
|
|
||||||
usemtl Material
|
|
||||||
s off
|
|
||||||
f 5/1/1 3/2/1 1/3/1
|
|
||||||
f 3/2/2 8/4/2 4/5/2
|
|
||||||
f 8/6/3 5/7/3 6/8/3
|
|
||||||
f 2/9/4 8/10/4 6/11/4
|
|
||||||
f 1/3/5 4/5/5 2/9/5
|
|
||||||
f 5/7/6 2/9/6 6/8/6
|
|
||||||
f 5/1/1 7/12/1 3/2/1
|
|
||||||
f 3/2/2 7/13/2 8/4/2
|
|
||||||
f 8/6/3 7/14/3 5/7/3
|
|
||||||
f 2/9/4 4/5/4 8/10/4
|
|
||||||
f 1/3/5 3/2/5 4/5/5
|
|
||||||
f 5/7/6 1/3/6 2/9/6
|
|
||||||
38
res/models/skybox-cube.obj
Executable file
@ -0,0 +1,38 @@
|
|||||||
|
# Blender v2.92.0 OBJ File: 'chair.blend'
|
||||||
|
# www.blender.org
|
||||||
|
o Cube.002_Cube.005
|
||||||
|
v -1.000000 -1.000000 1.000000
|
||||||
|
v -1.000000 1.000000 1.000000
|
||||||
|
v -1.000000 -1.000000 -1.000000
|
||||||
|
v -1.000000 1.000000 -1.000000
|
||||||
|
v 1.000000 -1.000000 1.000000
|
||||||
|
v 1.000000 1.000000 1.000000
|
||||||
|
v 1.000000 -1.000000 -1.000000
|
||||||
|
v 1.000000 1.000000 -1.000000
|
||||||
|
vt 0.375000 0.000000
|
||||||
|
vt 0.625000 0.000000
|
||||||
|
vt 0.625000 0.250000
|
||||||
|
vt 0.375000 0.250000
|
||||||
|
vt 0.625000 0.500000
|
||||||
|
vt 0.375000 0.500000
|
||||||
|
vt 0.625000 0.750000
|
||||||
|
vt 0.375000 0.750000
|
||||||
|
vt 0.625000 1.000000
|
||||||
|
vt 0.375000 1.000000
|
||||||
|
vt 0.125000 0.500000
|
||||||
|
vt 0.125000 0.750000
|
||||||
|
vt 0.875000 0.500000
|
||||||
|
vt 0.875000 0.750000
|
||||||
|
vn -1.0000 0.0000 0.0000
|
||||||
|
vn 0.0000 0.0000 -1.0000
|
||||||
|
vn 1.0000 0.0000 0.0000
|
||||||
|
vn 0.0000 0.0000 1.0000
|
||||||
|
vn 0.0000 -1.0000 0.0000
|
||||||
|
vn 0.0000 1.0000 0.0000
|
||||||
|
s off
|
||||||
|
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||||
|
f 3/4/2 4/3/2 8/5/2 7/6/2
|
||||||
|
f 7/6/3 8/5/3 6/7/3 5/8/3
|
||||||
|
f 5/8/4 6/7/4 2/9/4 1/10/4
|
||||||
|
f 3/11/5 7/6/5 5/8/5 1/12/5
|
||||||
|
f 8/5/6 4/13/6 2/14/6 6/7/6
|
||||||
52
res/shaders/debug-depth.glsl
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
//shader:vertex
|
||||||
|
#version 410
|
||||||
|
|
||||||
|
layout(location=0) in vec3 vertPosIn;
|
||||||
|
layout(location=1) in vec3 vertNormalIn;
|
||||||
|
layout(location=2) in vec2 vertUV0In;
|
||||||
|
layout(location=3) in vec3 vertColorIn;
|
||||||
|
|
||||||
|
out vec3 vertNormal;
|
||||||
|
out vec2 vertUV0;
|
||||||
|
out vec3 vertColor;
|
||||||
|
out vec3 fragPos;
|
||||||
|
|
||||||
|
//MVP = Model View Projection
|
||||||
|
uniform mat4 modelMat;
|
||||||
|
uniform mat4 viewMat;
|
||||||
|
uniform mat4 projMat;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vertNormal = mat3(transpose(inverse(modelMat))) * vertNormalIn;
|
||||||
|
vertUV0 = vertUV0In;
|
||||||
|
vertColor = vertColorIn;
|
||||||
|
fragPos = vec3(modelMat * vec4(vertPosIn, 1.0));
|
||||||
|
|
||||||
|
gl_Position = projMat * viewMat * modelMat * vec4(vertPosIn, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//shader:fragment
|
||||||
|
#version 410
|
||||||
|
|
||||||
|
in vec3 vertColor;
|
||||||
|
in vec3 vertNormal;
|
||||||
|
in vec2 vertUV0;
|
||||||
|
in vec3 fragPos;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform float near = 0.1;
|
||||||
|
uniform float far = 200.0;
|
||||||
|
|
||||||
|
float LinearizeDepth(float depth)
|
||||||
|
{
|
||||||
|
float z = depth * 2.0 - 1.0; // back to NDC
|
||||||
|
return (2.0 * near * far) / (far + near - z * (far - near));
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
float depth = LinearizeDepth(gl_FragCoord.z) / far;
|
||||||
|
fragColor = vec4(vec3(depth), 1.0);
|
||||||
|
}
|
||||||
@ -29,7 +29,7 @@ void main()
|
|||||||
//shader:fragment
|
//shader:fragment
|
||||||
#version 410
|
#version 410
|
||||||
|
|
||||||
uniform float ambientStrength = 0.1;
|
uniform float ambientStrength = 0;
|
||||||
uniform vec3 ambientLightColor = vec3(1, 1, 1);
|
uniform vec3 ambientLightColor = vec3(1, 1, 1);
|
||||||
|
|
||||||
uniform vec3 lightPos1;
|
uniform vec3 lightPos1;
|
||||||
|
|||||||
33
res/shaders/skybox.glsl
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
//shader:vertex
|
||||||
|
#version 410
|
||||||
|
|
||||||
|
layout(location=0) in vec3 vertPosIn;
|
||||||
|
layout(location=1) in vec3 vertNormalIn;
|
||||||
|
layout(location=2) in vec2 vertUV0In;
|
||||||
|
layout(location=3) in vec3 vertColorIn;
|
||||||
|
|
||||||
|
out vec3 vertUV0;
|
||||||
|
|
||||||
|
uniform mat4 viewMat;
|
||||||
|
uniform mat4 projMat;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vertUV0 = vec3(vertPosIn.x, vertPosIn.y, -vertPosIn.z);
|
||||||
|
vec4 pos = projMat * viewMat * vec4(vertPosIn, 1.0);
|
||||||
|
gl_Position = pos.xyww;
|
||||||
|
}
|
||||||
|
|
||||||
|
//shader:fragment
|
||||||
|
#version 410
|
||||||
|
|
||||||
|
in vec3 vertUV0;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform samplerCube skybox;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
fragColor = texture(skybox, vertUV0);
|
||||||
|
}
|
||||||
BIN
res/textures/pallete-endesga-64-1x.png
Executable file
|
After Width: | Height: | Size: 355 B |
BIN
res/textures/sb-back.jpg
Executable file
|
After Width: | Height: | Size: 1008 KiB |
BIN
res/textures/sb-bottom.jpg
Executable file
|
After Width: | Height: | Size: 274 KiB |
BIN
res/textures/sb-front.jpg
Executable file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
res/textures/sb-left.jpg
Executable file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
res/textures/sb-right.jpg
Executable file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
res/textures/sb-top.jpg
Executable file
|
After Width: | Height: | Size: 338 KiB |