Compare commits

...

6 Commits

Author SHA1 Message Date
8e96cf7050 Reduce objects 2022-02-05 23:13:23 +04:00
56e10049e9 Textures + basic asset loading system+ uvs 2022-02-05 23:00:19 +04:00
2bfba880a9 Textured model 2022-02-05 19:27:14 +04:00
ffc9b6aa7c Fix crash 2022-02-05 19:24:55 +04:00
f49c6bc9bb Simplify interleave code 2022-01-27 10:34:51 +04:00
c989505aa7 Remove done todos 2022-01-27 09:01:16 +04:00
9 changed files with 247 additions and 60 deletions

View File

@ -6,10 +6,7 @@ import (
)
func T(check bool, msg string) {
if !consts.Debug || check {
return
if consts.Debug && !check {
logging.ErrLog.Panicln("Assert failed:", msg)
}
logging.ErrLog.Panicln("Assert failed:", msg)
}

27
assets/assets.go Executable file
View File

@ -0,0 +1,27 @@
package assets
var (
Textures map[uint32]Texture = make(map[uint32]Texture)
TexturePaths map[string]uint32 = make(map[string]uint32)
)
func SetTexture(t Texture) {
if _, ok := TexturePaths[t.Path]; ok {
return
}
println("Loaded texture:", t.Path)
Textures[t.TexID] = t
TexturePaths[t.Path] = t.TexID
}
func GetTexture(texID uint32) (Texture, bool) {
tex, ok := Textures[texID]
return tex, ok
}
func GetTexturePath(path string) (Texture, bool) {
tex, ok := Textures[TexturePaths[path]]
return tex, ok
}

84
assets/textures.go Executable file
View File

@ -0,0 +1,84 @@
package assets
import (
"bytes"
"image/color"
"image/png"
"os"
"unsafe"
"github.com/go-gl/gl/v4.1-core/gl"
)
type ColorFormat int
const (
ColorFormat_RGBA8 ColorFormat = iota
)
type Texture struct {
Path string
TexID uint32
Width int32
Height int32
Pixels []byte
}
func LoadPNG(file string) (Texture, error) {
if tex, ok := GetTexturePath(file); ok {
return tex, nil
}
//Load from disk
fileBytes, err := os.ReadFile(file)
if err != nil {
return Texture{}, err
}
img, err := png.Decode(bytes.NewReader(fileBytes))
if err != nil {
return Texture{}, err
}
tex := Texture{
Path: file,
Width: int32(img.Bounds().Dx()),
Height: int32(img.Bounds().Dy()),
Pixels: make([]byte, img.Bounds().Dx()*img.Bounds().Dy()*4),
}
//NOTE: We only support 8-bit channels (32-bit colors) for now
i := 0
for y := 0; y < img.Bounds().Dy(); y++ {
for x := 0; x < img.Bounds().Dx(); x++ {
c := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA)
tex.Pixels[i] = c.R
tex.Pixels[i+1] = c.G
tex.Pixels[i+2] = c.B
tex.Pixels[i+3] = c.A
i += 4
}
}
//Prepare opengl stuff
gl.GenTextures(1, &tex.TexID)
gl.BindTexture(gl.TEXTURE_2D, tex.TexID)
// set the texture wrapping/filtering options (on the currently bound texture object)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
// load and generate the texture
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, tex.Width, tex.Height, 0, gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(&tex.Pixels[0]))
gl.GenerateMipmap(gl.TEXTURE_2D)
SetTexture(tex)
return tex, nil
}

15
main.go
View File

@ -23,13 +23,10 @@ import (
//Flesh out the material system
//Object
//Abstract UI
//Textures
//Audio
//Low Priority:
// Proper Asset loading
// Rework buffers package
// Interleaved or packed buffers (xyzxyzxyz OR xxxyyyzzz)
type ImguiInfo struct {
imCtx *imgui.Context
@ -84,11 +81,16 @@ func main() {
imguiMat = materials.NewMaterial("ImGUI Mat", "./res/shaders/imgui")
//Load meshes
cubeMesh, err = meshes.NewMesh("Cube", "./res/models/color-cube.fbx", asig.PostProcess(0))
cubeMesh, err = meshes.NewMesh("Cube", "./res/models/tex-cube.fbx", asig.PostProcess(0))
if err != nil {
logging.ErrLog.Fatalln("Failed to load cube mesh. Err: ", err)
}
//Set mesh textures on material
for _, v := range cubeMesh.TextureIDs {
simpleMat.AddTextureID(v)
}
initImGUI()
//Enable vertex attributes
@ -354,7 +356,7 @@ func draw() {
cubeMesh.Buf.Bind()
tempModelMat := modelMat.Clone()
rowSize := 100
rowSize := 10
for y := 0; y < rowSize; y++ {
for x := 0; x < rowSize; x++ {
simpleMat.SetUnifMat4("modelMat", &tempModelMat.Translate(gglm.NewVec3(-1, 0, 0)).Mat4)
@ -362,8 +364,9 @@ func draw() {
}
simpleMat.SetUnifMat4("modelMat", &tempModelMat.Translate(gglm.NewVec3(float32(rowSize), -1, 0)).Mat4)
}
simpleMat.SetUnifMat4("modelMat", &modelMat.Mat4)
drawUI()
window.SDLWin.GLSwap()
}

View File

@ -11,14 +11,25 @@ import (
type Material struct {
Name string
ShaderProg shaders.ShaderProgram
TexIDs []uint32
}
func (m *Material) Bind() {
gl.UseProgram(m.ShaderProg.ID)
for i, v := range m.TexIDs {
gl.ActiveTexture(gl.TEXTURE0 + uint32(i))
gl.BindTexture(gl.TEXTURE_2D, v)
}
}
func (m *Material) UnBind() {
gl.UseProgram(0)
for i := range m.TexIDs {
gl.ActiveTexture(gl.TEXTURE0 + uint32(i))
gl.BindTexture(gl.TEXTURE_2D, 0)
}
}
func (m *Material) GetAttribLoc(attribName string) int32 {
@ -42,6 +53,10 @@ func (m *Material) SetAttribute(bufObj buffers.Buffer) {
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
}
func (m *Material) AddTextureID(texID uint32) {
m.TexIDs = append(m.TexIDs, texID)
}
func (m *Material) EnableAttribute(attribName string) {
gl.EnableVertexAttribArray(uint32(m.GetAttribLoc(attribName)))
}
@ -50,6 +65,11 @@ func (m *Material) DisableAttribute(attribName string) {
gl.DisableVertexAttribArray(uint32(m.GetAttribLoc(attribName)))
}
func (m *Material) SetUnifInt32(uniformName string, val int32) {
loc := gl.GetUniformLocation(m.ShaderProg.ID, gl.Str(uniformName+"\x00"))
gl.ProgramUniform1i(m.ShaderProg.ID, loc, val)
}
func (m *Material) SetUnifFloat32(uniformName string, val float32) {
loc := gl.GetUniformLocation(m.ShaderProg.ID, gl.Str(uniformName+"\x00"))
gl.ProgramUniform1f(m.ShaderProg.ID, loc, val)

View File

@ -7,12 +7,15 @@ import (
"github.com/bloeys/assimp-go/asig"
"github.com/bloeys/gglm/gglm"
"github.com/bloeys/nmage/asserts"
"github.com/bloeys/nmage/assets"
"github.com/bloeys/nmage/buffers"
"github.com/bloeys/nmage/logging"
)
type Mesh struct {
Name string
Buf buffers.Buffer
Name string
TextureIDs []uint32
Buf buffers.Buffer
}
func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh, error) {
@ -31,63 +34,110 @@ func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh,
sceneMesh := scene.Meshes[0]
mesh.Buf = buffers.NewBuffer()
dataSize := len(sceneMesh.Vertices)*3 + len(sceneMesh.Normals)*3
layoutToUse := []buffers.Element{{ElementType: buffers.DataTypeVec3}, {ElementType: buffers.DataTypeVec3}}
if len(sceneMesh.ColorSets) > 0 {
layoutToUse = append(layoutToUse, buffers.Element{ElementType: buffers.DataTypeVec4})
dataSize += len(sceneMesh.ColorSets) * 4
}
asserts.T(len(sceneMesh.TexCoords[0]) > 0, "Mesh has no UV0")
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})
}
mesh.Buf.SetLayout(layoutToUse...)
positions := flattenVec3(sceneMesh.Vertices)
normals := flattenVec3(sceneMesh.Normals)
colors := []float32{}
if len(sceneMesh.ColorSets) > 0 {
colors = flattenVec4(sceneMesh.ColorSets[0])
//Load diffuse textures
mat := scene.Materials[sceneMesh.MaterialIndex]
if asig.GetMaterialTextureCount(mat, asig.TextureTypeDiffuse) > 0 {
texInfo, err := asig.GetMaterialTexture(mat, asig.TextureTypeDiffuse, 0)
if err != nil {
logging.ErrLog.Fatalf("Failed to get material texture of index 0. Err: %e\n", err)
}
tex, err := assets.LoadPNG(texInfo.Path)
if err != nil {
logging.ErrLog.Fatalf("Loading PNG with path '%s' failed. Err: %e\n", texInfo.Path, err)
}
mesh.TextureIDs = append(mesh.TextureIDs, tex.TexID)
}
var values []float32
if len(colors) > 0 {
values = interleave(
arrInfo{values: positions, valsPerComp: 3},
arrInfo{values: normals, valsPerComp: 3},
arrInfo{values: colors, valsPerComp: 4},
)
} else {
values = interleave(
arrInfo{values: positions, valsPerComp: 3},
arrInfo{values: normals, valsPerComp: 3},
)
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]})
}
values = interleave(arrs...)
mesh.Buf.SetData(values)
mesh.Buf.SetIndexBufData(flattenFaces(sceneMesh.Faces))
return mesh, nil
}
type arrInfo struct {
values []float32
valsPerComp int
func v3sToV2s(v3s []gglm.Vec3) []gglm.Vec2 {
v2s := make([]gglm.Vec2, len(v3s))
for i := 0; i < len(v3s); i++ {
v2s[i] = gglm.Vec2{
Data: [2]float32{v3s[i].X(), v3s[i].Y()},
}
}
return v2s
}
func interleave(arrs ...arrInfo) []float32 {
type arrToInterleave struct {
V2s []gglm.Vec2
V3s []gglm.Vec3
V4s []gglm.Vec4
}
if len(arrs) == 0 || len(arrs[0].values) == 0 {
panic("No input to interleave or arrays are empty")
func (a *arrToInterleave) get(i int) []float32 {
asserts.T(len(a.V2s) == 0 || len(a.V3s) == 0, "One array should be set in arrToInterleave, but both arrays are set")
asserts.T(len(a.V2s) == 0 || len(a.V4s) == 0, "One array should be set in arrToInterleave, but both arrays are set")
asserts.T(len(a.V3s) == 0 || len(a.V4s) == 0, "One array should be set in arrToInterleave, but both arrays are set")
if len(a.V2s) > 0 {
return a.V2s[i].Data[:]
} else if len(a.V3s) > 0 {
return a.V3s[i].Data[:]
} else {
return a.V4s[i].Data[:]
}
}
func interleave(arrs ...arrToInterleave) []float32 {
asserts.T(len(arrs) > 0, "No input sent to interleave")
asserts.T(len(arrs[0].V2s) > 0 || len(arrs[0].V3s) > 0 || len(arrs[0].V4s) > 0, "Interleave arrays are empty")
elementCount := 0
if len(arrs[0].V2s) > 0 {
elementCount = len(arrs[0].V2s)
} else if len(arrs[0].V3s) > 0 {
elementCount = len(arrs[0].V3s)
} else {
elementCount = len(arrs[0].V4s)
}
size := 0
//Calculate final size of the float buffer
totalSize := 0
for i := 0; i < len(arrs); i++ {
size += len(arrs[i].values)
asserts.T(len(arrs[i].V2s) == elementCount || len(arrs[i].V3s) == elementCount || len(arrs[i].V4s) == elementCount, "Mesh vertex data given to interleave is not the same length")
if len(arrs[i].V2s) > 0 {
totalSize += len(arrs[i].V2s) * 2
} else if len(arrs[i].V3s) > 0 {
totalSize += len(arrs[i].V3s) * 3
} else {
totalSize += len(arrs[i].V4s) * 4
}
}
out := make([]float32, 0, size)
for posInArr := 0; posInArr < len(arrs[0].values)/arrs[0].valsPerComp; posInArr++ {
out := make([]float32, 0, totalSize)
for i := 0; i < elementCount; i++ {
for arrToUse := 0; arrToUse < len(arrs); arrToUse++ {
for compToAdd := 0; compToAdd < arrs[arrToUse].valsPerComp; compToAdd++ {
out = append(out, arrs[arrToUse].values[posInArr*arrs[arrToUse].valsPerComp+compToAdd])
}
out = append(out, arrs[arrToUse].get(i)...)
}
}

BIN
res/models/tex-cube.fbx Executable file

Binary file not shown.

View File

@ -1,23 +1,26 @@
#version 410
in vec3 vertColor;
in vec3 vertNormal;
in vec3 fragPos;
out vec4 fragColor;
uniform float ambientStrength = 0.1;
uniform vec3 ambientLightColor = vec3(1, 1, 1);
uniform vec3 lightPos1;
uniform vec3 lightColor1;
uniform sampler2D diffTex;
in vec3 vertColor;
in vec3 vertNormal;
in vec2 vertUV0;
in vec3 fragPos;
out vec4 fragColor;
void main()
{
// vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos1 - fragPos);
float diffStrength = max(0.0, dot(normalize(vertNormal), lightDir));
vec3 finalAmbientColor = ambientLightColor * ambientStrength;
fragColor = vec4(vertColor * (finalAmbientColor + diffStrength*lightColor1), 1.0);
vec4 texColor = texture(diffTex, vertUV0);
fragColor = vec4(texColor.rgb * vertColor * (finalAmbientColor + diffStrength*lightColor1) , texColor.a);
}

View File

@ -2,10 +2,12 @@
layout(location=0) in vec3 vertPosIn;
layout(location=1) in vec3 vertNormalIn;
layout(location=2) in vec3 vertColorIn;
layout(location=2) in vec2 vertUV0In;
layout(location=3) in vec3 vertColorIn;
out vec3 vertColor;
out vec3 vertNormal;
out vec2 vertUV0;
out vec3 vertColor;
out vec3 fragPos;
//MVP = Model View Projection
@ -15,8 +17,9 @@ uniform mat4 projMat;
void main()
{
vertColor = vertColorIn;
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);