package com.akjava.gwt.threejsexamples.client.examples.original; import java.util.Map; import com.akjava.gwt.lib.client.LogUtils; import com.akjava.gwt.stats.client.Stats; import com.akjava.gwt.three.client.gwt.GWTParamUtils; import com.akjava.gwt.three.client.gwt.boneanimation.AnimationBone; import com.akjava.gwt.three.client.gwt.ui.LabeledInputRangeWidget2; import com.akjava.gwt.three.client.java.ThreeLog; import com.akjava.gwt.three.client.java.ui.example.AbstractExample; import com.akjava.gwt.three.client.java.ui.experiments.SimpleVector3Editor; import com.akjava.gwt.three.client.java.ui.experiments.SimpleVector3Editor.SimpleVector3EditorListener; import com.akjava.gwt.three.client.java.utils.AnimationUtils; import com.akjava.gwt.three.client.java.utils.GWTThreeUtils; import com.akjava.gwt.three.client.js.THREE; import com.akjava.gwt.three.client.js.animation.AnimationClip; import com.akjava.gwt.three.client.js.animation.AnimationMixer; import com.akjava.gwt.three.client.js.animation.KeyframeTrack; import com.akjava.gwt.three.client.js.animation.tracks.QuaternionKeyframeTrack; import com.akjava.gwt.three.client.js.animation.tracks.VectorKeyframeTrack; import com.akjava.gwt.three.client.js.cameras.PerspectiveCamera; import com.akjava.gwt.three.client.js.core.Clock; import com.akjava.gwt.three.client.js.core.Geometry; import com.akjava.gwt.three.client.js.extras.helpers.SkeletonHelper; import com.akjava.gwt.three.client.js.lights.AmbientLight; import com.akjava.gwt.three.client.js.lights.DirectionalLight; import com.akjava.gwt.three.client.js.math.Euler; import com.akjava.gwt.three.client.js.math.Matrix4; import com.akjava.gwt.three.client.js.math.Quaternion; import com.akjava.gwt.three.client.js.math.Vector3; import com.akjava.gwt.three.client.js.objects.LineSegments; import com.akjava.gwt.three.client.js.objects.Mesh; import com.akjava.gwt.three.client.js.objects.SkinnedMesh; import com.akjava.gwt.three.client.js.renderers.WebGLRenderer; import com.akjava.gwt.three.client.js.scenes.Scene; import com.akjava.gwt.threejsexamples.client.examples.original.ik.IKControler; import com.google.common.collect.Maps; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArray; import com.google.gwt.core.client.JsArrayNumber; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.dom.client.MouseMoveEvent; import com.google.gwt.event.dom.client.MouseMoveHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FocusPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.VerticalPanel; /** * trying line to bone ,but totally faild * @author aki * */ public class BoneAnimationExample extends AbstractExample{ @Override public String getName() { return "BoneAnimation Example"; } @Override public void animate(double timestamp) { render(timestamp); stats.update();//really deprecate?many still use this } private WebGLRenderer renderer; private Scene scene; private PerspectiveCamera camera; private Stats stats; double SCREEN_WIDTH; double SCREEN_HEIGHT; private int windowHalfX,windowHalfY; //private int mouseX,mouseY; Clock clock; private SkinnedMesh mesh; private Vector3 point1; private Vector3 point2; private Vector3 point3; private Vector3 point2Origin; private Vector3 point3Origin; /* public double calcurateXZ(Vector3 p1,Vector3 p2){ Vector3 tmp=THREE.Vector3(p2.getX(),p2.getY(),p2.getZ()); return p1.angleTo(tmp); } public double calcurateY(Vector3 p1,Vector3 p2){ Vector3 tmp=THREE.Vector3(p2.getX(),p2.getY(),p2.getZ()); return p1.angleTo(tmp); } */ private Mesh sphere2; private Mesh sphere3; private Mesh sphere4; @Override public void init() { point1=THREE.Vector3();//abstract is not initialized point2=THREE.Vector3(x,y,z); point3=THREE.Vector3(x2,y2,z2); point2Origin=THREE.Vector3().copy(point2); point3Origin=THREE.Vector3().copy(point3);//origin must be sub clock=THREE.Clock(); SCREEN_WIDTH = getWindowInnerWidth(); SCREEN_HEIGHT = getWindowInnerHeight(); windowHalfX= (int)(SCREEN_WIDTH/2); windowHalfY= (int)(SCREEN_HEIGHT/2); FocusPanel container = createContainerPanel(); container.addMouseMoveHandler(new MouseMoveHandler() { @Override public void onMouseMove(MouseMoveEvent event) { onDocumentMouseMove(event); } }); container.addMouseDownHandler(new MouseDownHandler() { @Override public void onMouseDown(MouseDownEvent event) { doMouseDown(event); } }); // renderer renderer = THREE.WebGLRenderer(GWTParamUtils.WebGLRenderer().preserveDrawingBuffer(true)); renderer.setPixelRatio( GWTThreeUtils.getWindowDevicePixelRatio() ); renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT ); renderer.setClearColor( THREE.Color( 0xffffff ) ); container.getElement().appendChild( renderer.getDomElement() ); // scene scene = THREE.Scene(); // camera double cameraY=700; camera = THREE.PerspectiveCamera(45, getWindowInnerWidth()/getWindowInnerHeight(), .1, 10000); camera.getPosition().set(0, cameraY, 500); camera.lookAt(point1); root = THREE.Mesh(THREE.SphereBufferGeometry(10, 10, 10),THREE.MeshLambertMaterial(GWTParamUtils.MeshLambertMaterial().color(0xff0000))); scene.add(root); root.getPosition().copy(point1); sphere1 = THREE.Mesh(THREE.SphereBufferGeometry(5, 5, 5),THREE.MeshLambertMaterial(GWTParamUtils.MeshLambertMaterial().color(0x0000ff).wireframe(true))); scene.add(sphere1); sphere2 = THREE.Mesh(THREE.SphereBufferGeometry(5, 5, 5),THREE.MeshLambertMaterial(GWTParamUtils.MeshLambertMaterial().color(0x000088).wireframe(true))); scene.add(sphere2); sphere3 = THREE.Mesh(THREE.SphereBufferGeometry(5, 5, 5),THREE.MeshLambertMaterial(GWTParamUtils.MeshLambertMaterial().color(0x00f800).wireframe(true))); scene.add(sphere3); sphere4 = THREE.Mesh(THREE.SphereBufferGeometry(10, 10, 10),THREE.MeshLambertMaterial(GWTParamUtils.MeshLambertMaterial().color(0xf000f0).wireframe(true))); scene.add(sphere4); Geometry box=THREE.BoxGeometry(30, 30, 30); box.setBones(makeBones()); //SkinnedMesh mesh=THREE.SkinnedMesh(box, THREE.MeshLambertMaterial(GWTParamUtils.MeshLambertMaterial().skinning(true).color(0x00ff00).wireframe(true))); scene.add(mesh); mixer=THREE.AnimationMixer(mesh); helper = THREE.SkeletonHelper(mesh); scene.add(helper); //make light AmbientLight ambient = THREE.AmbientLight( 0xaaaaaa );//var ambient = new THREE.AmbientLight( 0xffffff ); scene.add( ambient ); DirectionalLight directionalLight = THREE.DirectionalLight( 0x333333 );//var directionalLight = new THREE.DirectionalLight( 0x444444 ); directionalLight.getPosition().set( -1, 1, 1 ).normalize();//directionalLight.position.set( -1, 1, 1 ).normalize(); scene.add( directionalLight ); //stats stats = Stats.create(); stats.setPosition(0, 0); container.getElement().appendChild(stats.domElement()); //add html info container.add(createAbsoluteHTML( "<div style='text:white'>" + "<a href='http://threejs.org' target='_blank'>three.js</a>" + " - Bone Animtion" + "</div>" ,100,10)); //handle resize & gui gui=initResizeHandlerAndGUI(); //setDebugAnimateOneTimeOnly(true); ikControler = new IKControler(mixer, mesh.getGeometry().getBones()); } public JsArray<AnimationBone> makeBones(){ JsArray<AnimationBone> bones=JavaScriptObject.createArray().cast(); AnimationBone bone=AnimationUtils.createAnimationBone(); bone.setParent(-1); bone.setName("root"); bone.setPos(point1); bones.push(bone); //bone.setRotq(q); AnimationBone bone1=AnimationUtils.createAnimationBone(); bone1.setParent(0); bone1.setName("bone1"); bone1.setPos(point2);//no need sub,root is 000 bones.push(bone1); AnimationBone bone2=AnimationUtils.createAnimationBone(); bone2.setParent(1); bone2.setName("bone2"); bone2.setPos(point3.clone().sub(point2)); bones.push(bone2); return bones; } protected void doMouseDown(MouseDownEvent event) { //double mx=event.getX(); //double my=event.getY(); //point2.setY(point2.getY()+10); //root.lookAt(point2); /* Matrix4 m4=THREE.Matrix4(); m4.lookAt(point2, point1, Object3D.getDefaultUp()); Euler changedEuler = THREE.Euler(0, 0, 0).setFromRotationMatrix(m4); ThreeLog.log(point2); ThreeLog.log(changedEuler); double x=changedEuler.getX()-defaultEuler.getX(); double y=changedEuler.getY()-defaultEuler.getY(); double z=changedEuler.getZ()-defaultEuler.getZ(); //Quaternion q=THREE.Quaternion().setFromEuler(THREE.Euler(x, y, z), false); Quaternion q=THREE.Quaternion().setFromRotationMatrix(m4); //LogUtils.log(q); //make angle //mesh.getSkeleton().getBones().get(0).setRotationFromQuaternion(q); */ /* * i don't know how to update by hand,give up and */ /* mesh.getSkeleton().getBones().get(0).setRot(quaternion); mesh.updateMatrixWorld(true); mesh.setMatrixWorldNeedsUpdate(true); */ } LineSegments selectedLine; private SimpleVector3Editor bone1AngleEditor; private SimpleVector3Editor bone0TranslateEditor; private SimpleVector3Editor bone1TranslateEditor; private VerticalPanel initResizeHandlerAndGUI() { VerticalPanel gui=addResizeHandlerAndCreateGUIPanel(); gui.setWidth("200px");//some widget broke,like checkbox without parent size gui.setSpacing(2); //TODO add other ui or after return; LabeledInputRangeWidget2 xRange=new LabeledInputRangeWidget2("x", -400, 400, 1); xRange.addtRangeListener(new ValueChangeHandler<Number>() { @Override public void onValueChange(ValueChangeEvent<Number> event) { x=event.getValue().doubleValue(); updateRange(); } }); xRange.setValue(x); final LabeledInputRangeWidget2 yRange=new LabeledInputRangeWidget2("y", -400, 400, 1); yRange.addtRangeListener(new ValueChangeHandler<Number>() { @Override public void onValueChange(ValueChangeEvent<Number> event) { y=event.getValue().doubleValue(); updateRange(); } }); yRange.setValue(y); final LabeledInputRangeWidget2 zRange=new LabeledInputRangeWidget2("z", -400, 400, 1); zRange.addtRangeListener(new ValueChangeHandler<Number>() { @Override public void onValueChange(ValueChangeEvent<Number> event) { z=event.getValue().doubleValue(); updateRange(); } }); zRange.setValue(z); gui.add(xRange); gui.add(yRange); gui.add(zRange); gui.add(new HTML("Sphere2")); LabeledInputRangeWidget2 xRange2=new LabeledInputRangeWidget2("x", -200, 200, 1); xRange2.addtRangeListener(new ValueChangeHandler<Number>() { @Override public void onValueChange(ValueChangeEvent<Number> event) { x2=event.getValue().doubleValue(); updateRange(); } }); xRange2.setValue(x2); LabeledInputRangeWidget2 yRange2=new LabeledInputRangeWidget2("y", -200, 200, 1); yRange2.addtRangeListener(new ValueChangeHandler<Number>() { @Override public void onValueChange(ValueChangeEvent<Number> event) { y2=event.getValue().doubleValue(); updateRange(); } }); yRange2.setValue(y2); LabeledInputRangeWidget2 zRange2=new LabeledInputRangeWidget2("z", -200, 200, 1); zRange2.addtRangeListener(new ValueChangeHandler<Number>() { @Override public void onValueChange(ValueChangeEvent<Number> event) { z2=event.getValue().doubleValue(); updateRange(); } }); zRange2.setValue(z2); gui.add(xRange2); gui.add(yRange2); gui.add(zRange2); Button test=new Button("test z=10",new ClickHandler() { @Override public void onClick(ClickEvent event) { // TODO Auto-generated method stub zRange.setValue(10, true); } }); gui.add(test); Button test3=new Button("test y=45",new ClickHandler() { @Override public void onClick(ClickEvent event) { // TODO Auto-generated method stub yRange.setValue(45, true); } }); gui.add(test3); Button test2=new Button("current q",new ClickHandler() { @Override public void onClick(ClickEvent event) { Quaternion q=mesh.getSkeleton().getBones().get(1).getQuaternion(); Euler e=THREE.Euler().setFromQuaternion(q, "XYZ", false); ThreeLog.log("current-angle",e); } }); gui.add(test2); Button test4=new Button("turn bone[0] angle",new ClickHandler() { @Override public void onClick(ClickEvent event) { Vector3 translation=ikControler.getBaseMatrixs().get(1).getPosition();//no move Quaternion q=THREE.Quaternion().setFromEuler(THREE.Euler(0,Math.toRadians(45), 0), false); //doAnimation("test1",0,q,translation,false); //not working yet ikControler.getAnimationBoneData().getBonesAngleAndMatrixs().get(0).getDegreeAngle().setY(45); ikControler.doPose(); } }); gui.add(test4); Button test5=new Button("turn bone[1] angle",new ClickHandler() { @Override public void onClick(ClickEvent event) { Vector3 translation=ikControler.getBaseMatrixs().get(2).getPosition();//no move Quaternion q=THREE.Quaternion().setFromEuler(THREE.Euler(0,Math.toRadians(45), 0), false); ikControler.getAnimationBoneData().getBonesAngleAndMatrixs().get(1).getDegreeAngle().setY(45); ikControler.doPose(); //doAnimation("test2",1,q,translation,false); } }); gui.add(test5); gui.add(new HTML("<h4>Bone[0]</h4>")); gui.add(new Label("Rotate")); bone0AngleEditor = new SimpleVector3Editor(new SimpleVector3EditorListener() { @Override public void onValueChanged(Vector3 value) { updateAngleFromEditor(); } }); gui.add(bone0AngleEditor); gui.add(new Label("Translate(bone1)")); bone0TranslateEditor = new SimpleVector3Editor(new SimpleVector3EditorListener() { @Override public void onValueChanged(Vector3 value) { updateAngleFromEditor(); } }); gui.add(bone0TranslateEditor); gui.add(new HTML("<h4>Bone[1]</h4>")); gui.add(new Label("Rotate")); bone1AngleEditor = new SimpleVector3Editor(new SimpleVector3EditorListener() { @Override public void onValueChanged(Vector3 value) { updateAngleFromEditor(); } }); gui.add(bone1AngleEditor); gui.add(new Label("Translate(bone2)")); bone1TranslateEditor = new SimpleVector3Editor(new SimpleVector3EditorListener() { @Override public void onValueChanged(Vector3 value) { updateAngleFromEditor(); } }); gui.add(bone1TranslateEditor); return gui; } public void updateAngleFromEditor(){ Vector3 bone0=bone0AngleEditor.getValue(); Vector3 bone0r=GWTThreeUtils.degreeToRagiant(bone0); //ThreeLog.log("bone0:",THREE.Euler(bone0r.getX(),bone0r.getY(),bone0r.getZ())); //ThreeLog.log("bone0-t:",bone0TranslateEditor.getValue()); Quaternion q=THREE.Quaternion().setFromEuler(THREE.Euler(bone0r.getX(),bone0r.getY(),bone0r.getZ())); doAnimation("bone0", 0, q, bone0TranslateEditor.getValue(), true); Vector3 bone1=bone1AngleEditor.getValue(); Vector3 bone1r=GWTThreeUtils.degreeToRagiant(bone1); Quaternion q1=THREE.Quaternion().setFromEuler(THREE.Euler(bone1r.getX(),bone1r.getY(),bone1r.getZ())); doAnimation("bone1", 1, q1, bone1TranslateEditor.getValue(), false); } //tried ik base,but faild Map<String,Euler> ikMap; //Map<String,Vector3> ikMap; public void updateRange3(){ point2.set(x, y, z); double length=point2.distanceTo(point1); Vector3 modified=point2Origin.clone().normalize().multiplyScalar(length); bone0TranslateEditor.copy(modified, false); Vector3 baseLine=point2Origin.clone().sub(point1); Euler bone0Euler=findEuler(baseLine,point2.clone().sub(point1)); Quaternion quaternion = THREE.Quaternion().setFromEuler(bone0Euler, false); JsArray<KeyframeTrack> tracks1=makeTrackAnimation("test",0,quaternion,modified,true); point3.set(x2, y2, z2); //where is correct bone Vector3 bone2Origin=point3Origin.clone().sub(point2Origin).applyQuaternion(quaternion).add(point2); sphere3.getPosition().copy(bone2Origin); double length2=point3.distanceTo(point2); Vector3 modified2=point3Origin.clone().sub(point2Origin).normalize().multiplyScalar(length2); Vector3 baseLine2=bone2Origin.clone().sub(point2);//.normalize().multiplyScalar(length2); Euler euler=findEuler(baseLine2,point3.clone().sub(point2)); Quaternion quaternion2 = THREE.Quaternion().setFromEuler(euler, false); bone0AngleEditor.copyWithToDegree(bone0Euler.toVector3(), false); Vector3 tmp=baseLine2.clone().applyQuaternion(quaternion2); tmp.add(point2); sphere4.getPosition().copy(tmp); bone1AngleEditor.copyWithToDegree(euler.toVector3(), false); //quaternion2.multiplyQuaternions(quaternion2, quaternion); JsArray<KeyframeTrack> tracks2=makeTrackAnimation("test1",1,quaternion2,modified2,false); for(int i=0;i<tracks2.length();i++){ tracks1.push(tracks2.get(i)); } AnimationClip clip=THREE.AnimationClip("test1", -1, tracks1); //LogUtils.log(track.validate()); mixer.stopAllAction(); mixer.uncacheClip(clip);//reset can cache? mixer.clipAction(clip).play(); } public Quaternion createAngleQuaternion(double x,double y,double z){ Quaternion q=THREE.Quaternion(); Quaternion xq=THREE.Quaternion().setFromAxisAngle(THREE.Vector3(1, 0, 0), x); q.multiply(xq); Quaternion yq=THREE.Quaternion().setFromAxisAngle(THREE.Vector3(0, 1, 0), y); q.multiply(yq); Quaternion zq=THREE.Quaternion().setFromAxisAngle(THREE.Vector3(0, 0, 1), z); q.multiply(zq); return q; } private Euler findEuler(Vector3 baseLine,Vector3 target){ int xincrement=360; if(ikMap==null){ ikMap=Maps.newHashMap(); for(int x=0;x<360;x+=xincrement){ for(int y=0;y<360;y++){ for(int z=0;z<360;z++){ //ikMap.put(y+","+z,THREE.Euler(0,Math.toRadians( y), Math.toRadians(z),"ZYX")); ikMap.put(x+","+y+","+z,THREE.Euler(Math.toRadians( x),Math.toRadians( y), Math.toRadians(z),"XYZ")); } } } } //pick closed one double diff=0; int closedY=-1; int closedZ=-1; int closedX=-1; for(int x=0;x<360;x+=xincrement){ for(int y=0;y<360;y+=1){ for(int z=0;z<360;z+=1){ if(y==180 && z==180){ continue;//turn } Vector3 v=baseLine.clone().applyEuler(ikMap.get(x+","+y+","+z)); double tmpdiff=v.distanceTo(target); if(closedY==-1){//initial //ThreeLog.log("point2", point2); //ThreeLog.log("v", v); //ThreeLog.log("tmpdiff", tmpdiff); diff=tmpdiff; closedX=x; closedY=y; closedZ=z; }else{ //ThreeLog.log("tmpdiff-"+y+","+z, tmpdiff); if(tmpdiff<diff){ diff=tmpdiff; closedY=y; closedZ=z; closedX=x; } } if(z==0&&y<20){ // LogUtils.log(y+","+z+","+tmpdiff); } } } } //final LogUtils.log(closedY+","+closedZ+","+diff); Euler euler=THREE.Euler(Math.toRadians(closedX), Math.toRadians(closedY), Math.toRadians(closedZ)); return euler; } /* * i give up this,i can' test all situation */ public void updadeRange1(){ point2.set(x, y, z); double length=point2.distanceTo(point1); Vector3 modified=point2Origin.clone().normalize().multiplyScalar(length); /* Quaternion quaternion = THREE.Quaternion().setFromUnitVectors( point2Origin.clone().normalize() ,point2.clone().normalize() ); Euler euler=THREE.Euler(0, 0, 0).setFromQuaternion(quaternion, "XYZ", false); LogUtils.log("bone1-XY"); ThreeLog.log(euler); */ //Y-angle double ny=point2Origin.clone().setY(0).normalize().angleTo(point2.clone().normalize().setY(0)); //Z-angle double nz=point2.clone().setY(0).normalize().angleTo(point2.clone().normalize()); if(z>0){ ny*=-1; } if(y<0){ nz*=-1; } //tryturning without Z LogUtils.log("Y:"+Math.toDegrees(ny)); LogUtils.log("Z:"+Math.toDegrees(nz)); Quaternion q=THREE.Quaternion().setFromEuler(THREE.Euler(0, ny, nz), false); doAnimation("test",0,q,modified,true); //bone2 point3.set(x2, y2, z2); double length2=point3.distanceTo(point2); Vector3 modified2=point3Origin.clone().sub(point2Origin).normalize().multiplyScalar(length2); Vector3 bone2Origin=point3Origin.clone().applyQuaternion(q); LogUtils.log("bone2-origin"); ThreeLog.log(bone2Origin); LogUtils.log("pt2"); LogUtils.log(point2); LogUtils.log("sub"); LogUtils.log(bone2Origin.clone().sub(point2)); LogUtils.log(point3.clone().sub(point2).setY(0)); double ny2=bone2Origin.clone().setY(0).angleTo(point3.clone().setY(0)); //Z-angle double nz2=point3.clone().sub(point2).setY(0).normalize().angleTo(point3.clone().sub(point2).normalize()); if(z2>0){ ny2*=-1; } if(y2<0){ nz2*=-1; } LogUtils.log("Y2:"+Math.toDegrees(ny2)); LogUtils.log("Z2:"+Math.toDegrees(nz2)); Quaternion q2=THREE.Quaternion().setFromEuler(THREE.Euler(0, ny2, nz2), false); q2.multiplyQuaternions(q2, q); doAnimation("test1",1,q2,modified2,false); } public void updateRange(){ updateRange2(); } public void updateRange0(){ ikControler.doPose(); } /* * * use makeRotationFromQuaternion, * somehow broken. * * i'm not sure pos-transform */ //not good when y-changed public void updateRange2(){ point2.set(x, y, z); double length=point2.distanceTo(point1); Vector3 modified=point2Origin.clone().normalize().multiplyScalar(length); bone0TranslateEditor.copy(modified, false); Vector3 subbed=modified.clone().sub(point2Origin); Quaternion quaternion = THREE.Quaternion().setFromUnitVectors( point2Origin.clone().normalize() ,point2.clone().normalize() ); doAnimation("test",0,quaternion,modified,true); Euler euler=THREE.Euler(0, 0, 0).setFromQuaternion(quaternion, "XYZ", false); ThreeLog.log("bone[1]",euler); Matrix4 rotate=THREE.Matrix4().makeRotationFromQuaternion(quaternion); Matrix4 mixed=THREE.Matrix4().makeTranslation(subbed.getX(), subbed.getY(), subbed.getZ()).multiply(rotate); bone0AngleEditor.copyWithToDegree(euler.toVector3(), false); point3.set(x2, y2, z2); Vector3 diffPoint3Sub2=point3.clone().sub(point2); //Vector3 diffPoint3Sub2=point3.clone().sub(point2Origin.applyQuaternion(quaternion)); double length2=point3.distanceTo(point2); Vector3 modified2=point3Origin.clone().sub(point2Origin).normalize().multiplyScalar(length2); bone1TranslateEditor.copy(modified2, false); ThreeLog.log(point3Origin.clone()); LogUtils.log("changed1"); //original position seems good Vector3 originalPos=modified2.clone();//point3Origin.clone().sub(point2Origin);//// //ThreeLog.log("green-line",originalPos); //I'm not sure //originalPos.applyProjection(rotate); originalPos.applyQuaternion(quaternion); //diff.applyMatrix4(mixed); originalPos.add(point2); sphere3.getPosition().copy(originalPos); LogUtils.log("length"); LogUtils.log(originalPos.clone().sub(point2).length()); LogUtils.log(point3.clone().sub(point2).length()); //ThreeLog.log(point3Origin.clone().sub(point2Origin).applyMatrix4(m1)); //streched Vector3 trueOrigin2=originalPos.clone().sub(point2);//modified; Vector3 diffOrigin2=point3.clone().sub(point2); Quaternion quaternion2 = THREE.Quaternion().setFromUnitVectors(trueOrigin2.normalize() ,diffOrigin2.normalize() ); //quaternion2.multiplyQuaternions(quaternion2.clone(),quaternion.inverse());//no need //Quaternion quaternion2 = THREE.Quaternion().setFromUnitVectors( originalPos.clone().sub(point2).normalize() ,diffPoint3Sub2.clone().normalize() ); Vector3 baseLine2=originalPos.clone().sub(point2).normalize().multiplyScalar(length2); Vector3 tmp=baseLine2.clone().applyQuaternion(quaternion2); tmp.add(point2); sphere4.getPosition().copy(tmp); //Matrix4 m2=THREE.Matrix4().makeRotationFromQuaternion(quaternion2); //quaternion2=quaternion2.multiplyQuaternions(quaternion2.clone(),quaternion); Euler euler2=THREE.Euler(0, 0, 0).setFromQuaternion(quaternion2, "XYZ", false); bone1AngleEditor.copyWithToDegree(euler2.toVector3(), false); doAnimation("test1",1,quaternion2,modified2,false); } private double x=100,y,z; private double x2=200,y2=0,z2; private VerticalPanel gui; int mouseX,mouseY; private Euler defaultEuler; private Mesh sphere1; private SkeletonHelper helper; protected void onDocumentMouseMove(MouseMoveEvent event) { mouseX = ( event.getClientX() - windowHalfX ); mouseY = ( event.getClientY() - windowHalfY )*2; } private AnimationMixer mixer; private Mesh root; private IKControler ikControler; private SimpleVector3Editor bone0AngleEditor; public JsArray<KeyframeTrack> makeTrackAnimation(String name,int boneIndex,Quaternion q,Vector3 pos,boolean clearAnimation){ JsArray<KeyframeTrack> tracks=JavaScriptObject.createArray().cast(); JsArrayNumber times=JavaScriptObject.createArray().cast(); times.push(0); //I'm not sure is there reset-pose has value JsArrayNumber values=JsArray.createArray().cast(); concat(values,q.toArray()); QuaternionKeyframeTrack track=THREE.QuaternionKeyframeTrack(".bones["+boneIndex+"].quaternion", times, values); tracks.push(track); //position changed if(pos!=null){ JsArrayNumber times2=JavaScriptObject.createArray().cast(); times2.push(0); //I'm not sure is there reset-pose has value JsArrayNumber values2=JsArray.createArray().cast(); concat(values2,pos.toArray()); VectorKeyframeTrack track2=THREE.VectorKeyframeTrack(".bones["+(boneIndex+1)+"].position", times2, values2); tracks.push(track2); } return tracks; } public void doAnimation(String name,int boneIndex,Quaternion q,Vector3 pos,boolean clearAnimation){ JsArray<KeyframeTrack> tracks=JavaScriptObject.createArray().cast(); JsArrayNumber times=JavaScriptObject.createArray().cast(); times.push(0); //I'm not sure is there reset-pose has value JsArrayNumber values=JsArray.createArray().cast(); concat(values,q.toArray()); QuaternionKeyframeTrack track=THREE.QuaternionKeyframeTrack(".bones["+boneIndex+"].quaternion", times, values); tracks.push(track); //position changed if(pos!=null){ JsArrayNumber times2=JavaScriptObject.createArray().cast(); times2.push(0); //I'm not sure is there reset-pose has value JsArrayNumber values2=JsArray.createArray().cast(); concat(values2,pos.toArray()); VectorKeyframeTrack track2=THREE.VectorKeyframeTrack(".bones["+(boneIndex+1)+"].position", times2, values2); tracks.push(track2); } AnimationClip clip=THREE.AnimationClip(name, -1, tracks); //LogUtils.log(track.validate()); if(clearAnimation){ mixer.stopAllAction(); } mixer.uncacheClip(clip);//reset can cache? mixer.clipAction(clip).play(); } public void concat(JsArrayNumber target,JsArrayNumber values){ for(int i=0;i<values.length();i++){ target.push(values.get(i)); } } public void onWindowResize() { SCREEN_WIDTH = getWindowInnerWidth(); SCREEN_HEIGHT = getWindowInnerHeight(); //re read because of double camera.setAspect(SCREEN_WIDTH / SCREEN_HEIGHT); camera.updateProjectionMatrix(); renderer.setSize( SCREEN_WIDTH , SCREEN_HEIGHT ); } public void render(double now) { sphere1.getPosition().copy(point2); sphere2.getPosition().copy(point3); if(mixer!=null){ mixer.update(1.0/60); } helper.update(); renderer.render(scene, camera); } @Override public String getTokenKey() { return "boneAnimation"; } }