Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shadow issue #93

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
331 changes: 331 additions & 0 deletions examples/Lack.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
module Lack exposing (main)

{-| This demo allows dragging objects with mouse, try flipping the table!

1. Uses `World.raycast` on mouse down to pick a body
2. Creates a temporary body at the mouse position
3. Connects the temporary body with the selected body using a point to point constraint
4. Moves the temporary body on mouse move
5. Removes the temporary body on mouse up

-}

import Acceleration
import Angle
import Axis3d exposing (Axis3d)
import Block3d exposing (Block3d)
import Browser
import Browser.Dom
import Browser.Events
import Camera3d exposing (Camera3d)
import Color
import Direction3d
import Duration exposing (seconds)
import Html exposing (Html)
import Html.Attributes
import Html.Events
import Json.Decode exposing (Decoder)
import Length exposing (Meters, meters, millimeters)
import Mass exposing (kilograms)
import Physics.Body as Body exposing (Body)
import Physics.Constraint as Constraint
import Physics.Coordinates exposing (BodyCoordinates, WorldCoordinates)
import Physics.Shape
import Physics.World as World exposing (RaycastResult, World)
import Pixels exposing (Pixels, pixels)
import Plane3d
import Point2d
import Point3d
import Quantity exposing (Quantity)
import Rectangle2d
import Scene3d exposing (Entity)
import Scene3d.Material as Material
import Sphere3d
import Task
import Viewpoint3d


type Id
= Mouse
| Floor
| Table


type alias Model =
{ world : World Id
, width : Quantity Float Pixels
, height : Quantity Float Pixels
, maybeRaycastResult : Maybe (RaycastResult Id)
}


type Msg
= AnimationFrame
| Resize Int Int
| MouseDown (Axis3d Meters WorldCoordinates)
| MouseMove (Axis3d Meters WorldCoordinates)
| MouseUp


main : Program () Model Msg
main =
Browser.element
{ init = init
, update = \msg model -> ( update msg model, Cmd.none )
, subscriptions = subscriptions
, view = view
}


init : () -> ( Model, Cmd Msg )
init _ =
( { world = initialWorld
, width = pixels 0
, height = pixels 0
, maybeRaycastResult = Nothing
}
, Task.perform
(\{ viewport } ->
Resize (round viewport.width) (round viewport.height)
)
Browser.Dom.getViewport
)


subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.batch
[ Browser.Events.onResize Resize
, Browser.Events.onAnimationFrame (\_ -> AnimationFrame)
]


initialWorld : World Id
initialWorld =
World.empty
|> World.withGravity (Acceleration.gees 1) Direction3d.negativeZ
|> World.add table
|> World.add (Body.plane Floor)


tableBlocks : List (Block3d Meters BodyCoordinates)
tableBlocks =
[ Block3d.from
(Point3d.millimeters 222 222 0)
(Point3d.millimeters 272 272 400)
, Block3d.from
(Point3d.millimeters -272 222 0)
(Point3d.millimeters -222 272 400)
, Block3d.from
(Point3d.millimeters -272 -272 0)
(Point3d.millimeters -222 -222 400)
, Block3d.from
(Point3d.millimeters 222 -272 0)
(Point3d.millimeters 272 -222 400)
, Block3d.from
(Point3d.millimeters -275 -275 400)
(Point3d.millimeters 275 275 450)
]


table : Body Id
table =
Body.compound (List.map Physics.Shape.block tableBlocks) Table
|> Body.withBehavior (Body.dynamic (kilograms 3.58))


camera : Camera3d Meters WorldCoordinates
camera =
Camera3d.perspective
{ viewpoint =
Viewpoint3d.lookAt
{ eyePoint = Point3d.meters 3 4 2
, focalPoint = Point3d.meters -0.5 -0.5 0
, upDirection = Direction3d.positiveZ
}
, verticalFieldOfView = Angle.degrees 24
}


view : Model -> Html Msg
view { world, width, height } =
Html.div
[ Html.Attributes.style "position" "absolute"
, Html.Attributes.style "left" "0"
, Html.Attributes.style "top" "0"
, Html.Events.on "mousedown" (decodeMouseRay camera width height MouseDown)
, Html.Events.on "mousemove" (decodeMouseRay camera width height MouseMove)
, Html.Events.onMouseUp MouseUp
]
[ Scene3d.sunny
{ upDirection = Direction3d.z
, sunlightDirection = Direction3d.xyZ (Angle.degrees 135) (Angle.degrees -60)
, shadows = True
, camera = camera
, dimensions =
( Pixels.int (round (Pixels.toFloat width))
, Pixels.int (round (Pixels.toFloat height))
)
, background = Scene3d.transparentBackground
, clipDepth = Length.meters 0.1
, entities = List.map bodyToEntity (World.bodies world)
}
]


bodyToEntity : Body Id -> Entity WorldCoordinates
bodyToEntity body =
let
frame =
Body.frame body

id =
Body.data body
in
Scene3d.placeIn frame <|
case id of
Mouse ->
Scene3d.sphere (Material.matte Color.white)
(Sphere3d.atOrigin (millimeters 20))

Table ->
tableBlocks
|> List.map
(Scene3d.blockWithShadow
(Material.nonmetal
{ baseColor = Color.white
, roughness = 0.25
}
)
)
|> Scene3d.group

Floor ->
Scene3d.quad (Material.matte Color.darkCharcoal)
(Point3d.meters -100 -100 0)
(Point3d.meters -100 100 0)
(Point3d.meters 100 100 0)
(Point3d.meters 100 -100 0)


update : Msg -> Model -> Model
update msg model =
case msg of
AnimationFrame ->
{ model | world = World.simulate (seconds (1 / 60)) model.world }

Resize width height ->
{ model
| width = Pixels.float (toFloat width)
, height = Pixels.float (toFloat height)
}

MouseDown mouseRay ->
case World.raycast mouseRay model.world of
Just raycastResult ->
case Body.data raycastResult.body of
Table ->
let
worldPoint =
Point3d.placeIn
(Body.frame raycastResult.body)
raycastResult.point

mouse =
Body.compound [] Mouse
|> Body.moveTo worldPoint
in
{ model
| maybeRaycastResult = Just raycastResult
, world =
model.world
|> World.add mouse
|> World.constrain
(\b1 b2 ->
case ( Body.data b1, Body.data b2 ) of
( Mouse, Table ) ->
[ Constraint.pointToPoint
Point3d.origin
raycastResult.point
]

_ ->
[]
)
}

_ ->
model

Nothing ->
model

MouseMove mouseRay ->
case model.maybeRaycastResult of
Just raycastResult ->
let
worldPoint =
Point3d.placeIn
(Body.frame raycastResult.body)
raycastResult.point

plane =
Plane3d.through
worldPoint
(Viewpoint3d.viewDirection (Camera3d.viewpoint camera))
in
{ model
| world =
World.update
(\body ->
if Body.data body == Mouse then
case Axis3d.intersectionWithPlane plane mouseRay of
Just intersection ->
Body.moveTo intersection body

Nothing ->
body

else
body
)
model.world
}

Nothing ->
model

MouseUp ->
{ model
| maybeRaycastResult = Nothing
, world =
World.keepIf
(\body -> Body.data body /= Mouse)
model.world
}


decodeMouseRay :
Camera3d Meters WorldCoordinates
-> Quantity Float Pixels
-> Quantity Float Pixels
-> (Axis3d Meters WorldCoordinates -> msg)
-> Decoder msg
decodeMouseRay camera3d width height rayToMsg =
Json.Decode.map2
(\x y ->
rayToMsg
(Camera3d.ray
camera3d
(Rectangle2d.with
{ x1 = pixels 0
, y1 = height
, x2 = width
, y2 = pixels 0
}
)
(Point2d.pixels x y)
)
)
(Json.Decode.field "pageX" Json.Decode.float)
(Json.Decode.field "pageY" Json.Decode.float)
4 changes: 2 additions & 2 deletions examples/elm.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"mdgriffith/elm-ui": "1.1.8",
"ohanhi/keyboard": "2.0.1",
"w0rm/elm-obj-file": "1.0.0",
"w0rm/elm-physics": "5.1.1"
"w0rm/elm-physics": "5.1.3"
},
"indirect": {
"elm/bytes": "1.0.8",
Expand All @@ -43,4 +43,4 @@
"direct": {},
"indirect": {}
}
}
}