From 56e10049e9d7f25f06638fb57c2b79c59f73e357 Mon Sep 17 00:00:00 2001 From: bloeys Date: Sat, 5 Feb 2022 23:00:19 +0400 Subject: [PATCH] Textures + basic asset loading system+ uvs --- assets/assets.go | 26 +++++++++++ assets/textures.go | 84 ++++++++++++++++++++++++++++++++++++ main.go | 10 +++-- materials/material.go | 20 +++++++++ meshes/mesh.go | 80 +++++++++++++++++++++++++--------- res/shaders/simple.frag.glsl | 19 ++++---- res/shaders/simple.vert.glsl | 9 ++-- 7 files changed, 213 insertions(+), 35 deletions(-) create mode 100755 assets/assets.go create mode 100755 assets/textures.go diff --git a/assets/assets.go b/assets/assets.go new file mode 100755 index 0000000..f1adde5 --- /dev/null +++ b/assets/assets.go @@ -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 +} diff --git a/assets/textures.go b/assets/textures.go new file mode 100755 index 0000000..9ea2360 --- /dev/null +++ b/assets/textures.go @@ -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 +} diff --git a/main.go b/main.go index d837d38..6f61903 100755 --- a/main.go +++ b/main.go @@ -23,7 +23,6 @@ import ( //Flesh out the material system //Object //Abstract UI -//Textures //Audio //Low Priority: @@ -82,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 @@ -352,7 +356,7 @@ func draw() { cubeMesh.Buf.Bind() tempModelMat := modelMat.Clone() - rowSize := 10 + rowSize := 100 for y := 0; y < rowSize; y++ { for x := 0; x < rowSize; x++ { simpleMat.SetUnifMat4("modelMat", &tempModelMat.Translate(gglm.NewVec3(-1, 0, 0)).Mat4) diff --git a/materials/material.go b/materials/material.go index 224fbef..b5391f0 100755 --- a/materials/material.go +++ b/materials/material.go @@ -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) diff --git a/meshes/mesh.go b/meshes/mesh.go index 05f05be..3371b8f 100755 --- a/meshes/mesh.go +++ b/meshes/mesh.go @@ -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,41 +34,71 @@ func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh, sceneMesh := scene.Meshes[0] mesh.Buf = buffers.NewBuffer() - layoutToUse := []buffers.Element{{ElementType: buffers.DataTypeVec3}, {ElementType: buffers.DataTypeVec3}} - if len(sceneMesh.ColorSets) > 0 { + 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...) - var values []float32 - if len(sceneMesh.ColorSets) > 0 && len(sceneMesh.ColorSets[0]) > 0 { - values = interleave( - arrToInterleave{V3s: sceneMesh.Vertices}, - arrToInterleave{V3s: sceneMesh.Normals}, - arrToInterleave{V4s: sceneMesh.ColorSets[0]}, - ) - } else { - values = interleave( - arrToInterleave{V3s: sceneMesh.Vertices}, - arrToInterleave{V3s: sceneMesh.Normals}, - ) + //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 + 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 } +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 { + V2s []gglm.Vec2 V3s []gglm.Vec3 V4s []gglm.Vec4 } 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.V3s) > 0 { + 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[:] @@ -75,21 +108,26 @@ func (a *arrToInterleave) get(i int) []float32 { func interleave(arrs ...arrToInterleave) []float32 { 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 - 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) } else { elementCount = len(arrs[0].V4s) } + //Calculate final size of the float buffer totalSize := 0 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 } else { totalSize += len(arrs[i].V4s) * 4 diff --git a/res/shaders/simple.frag.glsl b/res/shaders/simple.frag.glsl index 561891a..57f9c9c 100755 --- a/res/shaders/simple.frag.glsl +++ b/res/shaders/simple.frag.glsl @@ -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); } diff --git a/res/shaders/simple.vert.glsl b/res/shaders/simple.vert.glsl index 3b98682..fa88385 100755 --- a/res/shaders/simple.vert.glsl +++ b/res/shaders/simple.vert.glsl @@ -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);