verify when a clipAction animation finished playing in three.js - json

I’ve made an animated mesh in Blender containing two animations clips that I named intro and idle. I’ve export it with the Blender exporter in JSON. Everything works great. I can toggle which animation I want to play. My problem is that I want to play the first animation entirely and then switch to the second animation that I’ll set to loop.
Here is the parts of the code I use for the JSON and animation part :
var jsonLoader = new THREE.JSONLoader();
jsonLoader.load('models/feuille(19fevrier).json',
function (geometry, materials) {
mesh = new THREE.SkinnedMesh(geometry,
new THREE.MeshLambertMaterial({
skinning: true
}));
mixer = new THREE.AnimationMixer(mesh);
action.intro = mixer.clipAction(geometry.animations[ 0 ]);
action.idle = mixer.clipAction(geometry.animations[ 1 ]);
action.intro.setEffectiveWeight(1);
action.idle.setEffectiveWeight(1);
action.intro.enabled = true;
action.idle.enabled = true;
action.intro.setLoop(THREE.LoopOnce, 0);
scene.add(mesh);
scene.traverse(function(children){
objects.push(children);
});
action.intro.play();
});
What I was looking for is a function that tells me that the action is complete, somethings like onComplete event when a clipAction finished playing. I found this response on the three.js GitHub :
mesh.mixer.addEventListener('finished',function(e){
// some code
});
It seems to work but I don’t really understand the addEventListener in that context and neither what function(e) means.
There is the link of the code I found on GitHub

addEventListener is part of the standard JavaScript dispatchEvent/addEventListener concept for handling events. The three.js Animation subsystem dispatches an event whenever the animation is finished. From AnimationAction.js:
this._mixer.dispatchEvent( {
type: 'finished', action: this,
direction: deltaTime < 0 ? -1 : 1
} );
As you can see, the e event argument provides you with the complete AnimationAction object (e.action), as well as an integer representing the direction of the animation (e.direction).
Do not feel discouraged by the fact that this is undocumented behavior (three.js has grown a lot faster than Mr Doob could ever properly document). Make use of the available functionality, but do keep an eye open for possible changes in feature releases.

Related

How to get loop animation progress in scripts (Spark AR)

I need to get loop animation progress (and looped) information (if it will be in patch editor, it 's the outputs). But I can't find this information on documentation. How can I get needed information?
For "looped" you can use onAfterIteration event on the TimeDriver:
https://sparkar.facebook.com/ar-studio/learn/documentation/reference/classes/animationmodule.timedriver
For progress, unfortunately there isn't a progress available on the TimeDriver, but you can use a multiTrigger event listener on the animation signal. Depending on what exactly you are trying to achieve, there are a few different ways of doing it:
https://sparkar.facebook.com/ar-studio/learn/documentation/reference/classes/reactivemodule.scalarsignal
For example:
//setup your animation
const Animation = require('Animation');
let driver = Animation.timeDriver({durationMilliseconds:1000, loopCount:Infinity});
let sampler = Animation.samplers.linear(0,1);
let animation = Animation.animate(driver, sampler);
//add listener for "looped" to the driver
driver.onAfterIteration().subscribe(function(e){
//do stuff on here...
});
//add listener for progress to the animation signal
//this will trigger when the animation signal goes above .5
animation.multiTrigger(.5).subscribe(function(e){
//do stuff here...
})

How to know that fitToView() has finished completely

I want to know that fitToView() finished completely.
Some program procedures do not work after fitToView() without setTimeout().
For example, the following code not work.
const dbid = [1141]
this.viewer.select(dbid)
this.viewer.fitToView(dbid, viewer.model)
zoom() //This will not work
//code from:
function zoom (){
var nav = viewer.navigation
var pos = nav.getPosition()
var target = nav.getTarget()
var viewdir = new THREE.Vector3()
viewdir.subVectors (pos, target).normalize()
// zooms out by 100 along the view direction
viewdir.multiplyScalar (1000)
pos.add(viewdir)
nav.setPosition(pos)
}
The following code work well.
this.viewer.fitToView(dbid, viewer.model)
setTimeout(function(){
zoom() //This will work fine
}, 2000)
However, I don't want to use the setTimeout as much as possible.
Is there a way to know that fitToView () is finished completely?
If you use version 3.2.1 of the viewer a new event Autodesk.Viewing.CAMERA_TRANSITION_COMPLETED, it will be fired while following transitions are finished:
Go Home transition
Focus / Fit to View transition
Restore State transition
Named Views transition
Any other camera transitions
// Hook the event
viewer.addEventListener(Autodesk.Viewing.CAMERA_TRANSITION_COMPLETED, function(){
console.log('camera is no longer moving');
});
// Trigger an action that will move the camera and fire the event
viewer.fitToView();
You can see more about the Viewer Version changes here.
https://developer.autodesk.com/en/docs/viewer/v2/overview/changelog/3.2.1/

Making multiple objects draggable

I have about 50 symbols that I want to make draggable. Nothing fancy, just the ability to click it and drag it to a different location.
I found as3 code for doing so but when I paste it into my file it gives me errors:
**Error** Scene=Scene 1, layer=Units, frame=1:Line 9: The class or interface 'MouseEvent' could not be loaded.
function mouseDownHandler(evt:MouseEvent):void {
That code is:
// Register mouse event functions
fighter_uk.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
fighter_uk.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
fighter_uk.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
fighter_uk.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
// Define a mouse down handler (user is dragging)
function mouseDownHandler(evt:MouseEvent):void {
var object = evt.target;
// we should limit dragging to the area inside the canvas
object.startDrag();
}
function mouseUpHandler(evt:MouseEvent):void {
var obj = evt.target;
obj.stopDrag();
}
I'm using flash pro 8, so I tried finding as2 code but couldn't find it.
Also, is there an 'easy' way to code all 50 objects?
I think you're trying to compile AS3 code with AS2 compiler. Try changing your compilation settings to target AS3.
Also you may need to include the class import at the top of your code:
import flash.events.MouseEvent;
To drag 50 objects, add them all on the same container sprite and add the listener to the container sprite only:
var holder:Sprite = new Sprite();
for ( var i:int = 0, l:int = 50; i < l; i++ ) {
var dragee:YOUR_CUSTOM_OBJECT = new YOUR_CUSTOM_OBJECT();
holder.addChild(dragee);
}
addChild(holder);
holder.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
holder.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
holder.addEventListener(Event.MOUSE_LEAVE, mouseUpHandler);
var currentDragee:YOUR_CUSTOM_OBJECT = null;
function mouseDownHandler(evt:MouseEvent):void {
currentDragee = evt.target as YOUR_CUSTOM_OBJECT;
if ( currentDragee !== null ) {
currentDragee.startDrag();
holder.addChild(currentDragee); // bring current to front position
}
}
function mouseUpHandler(evt:Event):void {
if ( currentDragee !== null ) currentDragee.stopDrag();
currentDragee = null;
}
YOUR_CUSTOM_OBJECT being the object class you need to drag. Hope it helps!
This page seems to have the answers you are looking for (AS2 drag and drop). If you've already seen it, you'll need to explain why it's not good enough for your needs.
If you want to drag/drop multiple instances in AS2, you can still add the code to the movieClip symbol, export it from the library and load the instances up using attachMovie (all 50 of them). If they are all different, then attach the code as necessary to the clips themselves, or to some function elsewhere that will capture all the clicks and decide what was clicked. This is all very doable in AS2.
Remember you can use your onClipEvent(load) function to set up a lot of the initial lifting.
Here's a sample I made in AS2 for making a node tree. It's all draggable (mouse drag) and zoomable (with mouse Wheel). You can add nodes by clicking on the little down arrow in the node box. Each node is listening for the mouse.
You'll want to look at this section for the most part:
// Enable drag on button press
on (press)
{
startDrag(this);
}
// Stop the drag on release of mouse button
on (release)
{
stopDrag();
}
Besides this, I'm not really sure how your setup looks, so I hope this helps get the ball rolling. (Check the link, there's lots of little gems in there).
Flash Professional 8 only supports ActionScript 2 & 1
You can follow this official URL and learn how to do that in ActionScript 2, but I extremely recommend you to work with ActionScript 3.

AS3 Loading multiple external SWFs and them closing them

I am trying to load multiple external SWFs in one main SWF.
I start with the main swf. Now I need to link 4 games to it.
I have created 4 different loaders and I make it load each game into a different frame for each game. Ie when I go to frame 1 for example it will load game1. The code I am using for each game frame is:
public function LoadGame1(evt:MouseEvent):void {
trace("load game1");
this.gotoAndPlay("Game1");
var url:URLRequest = new URLRequest("game1.swf");
game1Loader.load(url);
addChild(game1Loader);
}
game 2 is the same with the exception of game1 it will be game2 etc.
Now my issue is when I close each game and go to another it does not work. I can not load another game or reload it. When I go back to the menu I use:
public function backFunc(evt:MouseEvent):void {
trace("menu");
myLoader.unload();
game1Loader.unload();
game2Loader.unload();
game3Loader.unload();
game4Loader.unload();
this.gotoAndPlay("Menu");
}
I'm assuming it has something to do with the unloading - it unloads it all and closes the loader. Ive tried removeChild(game1Loader) etc but it doesn't work? The game will close and it will go back to the menu but then I will not be able to get back into the game or load another one?
Please help :(
Flash timelines are kind of like a static state machine; moving from frame-to-frame will run all of the document code at that frame (every time). It also resets the value of the content to the state it was in during design time (so, frame = design + code). Because of the headaches this model can cause, I highly recommend you do all of your design & code in a single frame.
The way you've written your sample code, it seems to imply that you're writing custom functions for each of your game loaders. You can unify that by driving all the btn events through the same function and depending on some differentiating property (like their names), load the appropriate game.
Finally, I suspect the reason your loaders stop working is because you've unloaded and removed them from the stage. That stuff gets garbage collected. Rather than making the loader a global object, feel free to create a new one each time you make your loadGame() call.
// We'll store our buttons in an array to make it easier to register for events.
var btns:Array = [
btn_Load1,
btn_Load2,
btn_Load3
]
// Now cycle over them in bind to our listener
for each (var btn:MovieClip in btns) {
btn.addEventListener("mouseUp", btnEvents);
}
function btnEvents(e:MouseEvent):void {
// With one event listener, we can sort out each condition
switch (e.currentTarget.name) {
case "btn_Load1":
loadGame("game1.swf")
break;
case "btn_Load2":
loadGame("game2.swf")
break;
case "btn_Load3":
loadGame("game3.swf")
break;
}
}
function loadGame(url:String):void {
// Now we only have to write our loader code once (for all buttons)
trace("Loading " + url);
// Because we're unloading our swfs (and unloaded assets get garbage collected),
// we'll want to make a new loader each time we call a load op
var loader:Loader = new Loader();
loader.name = "current_game";
loader.load(new URLRequest(url));
addChild(loader);
}
function backFunc(evt:MouseEvent):void {
// Dynamically find our current game, and unload it.
trace("menu");
getChildByName("current_game").unload();
}

away3d change 3ds mesh material on run-time

I'am trying to figure out how to change material on loaded 3ds object/mesh during run-time after mouse click.
(Away3D 3.5/3.6)
3ds object is loaded with Loader3D:
//global mesh variable and view3d
var my_mesh:Mesh;
var view:View3D = new View3D();
//creating a parser with initial material
var max3ds_parser:Max3DS = new Max3DS();
max3ds_parser.material = new WireColorMaterial(0xFF0000);
var loader:Loader3D = new Loader3D();
loader.addEventListener(Loader3DEvent.ON_SUCCESS, onSuccess);
loader.loadGeometry("myMesh.3ds", max3ds_parser);
addChild(view);
addEventListener(Event.ENTER_FRAME, onEnterFrameRenderScene);
function onSuccess(e:Loader3DEvent):void{
my_mesh = Mesh(e.loader.handle);
view.scene.addChild(my_mesh)
}
function onEnterFrameRenderScene(e:Event):void{
my_mesh.rotationY += 15;
view.render();
}
So, after all this the 3ds object is added to the scene with initial material (WireColorMaterial) applied with parser object. But now I want to change the initial material after mouse click, so:
stage.addEventListener(MouseEvent.CLICK, onClick);
function onClick(e:MouseEvent):void{
//start FAIL here:
my_mesh.material = new WireframeMaterial(0x000000);
//end FAIL
trace("clicked!");
trace(my_mesh.material)
}
After the mouse click nothing changes in the view, my_mesh spins as it did with the initial material on. But the trace material shows that the new material was indeed applied.
Is there any other way to do this, or is there some kind of a way to refresh the scene to make it use the new material? Or refresh the view? Or should you somehow parse my_mesh again? Cheers.
I would recommend importing your models via Prefab3D. This will allows you to pre-process all these assets in a visual tool.
I either use the awd format or just export meshes as AS3 classes. Export as AS3 classes also compresses that data with the rest of you assets in the swf, a nice little bonus :)