-
Notifications
You must be signed in to change notification settings - Fork 18
/
material.go
218 lines (184 loc) · 9.81 KB
/
material.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
package tetra3d
import (
"fmt"
"github.com/hajimehoshi/ebiten/v2"
)
const (
TriangleSortModeBackToFront = iota // TriangleSortBackToFront sorts the triangles from back to front (naturally). This is the default.
TriangleSortModeFrontToBack // TriangleSortFrontToBack sorts the triangles in reverse order.
TriangleSortModeNone // TriangleSortNone doesn't sort the triangles at all; this is the fastest triangle sorting mode, while also being the most graphically inaccurate. Usable if triangles don't visually intersect.
)
const (
// TransparencyModeAuto means it will be opaque if the object or material's alpha >= 1, and transparent otherwise.
TransparencyModeAuto = iota
// TransparencyModeOpaque means the triangles are rendered to the color and depth buffer as normal.
TransparencyModeOpaque
// TransparencyModeAlphaClip means the triangles are rendered to the color and depth buffer, using the alpha of the triangles' texture to "cut out" the triangles.
TransparencyModeAlphaClip
// TransparencyModeTransparent means the triangles are not rendered to the depth buffer, but are rendered in a second pass after opaque and alpha-clip triangles. They are automatically sorted from back-to-front.
TransparencyModeTransparent
)
const (
BillboardModeNone = iota // No billboarding
BillboardModeFixedVertical // Billboard to face forward relative to the camera / screen under all circumstances; up is screen up / up relative to the camera, locally (local +Y)
BillboardModeHorizontal // Billboard to face towards the camera, but skews as you go above / below the object)
BillboardModeAll // Billboard on all axes
)
const (
LightingModeDefault = iota // Default lighting mode
LightingModeFixedNormals // Lighting applies as though faces always point towards light sources; good for 2D sprites
LightingModeDoubleSided // Lighting applies for double-sided faces
)
type Material struct {
library *Library // library is a reference to the Library that this Material came from.
Name string // Name is the name of the Material.
Color Color // The overall color of the Material.
Texture *ebiten.Image // The texture applied to the Material.
TexturePath string // The path to the texture, if it was not packed into the exporter.
TextureFilterMode ebiten.Filter // Texture filtering mode
textureWrapMode ebiten.Address // Texture wrapping mode; this is ignored currently, as all triangles render through shaders, where looping is enforced.
properties Properties // Properties allows you to specify auxiliary data on the Material. This is loaded from GLTF files or Blender's Custom Properties if the setting is enabled on the export menu.
BackfaceCulling bool // If backface culling is enabled (which it is by default), faces turned away from the camera aren't rendered.
TriangleSortMode int // TriangleSortMode influences how triangles with this Material are sorted.
Shadeless bool // If the material should be shadeless (unlit) or not
Fogless bool // If the material should be fogless or not
Blend ebiten.Blend // Blend mode to use when rendering the material (i.e. additive, multiplicative, etc)
BillboardMode int // Billboard mode
Visible bool // Whether the material is visible or not
// fragmentShader represents a shader used to render the material with. This shader is activated after rendering
// to the depth texture, but before compositing the finished render to the screen after fog.
fragmentShader *ebiten.Shader
// FragmentShaderOn is an easy boolean toggle to control whether the shader is activated or not (it defaults to on).
FragmentShaderOn bool
// FragmentShaderOptions allows you to customize the custom fragment shader with uniforms or images.
// By default, it's an empty DrawTrianglesShaderOptions struct.
// Note that the first image slot is reserved for the color texture associated with the Material.
// The second slot is reserved for a depth texture (primarily the intermediate texture used to "cut" a
// rendered model).
// If you want a custom fragment shader that already has fog and depth-testing, use Extend3DBaseShader() to
// extend your custom fragment shader from Tetra3D's base 3D shader.
FragmentShaderOptions *ebiten.DrawTrianglesShaderOptions
fragmentSrc []byte
// If a material is tagged as transparent, it's rendered in a separate render pass.
// Objects with transparent materials don't render to the depth texture and are sorted and rendered back-to-front, AFTER
// all non-transparent materials.
TransparencyMode int
CustomDepthOffsetOn bool // Whether custom depth offset is on or not.
CustomDepthOffsetValue float64 // How many world units to offset the depth of the material by.
LightingMode int // How materials are lit
// CustomDepthFunction is a customizeable function that takes the depth value of each vertex of a rendered MeshPart and
// transforms it, returning a different value.
// A good use for this would be to render sprites on billboarded planes with a higher depth, thereby fixing them
// "cutting" into geometry that's further back.
// The default value for CustomDepthFunction is nil.
CustomDepthFunction func(originalDepth float64) float64
}
// NewMaterial creates a new Material with the name given.
func NewMaterial(name string) *Material {
return &Material{
Name: name,
Color: NewColor(1, 1, 1, 1),
properties: NewProperties(),
TextureFilterMode: ebiten.FilterNearest,
textureWrapMode: ebiten.AddressRepeat,
BackfaceCulling: true,
TriangleSortMode: TriangleSortModeBackToFront,
TransparencyMode: TransparencyModeAuto,
FragmentShaderOptions: &ebiten.DrawTrianglesShaderOptions{},
FragmentShaderOn: true,
Blend: ebiten.BlendSourceOver,
Visible: true,
}
}
// Clone creates a clone of the specified Material. Note that Clone() cannot clone the Material's fragment shader or shader options.
func (m *Material) Clone() *Material {
newMat := NewMaterial(m.Name)
newMat.library = m.library
newMat.Color = m.Color
newMat.Texture = m.Texture
newMat.TexturePath = m.TexturePath
newMat.TextureFilterMode = m.TextureFilterMode
newMat.textureWrapMode = m.textureWrapMode
newMat.properties = m.properties.Clone()
newMat.BackfaceCulling = m.BackfaceCulling
newMat.TriangleSortMode = m.TriangleSortMode
newMat.Shadeless = m.Shadeless
newMat.Fogless = m.Fogless
newMat.Blend = m.Blend
newMat.BillboardMode = m.BillboardMode
newMat.Visible = m.Visible
newMat.SetShaderText(m.fragmentSrc)
newMat.FragmentShaderOn = m.FragmentShaderOn
newMat.FragmentShaderOptions.CompositeMode = m.FragmentShaderOptions.CompositeMode
newMat.FragmentShaderOptions.FillRule = m.FragmentShaderOptions.FillRule
for i := range m.FragmentShaderOptions.Images {
newMat.FragmentShaderOptions.Images[i] = m.FragmentShaderOptions.Images[i]
}
for k, v := range newMat.FragmentShaderOptions.Uniforms {
newMat.FragmentShaderOptions.Uniforms[k] = v
}
newMat.TransparencyMode = m.TransparencyMode
return newMat
}
// SetShaderText creates a new custom Kage fragment shader for the Material if provided the shader's source code as a []byte.
// This custom shader would be used to render the mesh utilizing the material after rendering to the depth texture, but before
// compositing the finished render to the screen after fog. If the shader is nil, the Material will render using the default Tetra3D
// render setup (e.g. texture, UV values, vertex colors, and vertex lighting).
// SetShader will return the Shader, and an error if the Shader failed to compile.
// Note that custom shaders require usage of pixel-unit Kage shaders.
func (m *Material) SetShaderText(src []byte) (*ebiten.Shader, error) {
if m.fragmentShader != nil {
m.fragmentShader.Dispose()
m.fragmentShader = nil
}
if src == nil {
m.fragmentShader = nil
m.fragmentSrc = nil
return nil, nil
}
newShader, err := ebiten.NewShader(src)
if err != nil {
return nil, err
}
m.fragmentShader = newShader
m.fragmentSrc = src
return m.fragmentShader, nil
}
// SetShader sets an already-compiled custom Kage shader to the Material.
// By default, a custom shader will render on top of everything and with no fog.
// Lighting will also be missing, but that's included in the Model's vertex color.
// If you want to extend the base 3D shader, use tetra3d.ExtendBase3DShader().
// Note that custom shaders require usage of pixel-unit Kage shaders.
func (m *Material) SetShader(shader *ebiten.Shader) {
if m.fragmentShader != shader {
m.fragmentShader = shader
m.fragmentSrc = nil
}
}
// Shader returns the custom Kage fragment shader for the Material.
func (m *Material) Shader() *ebiten.Shader {
return m.fragmentShader
}
// DisposeShader disposes the custom fragment Shader for the Material (assuming it has one). If it does not have a Shader, nothing happens.
func (m *Material) DisposeShader() {
if m.fragmentShader != nil {
m.fragmentShader.Dispose()
}
m.fragmentSrc = nil
m.fragmentShader = nil
}
// Library returns the Library from which this Material was loaded. If it was created through code, this function will return nil.
func (m *Material) Library() *Library {
return m.library
}
func (m *Material) String() string {
if ReadableReferences {
return "<" + m.Name + ">"
} else {
return fmt.Sprintf("%p", m)
}
}
// Properties returns this Material's game Properties struct.
func (m *Material) Properties() Properties {
return m.properties
}