]> git.openfl.eu Git - gummi.git/commitdiff
shader pupils and blink animation
authorFl_GUI <flor.guilini@hotmail.com>
Sun, 29 Jun 2025 11:44:59 +0000 (13:44 +0200)
committerFl_GUI <flor.guilini@hotmail.com>
Sun, 29 Jun 2025 11:44:59 +0000 (13:44 +0200)
Containerfile
meson.build
print/gummi shell.FCStd
src/animator.cc [new file with mode: 0644]
src/animator.h [new file with mode: 0644]
src/eye.cc
src/eye.h
src/eye_frames.h [new file with mode: 0644]
src/main.cc

index 8d9719dde79126bc5f994e5eee4bdaaa89235cfd..dd1935a47951391bb3197130037061f0f603338f 100644 (file)
@@ -7,8 +7,8 @@ RUN apt update -q -y && \
     dpkg --add-architecture arm64
 
 # install arm build dependencies
-RUN apt update -q -y
-RUN apt install -f -y --allow-unauthenticated \
+RUN apt update -q -y && \
+    apt install -f -y --allow-unauthenticated \
     gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu g++-aarch64-linux-gnu
 
 # build project dependencies
index 438ac2e5066e2556af4baf5e2064124354480d7a..6f6d99901d1a1b527e851b713e871462da2701cf 100644 (file)
@@ -5,7 +5,7 @@ src = 'src'
 subdir(src / 'glad')
 
 deps = [dependency('glfw3'), glad_dep]
-srcs = [src/'main.cc', src/'eye.cc', src/'log.cc', src/'mouth.cc']
+srcs = [src/'main.cc', src/'eye.cc', src/'log.cc', src/'mouth.cc', src/'animator.cc']
 
 executable('gummi',
   sources: srcs,
index 547febb7aa8fa2d8bf6c8ec45bac6b4a3d71ca04..0335f8387d2969f480c8524fa487d135681c218d 100644 (file)
Binary files a/print/gummi shell.FCStd and b/print/gummi shell.FCStd differ
diff --git a/src/animator.cc b/src/animator.cc
new file mode 100644 (file)
index 0000000..ddd2d3e
--- /dev/null
@@ -0,0 +1,46 @@
+#include "animator.h"
+#include "eye.h"
+
+#include <random>
+
+struct {
+  double blink;
+} animation;
+
+std::random_device rd;
+
+const double blinkDuration = 0.2;
+
+void blink(double dt) {
+  if (animation.blink < -1) {
+    animation.blink = blinkDuration;
+  } else if (animation.blink <= 0.0) {
+    auto roll = rd();
+    if (roll > rd.max() * 0.8)
+      animation.blink -= dt;
+  } else {
+    animation.blink -= dt;
+  }
+
+  if (animation.blink <= 0.0) {
+    eyes[0].blinkFrame = open;
+    eyes[1].blinkFrame = open;
+  } else {
+    const double frameTimings[BLINKFRAME_MAX] {blinkDuration, blinkDuration*0.6, blinkDuration*0.3, 0};
+    for (int i = open; i < BLINKFRAME_MAX; i++) {
+      auto frame = blinkDuration - animation.blink;
+      if (frameTimings[i+1] <= frame && frame <= frameTimings[i]) {
+        eyes[0].blinkFrame = (blinkFrame_t)(i+1);
+        eyes[1].blinkFrame = (blinkFrame_t)(i+1);
+      }
+    }
+  }
+}
+
+void updateAnimations(double dt) {
+  blink(dt);
+}
+
+void initAnimator() {
+  animation.blink = 0.0;
+}
diff --git a/src/animator.h b/src/animator.h
new file mode 100644 (file)
index 0000000..f5bf8f7
--- /dev/null
@@ -0,0 +1,2 @@
+void initAnimator();
+void updateAnimations(double dt);
index 368f88d8f896e4ab5d29838ca761aca32c89644b..77979b646475e49f340c37d0a41160360bdb745a 100644 (file)
@@ -1,4 +1,5 @@
 #include "eye.h"
+#include "eye_frames.h"
 
 #include <cstdio>
 #include <cstddef>
@@ -7,11 +8,11 @@
 
 const char* eye_vertex_shader = 
 "#version 120\n"
-"attribute vec3 aPos;\n"
-"uniform int eyeflipped;\n"
-"void main() {\n"
+"attribute vec3 aPos;"
+"uniform int eyeflipped;"
+"void main() {"
 "  // scale pixel position to +-1;\n"
-"  gl_Position = vec4(eyeflipped * (aPos.x/400 -1), aPos.y/240 -1, aPos.z, 1.0);\n"
+"  gl_Position = vec4(eyeflipped * (aPos.x/400 -1), aPos.y/240 -1, aPos.z, 1.0);"
 "};";
 
 const char* eye_fragment_shader = 
@@ -22,45 +23,25 @@ const char* eye_fragment_shader =
 
 const char* eye_pupil_fragment_shader =
 "#version 120\n"
+"uniform ivec2 position;"
 "void main() {"
-"  gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);"
+"  float radius = 10.0;"
+"  vec2 pos = gl_FragCoord.xy - position;"
+"  float yUp = pos.y + 0;"
+"  float yDown = pos.y - 0;"
+"  float topTerm = sqrt( yUp * yUp/5 + pos.x * pos.x) - 8;"
+"  float equation = topTerm;"
+"  float width = 0.5;"
+"  if (equation - width < 0 && equation + width > 0) {"
+"    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);"
+"  } else {"
+"    discard;"
+"  }"
 "};";
 
-float eyeline[] {
-  150, 130, 0,
-  150, 190, 0,
-
-  150, 220, 0,
-  150, 280, 0,
-
-  150, 310, 0,
-  150, 370, 0,
-
-  160, 380, 0,
-  240, 380, 0,
-
-  260, 370, 0,
-  310, 310, 0,
-
-  320, 280, 0,
-  320, 220, 0,
-
-  320, 190, 0,
-  320, 130, 0,
-
-  330, 110, 0,
-  250, 110, 0,
-
-  230, 110, 0,
-  140, 110, 0,
-};
-
-float eyePos[] {
-  270, 280, 0,
-};
-
 GLuint eyeShaderProgram;
 GLuint eyeFlipLocation;
+GLuint eyePupilLocation;
 GLuint pupilShaderProgram;
 
 eye_t eyes[2];
@@ -87,7 +68,7 @@ void setEyeShaderProgram() {
   }
 
   auto pupilFragShader = glCreateShader(GL_FRAGMENT_SHADER);
-  glShaderSource(pupilFragShader, 1, &eye_fragment_shader, NULL);
+  glShaderSource(pupilFragShader, 1, &eye_pupil_fragment_shader, NULL);
   glCompileShader(pupilFragShader);
   glGetShaderiv(pupilFragShader, GL_COMPILE_STATUS, &status);
   if (status == GL_FALSE) {
@@ -123,6 +104,12 @@ void setEyeShaderProgram() {
   if (err) {
     printf("uhoh uniform location error: %d\n", err);
   }
+
+  eyePupilLocation = glGetUniformLocation(pupilShaderProgram, "position");
+  err = glGetError();
+  if (err) {
+    printf("uhoh uniform location error: %d\n", err);
+  }
 }
 
 void initEye(eye_t *eye) {
@@ -138,10 +125,16 @@ void initEye(eye_t *eye) {
 void initEyes() {
   auto eye = &eyes[0];
   eye->left = 1;
+  eye->blinkFrame = open;
+  eye->pupilLoc[0] = 250;
+  eye->pupilLoc[1] = 250;
   initEye(eye);
 
   eye = &eyes[1];
   eye->left = -1;
+  eye->blinkFrame = open;
+  eye->pupilLoc[0] = 550;
+  eye->pupilLoc[1] = 250;
   initEye(eye);
 
   setEyeShaderProgram();
@@ -151,14 +144,21 @@ void drawEye(eye_t *eye) {
   glUniform1i(eyeFlipLocation, eye->left);
   glBindBuffer(GL_ARRAY_BUFFER, eye->eyeVBO);
   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
-  glDrawArrays(GL_LINES, 0, 18);
+  int nPoints = eyeLineCount[eye->blinkFrame]*2;
+  int startIndex = eye->blinkFrame * eyeMaxPoints;
+  glDrawArrays(GL_LINES, startIndex, nPoints);
 }
 
 void drawPupil(eye_t *eye) {
+  if (eye->blinkFrame >= blink_30) {
+    return;
+  }
+  // Eyes are not symmetric, don't flip
   glUniform1i(eyeFlipLocation, eye->left);
+  glUniform2i(eyePupilLocation, eye->pupilLoc[0], eye->pupilLoc[1]);
   glBindBuffer(GL_ARRAY_BUFFER, eye->pupilVBO);
   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
-  glDrawArrays(GL_POINTS, 0, 1);
+  glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 }
 
 void drawEyes() {
index fbe4888033bb94013eed9624bfb8cecd92b0ff95..b9599a6b39a254116b5016b6820e0d9b19523278 100644 (file)
--- a/src/eye.h
+++ b/src/eye.h
@@ -2,9 +2,20 @@
 
 #include <glad/gl.h>
 
+typedef enum blinkFrame {
+  open,
+  blink_60,
+  blink_30,
+  closed,
+  BLINKFRAME_MAX,
+} blinkFrame_t;
+
 typedef struct eye {
   // eye state
   GLint left;
+  blinkFrame_t blinkFrame;
+  // pupil position
+  GLint pupilLoc[2];
   // index to buffer object
   GLuint eyeVBO;
   GLuint pupilVBO;
diff --git a/src/eye_frames.h b/src/eye_frames.h
new file mode 100644 (file)
index 0000000..7568771
--- /dev/null
@@ -0,0 +1,156 @@
+
+// Number of lines that make up a blink frame
+const int eyeLineCount[BLINKFRAME_MAX] {9,8,6,2};
+
+const int eyeMaxPoints = 18*2;
+
+// The points that make up a blink frame.
+float eyeline[BLINKFRAME_MAX][eyeMaxPoints*3]
+{
+  // blinkFrame::open
+  {
+  150, 130, 0,
+  150, 190, 0,
+
+  150, 220, 0,
+  150, 280, 0,
+
+  150, 310, 0,
+  150, 370, 0,
+
+  160, 380, 0,
+  240, 380, 0,
+
+  260, 370, 0,
+  310, 310, 0,
+
+  320, 280, 0,
+  320, 220, 0,
+
+  320, 190, 0,
+  320, 130, 0,
+
+  330, 110, 0,
+  250, 110, 0,
+
+  230, 110, 0,
+  140, 110, 0,
+  150, 130, 0,
+  150, 190, 0,
+
+  150, 220, 0,
+  150, 280, 0,
+
+  150, 310, 0,
+  150, 370, 0,
+
+  160, 380, 0,
+  240, 380, 0,
+
+  260, 370, 0,
+  310, 310, 0,
+
+  320, 280, 0,
+  320, 220, 0,
+
+  320, 190, 0,
+  320, 130, 0,
+
+  330, 110, 0,
+  250, 110, 0,
+
+  230, 110, 0,
+  140, 110, 0,
+  },
+  // blinkFrame::blink_60
+  {
+  150, 130, 0,
+  150, 190, 0,
+
+  150, 220, 0,
+  150, 280, 0,
+
+  160, 290, 0,
+  240, 290, 0,
+
+  260, 290, 0,
+  310, 290, 0,
+
+  320, 280, 0,
+  320, 220, 0,
+
+  320, 190, 0,
+  320, 130, 0,
+
+  330, 110, 0,
+  250, 110, 0,
+
+  230, 110, 0,
+  140, 110, 0,
+  },
+  // blinkFrame::blink_30
+  {
+  150, 130, 0,
+  150, 190, 0,
+
+  160, 200, 0,
+  240, 200, 0,
+
+  260, 200, 0,
+  310, 200, 0,
+
+  320, 190, 0,
+  320, 130, 0,
+
+  330, 110, 0,
+  250, 110, 0,
+
+  230, 110, 0,
+  140, 110, 0,
+  150, 130, 0,
+  150, 190, 0,
+
+  150, 220, 0,
+  150, 280, 0,
+
+  150, 310, 0,
+  150, 370, 0,
+
+  160, 380, 0,
+  240, 380, 0,
+
+  260, 370, 0,
+  310, 310, 0,
+
+  320, 280, 0,
+  320, 220, 0,
+
+  320, 190, 0,
+  320, 130, 0,
+
+  330, 110, 0,
+  250, 110, 0,
+
+  230, 110, 0,
+  140, 110, 0,
+  },
+  // blinkFrame::closed
+  {
+  330, 110, 0,
+  250, 110, 0,
+
+  230, 110, 0,
+  140, 110, 0,
+  }
+};
+
+/*{
+};*/
+
+// Cover half the screen, since the shader will draw the detail anyway
+float eyePos[] {
+  0, 0, 0,
+  400, 0, 0,
+  400, 480, 0,
+  0, 480, 0,
+};
index 630e68d58ee91642a3846509ccf75725c38263c0..a6654312b7a41e62d18258e1647cc4f78fe7399f 100644 (file)
@@ -6,6 +6,7 @@
 #include "log.h"
 #include "eye.h"
 #include "mouth.h"
+#include "animator.h"
 
 float triangles[] = {
   -0.5,0.5,0,
@@ -121,6 +122,7 @@ int main() {
 
   initEyes();
   initMouth();
+  initAnimator();
 
   GLuint VBOS[2];
   glGenBuffers(2, (GLuint*) &VBOS);
@@ -133,15 +135,29 @@ int main() {
 
   glEnableVertexAttribArray(0);
 
+  double targetFps = 24.0;
+  double targetRate = 1 / targetFps;
+  double currentTime, lastUpdateTime =0.0,  lastRenderTime = 0.0;
+
   while(!glfwWindowShouldClose(window)) {
-    glClearColor(.0f, .0f, .0f, 1.0f);
-    glClear(GL_COLOR_BUFFER_BIT);
+    currentTime = glfwGetTime();
+    glfwPollEvents();
 
-    drawEyes();
-    drawMouth();
+    // update
+    updateAnimations(currentTime - lastUpdateTime);
+    lastUpdateTime = currentTime;
+
+    if (currentTime - lastRenderTime > targetRate) {
+      lastRenderTime = currentTime;
+
+      // render
+      glfwSwapBuffers(window);
+      glClearColor(.0f, .0f, .0f, 1.0f);
+      glClear(GL_COLOR_BUFFER_BIT);
+      drawEyes();
+      drawMouth();
+    }
 
-    glfwSwapBuffers(window);
-    glfwPollEvents();
   }
   glfwTerminate();
 }