Initial commit

This commit is contained in:
2022-01-20 21:58:50 +08:00
commit b44d41ec66
86 changed files with 5415 additions and 0 deletions

View File

@ -0,0 +1,10 @@
// define the two symbols on Windows so that application
// could benefit from using the more powerful discrete GPU
#if defined (_WIN32)
__declspec(dllexport) unsigned long NvOptimusEnablement = 1;
__declspec(dllexport) unsigned long AmdPowerXpressRequestHighPerformance = 1;
#endif

View File

@ -0,0 +1,3 @@
package gpu_preference
import "C"

View File

@ -0,0 +1,15 @@
//+build nvidia
package gpu_preference
import (
"os"
"runtime"
)
func init() {
if runtime.GOOS == "linux" {
os.Setenv("__NV_PRIME_RENDER_OFFLOAD", "1")
os.Setenv("__GLX_VENDOR_LIBRARY_NAME", "nvidia")
}
}

View File

@ -0,0 +1,127 @@
package render
import (
"image/color"
"unsafe"
"edgaru089.ml/go/gl01/internal/asset"
"edgaru089.ml/go/gl01/internal/util/itype"
"github.com/go-gl/gl/all-core/gl"
"github.com/go-gl/mathgl/mgl32"
)
// framewire vertex
type frvertex struct {
pos itype.Vec3f
color itype.Vec4f
}
// FramewireRenderer is a renderer drawing framewires.
//
// It is mainly for debugging uses.
type FramewireRenderer struct {
shader *Shader
vao, vbo uint32
vertex []frvertex
}
// Framewire is a global FramewireRenderer managed by game logic, for debugging.
var Framewire = &FramewireRenderer{}
func (f *FramewireRenderer) Init() (err error) {
f.shader, err = NewShader(asset.FramewireShaderVert, asset.FramewireShaderFrag)
if err != nil {
return err
}
f.shader.SetUniformMat4("model", mgl32.Ident4())
gl.BindFragDataLocation(f.shader.Handle(), 0, gl.Str("outputColor\x00"))
gl.GenVertexArrays(1, &f.vao)
gl.BindVertexArray(f.vao)
gl.GenBuffers(1, &f.vbo)
gl.BindBuffer(gl.ARRAY_BUFFER, f.vbo)
vertAttrib := uint32(gl.GetAttribLocation(f.shader.Handle(), gl.Str("vert\x00")))
gl.VertexAttribPointer(vertAttrib, 3, gl.FLOAT, false, int32(unsafe.Sizeof(frvertex{})), gl.PtrOffset(int(unsafe.Offsetof(frvertex{}.pos))))
colorAttrib := uint32(gl.GetAttribLocation(f.shader.Handle(), gl.Str("vertColor\x00")))
gl.VertexAttribPointer(colorAttrib, 4, gl.FLOAT, false, int32(unsafe.Sizeof(frvertex{})), gl.PtrOffset(int(unsafe.Offsetof(frvertex{}.color))))
gl.EnableVertexAttribArray(vertAttrib)
gl.EnableVertexAttribArray(colorAttrib)
return nil
}
func colorToVec4f(color color.Color) itype.Vec4f {
r, g, b, a := color.RGBA()
return itype.Vec4f{float32(r) / 0xffff, float32(g) / 0xffff, float32(b) / 0xffff, float32(a) / 0xffff}
}
// PushLine pushes a line from p0 to p1 into the vertex array.
func (f *FramewireRenderer) PushLine(p0, p1 itype.Vec3f, color0, color1 color.Color) {
f.vertex = append(f.vertex,
frvertex{
pos: p0,
color: colorToVec4f(color0),
},
frvertex{
pos: p1,
color: colorToVec4f(color1),
},
)
}
const FramewireSizeShrink = 1e-2
// PushBox pushes a 3D box into the vertex array.
//
// The size of the box is shrunk by a little bit so that it is not obstructed by the world.
func (f *FramewireRenderer) PushBox(box itype.Boxf, color color.Color) {
box = itype.Boxf{
OffX: box.OffX + FramewireSizeShrink,
OffY: box.OffY + 5*FramewireSizeShrink,
OffZ: box.OffZ + FramewireSizeShrink,
SizeX: box.SizeX - 2*FramewireSizeShrink,
SizeY: box.SizeY - 10*FramewireSizeShrink,
SizeZ: box.SizeZ - 2*FramewireSizeShrink,
}
base := box.MinPoint()
f.PushLine(base, base.Addv(box.SizeX, 0, 0), color, color)
f.PushLine(base, base.Addv(0, 0, box.SizeZ), color, color)
f.PushLine(base.Addv(box.SizeX, 0, box.SizeZ), base.Addv(box.SizeX, 0, 0), color, color)
f.PushLine(base.Addv(box.SizeX, 0, box.SizeZ), base.Addv(0, 0, box.SizeZ), color, color)
f.PushLine(base, base.Addv(0, box.SizeY, 0), color, color)
f.PushLine(base.Addv(box.SizeX, 0, 0), base.Addv(box.SizeX, box.SizeY, 0), color, color)
f.PushLine(base.Addv(0, 0, box.SizeZ), base.Addv(0, box.SizeY, box.SizeZ), color, color)
f.PushLine(base.Addv(box.SizeX, 0, box.SizeZ), base.Addv(box.SizeX, box.SizeY, box.SizeZ), color, color)
f.PushLine(base.Addv(0, box.SizeY, 0), base.Addv(box.SizeX, box.SizeY, 0), color, color)
f.PushLine(base.Addv(0, box.SizeY, 0), base.Addv(0, box.SizeY, box.SizeZ), color, color)
f.PushLine(base.Addv(box.SizeX, box.SizeY, box.SizeZ), base.Addv(box.SizeX, box.SizeY, 0), color, color)
f.PushLine(base.Addv(box.SizeX, box.SizeY, box.SizeZ), base.Addv(0, box.SizeY, box.SizeZ), color, color)
}
// Render renders the framewire into the current OpenGL context and clears it.
func (f *FramewireRenderer) Render(view *View) {
if len(f.vertex) == 0 {
return
}
f.shader.UseProgram()
f.shader.SetUniformMat4("view", view.View())
f.shader.SetUniformMat4("projection", view.Perspective())
gl.BindVertexArray(f.vao)
gl.BindBuffer(gl.ARRAY_BUFFER, f.vbo)
gl.BufferData(gl.ARRAY_BUFFER, len(f.vertex)*int(unsafe.Sizeof(frvertex{})), gl.Ptr(f.vertex), gl.STREAM_DRAW)
gl.DrawArrays(gl.LINES, 0, int32(len(f.vertex)))
f.vertex = f.vertex[0:0]
}

View File

@ -0,0 +1,130 @@
package render
import (
"unsafe"
"edgaru089.ml/go/gl01/internal/asset"
"edgaru089.ml/go/gl01/internal/io"
"edgaru089.ml/go/gl01/internal/util/itype"
"edgaru089.ml/go/gl01/internal/world"
"github.com/go-gl/gl/all-core/gl"
"github.com/go-gl/mathgl/mgl32"
"github.com/inkyblackness/imgui-go/v4"
)
var (
ShadowmapSize = itype.Vec2i{3072, 3072} // Size of the shadow mapping
)
// WorldRenderer holds texture/shader resource and viewport
// information for world rendering.
type WorldRenderer struct {
shader *Shader
texture *Texture
depthmapFBO, depthmap uint32
depthmapShader *Shader
}
// The default WorldRenderer.
var DefaultWorldRenderer WorldRenderer
// Init initializes the WorldRenderer.
func (r *WorldRenderer) Init(w *world.World) (err error) {
r.shader, err = NewShader(asset.WorldShaderVert, asset.WorldShaderFrag)
if err != nil {
return err
}
r.depthmapShader, err = NewShader(asset.WorldShaderShadowmapVert, asset.WorldShaderShadowmapFrag)
if err != nil {
return err
}
asset.InitWorldTextureAtlas()
r.texture = NewTextureRGBA(asset.WorldTextureAtlas.Image)
r.shader.SetUniformTexture("tex", r.texture)
r.shader.SetUniformMat4("model", mgl32.Ident4())
r.depthmapShader.SetUniformMat4("model", mgl32.Ident4())
// and view and projection uniforms not yet set
gl.BindFragDataLocation(r.shader.Handle(), 0, gl.Str("outputColor\x00"))
// Set the pointers for attributions
vertAttrib := r.shader.GetAttribLocation("vert")
gl.VertexAttribPointer(vertAttrib, 3, gl.FLOAT, false, int32(unsafe.Sizeof(world.Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(world.Vertex{}.World))))
normalAttrib := r.shader.GetAttribLocation("normal")
gl.VertexAttribPointer(normalAttrib, 3, gl.FLOAT, false, int32(unsafe.Sizeof(world.Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(world.Vertex{}.Normal))))
texCoordAttrib := r.shader.GetAttribLocation("vertTexCoord")
gl.VertexAttribPointer(texCoordAttrib, 2, gl.FLOAT, false, int32(unsafe.Sizeof(world.Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(world.Vertex{}.Texture))))
lightAttrib := r.shader.GetAttribLocation("light")
gl.VertexAttribPointer(lightAttrib, 1, gl.FLOAT, false, int32(unsafe.Sizeof(world.Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(world.Vertex{}.Light))))
// generate the depthmap and depthmap FBO
gl.GenFramebuffers(1, &r.depthmapFBO)
gl.GenTextures(1, &r.depthmap)
gl.BindTexture(gl.TEXTURE_2D, r.depthmap)
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, int32(ShadowmapSize[0]), int32(ShadowmapSize[1]), 0, gl.DEPTH_COMPONENT, gl.FLOAT, nil)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_BORDER)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_BORDER)
borderColor := []float32{1, 1, 1, 1}
gl.TexParameterfv(gl.TEXTURE_2D, gl.TEXTURE_BORDER_COLOR, &borderColor[0])
// attach depth texture as FBO's depth buffer
gl.BindFramebuffer(gl.FRAMEBUFFER, r.depthmapFBO)
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, r.depthmap, 0)
gl.DrawBuffer(gl.NONE)
gl.ReadBuffer(gl.NONE)
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
r.shader.SetUniformTextureHandle("shadowmap", r.depthmap)
gl.Enable(gl.CULL_FACE)
gl.Enable(gl.DEPTH_TEST)
gl.DepthFunc(gl.LESS)
return nil
}
var sun = [3]float32{0.2, 0.4, 0.3}
func (r *WorldRenderer) Render(world *world.World, view *View) {
imgui.SliderFloat3("Sun", &sun, -1, 1)
normalSun := itype.Vec3f(sun).Normalize()
// 1. Render to depth map
gl.Viewport(0, 0, int32(ShadowmapSize[0]), int32(ShadowmapSize[1]))
gl.BindFramebuffer(gl.FRAMEBUFFER, r.depthmapFBO)
gl.Clear(gl.DEPTH_BUFFER_BIT)
lightPos := view.EyePos.Add(normalSun.Multiply(20))
lightView := mgl32.LookAt(lightPos[0], lightPos[1], lightPos[2], view.EyePos[0], view.EyePos[1], view.EyePos[2], 0, 1, 0)
lightProjection := mgl32.Ortho(-50, 50, -50, 50, 1, 50)
lightspace := lightProjection.Mul4(lightView)
r.depthmapShader.UseProgram()
r.depthmapShader.SetUniformMat4("lightspace", lightspace)
world.Render()
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
// 2. Render the scene
gl.Viewport(0, 0, int32(io.DisplaySize[0]), int32(io.DisplaySize[1]))
gl.Clear(gl.DEPTH_BUFFER_BIT)
r.shader.UseProgram()
r.shader.BindTextures()
r.shader.SetUniformMat4("lightspace", lightspace)
r.shader.SetUniformMat4("view", view.View())
r.shader.SetUniformMat4("projection", view.Perspective())
r.shader.SetUniformVec3f("viewPos", view.EyePos)
r.shader.SetUniformVec4f("fogColor", itype.Vec4f{0.6, 0.8, 1.0, 1.0})
r.shader.SetUniformVec3f("sun", normalSun)
gl.Enable(gl.FRAMEBUFFER_SRGB)
world.Render()
gl.Disable(gl.FRAMEBUFFER_SRGB)
}

View File

@ -0,0 +1,119 @@
package render
import (
"time"
"unsafe"
"github.com/Edgaru089/gl01/internal/asset"
"github.com/Edgaru089/gl01/internal/util/itype"
"github.com/Edgaru089/gl01/internal/world"
"github.com/go-gl/gl/all-core/gl"
"github.com/go-gl/mathgl/mgl32"
)
// WorldRenderer holds texture/shader resource and viewport
// information for world rendering.
type WorldRenderer struct {
shader *Shader
texture *Texture
vao, vbo uint32
vbolen int
vertex []world.Vertex
// for testing!
initTime time.Time
}
// The default WorldRenderer.
var DefaultWorldRenderer WorldRenderer
// Init initializes the WorldRenderer.
func (r *WorldRenderer) Init(w *world.World) (err error) {
r.shader, err = NewShader(asset.WorldShaderVert, asset.WorldShaderFrag)
if err != nil {
return err
}
asset.InitWorldTextureAtlas()
r.texture = NewTextureRGBA(asset.WorldTextureAtlas.Image)
r.shader.SetUniformTexture("tex", r.texture)
r.shader.SetUniformMat4("model", mgl32.Ident4())
// and view and projection uniforms not yet set
gl.BindFragDataLocation(r.shader.Handle(), 0, gl.Str("outputColor\x00"))
gl.GenVertexArrays(1, &r.vao)
gl.BindVertexArray(r.vao)
gl.GenBuffers(1, &r.vbo)
gl.BindBuffer(gl.ARRAY_BUFFER, r.vbo)
gensync := make(chan []world.Vertex, len(w.Chunks))
for _, c := range w.Chunks {
chunk := c
go func() {
arr := chunk.AppendVertex([]world.Vertex{})
gensync <- arr
}()
}
for range w.Chunks {
r.vertex = append(r.vertex, (<-gensync)...)
}
close(gensync)
gl.BufferData(gl.ARRAY_BUFFER, int(unsafe.Sizeof(world.Vertex{}))*len(r.vertex), gl.Ptr(r.vertex), gl.DYNAMIC_DRAW)
vertAttrib := uint32(gl.GetAttribLocation(r.shader.Handle(), gl.Str("vert\x00")))
gl.VertexAttribPointer(vertAttrib, 3, gl.FLOAT, false, int32(unsafe.Sizeof(world.Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(world.Vertex{}.World))))
normalAttrib := uint32(gl.GetAttribLocation(r.shader.Handle(), gl.Str("normal\x00")))
gl.VertexAttribPointer(normalAttrib, 3, gl.FLOAT, false, int32(unsafe.Sizeof(world.Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(world.Vertex{}.Normal))))
texCoordAttrib := uint32(gl.GetAttribLocation(r.shader.Handle(), gl.Str("vertTexCoord\x00")))
gl.VertexAttribPointer(texCoordAttrib, 2, gl.FLOAT, false, int32(unsafe.Sizeof(world.Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(world.Vertex{}.Texture))))
lightAttrib := uint32(gl.GetAttribLocation(r.shader.Handle(), gl.Str("light\x00")))
gl.VertexAttribPointer(lightAttrib, 1, gl.FLOAT, false, int32(unsafe.Sizeof(world.Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(world.Vertex{}.Light))))
gl.EnableVertexAttribArray(vertAttrib)
gl.EnableVertexAttribArray(normalAttrib)
gl.EnableVertexAttribArray(texCoordAttrib)
gl.EnableVertexAttribArray(lightAttrib)
w.EnableVertexArrayAttrib(vertAttrib)
w.EnableVertexArrayAttrib(normalAttrib)
w.EnableVertexArrayAttrib(texCoordAttrib)
w.EnableVertexArrayAttrib(lightAttrib)
gl.Enable(gl.CULL_FACE)
gl.Enable(gl.DEPTH_TEST)
gl.DepthFunc(gl.LESS)
r.initTime = time.Now()
return nil
}
func (r *WorldRenderer) Render(world *world.World, view *View) {
r.shader.UseProgram()
r.shader.BindTextures()
r.shader.SetUniformMat4("view", view.view)
r.shader.SetUniformMat4("projection", mgl32.Perspective(view.fovy, view.aspect, 0.1, 200))
r.shader.SetUniformVec3f("sun", itype.Vec3f{-0.2, 1, 0.8}.Normalize())
// for testing!
//model := mgl32.HomogRotate3D(float32(time.Since(r.initTime).Seconds()), mgl32.Vec3{0, 1, 0})
//r.shader.SetUniformMat4("model", model)
//r.shader.SetUniformMat4("view", r.view)
gl.BindVertexArray(r.vao)
gl.BindBuffer(gl.ARRAY_BUFFER, r.vbo)
gl.DrawArrays(gl.TRIANGLES, 0, int32(len(r.vertex)))
world.Render()
//world.Chunk(0, 0).Render()
}

257
internal/render/shader.go Normal file
View File

@ -0,0 +1,257 @@
package render
import (
"fmt"
"log"
"strings"
"edgaru089.ml/go/gl01/internal/util/itype"
"github.com/go-gl/gl/all-core/gl"
"github.com/go-gl/mathgl/mgl32"
)
// returns max texture unit count
func getMaxTextureUnits() int32 {
var cnt int32
gl.GetIntegerv(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS, &cnt)
return cnt
}
// Shader contains a shader program, with a vertex and a fragment (pixel) shader.
type Shader struct {
prog uint32
uniforms map[string]int32
textures map[int32]*Texture // maps uniform location to *Texture
}
// helper construct to get uniforms and restore previous glUseProgram
func (s *Shader) uniformBlender(name string) (location int32, restore func()) {
if s.prog != 0 {
var saved int32
// Use program object
gl.GetIntegerv(gl.CURRENT_PROGRAM, &saved)
if uint32(saved) != s.prog {
gl.UseProgram(s.prog)
}
return s.UniformLocation(name), func() {
if uint32(saved) != s.prog {
gl.UseProgram(uint32(saved))
}
}
}
return 0, func() {}
}
func compileShader(src string, stype uint32) (prog uint32, err error) {
prog = gl.CreateShader(stype)
strs, free := gl.Strs(src, "\x00")
gl.ShaderSource(prog, 1, strs, nil)
free()
gl.CompileShader(prog)
var status int32
gl.GetShaderiv(prog, gl.COMPILE_STATUS, &status)
if status == gl.FALSE {
var len int32
gl.GetShaderiv(prog, gl.INFO_LOG_LENGTH, &len)
log := strings.Repeat("\x00", int(len+1))
gl.GetShaderInfoLog(prog, len, nil, gl.Str(log))
gl.DeleteShader(prog)
switch stype {
case gl.VERTEX_SHADER:
return 0, fmt.Errorf("failed to compile Vertex Shader: %s", log)
case gl.FRAGMENT_SHADER:
return 0, fmt.Errorf("failed to compile Fragment Shader: %s", log)
default:
return 0, fmt.Errorf("failed to compile Unknown(%d) Shader: %s", stype, log)
}
}
return
}
// NewShader compiles and links a Vertex and a Fragment (Pixel) shader into one program.
// The source code does not need to be terminated with \x00.
func NewShader(vert, frag string) (s *Shader, err error) {
vertid, err := compileShader(vert, gl.VERTEX_SHADER)
if err != nil {
return nil, err
}
fragid, err := compileShader(frag, gl.FRAGMENT_SHADER)
if err != nil {
gl.DeleteShader(vertid)
return nil, err
}
s = &Shader{}
s.uniforms = make(map[string]int32)
s.textures = make(map[int32]*Texture)
s.prog = gl.CreateProgram()
gl.AttachShader(s.prog, vertid)
gl.AttachShader(s.prog, fragid)
gl.LinkProgram(s.prog)
var status int32
gl.GetProgramiv(s.prog, gl.LINK_STATUS, &status)
if status == gl.FALSE {
var len int32
gl.GetProgramiv(s.prog, gl.INFO_LOG_LENGTH, &len)
log := strings.Repeat("\x00", int(len+1))
gl.GetProgramInfoLog(s.prog, len, nil, gl.Str(log))
gl.DeleteProgram(s.prog)
gl.DeleteShader(vertid)
gl.DeleteShader(fragid)
return nil, fmt.Errorf("failed to link Program: %s", log)
}
gl.DeleteShader(vertid)
gl.DeleteShader(fragid)
return
}
// UniformLocation returns the location id of the given uniform.
// it returns -1 if the uniform is not found.
func (s *Shader) UniformLocation(name string) int32 {
if id, ok := s.uniforms[name]; ok {
return id
} else {
location := gl.GetUniformLocation(s.prog, gl.Str(name+"\x00"))
s.uniforms[name] = location
if location == -1 {
log.Printf("Shader: uniform \"%s\" not found", name)
}
return location
}
}
// UseProgram calls glUseProgram.
func (s *Shader) UseProgram() {
gl.UseProgram(s.prog)
}
// BindTextures calls glActiveTexture and glBindTexture, updating the texture unit slots.
func (s *Shader) BindTextures() {
var i int
for loc, tex := range s.textures {
index := int32(i + 1)
gl.Uniform1i(loc, index)
gl.ActiveTexture(uint32(gl.TEXTURE0 + index))
gl.BindTexture(gl.TEXTURE_2D, tex.tex)
i++
}
gl.ActiveTexture(gl.TEXTURE0)
}
// Handle returns the OpenGL handle of the program.
func (s *Shader) Handle() uint32 {
return s.prog
}
func (s *Shader) SetUniformTexture(name string, tex *Texture) {
if s.prog == 0 {
return
}
loc := s.UniformLocation(name)
if loc == -1 {
return
}
// Store the location to texture map
_, ok := s.textures[loc]
if !ok {
// new texture, make sure there are enough texture units
if len(s.textures)+1 >= int(getMaxTextureUnits()) {
log.Printf("Shader: Warning: Impossible to use texture \"%s\" for shader: all available texture units are used", name)
return
}
}
s.textures[loc] = tex
}
// SetUniformTextureHandle sets a uniform as a sampler2D from an external OpenGL texture.
// tex is the OpenGL texture handle (the one you get with glGenTextures())
func (s *Shader) SetUniformTextureHandle(name string, tex uint32) {
s.SetUniformTexture(name, &Texture{tex: tex})
}
func (s *Shader) SetUniformMat4(name string, value mgl32.Mat4) {
loc, restore := s.uniformBlender(name)
defer restore()
gl.UniformMatrix4fv(loc, 1, false, &value[0])
}
func (s *Shader) SetUniformFloat(name string, value float32) {
loc, restore := s.uniformBlender(name)
defer restore()
gl.Uniform1f(loc, value)
}
func (s *Shader) SetUniformVec2f(name string, value itype.Vec2f) {
loc, restore := s.uniformBlender(name)
defer restore()
gl.Uniform2f(loc, value[0], value[1])
}
func (s *Shader) SetUniformVec3f(name string, value itype.Vec3f) {
loc, restore := s.uniformBlender(name)
defer restore()
gl.Uniform3f(loc, value[0], value[1], value[2])
}
func (s *Shader) SetUniformVec4f(name string, value itype.Vec4f) {
loc, restore := s.uniformBlender(name)
defer restore()
gl.Uniform4f(loc, value[0], value[1], value[2], value[3])
}
func (s *Shader) SetUniformInt(name string, value int32) {
loc, restore := s.uniformBlender(name)
defer restore()
gl.Uniform1i(loc, value)
}
func (s *Shader) SetUniformVec2i(name string, value itype.Vec2i) {
loc, restore := s.uniformBlender(name)
defer restore()
gl.Uniform2i(loc, int32(value[0]), int32(value[1]))
}
func (s *Shader) SetUniformVec3i(name string, value itype.Vec3i) {
loc, restore := s.uniformBlender(name)
defer restore()
gl.Uniform3i(loc, int32(value[0]), int32(value[1]), int32(value[2]))
}
func (s *Shader) SetUniformVec4i(name string, value itype.Vec4i) {
loc, restore := s.uniformBlender(name)
defer restore()
gl.Uniform4i(loc, int32(value[0]), int32(value[1]), int32(value[2]), int32(value[3]))
}
func (s *Shader) GetAttribLocation(name string) uint32 {
name = name + "\x00"
return uint32(gl.GetAttribLocation(s.prog, gl.Str(name)))
}

143
internal/render/texture.go Normal file
View File

@ -0,0 +1,143 @@
package render
import (
"image"
"github.com/go-gl/gl/all-core/gl"
)
func curTextureBinding() uint32 {
var id int32
gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &id)
return uint32(id)
}
// Texture holds handle to OpenGL Texture on the graphics card memory.
type Texture struct {
tex uint32
hasMipmap bool
}
// NewTexture creates a new, empty Texture.
func NewTexture() *Texture {
// Restore current texture binding
defer gl.BindTexture(gl.TEXTURE_2D, curTextureBinding())
var tex uint32
gl.GenTextures(1, &tex)
gl.BindTexture(gl.TEXTURE_2D, tex)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
return &Texture{tex: tex}
}
// NewTextureRGBA creates a new Texture with image.
func NewTextureRGBA(image *image.RGBA) *Texture {
// Restore current texture binding
defer gl.BindTexture(gl.TEXTURE_2D, curTextureBinding())
var tex uint32
gl.GenTextures(1, &tex)
gl.BindTexture(gl.TEXTURE_2D, tex)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
gl.TexImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
int32(image.Rect.Size().X),
int32(image.Rect.Size().Y),
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
gl.Ptr(image.Pix),
)
return &Texture{tex: tex}
}
// SetSmooth sets the min/mag filters to LINEAR(smooth) or NEAREST(not smooth)
// TODO: Not working
func (t *Texture) SetSmooth(smooth bool) {
defer gl.BindTexture(gl.TEXTURE_2D, curTextureBinding())
gl.BindTexture(gl.TEXTURE_2D, t.tex)
if smooth {
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
} else {
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
}
}
// UpdateRGBA updates the content of the texture with image.
// It deletes existing mipmap, you need to generate it again.
func (t *Texture) UpdateRGBA(image *image.RGBA) {
// Restore current texture binding
defer gl.BindTexture(gl.TEXTURE_2D, curTextureBinding())
gl.BindTexture(gl.TEXTURE_2D, t.tex)
gl.TexImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
int32(image.Rect.Size().X),
int32(image.Rect.Size().Y),
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
gl.Ptr(image.Pix),
)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
t.hasMipmap = false
}
// GenerateMipMap generates mipmap for the texture.
func (t *Texture) GenerateMipMap() {
// Restore current texture binding
defer gl.BindTexture(gl.TEXTURE_2D, curTextureBinding())
gl.BindTexture(gl.TEXTURE_2D, t.tex)
gl.GenerateMipmap(gl.TEXTURE_2D)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR)
t.hasMipmap = true
}
// InvalidateMipMap invalidates mipmap for the texture.
func (t *Texture) InvalidateMipMap() {
// Restore current texture binding
defer gl.BindTexture(gl.TEXTURE_2D, curTextureBinding())
gl.BindTexture(gl.TEXTURE_2D, t.tex)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
t.hasMipmap = false
}
// Handle returns the OpenGL handle of the texture.
func (t *Texture) Handle() uint32 {
return t.tex
}
// Free deletes the texture.
func (t *Texture) Free() {
if t.tex != 0 {
gl.DeleteTextures(1, &t.tex)
}
}

80
internal/render/view.go Normal file
View File

@ -0,0 +1,80 @@
package render
import (
"edgaru089.ml/go/gl01/internal/util/itype"
"github.com/go-gl/mathgl/mgl32"
)
// View represents a viewport in the 3-d world.
// It is essentialy a projection and a view matrix.
type View struct {
EyePos itype.Vec3f // position of the eye
rotY, rotZ, fovY itype.Angle
aspect float32
near, far float32
clipSet bool
}
// View returns the View matrix from parameters set with LookAt().
func (r *View) View() mgl32.Mat4 {
delta := mgl32.Rotate3DY(r.rotY.Radians()).Mul3(mgl32.Rotate3DZ(r.rotZ.Radians())).Mul3x1(mgl32.Vec3{1, 0, 0})
return mgl32.LookAt(
r.EyePos[0], r.EyePos[1], r.EyePos[2],
r.EyePos[0]+delta[0],
r.EyePos[1]+delta[1],
r.EyePos[2]+delta[2],
0, 1, 0,
)
}
// Perspective returns the Perspective matrix with parameters set with Aspect(), FovY() and Clip().
func (r *View) Perspective() mgl32.Mat4 {
if r.clipSet {
return mgl32.Perspective(r.fovY.Radians(), r.aspect, r.near, r.far)
} else {
return mgl32.Perspective(r.fovY.Radians(), r.aspect, 0.1, 200)
}
}
// LookAt resets the View matrix with a camera looking from Eye.
// The camera points to +RotY from X+ axis rotating around Y axis (right-handed);
// +/-RotZ form X+ axis rotating up/down around Z axis.
// ( RotZ should be in the (-90,90) degrees range )
//
// It also returns itself so that operations can be chained.
func (r *View) LookAt(eye itype.Vec3f, rotY, rotZ itype.Angle) *View {
r.EyePos = eye
r.rotY = rotY
r.rotZ = rotZ
return r
}
// Aspect sets the aspect (width / height) of the viewport.
//
// It also returns itself so that operations can be chained.
func (r *View) Aspect(aspectRatio float32) *View {
r.aspect = aspectRatio
return r
}
// FovY sets the Field of View (an angle) on the Y axis.
// FovX = FovY * aspect.
//
// With FovX, call SetFovY(FovX / aspect).
//
// It also returns itself so that operations can be chained.
func (r *View) FovY(fovy itype.Angle) *View {
r.fovY = fovy
return r
}
// Clip sets the near/far clipping distance of the Perspective.
// It defaults to 0.1 / 200.
//
// It also returns itself so that operations can be chained.
func (r *View) Clip(near, far float32) *View {
r.near, r.far = near, far
r.clipSet = true
return r
}