reanimate-0.4.3.0: src/Reanimate/Builtin/Flip.hs
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{- HLINT ignore -}
{-|
Copyright : Written by David Himmelstrup
License : Unlicense
Maintainer : lemmih@gmail.com
Stability : experimental
Portability : POSIX
-}
module Reanimate.Builtin.Flip
( FlipSprite(..)
, flipSprite
, flipTransition
) where
import Reanimate.Animation (Animation, duration, frameAt, setDuration)
import Reanimate.Blender (blender)
import Reanimate.Ease (fromToS, oscillateS)
import Reanimate.Raster (svgAsPngFile)
import Reanimate.Scene (Scene, Sprite, Var, fork, newSprite, newVar, scene,
spriteDuration, spriteT, tweenVar, unVar)
import Reanimate.Svg.Constructors (flipXAxis)
import Reanimate.Transition (Transition)
import qualified Data.Text as T
import NeatInterpolation (text)
-- | Control structure with parameters for the blender script.
data FlipSprite s = FlipSprite
{ fsSprite :: Sprite s
, fsBend :: Var s Double
, fsZoom :: Var s Double
, fsWobble :: Var s Double
}
-- | Project two animations on each side of a plane and flip the plane
-- upside down.
flipSprite :: Animation -> Animation -> Scene s (FlipSprite s)
flipSprite front back = do
bend <- newVar 0
trans <- newVar 0
rotX <- newVar 0
sprite <- newSprite $ do
getBend <- unVar bend
getTrans <- unVar trans
getRotX <- unVar rotX
time <- spriteT
dur <- spriteDuration
return $
let rotY = fromToS 0 pi (time/dur)
frontTexture = svgAsPngFile (frameAt time $ setDuration dur front)
backTexture = svgAsPngFile (flipXAxis $ frameAt time $ setDuration dur back)
-- seq'ing frontTexture and backTexture is required to avoid segfaults. :(
in frontTexture `seq` backTexture `seq`
blender (script frontTexture backTexture getBend getTrans getRotX rotY)
return FlipSprite
{ fsSprite = sprite
, fsBend = bend
, fsZoom = trans
, fsWobble = rotX }
flipTransitionOpts :: Double -> Double -> Double -> Transition
flipTransitionOpts bend zoom wobble a b = scene $ do
FlipSprite{..} <- flipSprite a b
fork $ tweenVar fsZoom dur $ \v -> fromToS v zoom . oscillateS
fork $ tweenVar fsBend dur $ \v -> fromToS v bend . oscillateS
fork $ tweenVar fsWobble dur $ \v -> fromToS v wobble . oscillateS
where
dur = max (duration a) (duration b)
-- | 3D flip transition.
flipTransition :: Transition
flipTransition = flipTransitionOpts bend zoom wobble
where
bend = 1/3
zoom = 3
wobble = -pi*0.10
script :: FilePath -> FilePath -> Double -> Double -> Double -> Double -> T.Text
script frontImage backImage bend transZ rotX rotY =
let transZ_ = T.pack (show transZ)
rotX_ = T.pack (show rotX)
bend_ = T.pack (show bend)
yScale_ = T.pack (show $ fromToS (9/2) 4 bend)
frontImage_ = T.pack frontImage
backImage_ = T.pack backImage
rotY_ = T.pack (show rotY)
in [text|
import os
import math
import bpy
light = bpy.data.objects['Light']
bpy.ops.object.select_all(action='DESELECT')
light.select_set(True)
bpy.ops.object.delete()
cam = bpy.data.objects['Camera']
cam.location = (0,0,22.22 + $transZ_)
cam.rotation_euler = (0, 0, 0)
bpy.ops.object.empty_add(location=(0.0, 0, 0))
focus_target = bpy.context.object
bpy.ops.object.select_all(action='DESELECT')
cam.select_set(True)
focus_target.select_set(True)
bpy.ops.object.parent_set()
focus_target.rotation_euler = ($rotX_, 0, 0)
origin = bpy.data.objects['Cube']
bpy.ops.object.select_all(action='DESELECT')
origin.select_set(True)
bpy.ops.object.delete()
x = $bend_
bpy.ops.mesh.primitive_plane_add()
plane = bpy.context.object
plane.scale = (16/2,$yScale_,1)
bpy.ops.object.shade_smooth()
bpy.context.object.active_material = bpy.data.materials['Material']
mat = bpy.context.object.active_material
mix = mat.node_tree.nodes.new('ShaderNodeMixShader')
geo = mat.node_tree.nodes.new('ShaderNodeNewGeometry')
mat.blend_method = 'HASHED'
image_node = mat.node_tree.nodes.new('ShaderNodeTexImage')
gh_node = mat.node_tree.nodes.new('ShaderNodeTexImage')
output = mat.node_tree.nodes['Material Output']
gh_mix = mat.node_tree.nodes.new('ShaderNodeMixShader')
transparent = mat.node_tree.nodes.new('ShaderNodeBsdfTransparent')
mat.node_tree.links.new(geo.outputs['Backfacing'], mix.inputs['Fac'])
mat.node_tree.links.new(mix.outputs['Shader'], output.inputs['Surface'])
mat.node_tree.links.new(image_node.outputs['Color'], mix.inputs[1])
#mat.node_tree.links.new(gh_node.outputs['Color'], mix.inputs[2])
mat.node_tree.links.new(gh_node.outputs['Color'], gh_mix.inputs[2])
mat.node_tree.links.new(gh_node.outputs['Alpha'], gh_mix.inputs['Fac'])
mat.node_tree.links.new(transparent.outputs['BSDF'], gh_mix.inputs[1])
mat.node_tree.links.new(gh_mix.outputs['Shader'], mix.inputs[2])
image_node.image = bpy.data.images.load('${frontImage_}')
image_node.interpolation = 'Closest'
gh_node.image = bpy.data.images.load('${backImage_}')
gh_node.interpolation = 'Closest'
modifier = plane.modifiers.new(name='Subsurf', type='SUBSURF')
modifier.levels = 7
modifier.render_levels = 7
modifier.subdivision_type = 'SIMPLE'
bpy.ops.object.empty_add(type='ARROWS',rotation=(math.pi/2,0,0))
empty = bpy.context.object
bendUp = plane.modifiers.new(name='Bend up', type='SIMPLE_DEFORM')
bendUp.deform_method = 'BEND'
bendUp.origin = empty
bendUp.deform_axis = 'X'
bendUp.factor = -math.pi*x
bendAround = plane.modifiers.new(name='Bend around', type='SIMPLE_DEFORM')
bendAround.deform_method = 'BEND'
bendAround.origin = empty
bendAround.deform_axis = 'Z'
bendAround.factor = -math.pi*2*x
bpy.context.view_layer.objects.active = plane
bpy.ops.object.modifier_apply(modifier='Subsurf')
bpy.ops.object.modifier_apply(modifier='Bend up')
bpy.ops.object.modifier_apply(modifier='Bend around')
bpy.ops.object.select_all(action='DESELECT')
plane.select_set(True);
bpy.ops.object.origin_clear()
bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN')
plane.rotation_euler = (0, $rotY_, 0)
scn = bpy.context.scene
#scn.render.engine = 'CYCLES'
#scn.render.resolution_percentage = 10
scn.view_settings.view_transform = 'Standard'
scn.render.resolution_x = 2560
scn.render.resolution_y = 1440
scn.render.film_transparent = True
bpy.ops.render.render( write_still=True )
|]