From 7da1da5fff0e539fefd5772b891977f283639d65 Mon Sep 17 00:00:00 2001
From: Joe Groff <arcata@gmail.com>
Date: Sat, 2 Feb 2008 15:33:05 -0800
Subject: [PATCH] Modularize the new bunny demo, and adjust the bikeshed
 parameters a bit

---
 extra/bunny/cel-shaded/cel-shaded.factor      |  95 +++++++
 .../fixed-pipeline/fixed-pipeline.factor      |  25 ++
 extra/bunny/model/model.factor                | 114 +++++++++
 extra/bunny/outlined/outlined.factor          | 235 ++++++++++++++++++
 4 files changed, 469 insertions(+)
 create mode 100644 extra/bunny/cel-shaded/cel-shaded.factor
 create mode 100644 extra/bunny/fixed-pipeline/fixed-pipeline.factor
 create mode 100644 extra/bunny/model/model.factor
 create mode 100644 extra/bunny/outlined/outlined.factor

diff --git a/extra/bunny/cel-shaded/cel-shaded.factor b/extra/bunny/cel-shaded/cel-shaded.factor
new file mode 100644
index 0000000000..eb0924f50e
--- /dev/null
+++ b/extra/bunny/cel-shaded/cel-shaded.factor
@@ -0,0 +1,95 @@
+USING: arrays bunny.model combinators.lib continuations
+kernel multiline opengl opengl.gl sequences ;
+IN: bunny.cel-shaded
+
+STRING: vertex-shader-source
+varying vec3 position, normal, viewer;
+
+void
+main()
+{
+    gl_Position = ftransform();
+
+    position = gl_Vertex.xyz;
+    normal = gl_Normal;
+    viewer = vec3(0, 0, 1) * gl_NormalMatrix;
+}
+
+;
+
+STRING: cel-shaded-fragment-shader-lib-source
+varying vec3 position, normal, viewer;
+uniform vec3 light_direction;
+uniform vec4 color;
+uniform vec4 ambient, diffuse;
+uniform float shininess;
+
+float
+modulate(vec3 direction, vec3 normal)
+{
+    return dot(direction, normal) * 0.5 + 0.5;
+}
+
+float
+cel(float m)
+{
+    return smoothstep(0.25, 0.255, m) * 0.4 + smoothstep(0.695, 0.70, m) * 0.5;
+}
+
+vec4
+cel_light()
+{
+    vec3 direction = normalize(light_direction - position);
+    vec3 reflection = reflect(direction, normal);
+    vec4 ad = (ambient + diffuse * vec4(vec3(cel(modulate(direction, normal))), 1));
+    float s = cel(pow(max(dot(-reflection, viewer), 0.0), shininess));
+    return ad * color + vec4(vec3(s), 0);
+}
+
+;
+
+STRING: cel-shaded-fragment-shader-main-source
+vec4 cel_light();
+
+void
+main()
+{
+    gl_FragColor = cel_light();
+}
+
+;
+
+TUPLE: bunny-cel-shaded program ;
+
+: cel-shading-supported? ( -- ? )
+    "2.0" { "GL_ARB_shader_objects" }
+    has-gl-version-or-extensions? ;
+
+: <bunny-cel-shaded> ( gadget -- draw )
+    drop
+    cel-shading-supported? [
+        vertex-shader-source <vertex-shader> check-gl-shader
+        cel-shaded-fragment-shader-lib-source <fragment-shader> check-gl-shader
+        cel-shaded-fragment-shader-main-source <fragment-shader> check-gl-shader
+        3array <gl-program> check-gl-program
+        { set-bunny-cel-shaded-program } bunny-cel-shaded construct
+    ] [ f ] if ;
+
+: (draw-cel-shaded-bunny) ( geom program -- )
+    dup [
+        {
+            [ "light_direction" glGetUniformLocation 1.0 -1.0 1.0 glUniform3f ]
+            [ "color" glGetUniformLocation 0.6 0.5 0.5 1.0 glUniform4f ]
+            [ "ambient" glGetUniformLocation 0.2 0.2 0.2 0.2 glUniform4f ]
+            [ "diffuse" glGetUniformLocation 0.8 0.8 0.8 0.8 glUniform4f ]
+            [ "shininess" glGetUniformLocation 100.0 glUniform1f ]
+        } call-with
+        bunny-geom
+    ] with-gl-program ;
+
+M: bunny-cel-shaded draw-bunny
+    bunny-cel-shaded-program (draw-cel-shaded-bunny) ;
+
+M: bunny-cel-shaded dispose
+    bunny-cel-shaded-program delete-gl-program ;
+
diff --git a/extra/bunny/fixed-pipeline/fixed-pipeline.factor b/extra/bunny/fixed-pipeline/fixed-pipeline.factor
new file mode 100644
index 0000000000..f3fb68e515
--- /dev/null
+++ b/extra/bunny/fixed-pipeline/fixed-pipeline.factor
@@ -0,0 +1,25 @@
+USING: alien.c-types continuations kernel
+opengl opengl.gl bunny.model ;
+IN: bunny.fixed-pipeline
+
+TUPLE: bunny-fixed-pipeline ;
+
+: <bunny-fixed-pipeline> ( gadget -- draw )
+    drop
+    { } bunny-fixed-pipeline construct ;
+
+M: bunny-fixed-pipeline draw-bunny
+    drop
+    GL_LIGHTING glEnable
+    GL_LIGHT0 glEnable
+    GL_COLOR_MATERIAL glEnable
+    GL_LIGHT0 GL_POSITION { 1.0 -1.0 1.0 1.0 } >c-float-array glLightfv
+    GL_FRONT_AND_BACK GL_SHININESS 100.0 glMaterialf
+    GL_FRONT_AND_BACK GL_SPECULAR glColorMaterial
+    GL_FRONT_AND_BACK GL_AMBIENT_AND_DIFFUSE glColorMaterial
+    0.6 0.5 0.5 1.0 glColor4f
+    bunny-geom ;
+
+M: bunny-fixed-pipeline dispose
+    drop ;
+
diff --git a/extra/bunny/model/model.factor b/extra/bunny/model/model.factor
new file mode 100644
index 0000000000..a19adcb782
--- /dev/null
+++ b/extra/bunny/model/model.factor
@@ -0,0 +1,114 @@
+USING: alien alien.c-types arrays sequences math
+math.vectors math.matrices math.parser io io.files kernel opengl
+opengl.gl opengl.glu shuffle http.client vectors splitting
+tools.time system combinators combinators.lib combinators.cleave
+float-arrays continuations namespaces ;
+IN: bunny.model
+
+: numbers ( str -- seq )
+    " " split [ string>number ] map [ ] subset ;
+
+: (parse-model) ( vs is -- vs is )
+    readln [
+        numbers {
+            { [ dup length 5 = ] [ 3 head pick push ] }
+            { [ dup first 3 = ] [ 1 tail over push ] }
+            { [ t ] [ drop ] }
+        } cond (parse-model)
+    ] when* ;
+
+: parse-model ( stream -- vs is )
+    [
+        100000 <vector> 100000 <vector> (parse-model)
+    ] with-stream
+    [
+        over length # " vertices, " %
+        dup length # " triangles" %
+    ] "" make print ;
+
+: n ( vs triple -- n )
+    swap [ nth ] curry map
+    dup third over first v- >r dup second swap first v- r> cross
+    vneg normalize ;
+
+: normal ( ns vs triple -- )
+    [ n ] keep [ rot [ v+ ] change-nth ] each-with2 ;
+
+: normals ( vs is -- ns )
+    over length { 0.0 0.0 0.0 } <array> -rot
+    [ >r 2dup r> normal ] each drop
+    [ normalize ] map ;
+
+: read-model ( stream -- model )
+    "Reading model" print flush [
+        <file-reader> parse-model [ normals ] 2keep 3array
+    ] time ;
+
+: model-path "bun_zipper.ply" ;
+
+: model-url "http://factorcode.org/bun_zipper.ply" ;
+
+: maybe-download ( -- path )
+    model-path resource-path dup exists? [
+        "Downloading bunny from " write
+        model-url dup print flush
+        over download-to
+    ] unless ;
+
+: (draw-triangle) ( ns vs triple -- )
+    [ dup roll nth gl-normal swap nth gl-vertex ] each-with2 ;
+
+: draw-triangles ( ns vs is -- )
+    GL_TRIANGLES [ [ (draw-triangle) ] each-with2 ] do-state ;
+
+TUPLE: bunny-dlist list ;
+TUPLE: bunny-buffers array element-array nv ni ;
+
+: <bunny-dlist> ( model -- geom )
+    GL_COMPILE [ first3 draw-triangles ] make-dlist
+    bunny-dlist construct-boa ;
+
+: <bunny-buffers> ( model -- geom )
+    [
+        [ first concat ] [ second concat ] bi
+        append >float-array
+        GL_ARRAY_BUFFER swap GL_STATIC_DRAW <gl-buffer>
+    ] [
+        third concat >c-uint-array
+        GL_ELEMENT_ARRAY_BUFFER swap GL_STATIC_DRAW <gl-buffer>
+    ]
+    [ first length 3 * ] [ third length 3 * ] tetra
+    bunny-buffers construct-boa ;
+
+GENERIC: bunny-geom ( geom -- )
+GENERIC: draw-bunny ( geom draw -- )
+
+M: bunny-dlist bunny-geom
+    bunny-dlist-list glCallList ;
+
+M: bunny-buffers bunny-geom
+    dup {
+        bunny-buffers-array
+        bunny-buffers-element-array
+    } get-slots [
+        GL_VERTEX_ARRAY GL_NORMAL_ARRAY 2array [
+            GL_DOUBLE 0 0 buffer-offset glNormalPointer
+            dup bunny-buffers-nv "double" heap-size * buffer-offset
+            3 GL_DOUBLE 0 roll glVertexPointer
+            bunny-buffers-ni
+            GL_TRIANGLES swap GL_UNSIGNED_INT 0 buffer-offset glDrawElements
+        ] all-enabled-client-state
+    ] with-array-element-buffers ;
+
+M: bunny-dlist dispose
+    bunny-dlist-list delete-dlist ;
+
+M: bunny-buffers dispose
+    { bunny-buffers-array bunny-buffers-element-array } get-slots
+    delete-gl-buffer delete-gl-buffer ;
+
+: <bunny-geom> ( model -- geom )
+    "1.5" { "GL_ARB_vertex_buffer_object" }
+    has-gl-version-or-extensions?
+    [ <bunny-buffers> ] [ <bunny-dlist> ] if ;
+
diff --git a/extra/bunny/outlined/outlined.factor b/extra/bunny/outlined/outlined.factor
new file mode 100644
index 0000000000..021ac6b4d8
--- /dev/null
+++ b/extra/bunny/outlined/outlined.factor
@@ -0,0 +1,235 @@
+USING: arrays bunny.model bunny.cel-shaded
+combinators.lib continuations kernel math multiline
+opengl opengl.gl sequences ui.gadgets ;
+IN: bunny.outlined
+
+STRING: outlined-pass1-fragment-shader-main-source
+varying vec3 normal;
+vec4 cel_light();
+
+void
+main()
+{
+    gl_FragData[0] = cel_light();
+    gl_FragData[1] = vec4(normal, 1);
+}
+
+;
+
+STRING: outlined-pass2-vertex-shader-source
+varying vec2 coord;
+
+void
+main()
+{
+    gl_Position = ftransform();
+    coord = (gl_Vertex * vec4(0.5) + vec4(0.5)).xy;
+}
+
+;
+
+STRING: outlined-pass2-fragment-shader-source
+uniform sampler2D colormap, normalmap, depthmap;
+uniform vec4 line_color;
+varying vec2 coord;
+
+const float DEPTH_RATIO_THRESHOLD = 1.001, SAMPLE_SPREAD = 1.0/512.0;
+
+float
+depth_sample(vec2 c)
+{
+    return texture2D(depthmap, c).x;
+}
+bool
+are_depths_border(vec3 depths)
+{
+    return any(lessThan(depths, vec3(1.0/DEPTH_RATIO_THRESHOLD)))
+        || any(greaterThan(depths, vec3(DEPTH_RATIO_THRESHOLD)));
+}
+
+vec3
+normal_sample(vec2 c)
+{
+    return texture2D(normalmap, c).xyz;
+}
+
+float
+min6(float a, float b, float c, float d, float e, float f)
+{
+    return min(min(min(min(min(a, b), c), d), e), f);
+}
+
+float
+border_factor(vec2 c)
+{
+    vec2 coord1 = c + vec2(-SAMPLE_SPREAD, -SAMPLE_SPREAD),
+         coord2 = c + vec2( SAMPLE_SPREAD, -SAMPLE_SPREAD),
+         coord3 = c + vec2(-SAMPLE_SPREAD,  SAMPLE_SPREAD),
+         coord4 = c + vec2( SAMPLE_SPREAD,  SAMPLE_SPREAD);
+    
+    vec3 normal1 = normal_sample(coord1),
+         normal2 = normal_sample(coord2),
+         normal3 = normal_sample(coord3),
+         normal4 = normal_sample(coord4);
+
+    if (dot(normal1, normal1) < 0.5
+        && dot(normal2, normal2) < 0.5
+        && dot(normal3, normal3) < 0.5
+        && dot(normal4, normal4) < 0.5) {
+        return 0.0;
+    } else {
+        vec4 depths = vec4(depth_sample(coord1),
+                           depth_sample(coord2),
+                           depth_sample(coord3),
+                           depth_sample(coord4));
+    
+        vec3 ratios1 = depths.xxx/depths.yzw, ratios2 = depths.yyz/depths.zww;
+    
+        if (are_depths_border(ratios1) || are_depths_border(ratios2)) {
+            return 1.0;
+        } else {
+            float normal_border = 1.0 - min6(
+                dot(normal1, normal2),
+                dot(normal1, normal3),
+                dot(normal1, normal4),
+                dot(normal2, normal3),
+                dot(normal2, normal4),
+                dot(normal3, normal4)
+            );
+    
+            return normal_border;
+        }
+    }
+}
+
+void
+main()
+{
+    gl_FragColor = mix(texture2D(colormap, coord), line_color, border_factor(coord));
+}
+
+;
+
+TUPLE: bunny-outlined
+    gadget
+    pass1-program pass2-program
+    color-texture normal-texture depth-texture
+    framebuffer framebuffer-dim ;
+
+: outlining-supported? ( -- ? )
+    "2.0" {
+        "GL_ARB_shading_objects"
+        "GL_ARB_draw_buffers"
+        "GL_ARB_multitexture"
+    } has-gl-version-or-extensions? {
+        "GL_EXT_framebuffer_object"
+        "GL_ARB_texture_float"
+    } has-gl-extensions? and ;
+
+: pass1-program ( -- program )
+    vertex-shader-source <vertex-shader> check-gl-shader
+    cel-shaded-fragment-shader-lib-source <fragment-shader> check-gl-shader
+    outlined-pass1-fragment-shader-main-source <fragment-shader> check-gl-shader
+    3array <gl-program> check-gl-program ;
+
+: pass2-program ( -- program )
+    outlined-pass2-vertex-shader-source
+    outlined-pass2-fragment-shader-source <simple-gl-program> ;
+
+: <bunny-outlined> ( gadget -- draw )
+    outlining-supported? [
+        pass1-program pass2-program {
+            set-bunny-outlined-gadget
+            set-bunny-outlined-pass1-program
+            set-bunny-outlined-pass2-program
+        } bunny-outlined construct
+    ] [ drop f ] if ;
+
+: (framebuffer-texture) ( dim iformat xformat -- texture )
+    swapd >r >r >r
+    GL_TEXTURE0 glActiveTexture
+    gen-texture GL_TEXTURE_2D over glBindTexture
+    GL_TEXTURE_2D GL_TEXTURE_WRAP_S GL_CLAMP glTexParameteri
+    GL_TEXTURE_2D GL_TEXTURE_WRAP_T GL_CLAMP glTexParameteri
+    GL_TEXTURE_2D GL_TEXTURE_MAG_FILTER GL_NEAREST glTexParameteri
+    GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER GL_NEAREST glTexParameteri
+    GL_TEXTURE_2D 0 r> r> first2 0 r> GL_UNSIGNED_BYTE f glTexImage2D ;
+
+: (attach-framebuffer-texture) ( texture attachment -- )
+    swap >r >r
+    GL_FRAMEBUFFER_EXT r> GL_TEXTURE_2D r> 0 glFramebufferTexture2DEXT
+    gl-error ;
+
+: (make-framebuffer) ( color-texture normal-texture depth-texture -- framebuffer )
+    3array gen-framebuffer dup [
+        swap GL_COLOR_ATTACHMENT0_EXT
+             GL_COLOR_ATTACHMENT1_EXT
+             GL_DEPTH_ATTACHMENT_EXT 3array [ (attach-framebuffer-texture) ] 2each
+        check-framebuffer
+    ] with-framebuffer ;
+
+: remake-framebuffer-if-needed ( draw -- )
+    dup bunny-outlined-gadget rect-dim
+    over bunny-outlined-framebuffer-dim
+    over =
+    [ 2drop ]
+    [
+        swap >r
+        dup GL_RGBA16F_ARB GL_RGBA (framebuffer-texture)
+        swap dup GL_RGBA16F_ARB GL_RGBA (framebuffer-texture)
+        swap dup GL_DEPTH_COMPONENT32 GL_DEPTH_COMPONENT (framebuffer-texture)
+        swap >r
+        [ (make-framebuffer) ] 3keep
+        r> r> {
+            set-bunny-outlined-framebuffer
+            set-bunny-outlined-color-texture
+            set-bunny-outlined-normal-texture
+            set-bunny-outlined-depth-texture
+            set-bunny-outlined-framebuffer-dim
+        } set-slots
+    ] if ;
+
+: clear-framebuffer ( -- )
+    GL_COLOR_ATTACHMENT0_EXT glDrawBuffer
+    0.15 0.15 0.15 1.0 glClearColor
+    GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT bitor glClear
+    GL_COLOR_ATTACHMENT1_EXT glDrawBuffer
+    0.0 0.0 0.0 0.0 glClearColor
+    GL_COLOR_BUFFER_BIT glClear ;
+
+: (pass1) ( geom draw -- )
+    dup bunny-outlined-framebuffer [
+        clear-framebuffer
+        { GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT1_EXT } set-draw-buffers
+        bunny-outlined-pass1-program (draw-cel-shaded-bunny)
+    ] with-framebuffer ;
+
+: (pass2) ( draw -- )
+    init-matrices
+    dup bunny-outlined-color-texture GL_TEXTURE_2D GL_TEXTURE0 bind-texture-unit
+    dup bunny-outlined-normal-texture GL_TEXTURE_2D GL_TEXTURE1 bind-texture-unit
+    dup bunny-outlined-depth-texture GL_TEXTURE_2D GL_TEXTURE2 bind-texture-unit
+    bunny-outlined-pass2-program dup [
+        {
+            [ "colormap"  glGetUniformLocation 0 glUniform1i ]
+            [ "normalmap" glGetUniformLocation 1 glUniform1i ]
+            [ "depthmap"  glGetUniformLocation 2 glUniform1i ]
+            [ "line_color" glGetUniformLocation 0.1 0.0 0.1 1.0 glUniform4f ]
+        } call-with
+        { -1.0 -1.0 } { 1.0 1.0 } rect-vertices
+    ] with-gl-program ;
+
+M: bunny-outlined draw-bunny
+    dup remake-framebuffer-if-needed
+    [ (pass1) ] keep (pass2) ;
+
+M: bunny-outlined dispose
+    {
+        [ bunny-outlined-pass1-program [ delete-gl-program ] when* ]
+        [ bunny-outlined-pass2-program [ delete-gl-program ] when* ]
+        [ bunny-outlined-framebuffer [ delete-framebuffer ] when* ]
+        [ bunny-outlined-color-texture [ delete-texture ] when* ]
+        [ bunny-outlined-normal-texture [ delete-texture ] when* ]
+        [ bunny-outlined-depth-texture [ delete-texture ] when* ]
+        [ f swap set-bunny-outlined-framebuffer-dim ]
+    } call-with ;