{-# 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 ) |]