feat: 3d model and serious bloat incoming
This commit is contained in:
parent
b903f3755d
commit
0f4436787c
7 changed files with 57712 additions and 14 deletions
BIN
assets/3d/macg/image0.jpg
Normal file
BIN
assets/3d/macg/image0.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.5 MiB |
23
assets/3d/macg/macgregor-house-small.mtl
Normal file
23
assets/3d/macg/macgregor-house-small.mtl
Normal file
|
@ -0,0 +1,23 @@
|
|||
# File generated by ImageToStl.com - Free Image and 3D model conversion tools
|
||||
|
||||
newmtl mat0
|
||||
Ns 0
|
||||
Ka 1.0 1.0 1.0
|
||||
Kd 1 1 1
|
||||
Ks 0.5 0.5 0.5
|
||||
Ke 0.0 0.0 0.0
|
||||
Ni 1.0
|
||||
d 1
|
||||
illum 2
|
||||
map_Kd image0.jpg
|
||||
|
||||
newmtl mat1
|
||||
Ns 250
|
||||
Ka 1.0 1.0 1.0
|
||||
Kd 0.85 0.85 0.85
|
||||
Ks 0 0 0
|
||||
Ke 0.0 0.0 0.0
|
||||
Ni 1.0
|
||||
d 1
|
||||
illum 1
|
||||
|
57388
assets/3d/macg/macgregor.obj.txt
Normal file
57388
assets/3d/macg/macgregor.obj.txt
Normal file
File diff suppressed because it is too large
Load diff
BIN
assets/3d/macgregor.zip
Normal file
BIN
assets/3d/macgregor.zip
Normal file
Binary file not shown.
25
elm.json
25
elm.json
|
@ -6,16 +6,35 @@
|
|||
"elm-version": "0.19.1",
|
||||
"dependencies": {
|
||||
"direct": {
|
||||
"andrewMacmurray/elm-simple-animation": "2.3.2",
|
||||
"avh4/elm-color": "1.0.0",
|
||||
"elm/browser": "1.0.2",
|
||||
"elm/core": "1.0.5",
|
||||
"elm/html": "1.0.0",
|
||||
"mdgriffith/elm-ui": "1.1.8"
|
||||
"elm/http": "2.0.0",
|
||||
"elm/time": "1.0.0",
|
||||
"elm-explorations/webgl": "1.1.3",
|
||||
"ianmackenzie/elm-3d-camera": "3.1.0",
|
||||
"ianmackenzie/elm-3d-scene": "1.0.2",
|
||||
"ianmackenzie/elm-geometry": "3.11.0",
|
||||
"ianmackenzie/elm-triangular-mesh": "1.1.0",
|
||||
"ianmackenzie/elm-units": "2.10.0",
|
||||
"mdgriffith/elm-ui": "1.1.8",
|
||||
"w0rm/elm-obj-file": "1.2.1"
|
||||
},
|
||||
"indirect": {
|
||||
"elm/bytes": "1.0.8",
|
||||
"elm/file": "1.0.5",
|
||||
"elm/json": "1.1.3",
|
||||
"elm/time": "1.0.0",
|
||||
"elm/random": "1.0.0",
|
||||
"elm/url": "1.0.0",
|
||||
"elm/virtual-dom": "1.0.3"
|
||||
"elm/virtual-dom": "1.0.3",
|
||||
"elm-explorations/linear-algebra": "1.0.3",
|
||||
"ianmackenzie/elm-1d-parameter": "1.0.1",
|
||||
"ianmackenzie/elm-float-extra": "1.1.0",
|
||||
"ianmackenzie/elm-geometry-linear-algebra-interop": "2.0.2",
|
||||
"ianmackenzie/elm-interval": "3.1.0",
|
||||
"ianmackenzie/elm-units-interval": "3.2.0"
|
||||
}
|
||||
},
|
||||
"test-dependencies": {
|
||||
|
|
282
src/Main.elm
282
src/Main.elm
|
@ -1,11 +1,69 @@
|
|||
module Main exposing (main)
|
||||
|
||||
import Angle
|
||||
import Array
|
||||
import Browser
|
||||
import Browser.Events as Events
|
||||
import Camera3d
|
||||
import Color
|
||||
import Direction3d
|
||||
import Element exposing (..)
|
||||
import Element.Background as Background
|
||||
import Element.Font as Font
|
||||
import Html exposing (Html)
|
||||
import Http exposing (Error)
|
||||
import Length
|
||||
import Obj.Decode
|
||||
import Pixels
|
||||
import Point3d exposing (Point3d)
|
||||
import Scene3d exposing (Entity)
|
||||
import Scene3d.Material as Material
|
||||
import Scene3d.Mesh as Mesh
|
||||
import Simple.Animation as Animation exposing (Animation)
|
||||
import Simple.Animation.Animated as Animated
|
||||
import Simple.Animation.Property as P
|
||||
import Task
|
||||
import Time
|
||||
import TriangularMesh exposing (TriangularMesh)
|
||||
import Viewpoint3d
|
||||
import WebGL.Texture
|
||||
|
||||
|
||||
getMesh : Cmd Msg
|
||||
getMesh =
|
||||
Http.get
|
||||
{ url = "../assets/3d/macg/macgregor.obj.txt"
|
||||
, expect = Obj.Decode.expectObj GotMesh Length.meters Obj.Decode.texturedTriangles
|
||||
}
|
||||
|
||||
|
||||
getTexture : Cmd Msg
|
||||
getTexture =
|
||||
Material.loadWith Material.nearestNeighborFiltering "../assets/3d/macg/image0.jpg" |> Task.attempt GotTexture
|
||||
|
||||
|
||||
animatedUi :
|
||||
(List (Attribute msg) -> children -> Element msg)
|
||||
-> Animation
|
||||
-> List (Attribute msg)
|
||||
-> children
|
||||
-> Element msg
|
||||
animatedUi =
|
||||
Animated.ui
|
||||
{ behindContent = Element.behindContent
|
||||
, htmlAttribute = Element.htmlAttribute
|
||||
, html = Element.html
|
||||
}
|
||||
|
||||
|
||||
animatedEl : Animation -> List (Element.Attribute msg) -> Element msg -> Element msg
|
||||
animatedEl =
|
||||
animatedUi Element.el
|
||||
|
||||
|
||||
animatedCol : Animation -> List (Element.Attribute msg) -> List (Element msg) -> Element msg
|
||||
animatedCol =
|
||||
animatedUi Element.column
|
||||
|
||||
|
||||
main : Program Flags Model Msg
|
||||
|
@ -14,7 +72,7 @@ main =
|
|||
|
||||
|
||||
type alias Model =
|
||||
{ w : Int, h : Int }
|
||||
{ w : Int, h : Int, mesh : Maybe Object3d, textures : Maybe (Material.Textured Obj.Decode.ObjCoordinates), angle : Float }
|
||||
|
||||
|
||||
type alias Flags =
|
||||
|
@ -25,23 +83,76 @@ init : Flags -> ( Model, Cmd Msg )
|
|||
init flags =
|
||||
case flags of
|
||||
( width, height ) ->
|
||||
( { w = width, h = height }, Cmd.none )
|
||||
( { w = width, h = height, mesh = Nothing, textures = Nothing, angle = 0 }, Cmd.batch [ getMesh, getTexture ] )
|
||||
|
||||
|
||||
|
||||
-- type alias Object3d =
|
||||
-- TriangularMesh
|
||||
-- { position : Point3d Length.Meters Obj.Decode.ObjCoordinates
|
||||
-- , normal : Vector3d Quantity.Unitless Obj.Decode.ObjCoordinates
|
||||
-- , uv : ( Float, Float )
|
||||
-- }
|
||||
-- type alias Object3d =
|
||||
-- TriangularMesh (Point3d Length.Meters Obj.Decode.ObjCoordinates)
|
||||
|
||||
|
||||
type alias Object3d =
|
||||
TriangularMesh { position : Point3d Length.Meters Obj.Decode.ObjCoordinates, uv : ( Float, Float ) }
|
||||
|
||||
|
||||
type Msg
|
||||
= Resize Int Int
|
||||
| GotMesh (Result Http.Error Object3d)
|
||||
| GotTexture (Result WebGL.Texture.Error (Material.Texture Color.Color))
|
||||
| Rotate Time.Posix
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
let
|
||||
wrap : Model -> ( Model, Cmd Msg )
|
||||
wrap data =
|
||||
( data, Cmd.none )
|
||||
|
||||
pass : ( Model, Cmd Msg )
|
||||
pass =
|
||||
wrap model
|
||||
in
|
||||
case msg of
|
||||
Resize width height ->
|
||||
( { model | w = width, h = height }, Cmd.none )
|
||||
wrap { model | w = width, h = height }
|
||||
|
||||
GotMesh response ->
|
||||
case response of
|
||||
Err _ ->
|
||||
pass
|
||||
|
||||
Ok object ->
|
||||
wrap { model | mesh = Just object }
|
||||
|
||||
GotTexture result ->
|
||||
case result of
|
||||
Err _ ->
|
||||
pass
|
||||
|
||||
Ok texture ->
|
||||
wrap
|
||||
{ model
|
||||
| textures =
|
||||
Just (Material.texturedMatte texture)
|
||||
}
|
||||
|
||||
Rotate time ->
|
||||
wrap { model | angle = model.angle + 2 * (2 + sin (toFloat (Time.posixToMillis time) / 1000)) }
|
||||
|
||||
|
||||
subscribe : Model -> Sub Msg
|
||||
subscribe _ =
|
||||
Events.onResize Resize
|
||||
Sub.batch
|
||||
[ Events.onResize Resize
|
||||
, Time.every (1000 / 30) Rotate
|
||||
]
|
||||
|
||||
|
||||
htmlify : List (Element Msg) -> List (Html Msg)
|
||||
|
@ -64,19 +175,170 @@ view model =
|
|||
{ title = "MacGregor House"
|
||||
, body =
|
||||
htmlify
|
||||
[ el
|
||||
[ column
|
||||
[ width fill
|
||||
, height fill
|
||||
, Background.color (rgb255 0 0 0)
|
||||
, spacing (round (vh model -100))
|
||||
]
|
||||
(el
|
||||
[ el
|
||||
[ width fill
|
||||
, height (px (round (vh model 100)))
|
||||
, Background.color (rgb255 0 0 0)
|
||||
]
|
||||
Element.none
|
||||
, animatedEl crossfadeIn
|
||||
[ width fill
|
||||
, height (px (round (vh model 100)))
|
||||
, Background.gradient { angle = 45, steps = [ rgb255 200 0 100, rgb255 100 0 200 ] }
|
||||
]
|
||||
Element.none
|
||||
, animatedEl crossfadeOut
|
||||
[ width fill
|
||||
, height (px (round (vh model 100)))
|
||||
, Background.gradient { angle = 45, steps = [ rgb255 0 100 200, rgb255 0 200 100 ] }
|
||||
]
|
||||
Element.none
|
||||
, el
|
||||
[ alignLeft
|
||||
, alignTop
|
||||
, moveRight (vw model 10)
|
||||
, moveDown (vh model 50)
|
||||
, width (px (round (vw model 50)))
|
||||
, height (px (round (vh model 100)))
|
||||
, paddingEach
|
||||
{ top = round (vh model 50) - 96
|
||||
, bottom = 0
|
||||
, left = round (vw model 10)
|
||||
, right = 0
|
||||
}
|
||||
, Font.color (rgb255 255 255 255)
|
||||
, Font.family [ Font.typeface "Imbue" ]
|
||||
, Font.size 96
|
||||
]
|
||||
(text "MacGregor House")
|
||||
)
|
||||
, el
|
||||
[ alignRight
|
||||
, alignTop
|
||||
, width (px (round (vw model 60)))
|
||||
, height (px (round (vh model 100)))
|
||||
, paddingEach
|
||||
{ top = round (vh model 25)
|
||||
, bottom = round (vh model 25)
|
||||
, left = 0
|
||||
, right = 0
|
||||
}
|
||||
]
|
||||
(view3D model)
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
pyramidMesh : Mesh.Uniform coordinates
|
||||
pyramidMesh =
|
||||
let
|
||||
-- Define the vertices of our pyramid
|
||||
frontLeft =
|
||||
Point3d.centimeters 250 500 0
|
||||
|
||||
frontRight =
|
||||
Point3d.centimeters 400 0 -500
|
||||
|
||||
backLeft =
|
||||
Point3d.centimeters -250 500 -500
|
||||
|
||||
backRight =
|
||||
Point3d.centimeters -250 0 0
|
||||
|
||||
tip =
|
||||
Point3d.centimeters 0 0 500
|
||||
|
||||
-- Create a TriangularMesh value from an array of vertices and list
|
||||
-- of index triples defining faces (see https://package.elm-lang.org/packages/ianmackenzie/elm-triangular-mesh/latest/TriangularMesh#indexed)
|
||||
triangularMesh =
|
||||
TriangularMesh.indexed
|
||||
(Array.fromList
|
||||
[ frontLeft -- 0
|
||||
, frontRight -- 1
|
||||
, backLeft -- 2
|
||||
, backRight -- 3
|
||||
, tip -- 4
|
||||
]
|
||||
)
|
||||
[ ( 1, 0, 4 ) -- front
|
||||
, ( 0, 2, 4 ) -- left
|
||||
, ( 2, 3, 4 ) -- back
|
||||
, ( 3, 1, 4 ) -- right
|
||||
, ( 1, 3, 0 ) -- bottom
|
||||
, ( 0, 3, 2 ) -- bottom
|
||||
]
|
||||
in
|
||||
-- Create a elm-3d-scene Mesh value from the TriangularMesh; we use
|
||||
-- Mesh.indexedFacets so that normal vectors will be generated for each face
|
||||
Mesh.indexedFacets triangularMesh
|
||||
|
||||
|
||||
view3D : Model -> Element msg
|
||||
view3D model =
|
||||
Element.html
|
||||
(let
|
||||
entity : Entity Obj.Decode.ObjCoordinates
|
||||
entity =
|
||||
case model.mesh of
|
||||
Nothing ->
|
||||
Scene3d.mesh (Material.matte (Color.rgb255 173 111 101)) pyramidMesh
|
||||
|
||||
Just mesh ->
|
||||
case model.textures of
|
||||
Nothing ->
|
||||
Scene3d.mesh (Material.matte (Color.rgb255 173 111 101)) (Mesh.texturedFacets mesh)
|
||||
|
||||
Just textures ->
|
||||
Scene3d.mesh textures (Mesh.texturedFacets mesh)
|
||||
|
||||
camera : Camera3d.Camera3d Length.Meters coordinates
|
||||
camera =
|
||||
Camera3d.perspective
|
||||
{ viewpoint =
|
||||
Viewpoint3d.lookAt
|
||||
{ focalPoint = Point3d.origin
|
||||
, eyePoint =
|
||||
let
|
||||
theta =
|
||||
Angle.degrees model.angle
|
||||
in
|
||||
Point3d.meters (10 * Angle.cos theta) 2 (10 * Angle.sin theta)
|
||||
, upDirection = Direction3d.xy (Angle.degrees 90)
|
||||
}
|
||||
, verticalFieldOfView = Angle.degrees 100
|
||||
}
|
||||
in
|
||||
Scene3d.sunny
|
||||
{ entities = [ entity ]
|
||||
, camera = camera
|
||||
, upDirection = Direction3d.z
|
||||
, sunlightDirection = Direction3d.yz (Angle.degrees -120)
|
||||
, background = Scene3d.transparentBackground
|
||||
, clipDepth = Length.centimeters 1
|
||||
, shadows = False
|
||||
, dimensions = ( Pixels.int (round (vw model 60)), Pixels.int (round (vh model 100)) )
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
crossfadeIn : Animation
|
||||
crossfadeIn =
|
||||
Animation.fromTo
|
||||
{ duration = 2017
|
||||
, options = [ Animation.yoyo, Animation.loop ]
|
||||
}
|
||||
[ P.opacity 0 ]
|
||||
[ P.opacity 1 ]
|
||||
|
||||
|
||||
crossfadeOut : Animation
|
||||
crossfadeOut =
|
||||
Animation.fromTo
|
||||
{ duration = 2027
|
||||
, options = [ Animation.yoyo, Animation.loop ]
|
||||
}
|
||||
[ P.opacity 1 ]
|
||||
[ P.opacity 0 ]
|
||||
|
|
|
@ -3,10 +3,16 @@
|
|||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>MacGregor House</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Imbue:opsz,wght@10..100,100..900&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="../result/Main.js"></script>
|
||||
<script src="../compiled/Main.js"></script>
|
||||
<script>
|
||||
var app = Elm.Main.init({
|
||||
node: document.getElementById("app"),
|
||||
|
|
Loading…
Reference in a new issue