Initial commit
This commit is contained in:
10
internal/render/gpu_preference/gpu_preference.c
Normal file
10
internal/render/gpu_preference/gpu_preference.c
Normal 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
|
3
internal/render/gpu_preference/gpu_preference.go
Normal file
3
internal/render/gpu_preference/gpu_preference.go
Normal file
@ -0,0 +1,3 @@
|
||||
package gpu_preference
|
||||
|
||||
import "C"
|
15
internal/render/gpu_preference/linux_nvidia.go
Normal file
15
internal/render/gpu_preference/linux_nvidia.go
Normal 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")
|
||||
}
|
||||
}
|
127
internal/render/render_framewire.go
Normal file
127
internal/render/render_framewire.go
Normal 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]
|
||||
}
|
130
internal/render/render_world.go
Normal file
130
internal/render/render_world.go
Normal 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)
|
||||
}
|
119
internal/render/render_world.go.old
Executable file
119
internal/render/render_world.go.old
Executable 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
257
internal/render/shader.go
Normal 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
143
internal/render/texture.go
Normal 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
80
internal/render/view.go
Normal 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
|
||||
}
|
Reference in New Issue
Block a user