mirror of
https://github.com/bloeys/nmage.git
synced 2025-12-29 13:28:20 +00:00
Textures + basic asset loading system+ uvs
This commit is contained in:
26
assets/assets.go
Executable file
26
assets/assets.go
Executable 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
84
assets/textures.go
Executable 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
10
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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -7,11 +7,14 @@ 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
|
||||
TextureIDs []uint32
|
||||
Buf buffers.Buffer
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
Reference in New Issue
Block a user