Textures + basic asset loading system+ uvs

This commit is contained in:
bloeys
2022-02-05 23:00:19 +04:00
parent 2bfba880a9
commit 56e10049e9
7 changed files with 213 additions and 35 deletions

26
assets/assets.go Executable file
View File

@ -0,0 +1,26 @@
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
}
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
}

10
main.go
View File

@ -23,7 +23,6 @@ import (
//Flesh out the material system //Flesh out the material system
//Object //Object
//Abstract UI //Abstract UI
//Textures
//Audio //Audio
//Low Priority: //Low Priority:
@ -82,11 +81,16 @@ func main() {
imguiMat = materials.NewMaterial("ImGUI Mat", "./res/shaders/imgui") imguiMat = materials.NewMaterial("ImGUI Mat", "./res/shaders/imgui")
//Load meshes //Load meshes
cubeMesh, err = meshes.NewMesh("Cube", "./res/models/color-cube.fbx", asig.PostProcess(0)) cubeMesh, err = meshes.NewMesh("Cube", "./res/models/tex-cube.fbx", asig.PostProcess(0))
if err != nil { if err != nil {
logging.ErrLog.Fatalln("Failed to load cube mesh. Err: ", err) logging.ErrLog.Fatalln("Failed to load cube mesh. Err: ", err)
} }
//Set mesh textures on material
for _, v := range cubeMesh.TextureIDs {
simpleMat.AddTextureID(v)
}
initImGUI() initImGUI()
//Enable vertex attributes //Enable vertex attributes
@ -352,7 +356,7 @@ func draw() {
cubeMesh.Buf.Bind() cubeMesh.Buf.Bind()
tempModelMat := modelMat.Clone() tempModelMat := modelMat.Clone()
rowSize := 10 rowSize := 100
for y := 0; y < rowSize; y++ { for y := 0; y < rowSize; y++ {
for x := 0; x < rowSize; x++ { for x := 0; x < rowSize; x++ {
simpleMat.SetUnifMat4("modelMat", &tempModelMat.Translate(gglm.NewVec3(-1, 0, 0)).Mat4) simpleMat.SetUnifMat4("modelMat", &tempModelMat.Translate(gglm.NewVec3(-1, 0, 0)).Mat4)

View File

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

View File

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

View File

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

View File

@ -2,10 +2,12 @@
layout(location=0) in vec3 vertPosIn; layout(location=0) in vec3 vertPosIn;
layout(location=1) in vec3 vertNormalIn; layout(location=1) in vec3 vertNormalIn;
layout(location=2) in vec3 vertColorIn; layout(location=2) in vec2 vertUV0In;
layout(location=3) in vec3 vertColorIn;
out vec3 vertColor;
out vec3 vertNormal; out vec3 vertNormal;
out vec2 vertUV0;
out vec3 vertColor;
out vec3 fragPos; out vec3 fragPos;
//MVP = Model View Projection //MVP = Model View Projection
@ -15,8 +17,9 @@ uniform mat4 projMat;
void main() void main()
{ {
vertColor = vertColorIn;
vertNormal = mat3(transpose(inverse(modelMat))) * vertNormalIn; vertNormal = mat3(transpose(inverse(modelMat))) * vertNormalIn;
vertUV0 = vertUV0In;
vertColor = vertColorIn;
fragPos = vec3(modelMat * vec4(vertPosIn, 1.0)); fragPos = vec3(modelMat * vec4(vertPosIn, 1.0));
gl_Position = projMat * viewMat * modelMat * vec4(vertPosIn, 1.0); gl_Position = projMat * viewMat * modelMat * vec4(vertPosIn, 1.0);