module Suzanne exposing (..) import AnimationFrame import Html import Html.Attributes as Attr import Html.Events exposing (on) import Json.Decode as JD import Math.Matrix4 as M4 exposing (Mat4) import Math.Vector3 as V3 exposing (Vec3, vec3) import Task import WebGL as GL import WebGL.Texture import WebGL.Settings exposing (cullFace, front) import WebGL.Settings.DepthTest as DepthTest -- import OBJ import OBJ.Types exposing (MeshWith, VertexWithTexture) import Shaders exposing (reflectionVert, reflectionFrag) main : Program Never Model Msg main = Html.program { init = ( initModel, initCmd ) , view = view , subscriptions = (\model -> AnimationFrame.diffs Tick) , update = update } -- MODEL type alias Model = { time : Float , mesh : Result String (MeshWith VertexWithTexture) , zoom : Float , reflectionTexture : Result String GL.Texture } initModel : Model initModel = { mesh = Err "loading ...", time = 0, zoom = 10, reflectionTexture = Err "Loading texture..." } initCmd : Cmd Msg initCmd = Cmd.batch [ OBJ.loadMesh "meshes/suzanne.obj" LoadObj , loadTexture "textures/chavant.jpg" TextureLoaded ] -- UPDATE type Msg = Tick Float | LoadObj (Result String (MeshWith VertexWithTexture)) | Zoom Float | TextureLoaded (Result String GL.Texture) update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of Tick dt -> ( { model | time = model.time + dt / 1000 }, Cmd.none ) Zoom dy -> ( { model | zoom = model.zoom + dy / 100 }, Cmd.none ) LoadObj mesh -> ( { model | mesh = mesh }, Cmd.none ) TextureLoaded t -> ( { model | reflectionTexture = t }, Cmd.none ) -- VIEW / RENDER renderModel : Model -> GL.Texture -> MeshWith VertexWithTexture -> GL.Entity renderModel { zoom, time } texture { vertices, indices } = let ( camera, view ) = getCamera zoom time model = M4.makeRotate time (vec3 0 1 0) modelView = M4.mul view model in GL.entityWith [ DepthTest.default, cullFace front ] reflectionVert reflectionFrag (GL.indexedTriangles vertices indices) { camera = camera, mvMat = modelView, texture = texture } getCamera : Float -> Float -> ( Mat4, Mat4 ) getCamera zoom t = ( (M4.makePerspective 45 1 0.01 10000) , (M4.makeLookAt (vec3 (zoom) (zoom / 2) (zoom)) (vec3 0 0 0) (vec3 0 1 0)) ) view : Model -> Html.Html Msg view model = case ( model.mesh, model.reflectionTexture ) of ( Ok m, Ok t ) -> GL.toHtmlWith [ GL.antialias, GL.depth 1 ] [ onZoom, Attr.width 400, Attr.height 400 ] [ renderModel model t m ] ( a, b ) -> Html.div [] [ Html.text (toString a ++ "\n\n\n" ++ toString b) ] -- HELPERS loadTexture : String -> (Result String GL.Texture -> msg) -> Cmd msg loadTexture url msg = WebGL.Texture.load url |> Task.attempt (\r -> case r of Ok t -> msg (Ok t) Err e -> msg (Err ("Failed to load texture: " ++ toString e)) ) onZoom : Html.Attribute Msg onZoom = on "wheel" (JD.map Zoom (JD.field "deltaY" JD.float))