LDraw.org Discussion Forums
Motor animation - Printable Version

+- LDraw.org Discussion Forums (https://forums.ldraw.org)
+-- Forum: LDraw Programs (https://forums.ldraw.org/forum-7.html)
+--- Forum: Rendering Techniques (https://forums.ldraw.org/forum-20.html)
+--- Thread: Motor animation (/thread-22891.html)



Motor animation - NEMOOZ - 2018-07-29

Hi everyone,
I am wondering how is it possible to animate motor in LDCad?
I found great LDCAD animation on youtube :

I found Scripting API on Melkert website 

Here the model:
[Image: Motor_animation_view.png]

I am able to rotate the camshaft with this script:

Code:
--[[Sripted by Co for NEMOOZ]]
function register()

--****************************************************************************************
-- "Dissolve" script makes parts disappear progressively from the model ("Build" inverse)
--****************************************************************************************
    local ani=ldc.animation('Motor')

   --Set the length of the animation in seconds
   ani:setLength(10)
    
   ani:setEvent('start', 'motorStart')
   ani:setEvent('frame', 'motorFrame')
end

function motorStart()
     mainSf=ldc.subfile()
end

function motorFrame()

   local ani=ldc.animation.getCurrent()
   local ori=ldc.matrix()
 
   local angle=1800*ani:getFrameTime()/ani:getLength()
   
   local ax1=mainSf:getRef('axe_motor.ldr')
   ori:setRotate(angle, 1, 0, 0)
   ax1:setOri(ori)
end

register()


I searched the forums and found nothing on that particular subject.

Regards


RE: Motor animation - Roland Melkert - 2018-07-31

(2018-07-29, 19:04)NEMOOZ Wrote: Hi everyone,
I am wondering how is it possible to animate motor in LDCad?

Yes but you might need to split the axle from the rest of the motor so you can rotate the axle part of the motor.

Although usually this is not needed as the motor axle is not visible due to the gears attached to it.

Once you got a group or reference for the moving section you can use it like in your example script by calling :ConfusedetOri

As for the documentation on creating animations this is currently mostly found trough the comments in the sample animations.

The api on the site only explains the meaning of the objects and functions available inside LDCad's lua environment.


RE: Motor animation - NEMOOZ - 2018-08-07

Hi Roland,
first, i have to thank you for your amazing work about this soft ! 

I'm able to rotate axis motor but I can't animate Engine piston on cylinder:
  • make an axial movement of Engine Piston Round (2851.dat) into cylinder (2850.dat)
  • make an circular movement and not a rotation of valves (radius: 10,5 / center: 0,0,0)
I tried to use chain script to inspired me but without success, as you can see :
Code:
   local ani=ldc.animation.getCurrent()
   local angle=ani:getFrameTime()/ani:getLength()*360 --current angle of the wheel around the first chain point.

   local linkLen=10 --length of single chain segment.
   local pntRadius=10,5  --current radius of the first path point.
   local linkAngle=math.deg(math.tan(linkLen/pntRadius)) --angle difference on the wheel for a single link.
   local pntAngle=angle % linkAngle --frame's angle, this will create the illusion of a continues move around the whole chain.

   local chainSf=ldc.subfile('piston.ldr')
   local chainPnt=chainSf:getGroup('Group 1') --use a group as the current api can't set matrices for non references (is pending).

   --apply the angle this will trigger a path regeneration.
   local ori=ldc.matrix()
   ori:setRotate(pntAngle, 1, 0, 0)
   chainPnt:setOri(ori)



In fact if you can share me your 8860 script animation, i'll probably be able to make it !


RE: Motor animation - Roland Melkert - 2018-08-07

(2018-08-07, 18:33)NEMOOZ Wrote: In fact if you can share me your 8860 script animation, i'll probably be able to make it !

The 8860 animation is included in the 1.6 releases.

An example of calculating the piston can also be found in the  technic.lua global script


RE: Motor animation - NEMOOZ - 2018-09-02

I try to use your 8860.lua to isolate the engine script and try my Motor_animation.lua with this

Code:
--[[

Advanced animated mechanics for the 8860 set, extensively using the aniTools module.
Copyright Roland Melkert 2017
Free for non-commercial use.
Version 2017-12-29

--]]


--==Options=============================================================================================================
--[[ local doLenStats=false --prints info about minimal needed animation length and estimates POV-Ray rendering time.
local frtA=35 --normal radiosity
local frtB=3.5*60 --HQ radiosity
local doLeftChairTiltDemo=true --left or right chair tilt demo

]]
--==Includes============================================================================================================
genTools=require('genTools')
aniTools=require('aniTools')


--==Sub actors==========================================================================================================
function initEngine(actor)

 --Engine table
 local result={}

 --Animation elements
 result.mainAxleAngle=actor:addAniElm(aniTools.aniElm(0))

 result.pistonRodPosOri={}
 result.pistonTopPosOri={}
 local m=ldc.matrix() --can use a single one as aniElm clones it.
 for i=1,4 do
   result.pistonRodPosOri[i]=actor:addAniElm(aniTools.aniElm(m))
   result.pistonTopPosOri[i]=actor:addAniElm(aniTools.aniElm(m))
 end

 result.coverVis=actor:addAniElm(aniTools.aniElm(true))
 result.coverPos=actor:addAniElm(aniTools.aniElm(0))

 --Joints
 local sf=ldc.subfile()
 local ZZ=ldc.vector(0, 0, -1) --negative to force positive angles for forward motion.
 local mainAxleRef=sf:getRef('axe_motor.ldr')
 actor:addJoint(aniTools.angleJoint(mainAxleRef, result.mainAxleAngle, ZZ))


 result.pistonRodJoint={}
 result.pistonTopJoint={}
 local c=mainAxleRef:getPos()
 for i=1,4 do
   --Use relative posOri joints for both pistons and tops.
   -- This simplifies the below maths as we can assume everything is relative to points along the main axle.
   local ref=sf:getRef('piston.ldr', i)
   c:setZ(ref:getPos():getZ())
   result.pistonRodJoint[i]=actor:addJoint(aniTools.posOriJoint(ref, result.pistonRodPosOri[i], c, false, ldc.matrix()))
   result.pistonTopJoint[i]=actor:addJoint(aniTools.posOriJoint(sf:getRef('2851.dat', i), result.pistonTopPosOri[i], c, false, ldc.matrix()))
 end

 --All axles are depended on de main engine axle, and thus normally would/could be handled in the engine apply function below.
 -- But the gearbox is depended on the resulting drive axle angle, so in order to animate that non static (gear shifts) this angle must be known/available during sequence processing.
 -- For this to be possible actions must be used, so lets define a custom one doing all axles in one go.
 result.depsAction=function(self)
   result=aniTools.depAction(0)
   result.engine=self

   result.calc=function(self, time)
   end

   return result
 end

 result.apply=function(self)

   --The pistons are always depended on a 'normal' animation element, so we can handle their final position in this apply funciton outside normal sequence processing.
   -- This allows for a slightly more efficient way of dealing with all of them in one go and removes the need to worry about it during sequencing.

   --We need to calculate their orientation changes resulting from the cylinder constrains.
   local mainAxleOri=ldc.matrix()
   mainAxleOri:setRotate(self.mainAxleAngle.value, 0, 0, -1)
   for i=1,4 do
     --All related joints maintain a center along the main axle.
     -- So we need the relative position of the piston (in rest) to that.
     local rpNeg=genTools.IF(i>2, 1, -1) --The rods are connect left and right in pairs.
     local bpNeg=genTools.IF((i%2)==0, 1, -1) --The heads go from left to right, odd being at the positive side of X

     --The rods are connected to the offcenter axle's
     local p1=ldc.vector(rpNeg*16, 0, 0)
     --Rotate it to its current position.
     p1:transform(mainAxleOri)

     --The head is normally located 60 units further away.
     local blockPos=ldc.vector(bpNeg*(60+16), 0, 0)

     --Now we get the pistons 'Y' direction in local space directly from its original reference.
     local yDir=self.pistonRodJoint[i].orgAbsPosOri:getOriYRow()
     --How far is the piston position from the line the piston moves on.
     -- getDistance also returns the (pos/neg) distance from the line's position to p1 when based upon the normalized line vector.
     local dist, mul=p1:getDistance(blockPos, yDir)
     --Use this to calculate the (shortest) position on the movement line in relation to the piston position.
     local pShort=blockPos+yDir*mul

     --These two points together with the unknown top position form a triangle.
     -- We use this to calculate the distance of the 'used' part of the movement line in relation to the nearest point on it.
     local s=math.acos(dist/60)
     s=math.sin(s)*60
     --And then use that distance to move the 'near' point along the line.
     -- which is the position of the top.
     local p2=pShort-yDir*s

     --Now we need to orientate the piston so the joint can place it correctly.
     -- Local Y goes from piston to top, note neg due to LDraw.
     local pistonY=p1-p2
     -- Local Z never changes as the axle is parallel to it, so get it from the original reference.
     local pistonZ=self.pistonRodJoint[i].orgAbsPosOri:getOriZRow()
     -- And having two, we can get the third as the crossproduct because everything needs to be perpendicular.
     local pistonX=pistonY:getCross(pistonZ)

     --Use this and the calculated position to construct a matrix.
     local posOri=ldc.matrix()
     posOri:setOriRows(pistonX, pistonY, pistonZ) --Do note we can't do it in the matrix constructor function as that expects 'columns', you could invert it later though.
     posOri:setPos(p1)
     --Apply to the aniElm
     self.pistonRodJoint[i].elm.value:set(posOri)

     --The top piston only needs its position changed, but as we are using relative joints we also need to apply the original top orientation.
     posOri:setOri(self.pistonTopJoint[i].orgAbsPosOri)
     posOri:setPos(p2)
     --Apply to the aniElm
     self.pistonTopJoint[i].elm.value:set(posOri)
   end
 end

 return result
end


--==Main actor==========================================================================================================
function carActor()

 --Start with a basic actor table.
 local result=aniTools.actor()

 --Split mechanics / control elements into logical sub tables.
 result.engine=initEngine(result) -- This handles the engine axles and pistons.

 --Override the apply function so we can do all the auto dependencies.
 -- This could also be done using custom dependency actions.
 -- But there is no real advantage to that method in a large animation like this one.

 --Buffer the default apply function as we are overriding it to do some auto dependencies.
 -- You could skip this if you don't use joints in an animation, but we do.
 result.orgApply=result.apply

 result.apply=function(self)
   self.engine:apply()

   -- The default function processes all joints so it must be called in our version too.
   self:orgApply()
 end


 return result
end

function camActor(story)

 --Initial/rest state 3rd person camera.
 local cam=ldc.camera(3, ldc.vector(46.4297, -27.8264, -53.1873),  2074.9246,  69.8029,  35.1886, 0)

 --Register it as an output camera, the returned cam object is a DIFFERENT one as it needs to be a 'linked' one.
 cam=story:addCamera(cam)

 --Create an actor for this output camera.
 result=story:addActor(aniTools.cameraActor(cam))

 --Easy to use spin action generator.
 result.spin=function(self, seq, ofs, len)
   seq:addAction(aniTools.diffAction(self.yaw, ofs, len, 360))
   return len
 end

 return result
end



--==Events==============================================================================================================
function onStart()

 movie=aniTools.story()

 local car=movie:addActor(carActor())
 print (car)
--  local cam=camActor(movie)


 --Add the main sequence to the story
 local mainSeq=movie:addSequence(aniTools.sequence());

 -- Main dependency actions.
 mainSeq:addAction(car.engine:depsAction())

 --Engine speed
 local driveSeq=aniTools.sequence()
 driveSeq:addAction(aniTools.accelAction(car.engine.mainAxleAngle, 0, 2, 0, 150))
 driveSeq:addAction(aniTools.speedAction(car.engine.mainAxleAngle, 2, nil, 150))
 driveSeq:addAction(aniTools.accelAction(car.engine.mainAxleAngle, 0, 2, 150, 0))
 driveSeq.finLen=function(self, len) self.actions[2].len=len-4  self.actions[3].ofs=len-2 end --The length of motion is set at the end of storyboarding.

 --Put together the story.
 local ofs=0
 local len=0
 local driveOfs=ofs
 mainSeq:addRef(driveSeq, ofs, nil)

 --Done setting up the story
 movie:prep() --If not here it will be automatically done in the first apply call, which might cause a 'too slow playback' error in complicated animations.

 if doLenStats then
   print('Length needed: ', ofs, ', est render time: ', genTools.round(ofs*25*frtA/60/60, 2), ' hours, HQ: ', genTools.round(ofs*25*frtB/60/60, 2), ' hours.')
 end

end

function onFrame()

 movie:apply(ldc.animation():getFrameTime())
end


--==Register============================================================================================================
function register()
 local ani=ldc.animation('Demo')
 ani:setLength(10)
 ani:setEvent('start', 'onStart')
 ani:setEvent('frame', 'onFrame')

end

register()

but I've got this message :


Code:
[string "Motor_animation.lua"]:57: Active refline link needed.


I can't solve it.


RE: Motor animation - Roland Melkert - 2018-09-02

(2018-09-02, 13:56)NEMOOZ Wrote:
Code:
[string "Motor_animation.lua"]:57: Active refline link needed.

Line 56/57 is:
Code:
local ref=sf:getRef('piston.ldr', i)
c:setZ(ref:getPos():getZ())
Which is inside a loop of 4 (i).
So the error is about not finding the 1st, 2nd, 3rd or 4th piston.ldr in the linked model.