Skip to content

Commit 2c5f1b2

Browse files
authored
Merge pull request #537 from TyGrze/rlgl-vertex-draw-bindings
Add rlgl vertex draw bindings
2 parents eddf369 + 4d0e5c5 commit 2c5f1b2

6 files changed

Lines changed: 802 additions & 2 deletions

File tree

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
// This example demonstrates instanced drawing with low-level rlgl bindings:
2+
// - LoadVertexBufferElements: upload index data (EBO) to GPU
3+
// - DrawVertexArrayElementsInstanced: instanced indexed draw
4+
// - SetVertexAttributeDivisor: per-instance attribute advancement
5+
// - Camera3D with BeginMode3D/EndMode3D for orbital 3D camera control
6+
//
7+
// 625 quads are rendered in a 25x25 grid with animated Y-offsets (sine wave).
8+
package main
9+
10+
import (
11+
"math"
12+
13+
rl "github.com/gen2brain/raylib-go/raylib"
14+
)
15+
16+
const (
17+
screenWidth = 1024
18+
screenHeight = 768
19+
20+
cols = 25
21+
rows = 25
22+
instanceCount = cols * rows
23+
spacing = 1.5
24+
)
25+
26+
// GLSL 330 vertex shader with instancing support.
27+
const vertexShaderCode = `#version 330
28+
layout(location = 0) in vec3 vertexPosition;
29+
layout(location = 1) in vec2 vertexTexCoord;
30+
layout(location = 2) in vec3 instanceOffset;
31+
layout(location = 3) in vec4 instanceColor;
32+
33+
uniform mat4 mvp;
34+
35+
out vec4 fragColor;
36+
37+
void main() {
38+
gl_Position = mvp * vec4(vertexPosition + instanceOffset, 1.0);
39+
fragColor = instanceColor;
40+
}
41+
`
42+
43+
// GLSL 330 fragment shader.
44+
const fragmentShaderCode = `#version 330
45+
in vec4 fragColor;
46+
out vec4 finalColor;
47+
48+
void main() {
49+
finalColor = fragColor;
50+
}
51+
`
52+
53+
func main() {
54+
rl.SetConfigFlags(rl.FlagWindowResizable | rl.FlagMsaa4xHint)
55+
rl.InitWindow(screenWidth, screenHeight, "rlgl instanced quads example")
56+
defer rl.CloseWindow()
57+
rl.SetTargetFPS(60)
58+
59+
// -----------------------------------------------------------------
60+
// Quad geometry (XZ plane, unit-sized)
61+
// -----------------------------------------------------------------
62+
type QuadVertex struct {
63+
Pos rl.Vector3
64+
Tex rl.Vector2
65+
}
66+
// Texcords are implicitly zero, because we use the default white texture.
67+
quadVertices := []QuadVertex{
68+
{Pos: rl.NewVector3(-0.5, 0, -0.5)}, // 0 top-left
69+
{Pos: rl.NewVector3(0.5, 0, -0.5)}, // 1 top-right
70+
{Pos: rl.NewVector3(-0.5, 0, 0.5)}, // 2 bottom-left
71+
{Pos: rl.NewVector3(0.5, 0, 0.5)}, // 3 bottom-right
72+
}
73+
quadIndices := []uint16{
74+
0, 2, 1, // first triangle (CCW from above)
75+
1, 2, 3, // second triangle
76+
}
77+
78+
// -----------------------------------------------------------------
79+
// Instance data: offsets and colors for 625 instances
80+
// -----------------------------------------------------------------
81+
instanceOffsets := make([]rl.Vector3, instanceCount)
82+
instanceColors := make([]rl.Vector4, instanceCount)
83+
84+
// Center the grid at the origin
85+
originX := -float32(cols-1) * spacing / 2.0
86+
originZ := -float32(rows-1) * spacing / 2.0
87+
88+
for row := range rows {
89+
for col := range cols {
90+
i := row*cols + col
91+
x := originX + float32(col)*spacing
92+
z := originZ + float32(row)*spacing
93+
instanceOffsets[i] = rl.NewVector3(x, 0, z)
94+
95+
// HSV gradient: hue from column, saturation from row
96+
hue := float32(col) / float32(cols)
97+
sat := 0.5 + 0.5*float32(row)/float32(rows-1)
98+
r, g, b := hsvToRGB(hue, sat, 1.0)
99+
instanceColors[i] = rl.NewVector4(r, g, b, 1.0)
100+
}
101+
}
102+
103+
// Working buffer for animated offsets
104+
animOffsets := make([]rl.Vector3, instanceCount)
105+
106+
// -----------------------------------------------------------------
107+
// VAO + VBO + EBO setup
108+
// DONT FORGOT TO CLEANUP YOUR GPU RESOURCES
109+
// -----------------------------------------------------------------
110+
vao := rl.LoadVertexArray()
111+
rl.EnableVertexArray(vao)
112+
113+
// Quad vertex VBO (static)
114+
quadVBO := rl.LoadVertexBuffer(quadVertices, false)
115+
defer rl.UnloadVertexBuffer(quadVBO)
116+
rl.SetVertexAttributes(quadVertices, []rl.VertexAttributesConfig{
117+
{Field: "Pos", Attribute: 0},
118+
{Field: "Tex", Attribute: 1},
119+
})
120+
121+
// Instance offset VBO (dynamic, updated each frame)
122+
offsetVBO := rl.LoadVertexBuffer(animOffsets, true)
123+
defer rl.UnloadVertexBuffer(offsetVBO)
124+
rl.SetVertexAttribute(2, 3, rl.Float, false, 0, 0)
125+
rl.EnableVertexAttribute(2)
126+
rl.SetVertexAttributeDivisor(2, 1)
127+
128+
// Instance color VBO (static)
129+
colorVBO := rl.LoadVertexBuffer(instanceColors, false)
130+
defer rl.UnloadVertexBuffer(colorVBO)
131+
rl.SetVertexAttribute(3, 4, rl.Float, false, 0, 0)
132+
rl.EnableVertexAttribute(3)
133+
rl.SetVertexAttributeDivisor(3, 1)
134+
135+
// Index buffer (EBO)
136+
ebo := rl.LoadVertexBufferElements(quadIndices, false)
137+
defer rl.UnloadVertexBuffer(ebo)
138+
139+
rl.DisableVertexArray()
140+
141+
// -----------------------------------------------------------------
142+
// Custom shader
143+
// -----------------------------------------------------------------
144+
shaderID := rl.LoadShaderCode(vertexShaderCode, fragmentShaderCode)
145+
defer rl.UnloadShaderProgram(shaderID)
146+
mvpLoc := rl.GetLocationUniform(shaderID, "mvp")
147+
148+
// -----------------------------------------------------------------
149+
// Camera
150+
// -----------------------------------------------------------------
151+
camera := rl.Camera3D{
152+
Position: rl.NewVector3(30, 25, 30),
153+
Target: rl.NewVector3(0, 0, 0),
154+
Up: rl.NewVector3(0, 1, 0),
155+
Fovy: 45.0,
156+
Projection: rl.CameraPerspective,
157+
}
158+
159+
// -----------------------------------------------------------------
160+
// Render loop
161+
// -----------------------------------------------------------------
162+
for !rl.WindowShouldClose() {
163+
rl.UpdateCamera(&camera, rl.CameraOrbital)
164+
165+
// Animate instance Y-offsets with sine wave
166+
t := float64(rl.GetTime())
167+
for i := range instanceCount {
168+
base := instanceOffsets[i]
169+
// Phase based on XZ distance from origin for radial wave
170+
phase := float64(base.X+base.Z) * 0.3
171+
y := float32(math.Sin(t*2.0+phase)) * 2.0
172+
animOffsets[i] = rl.NewVector3(base.X, y, base.Z)
173+
}
174+
rl.UpdateVertexBuffer(offsetVBO, animOffsets, 0)
175+
176+
rl.BeginDrawing()
177+
rl.ClearBackground(rl.RayWhite)
178+
179+
rl.BeginMode3D(camera)
180+
181+
// Reference grid
182+
rl.DrawGrid(40, 1.0)
183+
184+
// Build MVP from current camera matrices
185+
mvp := rl.MatrixMultiply(rl.GetMatrixModelview(), rl.GetMatrixProjection())
186+
187+
// Flush raylib's internal batch before custom GL draws
188+
rl.DrawRenderBatchActive()
189+
190+
// Custom instanced draw
191+
rl.EnableShader(shaderID)
192+
rl.SetUniformMatrix(mvpLoc, mvp)
193+
194+
rl.EnableVertexArray(vao)
195+
rl.DrawVertexArrayElementsInstanced(0, 6, nil, int32(instanceCount))
196+
rl.DisableVertexArray()
197+
198+
rl.DisableShader()
199+
200+
// Restore state for raylib's internal renderer
201+
rl.DrawRenderBatchActive()
202+
203+
rl.EndMode3D()
204+
205+
// Text overlays
206+
rl.DrawText("rlgl Instanced Quads (625 instances)", 10, 10, 20, rl.DarkGray)
207+
rl.DrawText("Scroll to zoom", 10, 35, 16, rl.Gray)
208+
rl.DrawFPS(int32(rl.GetScreenWidth())-100, 10)
209+
210+
rl.EndDrawing()
211+
}
212+
}
213+
214+
// hsvToRGB converts HSV (all 0-1 range) to RGB (0-1 range).
215+
func hsvToRGB(h, s, v float32) (r, g, b float32) {
216+
if s == 0 {
217+
return v, v, v
218+
}
219+
h *= 6.0
220+
if h >= 6.0 {
221+
h = 0
222+
}
223+
i := int(h)
224+
f := h - float32(i)
225+
p := v * (1.0 - s)
226+
q := v * (1.0 - s*f)
227+
t := v * (1.0 - s*(1.0-f))
228+
229+
switch i {
230+
case 0:
231+
r, g, b = v, t, p
232+
case 1:
233+
r, g, b = q, v, p
234+
case 2:
235+
r, g, b = p, v, t
236+
case 3:
237+
r, g, b = p, q, v
238+
case 4:
239+
r, g, b = t, p, v
240+
default:
241+
r, g, b = v, p, q
242+
}
243+
return
244+
}

0 commit comments

Comments
 (0)