summaryrefslogtreecommitdiff
path: root/themes/src/spring/grass_renderer.cpp
diff options
context:
space:
mode:
authorMatt Kosarek <matt.kosarek@canonical.com>2026-02-19 16:58:58 -0500
committerMatt Kosarek <matt.kosarek@canonical.com>2026-02-19 16:58:58 -0500
commitda0eedbf1733e40613215ecd117e1a4e049089ad (patch)
treed83d5dc63b50efbd45084d692ae037cbe0f02b25 /themes/src/spring/grass_renderer.cpp
parent4d1beea73810af4641d074f974ad9c196a7e8d6e (diff)
Removed photo gallery + added cute little grass rendering for the rabbit and a nice gradient backgroundHEADmaster
Diffstat (limited to 'themes/src/spring/grass_renderer.cpp')
-rw-r--r--themes/src/spring/grass_renderer.cpp145
1 files changed, 127 insertions, 18 deletions
diff --git a/themes/src/spring/grass_renderer.cpp b/themes/src/spring/grass_renderer.cpp
index 685f733..e4a210c 100644
--- a/themes/src/spring/grass_renderer.cpp
+++ b/themes/src/spring/grass_renderer.cpp
@@ -1,29 +1,138 @@
#include "grass_renderer.hpp"
#include "../renderer_3d.h"
+#include "../shader.h"
+#include "../shaders/grass_frag.h"
+#include "../shaders/grass_vert.h"
+#include "mathlib.h"
+#include <cmath>
+#include <cstddef>
-void GrassRenderer::load(GrassRendererLoadData params, Renderer3d* renderer) {
- const f32 COLUMN_INCREMENT = GRASS_BLADES_PER_COL / params.area.x;
- const f32 ROW_INCREMENT = GRASS_BLADES_PER_ROW / params.area.y;
- for (i32 r = 0; r < GRASS_BLADES_PER_ROW; r++) {
- i32 indexOffset = r * GRASS_BLADES_PER_ROW;
- f32 y = ROW_INCREMENT * r;
- for (i32 c = 0; c < GRASS_BLADES_PER_COL; c++) {
- f32 x = COLUMN_INCREMENT * c;
- i32 index = indexOffset + c;
- grassBlades[index].position = Vector3(x, y, 0);
- grassBlades[index].top_offset = Vector2(0, 0);
- }
- }
-}
+void GrassRenderer::load(GrassRendererLoadData params, Renderer3d *renderer) {
+ bladeHeight = params.grassHeight;
+
+ // Place blades randomly within a circle. Using r = R*sqrt(u) with a
+ // uniform u in [0,1] gives uniform areal density (no center clustering).
+ const f32 radius = fminf(params.area.x, params.area.y) * 0.5f;
+ for (i32 i = 0; i < NUM_GRASS_BLADES; i++) {
+ f32 r = radius * sqrtf(randomFloatBetween(0.f, 1.f));
+ f32 theta = randomFloatBetween(0.f, 2.f * PI);
+ f32 x = params.origin.x + r * cosf(theta);
+ f32 z = params.origin.y + r * sinf(theta);
+ grassBlades[i].position = Vector3(x, 0, z);
+ grassBlades[i].top_offset =
+ Vector2(randomFloatBetween(0.f, 2.f * PI), // sway phase
+ randomFloatBetween(0.5f, 1.5f) // height scale
+ );
+ }
+
+ // Compile grass shader
+ shader = loadShader(shader_grass_vert, shader_grass_frag);
+ useShader(shader);
+
+ // Attribute locations
+ attributes.position = getShaderAttribute(shader, "position");
+ attributes.instancePos = getShaderAttribute(shader, "instancePos");
+ attributes.instancePhase = getShaderAttribute(shader, "instancePhase");
+ attributes.instanceHeight = getShaderAttribute(shader, "instanceHeight");
+
+ // Uniform locations
+ uniforms.projection = getShaderUniform(shader, "projection");
+ uniforms.view = getShaderUniform(shader, "view");
+ uniforms.time = getShaderUniform(shader, "time");
+ uniforms.bladeWidth = getShaderUniform(shader, "bladeWidth");
+ uniforms.bladeHeight = getShaderUniform(shader, "bladeHeight");
+ uniforms.swayAmount = getShaderUniform(shader, "swayAmount");
+
+ // Base quad: two triangles forming a unit quad
+ // x in [-0.5, 0.5], y in [0, 1]
+ Vector2 quadVertices[] = {Vector2(-0.5f, 0.0f), Vector2(0.5f, 0.0f),
+ Vector2(0.5f, 1.0f), Vector2(-0.5f, 0.0f),
+ Vector2(0.5f, 1.0f), Vector2(-0.5f, 1.0f)};
+
+ glGenVertexArrays(1, &vao);
+ glBindVertexArray(vao);
+
+ // Static quad VBO
+ glGenBuffers(1, &quadVbo);
+ glBindBuffer(GL_ARRAY_BUFFER, quadVbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices,
+ GL_STATIC_DRAW);
+ glEnableVertexAttribArray(attributes.position);
+ glVertexAttribPointer(attributes.position, 2, GL_FLOAT, GL_FALSE,
+ sizeof(Vector2), (GLvoid *)0);
-void GrassRenderer::update(f32 seconds) {
-
+ // Dynamic instance VBO
+ glGenBuffers(1, &instanceVbo);
+ glBindBuffer(GL_ARRAY_BUFFER, instanceVbo);
+ glBufferData(GL_ARRAY_BUFFER, NUM_GRASS_BLADES * sizeof(GrassInstanceData),
+ NULL, GL_DYNAMIC_DRAW);
+
+ // instancePos: vec3 (x, y, z) at offset 0
+ glEnableVertexAttribArray(attributes.instancePos);
+ glVertexAttribPointer(attributes.instancePos, 3, GL_FLOAT, GL_FALSE,
+ sizeof(GrassInstanceData),
+ (GLvoid *)offsetof(GrassInstanceData, x));
+ glVertexAttribDivisor(attributes.instancePos, 1);
+
+ // instancePhase: float at offset 12 (after 3 floats)
+ glEnableVertexAttribArray(attributes.instancePhase);
+ glVertexAttribPointer(attributes.instancePhase, 1, GL_FLOAT, GL_FALSE,
+ sizeof(GrassInstanceData),
+ (GLvoid *)offsetof(GrassInstanceData, phaseOffset));
+ glVertexAttribDivisor(attributes.instancePhase, 1);
+
+ // instanceHeight: float at offset 16 (after phaseOffset)
+ glEnableVertexAttribArray(attributes.instanceHeight);
+ glVertexAttribPointer(attributes.instanceHeight, 1, GL_FLOAT, GL_FALSE,
+ sizeof(GrassInstanceData),
+ (GLvoid *)offsetof(GrassInstanceData, heightScale));
+ glVertexAttribDivisor(attributes.instanceHeight, 1);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
}
-void GrassRenderer::render(Renderer3d* renderer) {
+void GrassRenderer::update(f32 dtSeconds) { time += dtSeconds; }
+void GrassRenderer::render(Renderer3d *renderer) {
+ useShader(shader);
+ setShaderMat4(uniforms.projection, renderer->projection);
+ setShaderMat4(uniforms.view, renderer->view);
+ setShaderFloat(uniforms.time, time);
+ setShaderFloat(uniforms.bladeWidth, bladeWidth);
+ setShaderFloat(uniforms.bladeHeight, bladeHeight);
+ setShaderFloat(uniforms.swayAmount, swayAmount);
+
+ // Build and upload instance data
+ GrassInstanceData instanceData[NUM_GRASS_BLADES];
+ for (i32 i = 0; i < NUM_GRASS_BLADES; i++) {
+ instanceData[i].x = grassBlades[i].position.x;
+ instanceData[i].y = grassBlades[i].position.y;
+ instanceData[i].z = grassBlades[i].position.z;
+ instanceData[i].phaseOffset = grassBlades[i].top_offset.x;
+ instanceData[i].heightScale = grassBlades[i].top_offset.y;
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, instanceVbo);
+ glBufferSubData(GL_ARRAY_BUFFER, 0,
+ NUM_GRASS_BLADES * sizeof(GrassInstanceData), instanceData);
+
+ glBindVertexArray(vao);
+ glDrawArraysInstanced(GL_TRIANGLES, 0, 6, NUM_GRASS_BLADES);
+ glBindVertexArray(0);
}
void GrassRenderer::unload() {
-
+ if (vao)
+ glDeleteVertexArrays(1, &vao);
+ if (quadVbo)
+ glDeleteBuffers(1, &quadVbo);
+ if (instanceVbo)
+ glDeleteBuffers(1, &instanceVbo);
+ if (shader)
+ glDeleteProgram(shader);
+ vao = 0;
+ quadVbo = 0;
+ instanceVbo = 0;
+ shader = 0;
}