From: Fl_GUI Date: Sun, 29 Jun 2025 11:44:59 +0000 (+0200) Subject: shader pupils and blink animation X-Git-Url: https://git.openfl.eu/?a=commitdiff_plain;h=23d49c12d9975875a2c64ca53b8c4f0ff7cd5bb0;p=gummi.git shader pupils and blink animation --- diff --git a/Containerfile b/Containerfile index 8d9719d..dd1935a 100644 --- a/Containerfile +++ b/Containerfile @@ -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 diff --git a/meson.build b/meson.build index 438ac2e..6f6d999 100644 --- a/meson.build +++ b/meson.build @@ -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, diff --git a/print/gummi shell.FCStd b/print/gummi shell.FCStd index 547febb..0335f83 100644 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 index 0000000..ddd2d3e --- /dev/null +++ b/src/animator.cc @@ -0,0 +1,46 @@ +#include "animator.h" +#include "eye.h" + +#include + +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 index 0000000..f5bf8f7 --- /dev/null +++ b/src/animator.h @@ -0,0 +1,2 @@ +void initAnimator(); +void updateAnimations(double dt); diff --git a/src/eye.cc b/src/eye.cc index 368f88d..77979b6 100644 --- a/src/eye.cc +++ b/src/eye.cc @@ -1,4 +1,5 @@ #include "eye.h" +#include "eye_frames.h" #include #include @@ -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() { diff --git a/src/eye.h b/src/eye.h index fbe4888..b9599a6 100644 --- a/src/eye.h +++ b/src/eye.h @@ -2,9 +2,20 @@ #include +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 index 0000000..7568771 --- /dev/null +++ b/src/eye_frames.h @@ -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, +}; diff --git a/src/main.cc b/src/main.cc index 630e68d..a665431 100644 --- a/src/main.cc +++ b/src/main.cc @@ -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(); }