package com.akjava.gwt.poseeditor.client;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.akjava.bvh.client.BVH;
import com.akjava.bvh.client.BVHMotion;
import com.akjava.bvh.client.BVHNode;
import com.akjava.bvh.client.BVHParser;
import com.akjava.bvh.client.BVHParser.InvalidLineException;
import com.akjava.bvh.client.BVHParser.ParserListener;
import com.akjava.bvh.client.BVHWriter;
import com.akjava.gwt.bvh.client.poseframe.PoseEditorData;
import com.akjava.gwt.bvh.client.poseframe.PoseFrameData;
import com.akjava.gwt.bvh.client.threejs.AnimationBoneConverter;
import com.akjava.gwt.bvh.client.threejs.AnimationDataConverter;
import com.akjava.gwt.bvh.client.threejs.BVHConverter;
import com.akjava.gwt.html5.client.InputRangeListener;
import com.akjava.gwt.html5.client.InputRangeWidget;
import com.akjava.gwt.html5.client.download.HTML5Download;
import com.akjava.gwt.html5.client.extra.HTML5Builder;
import com.akjava.gwt.html5.client.file.File;
import com.akjava.gwt.html5.client.file.FileUploadForm;
import com.akjava.gwt.html5.client.file.FileUtils;
import com.akjava.gwt.html5.client.file.FileUtils.DataURLListener;
import com.akjava.gwt.html5.client.file.ui.DropVerticalPanelBase;
import com.akjava.gwt.jsgif.client.GifAnimeBuilder;
import com.akjava.gwt.lib.client.CanvasUtils;
import com.akjava.gwt.lib.client.IStorageControler;
import com.akjava.gwt.lib.client.ImageElementUtils;
import com.akjava.gwt.lib.client.JsonValueUtils;
import com.akjava.gwt.lib.client.LogUtils;
import com.akjava.gwt.lib.client.StorageControler;
import com.akjava.gwt.lib.client.StorageException;
import com.akjava.gwt.lib.client.datalist.SimpleTextData;
import com.akjava.gwt.poseeditor.client.PreferenceTabPanel.PreferenceListener;
import com.akjava.gwt.poseeditor.client.resources.PoseEditorBundles;
import com.akjava.gwt.three.client.examples.renderers.Projector;
import com.akjava.gwt.three.client.examples.utils.GeometryUtils;
import com.akjava.gwt.three.client.gwt.GWTParamUtils;
import com.akjava.gwt.three.client.gwt.JSONModelFile;
import com.akjava.gwt.three.client.gwt.JSParameter;
import com.akjava.gwt.three.client.gwt.boneanimation.AngleAndPosition;
import com.akjava.gwt.three.client.gwt.boneanimation.AnimationBone;
import com.akjava.gwt.three.client.gwt.boneanimation.AnimationBonesData;
import com.akjava.gwt.three.client.gwt.boneanimation.AnimationData;
import com.akjava.gwt.three.client.gwt.boneanimation.AnimationHierarchyItem;
import com.akjava.gwt.three.client.gwt.boneanimation.AnimationKey;
import com.akjava.gwt.three.client.gwt.boneanimation.BoneLimit;
import com.akjava.gwt.three.client.gwt.boneanimation.NameAndVector3;
import com.akjava.gwt.three.client.gwt.boneanimation.ik.CDDIK;
import com.akjava.gwt.three.client.gwt.boneanimation.ik.IKData;
import com.akjava.gwt.three.client.gwt.core.BoundingBox;
import com.akjava.gwt.three.client.gwt.core.Intersect;
import com.akjava.gwt.three.client.gwt.materials.MeshBasicMaterialParameter;
import com.akjava.gwt.three.client.gwt.materials.MeshLambertMaterialParameter;
import com.akjava.gwt.three.client.java.GWTDragObjectControler;
import com.akjava.gwt.three.client.java.ThreeLog;
import com.akjava.gwt.three.client.java.animation.WeightBuilder;
import com.akjava.gwt.three.client.java.ui.SimpleTabDemoEntryPoint;
import com.akjava.gwt.three.client.java.utils.GWTGeometryUtils;
import com.akjava.gwt.three.client.java.utils.GWTThreeUtils;
import com.akjava.gwt.three.client.java.utils.GWTThreeUtils.InvalidModelFormatException;
import com.akjava.gwt.three.client.java.utils.Object3DUtils;
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.Camera;
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.core.Object3D;
import com.akjava.gwt.three.client.js.extras.ImageUtils;
import com.akjava.gwt.three.client.js.extras.geometries.BoxGeometry;
import com.akjava.gwt.three.client.js.extras.helpers.GridHelper;
import com.akjava.gwt.three.client.js.extras.helpers.SkeletonHelper;
import com.akjava.gwt.three.client.js.lights.Light;
import com.akjava.gwt.three.client.js.loaders.JSONLoader.JSONLoadHandler;
import com.akjava.gwt.three.client.js.materials.Material;
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.math.Vector4;
import com.akjava.gwt.three.client.js.objects.Group;
import com.akjava.gwt.three.client.js.objects.Line;
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.three.client.js.textures.Texture;
import com.akjava.lib.common.utils.ColorUtils;
import com.akjava.lib.common.utils.FileNames;
import com.akjava.lib.common.utils.ValuesUtils;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.gwt.canvas.client.Canvas;
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.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.ContextMenuEvent;
import com.google.gwt.event.dom.client.ContextMenuHandler;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.LoadEvent;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.dom.client.MouseWheelEvent;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.text.shared.Renderer;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.DoubleBox;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.user.client.ui.ValueListBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
/**
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class PoseEditor extends SimpleTabDemoEntryPoint implements PreferenceListener{
public static Logger logger = Logger.getLogger(PoseEditor.class.getName());
private BVH bvh;
protected JsArray<AnimationBone> animationBones;
private AnimationData animationData;
public static DateTimeFormat dateFormat=DateTimeFormat.getFormat("yy/MM/dd HH:mm");
private String version="7.0(for three.r74)";
private static boolean debug;
private static final String KEY_TRANSPARENT="poseeditor_key_transparent";
private static final String KEY_BASIC_MATERIAL="poseeditor_key_basicmaterial";
@Override
protected void beforeUpdate(WebGLRenderer renderer) {
if(rootObject!=null){
/*
root.getScale().set(upscale,upscale,upscale);
if(bone3D!=null){
//bone3D.getScale().set(upscale,upscale,upscale);
double itemScale=(1.0/upscale);
for(int i=0;i<bone3D.getChildren().length();i++){
if(bone3D.getChildren().get(i) instanceof Mesh){
bone3D.getChildren().get(i).getScale().set(itemScale,itemScale,itemScale);
}
}
}
*/
//usually positionZRange is zero ,controlled by camera pos
setRootPositionByRange(positionXRange.getValue(),positionYRange.getValue(),positionZRange.getValue());
rootObject.getRotation().set(Math.toRadians(rotationXRange.getValue()),Math.toRadians(rotationYRange.getValue()),Math.toRadians(rotationZRange.getValue()),Euler.XYZ);
//camera rotation style
//cameraHolder.setPosition((double)positionXRange.getValue()/10, (double)positionYRange.getValue()/10, (double)positionZRange.getValue()/10);
//cameraHolder.getRotation().set(Math.toRadians(rotationXRange.getValue()),-Math.toRadians(rotationYRange.getValue()),Math.toRadians(rotationZRange.getValue()));
//camera.lookAt(zero);
//camera.getPosition().set(cameraX, cameraY, cameraZ);
/*
Vector3 newPos=THREE.Vector3(0, 0, 100);
Quaternion prev=GWTThreeUtils.degreeRotationQuaternion(THREE.Vector3(rotationXRange.getValue(),0,0));
Quaternion next=GWTThreeUtils.degreeRotationQuaternion(THREE.Vector3(rotationXRange.getValue(),180,0));
Quaternion result=THREE.Quaternion();
double scale=1.0/360*rotationYRange.getValue() ;
//log("scale:"+scale);
THREE.Quaternion().slerp( prev, next, result, scale);
Matrix4 mx=GWTThreeUtils.rotationToMatrix4(result);
//log("newangle:"+GWTThreeUtils.rotationToDegreeVector3(mx));
mx.multiplyVector3(newPos);
*/
//newPos=GWTThreeUtils.rotationToVector3(result);
//newPos=GWTThreeUtils.degreeToRagiant(newPos);
}
}
public void setRootPositionRangeValues(int x,int y,int z){
positionXRange.setValue(x);
positionYRange.setValue(y);
setCameraZ((double)z/10);//z is special
//positionZRange.setValue(z);
}
public void setRootPositionByRange(int x,int y,int z){
rootObject.setPosition((double)x/posDivided, (double)y/posDivided, (double)z/posDivided);
}
public int getRootPositionXRange(){
return positionXRange.getValue();
}
public int getRootPositionYRange(){
return positionYRange.getValue();
}
/*right now ze not used
public int getRootPositionZRange(){
return positionZRange.getValue();
}
*/
private Object3D cameraHolder;
@Override
protected void updateCamera(Scene scene,int width,int height){
if(cameraHolder==null){
cameraHolder=THREE.Object3D();
scene.add(cameraHolder);
}
if(camera!=null){
//TODO find update way.
cameraHolder.remove(camera);
camera=null;
}
Camera camera = THREE.PerspectiveCamera(45,(double)width/height,0.001,600);
//camera.getPosition().set(0, 0, cameraZ);
cameraHolder.add(camera); //some kind of trick.
this.camera=camera;
}
Clock clock;
public void render(){
renderer.clear();
if(mixer!=null){
mixer.update(clock.getDelta());
}
if(skeltonHelper!=null){
skeltonHelper.update();
}
//some how this way not work,I'll do with scene2
renderer.render(scene, camera);
}
@Override
public void update(WebGLRenderer renderer) {
if(isUsingRenderer){//create gifanime or video
return;
}
beforeUpdate(renderer);
camera.getPosition().set(cameraX, cameraY, cameraZ);
//LogUtils.log("camera:"+ThreeLog.get(camera.getPosition()));
render();
//it's better to do in render update-loop
if(reservedScreenshot){
doScreenShot(renderer);
reservedScreenshot=false;
}
if(reservedCreateGifAnime){
doGifAnime();
reservedCreateGifAnime=false;
}
if(reservedSettingPreview){
updateBackgroundVisible(settingPanel.isPreviewGifShowBackground());
updateBonesVisible(settingPanel.isPreviewGifShowBone());
updateIKVisible(settingPanel.isPreviewGifShowIk());
renderer.render(scene, camera);
String url=canvas.getRenderer().gwtPngDataUrl();
settingPanel.setPreviewImage(url);
updateBackgroundVisible(showBackgroundCheck.getValue());
updateBonesVisible(showBonesCheck.getValue());
updateIKVisible(ikVisibleCheck.getValue());
reservedSettingPreview=false;
}
}
public boolean isReservedSettingPreview() {
return reservedSettingPreview;
}
public void setReservedSettingPreview(boolean reservedSettingPreview) {
this.reservedSettingPreview = reservedSettingPreview;
}
public void selectMainTab(){
tabPanel.selectTab(0);
}
@Override
public void resized(int width, int height) {
super.resized(width, height);
leftBottom(bottomPanel);
}
private WebGLRenderer renderer;
public static final String KEY_INDEX="DATA_INDEX";
public static final String KEY_DATA="DATA_VALUE";
public static final String KEY_IMAGE="DATA_IMAGE";
public static final String KEY_HEAD="DATA_HEAD";
public class ContextMenu implements ContextMenuHandler{
@Override
public void onContextMenu(ContextMenuEvent event) {
event.preventDefault();
event.stopPropagation();
showContextMenu(event.getNativeEvent().getClientX(), event.getNativeEvent().getClientY());
}
}
private GWTDragObjectControler dragObjectControler;
public static class Logger {
boolean enabled=false;
public static Logger getLogger(String name){
return new Logger();
}
public void fine(String log){
if(enabled){
LogUtils.log(log);
}
}
public void info(String log) {
if(enabled){
LogUtils.log(log);
}
}
}
private BoneLimit oppositeRL(BoneLimit baseLimit){
BoneLimit limit=new BoneLimit();
limit.setMinXDegit(baseLimit.getMinXDegit());
limit.setMaxXDegit(baseLimit.getMaxXDegit());
limit.setMinYDegit(baseLimit.getMaxYDegit()*-1);
limit.setMaxYDegit(baseLimit.getMinYDegit()*-1);
limit.setMinZDegit(baseLimit.getMaxZDegit()*-1);
limit.setMaxZDegit(baseLimit.getMinZDegit()*-1);
limit.setMinX(Math.toRadians(limit.getMinXDegit()));
limit.setMaxX(Math.toRadians(limit.getMaxXDegit()));
limit.setMinY(Math.toRadians(limit.getMinYDegit()));
limit.setMaxY(Math.toRadians(limit.getMaxYDegit()));
limit.setMinZ(Math.toRadians(limit.getMinZDegit()));
limit.setMaxZ(Math.toRadians(limit.getMaxZDegit()));
return limit;
}
private void createIKAndLimitBone(){
//TODO load from file
//bone limit is very important otherwise ik really slow
//make human1.0 bones
//head ik
//this is for rese pose and not work correctly
/*
ikdatas.add(createIKData(Lists.newArrayList("head","neck","chest","abdomen"),9));
boneLimits.put("abdomen",BoneLimit.createBoneLimit(-30, 30, -60, 60, -30, 30));
boneLimits.put("chest",BoneLimit.createBoneLimit(-30, 30, -40, 40, -40, 40));
boneLimits.put("neck",BoneLimit.createBoneLimit(-34, 34, -34, 34, -34, 34));
//righ-hands,now modifiing
ikdatas.add(createIKData(Lists.newArrayList("rHand","rForeArm","rShldr","rCollar"),9));
boneLimits.put("rForeArm",BoneLimit.createBoneLimit(-30, 60, -60, 90, 0,0));
boneLimits.put("rShldr",BoneLimit.createBoneLimit(-90, 60, -75, 80, -120, 60));
boneLimits.put("rCollar",BoneLimit.createBoneLimit(0,0,-20,0,-40,0));
//left-hand
ikdatas.add(createIKData(Lists.newArrayList("lHand","lForeArm","lShldr","lCollar"),9));
boneLimits.put("lForeArm",oppositeRL(boneLimits.get("rForeArm")));
boneLimits.put("lShldr",oppositeRL(boneLimits.get("rShldr")));
boneLimits.put("lCollar",oppositeRL(boneLimits.get("rCollar")));
//right leg
ikdatas.add(createIKData(Lists.newArrayList("rFoot","rShin","rThigh"),5));
boneLimits.put("rShin",BoneLimit.createBoneLimit(0, 160, 0, 0, -0, 0));
boneLimits.put("rThigh",BoneLimit.createBoneLimit(-90, 90, -30, 30, -60, 45));
ikdatas.add(createIKData(Lists.newArrayList("lFoot","lShin","lThigh"),5));
boneLimits.put("lShin",oppositeRL(boneLimits.get("rShin")));
boneLimits.put("lThigh",oppositeRL(boneLimits.get("rThigh")));
*/
//mbl3d
ikdatas.add(createIKData(Lists.newArrayList("head","neck","spine03","spine02","spine01"),9));//,"spline03","spline02","spline01"
boneLimits.put("neck",BoneLimit.createBoneLimit(-30, 30, 0,0, -30, 30));
boneLimits.put("spine03",BoneLimit.createBoneLimit(-30, 30, 0, 0, -30, 30));
boneLimits.put("spine02",BoneLimit.createBoneLimit(-30, 30, 0, 0, -30, 30));
boneLimits.put("spine01",BoneLimit.createBoneLimit(-30, 30, 0, 0, -30, 30));
ikdatas.add(createIKData(Lists.newArrayList("hand_R","lowerarm_R","upperarm_R","clavicle_R"),9));
boneLimits.put("lowerarm_R",BoneLimit.createBoneLimit(-30, 15, 0, 140, 0, 0));
boneLimits.put("upperarm_R",BoneLimit.createBoneLimit(-90, 80, -60, 91, -20, 100));
boneLimits.put("clavicle_R",BoneLimit.createBoneLimit(0,0,-20,10,-60,0));
ikdatas.add(createIKData(Lists.newArrayList("hand_L","lowerarm_L","upperarm_L","clavicle_L"),9));
boneLimits.put("lowerarm_L",oppositeRL(boneLimits.get("lowerarm_R")));
boneLimits.put("upperarm_L",oppositeRL(boneLimits.get("upperarm_R")));
boneLimits.put("clavicle_L",oppositeRL(boneLimits.get("clavicle_R")));
ikdatas.add(createIKData(Lists.newArrayList("foot_R","calf_R","thigh_R"),9));
boneLimits.put("calf_R",BoneLimit.createBoneLimit(0, 160, 0, 0, 0, 0));
boneLimits.put("thigh_R",BoneLimit.createBoneLimit(-120, 60, -35, 5, -80, 40));
ikdatas.add(createIKData(Lists.newArrayList("foot_L","calf_L","thigh_L"),9));
boneLimits.put("calf_L",oppositeRL(boneLimits.get("calf_R")));
boneLimits.put("thigh_L",oppositeRL(boneLimits.get("thigh_R")));
//head
/*
ikdatas.add(createIKData(Lists.newArrayList("head","neck","chest","abdomen"),9));
boneLimits.put("abdomen",BoneLimit.createBoneLimit(-30, 30, -60, 60, -30, 30));
boneLimits.put("chest",BoneLimit.createBoneLimit(-30, 30, -40, 40, -40, 40));//chest not stable if have more Y
boneLimits.put("neck",BoneLimit.createBoneLimit(-34, 34, -34, 34, -34, 34));
//righ-hand
ikdatas.add(createIKData(Lists.newArrayList("rHand","rForeArm","rShldr","rCollar"),9));
boneLimits.put("rForeArm",BoneLimit.createBoneLimit(0, 0, 0, 140, 0, 0));
boneLimits.put("rShldr",BoneLimit.createBoneLimit(-80, 60, -60, 91, -40, 100));
boneLimits.put("rCollar",BoneLimit.createBoneLimit(0,0,-20,0,-40,0));
//left-hand
ikdatas.add(createIKData(Lists.newArrayList("lHand","lForeArm","lShldr","lCollar"),9));
boneLimits.put("lForeArm",oppositeRL(boneLimits.get("rForeArm")));
boneLimits.put("lShldr",oppositeRL(boneLimits.get("rShldr")));
boneLimits.put("lCollar",oppositeRL(boneLimits.get("rCollar")));
//right leg
ikdatas.add(createIKData(Lists.newArrayList("rFoot","rShin","rThigh"),5));
boneLimits.put("rShin",BoneLimit.createBoneLimit(0, 160, 0, 0, 0, 0));
boneLimits.put("rThigh",BoneLimit.createBoneLimit(-120, 60, -35, 5, -80, 40));
ikdatas.add(createIKData(Lists.newArrayList("lFoot","lShin","lThigh"),5));
boneLimits.put("lShin",oppositeRL(boneLimits.get("rShin")));
boneLimits.put("lThigh",oppositeRL(boneLimits.get("rThigh")));
//old datas,right now just catch critibal bug
IKData ikdata3=new IKData("LeftUpLeg-LeftLeg");
//ikdata.setTargetPos(THREE.Vector3(0, -10, 0));
ikdata3.setLastBoneName("LeftFoot");
ikdata3.setBones(new String[]{"LeftLeg","LeftUpLeg"});
ikdata3.setIteration(5);
ikdatas.add(ikdata3);
boneLimits.put("RightForeArm",BoneLimit.createBoneLimit(-40, 10, 0, 140, -30, 10));
boneLimits.put("RightArm",BoneLimit.createBoneLimit(-80, 60, -75, 91, -70, 115));
boneLimits.put("LeftForeArm",BoneLimit.createBoneLimit(-40, 10, -140, 0, -10, 30));
boneLimits.put("LeftArm",BoneLimit.createBoneLimit(-80, 60, -91, 75, -115, 70));
boneLimits.put("RightLeg",BoneLimit.createBoneLimit(0, 160, 0, 0, 0, 20));
boneLimits.put("RightUpLeg",BoneLimit.createBoneLimit(-120, 60, -35, 5, -80, 40));
boneLimits.put("LeftLeg",BoneLimit.createBoneLimit(0, 160, 0, 0, -20, 0));
boneLimits.put("LeftUpLeg",BoneLimit.createBoneLimit(-120, 60, -5, 35, -40, 80));
boneLimits.put("LowerBack",BoneLimit.createBoneLimit(-30, 30, -60, 60, -30, 30));
boneLimits.put("Spine",BoneLimit.createBoneLimit(-30, 30, -40, 40, -40, 40));
//boneLimits.put("Spine1",BoneLimit.createBoneLimit(-30, 30, -30, 30, -30, 30));
boneLimits.put("Neck",BoneLimit.createBoneLimit(-29, 29, -29, 29, -29, 29));
boneLimits.put("Neck1",BoneLimit.createBoneLimit(-5, 5, -5, 5, -5, 5));
*/
}
private Scene scene2;
public static PoseEditor poseEditor;
@Override
protected void initializeOthers(WebGLRenderer renderer) {
scene2=THREE.Scene();
renderer.setAutoClear(false);
poseEditor=this;
cameraZ=500/posDivided;
clock=THREE.Clock();
this.renderer=renderer;
canvas.addDomHandler(new ContextMenu(), ContextMenuEvent.getType());
storageControler = new StorageControler();
this.renderer=renderer;
//maybe canvas is transparent
//renderer has already setted 0x333333.this is somekind confirm?
//canvas.setClearColorHex(0x333333);//qustion,what happen if no canvas.
//recently i feel this is good,less flick and
renderer.setClearColor(0, 0);
canvas.getElement().getStyle().setBackgroundColor("#333");
dragObjectControler=new GWTDragObjectControler(scene,projector);
//scene.add(THREE.AmbientLight(0xffffff));
Light pointLight = THREE.DirectionalLight(0xffffff,1);
pointLight.setPosition(0, 10, 300);
scene.add(pointLight);
Light pointLight2 = THREE.DirectionalLight(0xffffff,1);//for fix back side dark problem
pointLight2.setPosition(0, 10, -300);
//scene.add(pointLight2);
rootObject=THREE.Object3D();
scene.add(rootObject);
group1 = THREE.Group();
group2 = THREE.Group();
rootObject.add(group1);
rootObject.add(group2);
//background;
Geometry geo=THREE.PlaneGeometry(1000/posDivided*2, 1000/posDivided*2,20,20);
planeMesh = THREE.Mesh(geo, THREE.MeshBasicMaterial(GWTParamUtils.MeshBasicMaterial().color(0xaaaaaa).side(THREE.DoubleSide)
.transparent(true).opacity(0.5)));
group1.add(planeMesh);
planeMesh.getRotation().set(Math.toRadians(-90), 0, 0);
int size=1000/posDivided;
int step=size/20;
step=Math.max(1, step);
backgroundGrid = THREE.GridHelper(1000/posDivided, step);
group1.add(backgroundGrid);
//line removed,because of flicking
//Object3D xline=GWTGeometryUtils.createLineMesh(THREE.Vector3(-50, 0, 0.001), THREE.Vector3(50, 0, 0.001), 0x880000,3);
//root.add(xline);
//Object3D zline=GWTGeometryUtils.createLineMesh(THREE.Vector3(0, 0, -50), THREE.Vector3(0, 0, 50), 0x008800,3);
//root.add(zline);
double selectionSize=baseBoneCoreSize*2.5/posDivided;
selectionMesh=THREE.Mesh(THREE.BoxGeometry(selectionSize,selectionSize,selectionSize), THREE.MeshBasicMaterial(
GWTParamUtils.MeshBasicMaterial().color(0x00ff00).wireframe(true)
));
group2.add(selectionMesh);
selectionMesh.setVisible(false);
createIKAndLimitBone();
//line flicked think something
//delay make problem
//loadBVH("pose.bvh");//initial bone
/*
IKData ikdata1=new IKData("LowerBack-Neck1");
//ikdata1.setTargetPos(THREE.Vector3(0, 20, 0));
ikdata1.setLastBoneName("Head");
ikdata1.setBones(new String[]{"Neck1","Neck","Spine","LowerBack"});
//ikdata1.setBones(new String[]{"Neck1","Neck","Spine1","Spine","LowerBack"});
ikdata1.setIteration(9);
ikdatas.add(ikdata1);
IKData ikdata0=new IKData("RightArm-RightForeArm");
//ikdata0.setTargetPos(THREE.Vector3(-10, 5, 0));
ikdata0.setLastBoneName("RightHand");
ikdata0.setBones(new String[]{"RightForeArm","RightArm"});
// ikdata0.setBones(new String[]{"RightForeArm","RightArm","RightShoulder"});
ikdata0.setIteration(7);
ikdatas.add(ikdata0);
//
IKData ikdata=new IKData("RightUpLeg-RightLeg");
//ikdata.setTargetPos(THREE.Vector3(0, -10, 0));
ikdata.setLastBoneName("RightFoot");
ikdata.setBones(new String[]{"RightLeg","RightUpLeg"});
ikdata.setIteration(5);
ikdatas.add(ikdata);
IKData ikdata2=new IKData("LeftArm-LeftForeArm");
//ikdata0.setTargetPos(THREE.Vector3(-10, 5, 0));
ikdata2.setLastBoneName("LeftHand");
//ikdata2.setBones(new String[]{"LeftForeArm","LeftArm","LeftShoulder"});
ikdata2.setBones(new String[]{"LeftForeArm","LeftArm"});
ikdata2.setIteration(7);
ikdatas.add(ikdata2);
*/
//
//updateIkLabels();
//calcurate by bvh 80_*
/*
boneLimits.put("RightForeArm",BoneLimit.createBoneLimit(-118, 0, 0, 60, -170, 0));
boneLimits.put("RightArm",BoneLimit.createBoneLimit(-180, 180, -60, 91, -180, 180));
boneLimits.put("RightShoulder",BoneLimit.createBoneLimit(0, 0, 0, 0,0, 0));
boneLimits.put("LeftForeArm",BoneLimit.createBoneLimit(-40, 10, -170, 0, 0, 0));
boneLimits.put("LeftArm",BoneLimit.createBoneLimit(-80, 60, -91, 40, -120, 50));
boneLimits.put("LeftShoulder",BoneLimit.createBoneLimit(-15, 25, -20, 20,-10, 10));
boneLimits.put("RightLeg",BoneLimit.createBoneLimit(0, 160, 0, 0, 0, 20));
boneLimits.put("RightUpLeg",BoneLimit.createBoneLimit(-85, 91, -35, 5, -80, 40));
boneLimits.put("LeftLeg",BoneLimit.createBoneLimit(0, 160, 0, 0, -20, 0));
boneLimits.put("LeftUpLeg",BoneLimit.createBoneLimit(-85, 91, -5, 35, -40, 80));
boneLimits.put("LowerBack",BoneLimit.createBoneLimit(-30, 30, -60, 60, -30, 30));
boneLimits.put("Spine",BoneLimit.createBoneLimit(-30, 30, -40, 40, -40, 40));
//boneLimits.put("Spine1",BoneLimit.createBoneLimit(-30, 30, -30, 30, -30, 30));
boneLimits.put("Neck",BoneLimit.createBoneLimit(-45, 45, -45, 45, -45, 45));
boneLimits.put("Neck1",BoneLimit.createBoneLimit(-15, 15, -15, 15, -15, 15));
*/
//there are gimbal lock problem angle must be under 90
/*
* to manual change to joint angle,keep under 90 is better.
but gimbal lock problem happend alreay at IK result converted to eular angle
*/
/*
boneLimits.put("RightForeArm",BoneLimit.createBoneLimit(-89, 10, 0, 89, -10, 10));
boneLimits.put("RightArm",BoneLimit.createBoneLimit(-80, 60, -40, 89, -50,89));
boneLimits.put("LeftForeArm",BoneLimit.createBoneLimit(-89, 10, -89.9, 0, -10, 10));
boneLimits.put("LeftArm",BoneLimit.createBoneLimit(-80, 60, -89, 40, -89, 50));
boneLimits.put("RightLeg",BoneLimit.createBoneLimit(0, 89, 0, 0, 0, 40));
boneLimits.put("RightUpLeg",BoneLimit.createBoneLimit(-85, 89, -35, 5, -80, 40));
boneLimits.put("LeftLeg",BoneLimit.createBoneLimit(0, 89, 0, 0, -40, 0));
boneLimits.put("LeftUpLeg",BoneLimit.createBoneLimit(-85, 89, -5, 35, -40, 80));
boneLimits.put("LowerBack",BoneLimit.createBoneLimit(-30, 30, -60, 60, -30, 30));
boneLimits.put("Spine",BoneLimit.createBoneLimit(-30, 30, -40, 40, -40, 40));
//boneLimits.put("Spine1",BoneLimit.createBoneLimit(-30, 30, -30, 30, -30, 30));
boneLimits.put("Neck",BoneLimit.createBoneLimit(-35, 35, -35, 35, -35, 35));
boneLimits.put("Neck1",BoneLimit.createBoneLimit(-5, 5, -5, 5, -5, 5));
*/
//manual
//for initialize texture
texture=ImageUtils.loadTexture("blondhair_tshirt.png");//initial one //TODO change this.
//generateTexture();
//initial model to avoid async use clientbundle same as "tpose.bvh"
parseInitialBVHAndLoadModels(PoseEditorBundles.INSTANCE.pose().getText());
//model is loaded usually -1 index in modelName.txt on Bundles.
createTabs();
updateDatasPanel();
//
addShortcuts();
}
private void addShortcuts() {
canvas.addKeyDownHandler(new KeyDownHandler() {
@Override
public void onKeyDown(KeyDownEvent event) {
if(event.getNativeKeyCode()==KeyCodes.KEY_SHIFT || event.getNativeKeyCode()==KeyCodes.KEY_ALT){
return;//ignore them.
}
if(event.getNativeKeyCode()==KeyCodes.KEY_ENTER){
if(bone3D!=null && bone3D.getChildren().length()>0){
selectBone(bone3D.getChildren().get(0),0,0,false);
}
}else if(event.getNativeKeyCode()==KeyCodes.KEY_PAGEUP){
doPrevFrame();
}else if(event.getNativeKeyCode()==KeyCodes.KEY_PAGEDOWN){
doNextFrame();
}else if(event.getNativeKeyCode()==KeyCodes.KEY_HOME){
doFirstFrame();
}else if(event.getNativeKeyCode()==KeyCodes.KEY_TAB){
loopTabSelection(event.isShiftKeyDown());//shift key has problem
}else{
int code=event.getNativeKeyCode();
if(code==45){//Add last
insertFrame(getSelectedPoseEditorData().getPoseFrameDatas().size(),false);
}else if(code==32){//space
touchGroundZero();
}else{
//LogUtils.log(event.getNativeKeyCode());
}
}
}
});
}
/**
* bone mesh's name is same as bone name;
* @param boneName
* @return
*/
private Mesh getBoneMesh(String boneName){
if(bone3D==null){
return null;
}
for(int i=0;i<bone3D.getChildren().length();i++){
if(bone3D.getChildren().get(i).getName().equals(boneName)){
return (Mesh)bone3D.getChildren().get(i);
}
}
return null;
}
protected void loopTabSelection(boolean shiftKeyDown) {
if(isSelectedIk()){
List<IKData> ikLists=Lists.newArrayList(getAvaiableIkdatas());
int index=ikLists.indexOf(getCurrentIkData());
if(index==-1){
LogUtils.log("loopTabSelection() invalid ik selected:"+getCurrentIkData());
return;
}
if(shiftKeyDown){
index--;
if(index<0){
index=ikLists.size()-1;
}
}else{
index++;
if(index>=ikLists.size()){
index=0;
}
}
Object3D object=getIkObject3D(ikLists.get(index));
selectIk(object, 0, 0,false);
}else if(isSelectedBone()){
int index=boneList.indexOf(selectedBone);
LogUtils.log("loop:"+index);
if(index==-1){
LogUtils.log("loopTabSelection() invalid bone selected:"+selectedBone);
return;
}
if(shiftKeyDown){
index--;
if(index<0){
index=boneList.size()-1;
}
}else{
index++;
if(index>=boneList.size()){
index=0;
}
}
String newName=boneList.get(index);
Mesh boneMesh=getBoneMesh(newName);
if(boneMesh==null){
LogUtils.log("loopTabSelection() boneMesh not exist:"+newName);
return;
}
selectBone(boneMesh,0,0,false);
//loop bone
}else{
for(IKData ik:getAvaiableIkdatas()){
Object3D object=getIkObject3D(ik);
selectIk(object, 0, 0,false);
break;
}
//List<IKData> iks=Lists.newArrayList(getAvaiableIkdatas());
}
}
private Object3D getIkObject3D(IKData ik){
for(int i=0;i<ik3D.getChildren().length();i++){
Object3D object=ik3D.getChildren().get(i);
if(object.getName().equals("ik:"+ik.getLastBoneName())){
return object;
}
}
return null;
}
private IKData createIKData(List<String> names,int iteration){
List<String> boneNames=Lists.newArrayList(names);
String last=boneNames.remove(0);//something name is strange
IKData mhikdata3=new IKData(Joiner.on("-").join(boneNames));
//ikdata.setTargetPos(THREE.Vector3(0, -10, 0));
mhikdata3.setLastBoneName(last);
mhikdata3.setBones(boneNames.toArray(new String[0]));//what is this?
mhikdata3.setIteration(iteration);//what is this?
return mhikdata3;
}
private int defaultOffSetY=-40;
private void updateDatasPanel(){
datasPanel.clear();
try{
int index=storageControler.getValue(KEY_INDEX, 0);
for(int i=index;i>=0;i--){
//String b64=storageControler.getValue(KEY_IMAGE+i,null);
String json=storageControler.getValue(KEY_DATA+i,null);
String head=storageControler.getValue(KEY_HEAD+i,null);
if(json!=null){
DataPanel dp=new DataPanel(i,head,null,json);
//dp.setSize("200px", "200px");
datasPanel.add(dp);
}
}
}catch (StorageException e) {
LogUtils.log("updateDatasPanel faild:"+e.getMessage());
}
}
public class DataPanel extends HorizontalPanel{
private int index;
private String name;
private long cdate;
private String json;
public DataPanel(final int dataIndex,String head,String base64, String text){
this.setSpacing(4);
json=text;
this.index=dataIndex;
//right now stop using image.
Image img=new Image();
if(base64!=null){
img.setUrl(base64);
}else{
img.setVisible(false);
}
this.setVerticalAlignment(ALIGN_MIDDLE);
String name_cdate[]=head.split("\t");
name=name_cdate[0];
cdate=(long)(Double.parseDouble(name_cdate[1]));
String dlabel=dateFormat.format(new Date(cdate));
add(new Label(dlabel));
add(img);
final Label nameLabel=new Label(name);
nameLabel.setWidth("160px");
add(nameLabel);
Button loadBt=new Button("Load");
add(loadBt);
loadBt.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
int loadedIndex=isLoaded(dataIndex);
LogUtils.log("loadedIndex:"+loadedIndex);
if(loadedIndex!=-1){
//if already exist remove from list & alwasy recrete.because possiblly model's bone is alway difference.
poseEditorDatas.remove(loadedIndex);
LogUtils.log("old data is removed");
}
PoseEditorData ped=PoseEditorData.readData(json);
if(ped!=null){
ped.setFileId(dataIndex);
doLoad(ped);
}else{
//TODO error catch
Window.alert("load faild");
}
}
});
Button cloneBt=new Button("Clone");
add(cloneBt);
cloneBt.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
doClone(json);
}
});
Button editBt=new Button("Edit Name");
add(editBt);
editBt.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
JSONValue jsonValue=JSONParser.parseStrict(json);
JSONObject ped=jsonValue.isObject();
if(ped!=null){
String newName=Window.prompt("Change Name",name);
//ped.setName(newName);
name=newName;
ped.put("name", new JSONString(name));
json=ped.toString();
nameLabel.setText(name);
//JSONObject data=PoseEditorData.writeData(ped);
try{
storageControler.setValue(KEY_DATA+index, json);
storageControler.setValue(KEY_HEAD+index, name+"\t"+cdate);
}catch (StorageException e) {
LogUtils.log("Edit name faild:"+e.getMessage());
}
//rewrite
}else{
//TODO error catch
Window.alert("load faild");
}
}
});
Button removeBt=new Button("Delate");
add(removeBt);
removeBt.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
boolean ret=Window.confirm("Delete data:"+name);
if(ret){
doRemoveData(index);
}
}
});
Button exportBt=new Button("Export");
//add(exportBt); //stop support ,TODO replace json
exportBt.addClickHandler(new ClickHandler() {
private Anchor anchor;
@Override
public void onClick(ClickEvent event) {
PoseEditorData ped=PoseEditorData.readData(json);
String bvhText=convertBVHText(ped);
if(anchor!=null){
anchor.removeFromParent();
}
HTML5Download html5=new HTML5Download();
anchor = html5.generateTextDownloadLink(bvhText, nameLabel.getText()+".bvh", "Click to download",true);
add(anchor);
}
});
Button rawBt=new Button("Raw Json");
//add(rawBt); //this is for debug
rawBt.addClickHandler(new ClickHandler() {
private Anchor anchor;
@Override
public void onClick(ClickEvent event) {
if(anchor!=null){
anchor.removeFromParent();
}
HTML5Download html5=new HTML5Download();
JSONValue jsonValue=JSONParser.parseLenient(json);//possible error if json is invalid
anchor = html5.generateTextDownloadLink(JsonValueUtils.stringify(jsonValue.isObject().getJavaScriptObject()), "raw"+".json", "Click to download",true);
add(anchor);
}
});
}
protected void doClone(String json) {
PoseEditorData pdata=PoseEditorData.readData(json);
pdata.setName(pdata.getName()+"-copy");
JSONObject data=PoseEditorData.convertToJson(pdata);
//TODO
if(!storageControler.isAvailable()){
//TODO just export
Window.alert("not saved because your browser not supoort HTML5 storage");
return;
}
// Window.alert("hello");
//save database
int dataIndex=0;
try {
dataIndex = getNewDataIndex();
} catch (StorageException e) {
alert("save faild:"+e.getMessage());
return;
}
//TODO method?
//Canvas canvas=Canvas.createIfSupported();
/*
int thumbW=32;
int thumbH=32;
canvas.setSize(thumbW+"px", thumbH+"px");
canvas.setCoordinateSpaceWidth(thumbW);
canvas.setCoordinateSpaceHeight(thumbH);
//log(renderer.gwtCanvas());
//now stop write image.
//canvas.getContext2d().drawImage(renderer.gwtCanvas(),0,0,screenWidth,screenHeight,0,0,thumbW,thumbH);
String thumbnail=canvas.toDataUrl();
LogUtils.log(thumbnail);
*/
// Window.alert("hello1");
//Window.alert("hello1");
//Window.open(thumbnail, "tmp", null);
try{
storageControler.setValue(KEY_DATA+dataIndex, data.toString());
// Window.alert("hello2");
//storageControler.setValue(KEY_IMAGE+dataIndex, thumbnail);
storageControler.setValue(KEY_HEAD+dataIndex, pdata.getName()+"\t"+pdata.getCdate());
// Window.alert("hello3:"+dataIndex);
pdata.setFileId(dataIndex);
//increment
dataIndex++;
storageControler.setValue(KEY_INDEX, dataIndex);
}catch (Exception e) {
Window.alert("storage error:"+e.getMessage());
}
updateDatasPanel();
}
}
private int isLoaded(int index){
for(int i=0;i<poseEditorDatas.size();i++){
PoseEditorData data=poseEditorDatas.get(i);
if(data.getFileId()==index){
return i;
}
}
return -1;
}
private void doRemoveData(int index){
try{
storageControler.setValue(KEY_DATA+index, null);
storageControler.setValue(KEY_IMAGE+index, null);
storageControler.setValue(KEY_HEAD+index, null);
}catch (StorageException e) {
LogUtils.log("do remove data faild:"+e.getMessage());
}
updateDatasPanel();
}
private void createTabs(){
tabPanel.addSelectionHandler(new SelectionHandler<Integer>() {
@Override
public void onSelection(SelectionEvent<Integer> event) {
int selection=event.getSelectedItem();
if(selection==0){
stats.setVisible(true);
showControl();
bottomPanel.setVisible(true);
popupPanel.setVisible(true);
}else{
stats.setVisible(false);
bottomPanel.setVisible(false);
hideControl();
popupPanel.setVisible(false);
if(selection==3){//settings
settingPanel.synchUI();
reservedSettingPreview=true;
//isUsingRenderer=true;
}
}
resized(screenWidth,screenHeight);//for some blackout;
}
});
VerticalPanel datasRoot=new VerticalPanel();
tabPanel.add(datasRoot,"Motion & Pose");
HorizontalPanel dataButtons=new HorizontalPanel();
datasRoot.add(dataButtons);
FileUploadForm importBVH=FileUtils.createSingleTextFileUploadForm(new DataURLListener() {
@Override
public void uploaded(File file, String value) {
BVHParser parser=new BVHParser();
int dataIndex;
try {
dataIndex = getNewDataIndex();
} catch (StorageException e1) {
alert("faild getnewdataindex");
return;
}
JSONObject object=null;
try {
BVH bvh=parser.parse(value);
object=new PoseEditorDataConverter().convert(bvh);
} catch (InvalidLineException e) {
alert("invalid bvh:"+file.getFileName());
return;
}
String name=FileNames.getRemovedExtensionName(file.getFileName());
long ctime=System.currentTimeMillis();
object.put("name", new JSONString(name));
object.put("cdate", new JSONNumber(ctime));//
try {
storageControler.setValue(KEY_DATA+dataIndex, object.toString());
storageControler.setValue(KEY_HEAD+dataIndex,name+"\t"+ctime);
dataIndex++;
storageControler.setValue(KEY_INDEX, dataIndex);
} catch (StorageException e) {
try {
storageControler.removeValue(KEY_DATA+dataIndex);
storageControler.removeValue(KEY_HEAD+dataIndex);
} catch (StorageException e1) {
}
alert("data store faild:"+e.getMessage());
}
updateDatasPanel();
}
}, true);
importBVH.setAccept(".bvh");
/*
stop support
dataButtons.add(new Label("Import BVH"));
dataButtons.add(importBVH);
*/
datasPanel = new VerticalPanel();
//datasPanel.setStyleName("debug");
ScrollPanel scroll=new ScrollPanel(datasPanel);
scroll.setSize("800px", "500px");
datasRoot.add(scroll);
try {
logger.fine("selection:"+storageControler.getValue(PreferenceTabPanel.KEY_MODEL_SELECTION, 0));
} catch (StorageException e) {
e.printStackTrace();
}
//storageControler.setValue(PreferenceTabPanel.KEY_MODEL_SELECTION, 0);
preferencePanel=new PreferenceTabPanel(storageControler,this);
tabPanel.add(preferencePanel,"Model & Texture");
settingPanel=new SettingPanel();
tabPanel.add(settingPanel,"Settings");
//about
String html="";
html+="<br/>"+"[Howto Move]<br/><b>Select Nothing:</b><br/>Mouse Drag=Cammera Rotatation-XY<br/>Mouse Wheel= Zoom<br/> +ALT Move-XY Camera";
html+="<br/><br/>"+"<b>Select IK(Green Box):</b><br/>Mouse Drag=Move IK-XYZ <br/>Mouse Wheel=Move IK-Z <br/>+ALT=smoth-change <br/>+Shift=Move Only<br/>+ALT+Shift Follow other IK";
html+="<br/><br/>"+"<b>Select Bone(Red Box):</b><br/>Mouse Drag=Rotate-XY<br/>Mouse Wheel=Rotate-Z";
html+="<br/><br/>"+"<b>Select Root(Red Large Box):</b><br/>Mouse Drag=Rotate-XYZ<br/>Mouse Wheel=Rotate-Z +ALT=Follow IK +Shift=Move Position";
html+="<br/><br/>"+"yello box means under Y:0,orange box means near Y:0";
html+="<br/>On IK-Moving switching normal & +Alt(Smooth) is good tactics.";
html+="<br/>"+"<a href='http://webgl.akjava.com'>More info at webgl.akjava.com</a>";
HTML htmlP=new HTML(html);
VerticalPanel aboutRoot=new VerticalPanel();
//TODO credit
aboutRoot.add(htmlP);
tabPanel.add(aboutRoot,"About");
}
public boolean isUsingRenderer() {
return isUsingRenderer;
}
public void setUsingRenderer(boolean isUsingRenderer) {
this.isUsingRenderer = isUsingRenderer;
}
SettingPanel settingPanel;
PreferenceTabPanel preferencePanel;
public static Map<String,BoneLimit> boneLimits=new HashMap<String,BoneLimit>();
NumberFormat numberFormat= NumberFormat.getFormat("0.0");
private void updateIkPositionLabel(){
//i'm not sure why value x 10 times;
ikPositionLabelX.setText("Ik-X:"+numberFormat.format(getCurrentIkData().getTargetPos().getX()*10));
ikPositionLabelY.setText("Ik-Y:"+numberFormat.format(getCurrentIkData().getTargetPos().getY()*10));
ikPositionLabelZ.setText("Ik-Z:"+numberFormat.format(getCurrentIkData().getTargetPos().getZ()*10));
}
private void updateIkLabels(){
//log(""+boneNamesBox);
boneNamesBox.clear();
if(currentSelectionIkName!=null){
setEnableBoneRanges(false,false,true);//no root
updateIkPositionLabel();
//getCurrentIkData().getTargetPos().getX()
boneNamesBox.addItem("");
for(int i=0;i<getCurrentIkData().getBones().size();i++){
boneNamesBox.addItem(getCurrentIkData().getBones().get(i));
}
boneNamesBox.addItem(getCurrentIkData().getLastBoneName());//need this too
boneNamesBox.setSelectedIndex(0);
}else if(selectedBone!=null){
setEnableBoneRanges(true,true,false);
boneNamesBox.addItem(selectedBone);
boneNamesBox.setSelectedIndex(0);
updateBoneRanges();
}else{
setEnableBoneRanges(false,false,false);//no selection
}
if(boneNamesBox.getItemCount()==0){
rotateAndPosList.setEnabled(false);
boneRotationsPanel.setVisible(false);
bonePositionsPanel.setVisible(false);
}else{
rotateAndPosList.setEnabled(true);
if(rotateAndPosList.getSelectedIndex()==0){
boneRotationsPanel.setVisible(true);
}else{
bonePositionsPanel.setVisible(true);
}
}
}
private void setEnableBoneRanges(boolean rotate,boolean pos,boolean ikPos){
bonePositionsPanel.setVisible(pos);
boneRotationsPanel.setVisible(rotate);
rotationBoneXRange.setEnabled(rotate);
rotationBoneYRange.setEnabled(rotate);
rotationBoneZRange.setEnabled(rotate);
positionXBoneRange.setEnabled(pos);
positionYBoneRange.setEnabled(pos);
positionZBoneRange.setEnabled(pos);
//ik pos
ikPositionsPanel.setVisible(ikPos);
//TODO x,y
}
int ikdataIndex=1;
private List<IKData> ikdatas=new ArrayList<IKData>();
private String currentSelectionIkName;//TODO not use last bone name
Mesh selectionMesh;
final Projector projector=Projector.createProjector();
@Override
public void onMouseClick(ClickEvent event) {
//not work correctly on zoom
//Vector3 pos=GWTUtils.toWebGLXY(event.getX(), event.getY(), camera, screenWidth, screenHeight);
// targetPos.setX(pos.getX());
//targetPos.setY(pos.getY());
//doCDDIk();
//doPoseIkk(0);
}
private boolean isSelectedIk(){
return currentSelectionIkName!=null;
}
//TODO fix
//here is so slow.
private void switchSelectionIk(String name){
currentSelectionIkName=name;
currentMatrixs=AnimationBonesData.cloneAngleAndMatrix(ab.getBonesAngleAndMatrixs());
if(currentSelectionIkName!=null){
List<List<NameAndVector3>> result=createIKBases(getCurrentIkData());
//log("switchd:"+result.size());
/*for debug
List<NameAndVector3> tmp=result.get(result.size()-1);
for(NameAndVector3 value:tmp){
// log(value.getName()+":"+ThreeLog.get(value.getVector3()));
}
*/
if(candiateAngleAndMatrixs!=null){
candiateAngleAndMatrixs.clear();
}else{
candiateAngleAndMatrixs=new ArrayList<List<AngleAndPosition>>();
}
//must be lower .to keep lower add limit bone inside IK
//LogUtils.log("safe heresome how in danger:"+name);
int index=0;
for(List<NameAndVector3> nv:result){
//log("candiate:"+index);
List<AngleAndPosition> bm=mergeMeshMatrix(ab);
applyMatrix(bm, nv);
//for debug;
for(String bname:getCurrentIkData().getBones()){
Vector3 angle=bm.get(ab.getBoneIndex(bname)).getDegreeAngle();
//log(bname+":"+ThreeLog.get(angle));
}
candiateAngleAndMatrixs.add(bm);
index++;
}
}else{
//LogUtils.log("currentSelectionIkName not selected yet:"+name);
}
/*
LogUtils.log("end switchSelectionIk:"+name);
if(true){
return;
}
*/
updateIkLabels();
}
Map<IKData,List<List<NameAndVector3>>> ikBasesMap=new HashMap<IKData,List<List<NameAndVector3>>>();
public List<List<NameAndVector3>> createIKBases(IKData data){
if(ikBasesMap.get(data)!=null){
return ikBasesMap.get(data);
}
//int angle=30;
int angle=25;//how smooth?
//need change angle step if need more
if(data.getLastBoneName().equals("rShldr") || data.getLastBoneName().equals("lShldr") ){
angle=10; //chest is important?,tried but not so effected.
}
List<List<NameAndVector3>> all=new ArrayList<List<NameAndVector3>>();
List<List<NameAndVector3>> result=new ArrayList<List<NameAndVector3>>();
for(int i=0;i<data.getBones().size();i++){
String name=data.getBones().get(i);
List<NameAndVector3> patterns=createIKBases(name,angle); //90 //60 is slow
all.add(patterns);
//log(name+"-size:"+patterns.size());
}
//log(data.getLastBoneName()+"-joint-size:"+all.size());
addBase(all,result,data.getBones(),0,null,2);
ikBasesMap.put(data,result);//store
if(result.size()>1500){
LogUtils.log("warn many result-size:"+result.size()+" this almost freeze everything. are you forget limit bone.");
}
return result;
}
private static void addBase(List<List<NameAndVector3>> all,
List<List<NameAndVector3>> result, List<String> boneNames, int index,List<NameAndVector3> tmp,int depth) {
if(index>=boneNames.size() || index==depth){
result.add(tmp);
return;
}
if(index==0){
tmp=new ArrayList<NameAndVector3>();
}
for(NameAndVector3 child:all.get(index)){
//copied
List<NameAndVector3> list=new ArrayList<NameAndVector3>();
for(int i=0;i<tmp.size();i++){
list.add(tmp.get(i));
}
list.add(child);
addBase(all,result,boneNames,index+1,list,2);
}
}
private static List<NameAndVector3> createIKBases(String name,int step){
Set<NameAndVector3> patterns=new HashSet<NameAndVector3>();
BoneLimit limit=boneLimits.get(name);
/*
for(int x=-180;x<180;x+=step){
for(int y=-180;y<180;y+=step){
for(int z=-180;z<180;z+=step){
boolean pass=true;
if(limit!=null){
if(limit.getMinXDegit()>x || limit.getMaxXDegit()<x){
pass=false;
}
if(limit.getMinYDegit()>y || limit.getMaxYDegit()<y){
pass=false;
}
if(limit.getMinZDegit()>z || limit.getMaxZDegit()<z){
pass=false;
}
}
if(x==180||x==-180 || y==180||y==-180||z==180||z==-180){
//pass=false;//no need to limit?
}
if(pass){
log(name+" pass:"+x+","+y+","+z);
NameAndVector3 nvec=new NameAndVector3(name, Math.toRadians(x),Math.toRadians(y),Math.toRadians(z));
patterns.add(nvec);
}
}
}
}*/
//0 - -180
for(int x=0;x>=-180;x-=step){
for(int y=0;y>=-180;y-=step){
for(int z=0;z>=-180;z-=step){
boolean pass=true;
if(limit!=null){
if(limit.getMinXDegit()>x || limit.getMaxXDegit()<x){
pass=false;
}
if(limit.getMinYDegit()>y || limit.getMaxYDegit()<y){
pass=false;
}
if(limit.getMinZDegit()>z || limit.getMaxZDegit()<z){
pass=false;
}
}
if(x==180||x==-180 || y==180||y==-180||z==180||z==-180){
//pass=false;//no need to limit?
}
if(pass){
// log(name+" pass:"+x+","+y+","+z);
NameAndVector3 nvec=new NameAndVector3(name, Math.toRadians(x),Math.toRadians(y),Math.toRadians(z));
patterns.add(nvec);
}
}
}
}
//step - 179
for(int x=0;x<180;x+=step){
for(int y=0;y<180;y+=step){
for(int z=0;z<180;z+=step){
boolean pass=true;
if(limit!=null){
if(limit.getMinXDegit()>x || limit.getMaxXDegit()<x){
pass=false;
}
if(limit.getMinYDegit()>y || limit.getMaxYDegit()<y){
pass=false;
}
if(limit.getMinZDegit()>z || limit.getMaxZDegit()<z){
pass=false;
}
}
if(x==180||x==-180 || y==180||y==-180||z==180||z==-180){
//pass=false;//no need to limit?
}
if(pass){
// log(name+" pass:"+x+","+y+","+z);
NameAndVector3 nvec=new NameAndVector3(name, Math.toRadians(x),Math.toRadians(y),Math.toRadians(z));
patterns.add(nvec);
}
}
}
}
if(patterns.size()==0){
logger.fine(name+":use zero base");
patterns.add(new NameAndVector3(name,0,0,0));//empty not allowd
}
return new ArrayList<NameAndVector3>(patterns);
}
PopupPanel contextMenu;
private void showContextMenu(int left,int top){
if(contextMenu==null){
createContextMenu();
}
contextMenu.setPopupPosition(left, top);
updateContextMenu();
contextMenu.show();
}
private List<MenuItem> dynamicMenues=new ArrayList<MenuItem>();
//use enabled ,strange behavior when use visible
private void updateContextMenu() {
//remove old
for(MenuItem item:dynamicMenues){
if(item.getParentMenu()!=null){//usually never possible
item.getParentMenu().removeItem(item);
}
}
dynamicMenues.clear();
if(isSelectedIk()){
dynamicMenues.add(rootBar.addItem("selected Ik",ikSelectedBar));
}else if(isSelectedBone()){
dynamicMenues.add(rootBar.addItem("selected Bone",boneSelecteddBar));
}else{
}
}
private void hideContextMenu(){
if(contextMenu!=null){
contextMenu.hide();
}
}
private Predicate<IKData> existIkPredicate=new Predicate<IKData>() {
@Override
public boolean apply(IKData input) {
return existBone(input.getLastBoneName());
}
};
private Iterable<IKData> getAvaiableIkdatas(){
return Iterables.filter(ikdatas,existIkPredicate);
}
CopiedIk copiedIk;
CopiedBone copiedBone;
private class CopiedBone{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Vector3 getAngle() {
return angle;
}
public void setAngle(Vector3 angle) {
this.angle = angle;
}
public Vector3 getPosition() {
return position;
}
public void setPosition(Vector3 position) {
this.position = position;
}
public CopiedBone(String name, Vector3 angle, Vector3 position) {
super();
this.name = name;
this.angle = angle;
this.position = position;
}
private Vector3 angle;
private Vector3 position;
}
private class CopiedIk{
private String ikName;
public String getIkName() {
return ikName;
}
public void setIkName(String ikName) {
this.ikName = ikName;
}
public List<String> getNames() {
return names;
}
public List<Vector3> getAngles() {
return angles;
}
private List<String> names=new ArrayList<String>();//bone name
private List<Vector3> angles=new ArrayList<Vector3>();
private void clear(){
names.clear();
angles.clear();
}
private void add(String boneName,Vector3 angle){
names.add(boneName);
angles.add(angle.clone());
}
}
private void doPasteIk(){
if(copiedIk==null){
return;
}
boolean needSwap=false;
if(isSelectedIk()){
String ikName=getCurrentIkData().getLastBoneName();//TODO fix handle last bone name;
if(!copiedIk.getIkName().equals(ikName)){
if(copiedIk.getIkName().equals(getMirroredName(ikName))){
needSwap=true;
}else{
Window.alert("not supported paste selection ik selected ="+ikName+" copied "+copiedIk.getIkName());
return;
}
}
}
//do paste
for(int i=0;i<copiedIk.getNames().size();i++){
String targetName;
if(needSwap){
targetName=getMirroredName(copiedIk.getNames().get(i));
}else{
targetName=copiedIk.getNames().get(i);
}
if(targetName==null){
continue;
}
int index=ab.getBoneIndex(targetName);
if(index!=-1 ){
if(needSwap){
rotToBone(targetName, copiedIk.getAngles().get(i).getX(), -copiedIk.getAngles().get(i).getY(), -copiedIk.getAngles().get(i).getZ(),true);
}else{
rotToBone(targetName, copiedIk.getAngles().get(i).getX(), copiedIk.getAngles().get(i).getY(), copiedIk.getAngles().get(i).getZ(),true);
}
}
}
}
private void doPasteBone(){
if(copiedBone==null){
LogUtils.log("not copied bone yet");
return;
}
int srcIndex=ab.getBoneIndex(copiedBone.getName());
if(srcIndex!=-1){
ab.getBoneAngleAndMatrix(srcIndex).getPosition().copy(copiedBone.getPosition());
ab.getBoneAngleAndMatrix(srcIndex).getDegreeAngle().copy(copiedBone.getAngle());
ab.getBoneAngleAndMatrix(srcIndex).updateMatrix();
fitIkOnBone();
doPoseByMatrix(ab);
}else{
LogUtils.log("invalid bone selected:"+copiedBone.getName());
}
}
private void doCopyBone(){
if(isSelectedBone()){
String name=getSelectedBoneName();
int srcIndex=ab.getBoneIndex(name);
if(srcIndex!=-1){
Vector3 angle=ab.getBoneAngleAndMatrix(srcIndex).getDegreeAngle().clone();
Vector3 pos=ab.getBoneAngleAndMatrix(srcIndex).getPosition().clone();
if(copiedBone==null){
copiedBone=new CopiedBone(name, angle, pos);
}else{
copiedBone.setName(name);
copiedBone.setAngle(angle);
copiedBone.setPosition(pos);
}
}else{
LogUtils.log("invalid bone selected:"+name);
}
}
}
private void doCopyIk(boolean copyLastBone){
if(isSelectedIk()){
if(copiedIk==null){
copiedIk=new CopiedIk();
}else{
copiedIk.clear();
}
IKData ik=getCurrentIkData();
copiedIk.setIkName(ik.getLastBoneName());//TODO fix better ik-name by preset
List<String> boneNames=Lists.newArrayList(ik.getBones());
if(copyLastBone){
boneNames.add(ik.getLastBoneName());
}
for(String name:boneNames){
int srcIndex=ab.getBoneIndex(name);
if(srcIndex!=-1){
Vector3 angle=ab.getBoneAngleAndMatrix(srcIndex).getDegreeAngle();
copiedIk.add(name,angle);
}
}
}
}
private void createBoneSelectedMenu(MenuBar parent){
parent.addItem("Copy", new Command(){
@Override
public void execute() {
doCopyBone();
hideContextMenu();
}});
}
private void createIkSelectedMenu(MenuBar parent){
parent.addItem("Copy", new Command(){
@Override
public void execute() {
doCopyIk(false);
hideContextMenu();
}});
parent.addItem("Copy with lastBone", new Command(){
@Override
public void execute() {
doCopyIk(true);
hideContextMenu();
}});
parent.addItem("Paste", new Command(){
@Override
public void execute() {
doPasteIk();
fitIkOnBone();
hideContextMenu();
}});
parent.addItem("Move to Prev Frame Ik-pos", new Command(){
@Override
public void execute() {
if(isSelectedIk() && isCurrentHasPrevFrame()){
String boneName=getCurrentIkData().getLastBoneName();
Vector3 pos=getBonePositionAtFrame(boneName,poseFrameDataIndex-1);
if(pos!=null){
getCurrentIkData().getTargetPos().copy(pos);
syncIkPosition();
}
}
hideContextMenu();
}});
parent.addItem("Move to Current Frame Ik-pos", new Command(){
@Override
public void execute() {
if(isSelectedIk()){
String boneName=getCurrentIkData().getLastBoneName();
Vector3 pos=getBonePositionAtFrame(boneName,poseFrameDataIndex);
if(pos!=null){
getCurrentIkData().getTargetPos().copy(pos);
syncIkPosition();
}
}
hideContextMenu();
}});
parent.addItem("Move to Next Frame Ik-pos", new Command(){
@Override
public void execute() {
if(isSelectedIk() && isCurrentHasNextFrame()){
String boneName=getCurrentIkData().getLastBoneName();
Vector3 pos=getBonePositionAtFrame(boneName,poseFrameDataIndex+1);
if(pos!=null){
getCurrentIkData().getTargetPos().copy(pos);
syncIkPosition();
}
}
hideContextMenu();
}});
parent.addItem("Move to Between Frame Ik-pos", new Command(){
@Override
public void execute() {
if(isSelectedIk() && isCurrentHasNextFrame() && isCurrentHasPrevFrame()){
String boneName=getCurrentIkData().getLastBoneName();
Vector3 pos=getBonePositionAtFrame(boneName,poseFrameDataIndex-1).clone();//to modify to clone
Vector3 next=getBonePositionAtFrame(boneName,poseFrameDataIndex+1);
if(pos!=null && next!=null){
pos.add(next).divideScalar(2);
getCurrentIkData().getTargetPos().copy(pos);
syncIkPosition();
}
}
hideContextMenu();
}});
MenuBar moveToIk=new MenuBar(true);
parent.addItem("Root move to selection IK-Pos", moveToIk);
moveToIk.addItem("Pos-X", new Command(){
@Override
public void execute() {
if(!isSelectedIk()){
hideContextMenu();
return;
}
Vector3 target=getCurrentIkData().getTargetPos();
Vector3 rootPos=ab.getBonePosition(0);
Vector3 diff=target.clone().sub(rootPos);
diff.setY(0);
diff.setZ(0);
ab.getBoneAngleAndMatrix(0).setPosition(rootPos.add(diff));
ab.getBoneAngleAndMatrix(0).updateMatrix();
doPoseByMatrix(ab);
hideContextMenu();
}});
moveToIk.addItem("Pos-Y", new Command(){
@Override
public void execute() {
if(!isSelectedIk()){
hideContextMenu();
return;
}
Vector3 target=getCurrentIkData().getTargetPos();
Vector3 rootPos=ab.getBonePosition(0);
Vector3 diff=target.clone().sub(rootPos);
diff.setX(0);
diff.setZ(0);
ab.getBoneAngleAndMatrix(0).setPosition(rootPos.add(diff));
ab.getBoneAngleAndMatrix(0).updateMatrix();
doPoseByMatrix(ab);
hideContextMenu();
}});
moveToIk.addItem("Pos-Z", new Command(){
@Override
public void execute() {
if(!isSelectedIk()){
hideContextMenu();
return;
}
Vector3 target=getCurrentIkData().getTargetPos();
Vector3 rootPos=ab.getBonePosition(0);
Vector3 diff=target.clone().sub(rootPos);
diff.setY(0);
diff.setX(0);
ab.getBoneAngleAndMatrix(0).setPosition(rootPos.add(diff));
ab.getBoneAngleAndMatrix(0).updateMatrix();
doPoseByMatrix(ab);
hideContextMenu();
}});
moveToIk.addItem("Pos-All", new Command(){
@Override
public void execute() {
if(!isSelectedIk()){
hideContextMenu();
return;
}
Vector3 target=getCurrentIkData().getTargetPos();
Vector3 rootPos=ab.getBonePosition(0);
Vector3 diff=target.clone().sub(rootPos);
ab.getBoneAngleAndMatrix(0).setPosition(rootPos.add(diff));
ab.getBoneAngleAndMatrix(0).updateMatrix();
doPoseByMatrix(ab);
hideContextMenu();
/*
for(IKData ik:getAvaiableIkdatas()){
ik.getTargetPos().addSelf(diff);
doPoseByMatrix(ab);
hideContextMenu();
}
*/
}});
}
private MenuBar ikSelectedBar,boneSelecteddBar;
private void createContextMenu(){
contextMenu=new PopupPanel();
rootBar = new MenuBar(true);
contextMenu.add(rootBar);
rootBar.setAutoOpen(true);
ikSelectedBar=new MenuBar(true);
createIkSelectedMenu(ikSelectedBar);
boneSelecteddBar=new MenuBar(true);
createBoneSelectedMenu(boneSelecteddBar);
MenuBar ikBar=new MenuBar(true);
rootBar.addItem("Ik",ikBar);
ikBar.addItem("Exec hard", new Command(){
@Override
public void execute() {
execIk();
hideContextMenu();
}});
ikBar.addItem("Exec mild", new Command(){
@Override
public void execute() {
execIk(5,5);
hideContextMenu();
}});
ikBar.addItem("Fit ik on bone", new Command(){
@Override
public void execute() {
if(isSelectedIk()){
AnimationBonesData merged=new AnimationBonesData(animationBones,mergeMeshMatrix(ab));
//only do selected ik
Vector3 pos=merged.getBonePosition(getCurrentIkData().getLastBoneName());
getCurrentIkData().getTargetPos().set(pos.getX(), pos.getY(), pos.getZ());
}else{
fitIkOnBone();//do all
}
doPoseByMatrix(ab);//really need?
hideContextMenu();
}});
ikBar.addItem("Follow target", new Command(){
@Override
public void execute() {
if(isSelectedIk()){
AnimationBonesData merged=getMergedAnimationBonesData(ab);
if(existBone(getCurrentIkData().getLastBoneName())){
getCurrentIkData().getTargetPos().copy(getDefaultIkPos(merged.getBoneIndex(getCurrentIkData().getLastBoneName())));
//doPoseByMatrix(ab);
}
}else{
followTarget();//do all
}
doPoseByMatrix(ab);
hideContextMenu();
}});
ikBar.addItem("Paste", new Command(){
@Override
public void execute() {
doPasteIk();
fitIkOnBone();
hideContextMenu();
}});
ikBar.addItem("Y-Zero", new Command(){
@Override
public void execute() {
for(IKData ik:getAvaiableIkdatas()){
String name=ik.getLastBoneName();
Vector3 pos=ab.getBonePosition(name);
ik.getTargetPos().setY(0);
doPoseByMatrix(ab);
hideContextMenu();
}
}});
ikBar.addItem("Move to First-IK-XZ", new Command(){
@Override
public void execute() {
for(IKData ik:getAvaiableIkdatas()){
String name=ik.getBones().get(0);
Vector3 pos=ab.getBonePosition(name);
ik.getTargetPos().setX(pos.getX());
ik.getTargetPos().setZ(pos.getZ());
doPoseByMatrix(ab);
hideContextMenu();
}
}});
ikBar.addItem("Move to Last-IK-XZ", new Command(){
@Override
public void execute() {
for(IKData ik:getAvaiableIkdatas()){
String name=ik.getBones().get(ik.getBones().size()-1);
Vector3 pos=ab.getBonePosition(name);
ik.getTargetPos().setX(pos.getX());
ik.getTargetPos().setZ(pos.getZ());
doPoseByMatrix(ab);
hideContextMenu();
}
}});
createContextMenuRoot(rootBar);
MenuBar boneBar=new MenuBar(true);
MenuItem boneLimitMenuItem = new MenuItem("Bone",boneBar);//menu item can change label dynamic
boneBar.addItem("Paste",new Command(){
@Override
public void execute() {
doPasteBone();
updateBoneRanges();
hideContextMenu();
}});
rootBar.addItem(boneLimitMenuItem);
boneBar.addItem("Change bones'limit to none", new Command(){
@Override
public void execute() {
if(!isSelectedIk()){
hideContextMenu();
return;
}
IKData ik=getCurrentIkData();
for(String boneName:ik.getBones()){
boneLock.clearX(boneName);
boneLock.clearY(boneName);
boneLock.clearZ(boneName);
}
updateBoneRotationRanges();
hideContextMenu();
}});
boneBar.addItem("Change bones'limit to X", new Command(){
@Override
public void execute() {
if(!isSelectedIk()){
hideContextMenu();
return;
}
IKData ik=getCurrentIkData();
for(String boneName:ik.getBones()){
boneLock.clearY(boneName);
boneLock.clearZ(boneName);
boneLock.setX(boneName,ab.getBoneAngleAndMatrix(boneName).getDegreeAngle().getX());
}
updateBoneRotationRanges();
hideContextMenu();
}});
boneBar.addItem("Change bones'limit to Y", new Command(){
@Override
public void execute() {
if(!isSelectedIk()){
hideContextMenu();
return;
}
IKData ik=getCurrentIkData();
for(String boneName:ik.getBones()){
boneLock.clearX(boneName);
boneLock.clearZ(boneName);
boneLock.setY(boneName,ab.getBoneAngleAndMatrix(boneName).getDegreeAngle().getY());
}
updateBoneRotationRanges();
hideContextMenu();
}});
boneBar.addItem("Change bones'limit to Z", new Command(){
@Override
public void execute() {
if(!isSelectedIk()){
hideContextMenu();
return;
}
IKData ik=getCurrentIkData();
for(String boneName:ik.getBones()){
boneLock.clearX(boneName);
boneLock.clearY(boneName);
boneLock.setZ(boneName,ab.getBoneAngleAndMatrix(boneName).getDegreeAngle().getZ());
}
updateBoneRotationRanges();
hideContextMenu();
}});
boneBar.addItem("Change bones'limit to Y,Z", new Command(){
@Override
public void execute() {
if(!isSelectedIk()){
hideContextMenu();
return;
}
IKData ik=getCurrentIkData();
for(String boneName:ik.getBones()){
boneLock.clearX(boneName);
boneLock.setY(boneName,ab.getBoneAngleAndMatrix(boneName).getDegreeAngle().getY());
boneLock.setZ(boneName,ab.getBoneAngleAndMatrix(boneName).getDegreeAngle().getZ());
}
updateBoneRotationRanges();
hideContextMenu();
}});
boneBar.addItem("Change bones'limit to X,Z", new Command(){
@Override
public void execute() {
if(!isSelectedIk()){
hideContextMenu();
return;
}
IKData ik=getCurrentIkData();
for(String boneName:ik.getBones()){
boneLock.clearY(boneName);
boneLock.setX(boneName,ab.getBoneAngleAndMatrix(boneName).getDegreeAngle().getX());
boneLock.setZ(boneName,ab.getBoneAngleAndMatrix(boneName).getDegreeAngle().getZ());
}
updateBoneRotationRanges();
hideContextMenu();
}});
boneBar.addItem("Change bones'limit to Y,X", new Command(){
@Override
public void execute() {
if(!isSelectedIk()){
hideContextMenu();
return;
}
IKData ik=getCurrentIkData();
for(String boneName:ik.getBones()){
boneLock.clearZ(boneName);
boneLock.setY(boneName,ab.getBoneAngleAndMatrix(boneName).getDegreeAngle().getY());
boneLock.setX(boneName,ab.getBoneAngleAndMatrix(boneName).getDegreeAngle().getX());
}
updateBoneRotationRanges();
hideContextMenu();
}});
//createContextMenuFrames(rootBar);
MenuBar cameraBar=new MenuBar(true);
rootBar.addItem("Camera",cameraBar);
cameraBar.addItem("Front", new Command(){
@Override
public void execute() {
rotationXRange.setValue(0);
rotationYRange.setValue(0);
rotationZRange.setValue(0);
positionXRange.setValue(0);
positionYRange.setValue(defaultOffSetY);
hideContextMenu();
}});
cameraBar.addItem("Back", new Command(){
@Override
public void execute() {
rotationXRange.setValue(0);
rotationYRange.setValue(180);
rotationZRange.setValue(0);
positionXRange.setValue(0);
positionYRange.setValue(defaultOffSetY);
hideContextMenu();
}});
cameraBar.addItem("Quoter", new Command(){
@Override
public void execute() {
rotationXRange.setValue(45);
rotationYRange.setValue(45);
rotationZRange.setValue(0);
positionXRange.setValue(0);
positionYRange.setValue(defaultOffSetY);
hideContextMenu();
}});
cameraBar.addItem("Top", new Command(){
@Override
public void execute() {
rotationXRange.setValue(90);
rotationYRange.setValue(0);
rotationZRange.setValue(0);
positionXRange.setValue(0);
positionYRange.setValue(0);
hideContextMenu();
}});
cameraBar.addItem("Bottom", new Command(){
@Override
public void execute() {
rotationXRange.setValue(-90);
rotationYRange.setValue(0);
rotationZRange.setValue(0);
positionXRange.setValue(0);
positionYRange.setValue(0);
hideContextMenu();
}});
cameraBar.addItem("Right", new Command(){
@Override
public void execute() {
rotationXRange.setValue(0);
rotationYRange.setValue(90);
rotationZRange.setValue(0);
positionXRange.setValue(0);
positionYRange.setValue(defaultOffSetY);
hideContextMenu();
}});
cameraBar.addItem("Left", new Command(){
@Override
public void execute() {
rotationXRange.setValue(0);
rotationYRange.setValue(-90);
rotationZRange.setValue(0);
positionXRange.setValue(0);
positionYRange.setValue(defaultOffSetY);
hideContextMenu();
}});
}
//line has problem,TODO find a way to fix
protected void syncIkPosition() {
for(IKData ik:getAvaiableIkdatas()){
Mesh ikMesh=targetMeshs.get(ik.getLastBoneName());//TODO define ik name,;
if(ikMesh!=null){
ikMesh.setPosition(ik.getTargetPos());
}else{
LogUtils.log("ikMesh not found:"+ik.getLastBoneName());
}
}
if(isSelectedIk()){
selectionMesh.setPosition(getCurrentIkData().getTargetPos());
}
}
/**
*
* @param boneName
* @param frameIndex
* @return origin ,take care of modify
*/
private Vector3 getBonePositionAtFrame(String boneName,int frameIndex){
if(frameIndex<0 || frameIndex>getSelectedPoseEditorData().getPoseFrameDatas().size()-1){
LogUtils.log("getBonePositionAtFrame:out of range frame size= "+getSelectedPoseEditorData().getPoseFrameDatas().size()+" index="+frameIndex);
return null;//out of frame range;
}
if(!existBone(boneName)){
LogUtils.log("getBonePositionAtFrame:bone not found "+boneName);
return null;
}
int boneIndex=ab.getBoneIndex(boneName);
PoseFrameData frameData=getSelectedPoseEditorData().getPoseFrameDatas().get(frameIndex);
AnimationBonesData workingAnimationBoneData=new AnimationBonesData(animationBones, frameData.getAngleAndMatrixs());
return workingAnimationBoneData.getBonePosition(boneIndex);
}
private boolean isCurrentHasPrevFrame() {
return poseFrameDataIndex>0;
}
private boolean isCurrentHasNextFrame() {
return poseFrameDataIndex<getSelectedPoseEditorData().getPoseFrameDatas().size();
}
protected void followTarget() {
AnimationBonesData merged=getMergedAnimationBonesData(ab);
for(IKData ik:getAvaiableIkdatas()){
String name=ik.getLastBoneName();
if(existBone(name)){
ik.getTargetPos().copy(getDefaultIkPos(merged.getBoneIndex(name)));
//doPoseByMatrix(ab);
}
}
}
protected void execIk() {
execIk(45,10);
}
protected void execIk(int perLimit,int loop) {
for(IKData ik:getAvaiableIkdatas()){
doPoseIkk(0,false,perLimit,ik,loop);
}
}
private void createContextMenuRoot(MenuBar rootBar){
MenuBar rootBoneBar=new MenuBar(true);
rootBar.addItem("Root",rootBoneBar);
rootBoneBar.addItem("touch ground(Y-0)", new Command(){
@Override
public void execute() {
touchGroundZero();
}});
rootBoneBar.addItem("initial Position", new Command(){
@Override
public void execute() {
JsArrayNumber rootPos=animationBones.get(0).getPos();
double rootX=0;
double rootY=0;
double rootZ=0;
if(rootPos!=null && rootPos.length()==3){
rootX=rootPos.get(0);
rootY=rootPos.get(1);
rootZ=rootPos.get(2);
}
ab.getBoneAngleAndMatrix(0).getPosition().set(rootX, rootY, rootZ);//bone pos
ab.getBoneAngleAndMatrix(0).updateMatrix();
fitIkOnBone();
doPoseByMatrix(ab);
hideContextMenu();
}});
rootBoneBar.addItem("initial Pose", new Command(){
@Override
public void execute() {
//store position
Vector3 lastPosition=ab.getBoneAngleAndMatrix(0).getPosition().clone();
List<AngleAndPosition> angleAndPositions=AnimationBonesData.cloneAngleAndMatrix(initialPoseFrameData.getAngleAndMatrixs());
angleAndPositions.get(0).getPosition().copy(lastPosition);
angleAndPositions.get(0).updateMatrix();
selectAnimationDataData(angleAndPositions);
hideContextMenu();
}});
rootBoneBar.addItem("Move to Prev Frame Position", new Command(){
@Override
public void execute() {
if(poseFrameDataIndex<=0){
LogUtils.log("<=0:"+poseFrameDataIndex);
hideContextMenu();
//no frame
return;
}
if(poseFrameDataIndex>=getSelectedPoseEditorData().getPoseFrameDatas().size()){
//something invalid
LogUtils.log("invalid range");
hideContextMenu();
return ;
}
PoseFrameData prevFrameData=getSelectedPoseEditorData().getPoseFrameDatas().get(poseFrameDataIndex-1);
Vector3 prevFramePosition=prevFrameData.getAngleAndMatrixs().get(0).getPosition();
ab.getBoneAngleAndMatrix(0).getPosition().copy(prevFramePosition);
ab.getBoneAngleAndMatrix(0).updateMatrix();
fitIkOnBone();
doPoseByMatrix(ab);
hideContextMenu();
}});
rootBoneBar.addItem("Move to Current Frame Position", new Command(){
@Override
public void execute() {
//get not modified position
PoseFrameData prevFrameData=getSelectedPoseEditorData().getPoseFrameDatas().get(poseFrameDataIndex);
Vector3 prevFramePosition=prevFrameData.getAngleAndMatrixs().get(0).getPosition();
ab.getBoneAngleAndMatrix(0).getPosition().copy(prevFramePosition);
ab.getBoneAngleAndMatrix(0).updateMatrix();
fitIkOnBone();
doPoseByMatrix(ab);
hideContextMenu();
}});
rootBoneBar.addItem("Move to Next Frame Position", new Command(){
@Override
public void execute() {
if(poseFrameDataIndex<0){
hideContextMenu();
//no frame
return;
}
if(poseFrameDataIndex>=getSelectedPoseEditorData().getPoseFrameDatas().size()){
//something invalid
LogUtils.log("invalid range");
hideContextMenu();
return ;
}
if(poseFrameDataIndex==getSelectedPoseEditorData().getPoseFrameDatas().size()-1){
//at last frame
hideContextMenu();
return;
}
PoseFrameData prevFrameData=getSelectedPoseEditorData().getPoseFrameDatas().get(poseFrameDataIndex+1);
Vector3 prevFramePosition=prevFrameData.getAngleAndMatrixs().get(0).getPosition();
ab.getBoneAngleAndMatrix(0).getPosition().copy(prevFramePosition);
ab.getBoneAngleAndMatrix(0).updateMatrix();
fitIkOnBone();
doPoseByMatrix(ab);
hideContextMenu();
}});
rootBoneBar.addItem("Move to between Prev & Next Frame Position", new Command(){
@Override
public void execute() {
if(poseFrameDataIndex<=0){//need prev
hideContextMenu();
//no frame
return;
}
if(poseFrameDataIndex>=getSelectedPoseEditorData().getPoseFrameDatas().size()){
//something invalid
LogUtils.log("invalid range");
hideContextMenu();
return ;
}
if(poseFrameDataIndex==getSelectedPoseEditorData().getPoseFrameDatas().size()-1){
//need next frame
hideContextMenu();
return;
}
PoseFrameData prevFrameData=getSelectedPoseEditorData().getPoseFrameDatas().get(poseFrameDataIndex-1);
Vector3 prevFramePosition=prevFrameData.getAngleAndMatrixs().get(0).getPosition();
PoseFrameData nextFrameData=getSelectedPoseEditorData().getPoseFrameDatas().get(poseFrameDataIndex+1);
Vector3 nextFramePosition=nextFrameData.getAngleAndMatrixs().get(0).getPosition();
Vector3 newPos=prevFramePosition.clone().add(nextFramePosition).divideScalar(2);
ab.getBoneAngleAndMatrix(0).getPosition().copy(newPos);
ab.getBoneAngleAndMatrix(0).updateMatrix();
fitIkOnBone();
doPoseByMatrix(ab);
hideContextMenu();
}});
rootBoneBar.addItem("Swap all", new Command(){
@Override
public void execute() {
if(bone3D!=null && bone3D.getChildren().length()>0){
//selectBone(bone3D.getChildren().get(0),0,0,false);
//h-flip
//rotationBoneZRange.setValue(-rotationBoneZRange.getValue());
//rotToBone();
List<String> converted=new ArrayList<String>();
//swap all
for(IKData ik:getAvaiableIkdatas()){
List<String> targets=Lists.newArrayList(ik.getBones());
targets.add(ik.getLastBoneName());
for(String name:targets){
if(converted.contains(name)){
continue;
}
String targetName=getMirroredName(name);
if(targetName==null){
continue;
}
int index=ab.getBoneIndex(targetName);
int srcIndex=ab.getBoneIndex(name);
if(index!=-1 && srcIndex!=-1){
Vector3 angle1=ab.getBoneAngleAndMatrix(srcIndex).getDegreeAngle();
Vector3 angle=ab.getBoneAngleAndMatrix(index).getDegreeAngle();
rotToBone(name, angle.getX(), -angle.getY(), -angle.getZ(),false);
rotToBone(targetName, angle1.getX(), -angle1.getY(), -angle1.getZ(),true);
}
converted.add(name);
converted.add(targetName);
}
}
//swap remains
for(String bname:boneList){
if(converted.contains(bname)){
continue;
}
int index=ab.getBoneIndex(bname);
if(index==-1){
LogUtils.log("invalid bone:"+bname);
continue;
}
Vector3 angle=ab.getBoneAngleAndMatrix(index).getDegreeAngle();
rotToBone(bname, angle.getX(), -angle.getY(), -angle.getZ(),false);
}
doPoseByMatrix(ab);//update all
//TODO check what exactlly doing
updateBoneRanges();
updateIkLabels();
updateIkPositionLabel();
}
}
});
/*
* swap angle maybe need for old range
rootBoneBar.addItem("180 to -180", new Command(){
@Override
public void execute() {
Vector3 angle=ab.getBoneAngleAndMatrix(0).getAngle();
LogUtils.log(ThreeLog.get(angle));
if(angle.getX()==180){
angle.setX(-180);
}else if(angle.getX()==-180){
angle.setX(180);
}
if(angle.getY()==180){
angle.setY(-180);
}else if(angle.getY()==-180){
angle.setY(180);
}
if(angle.getZ()==180){
angle.setZ(-180);
}else if(angle.getZ()==-180){
angle.setZ(180);
}
//ab.getBoneAngleAndMatrix(0).setPosition(getInitialPoseFrameData().getPositions().get(0).clone());
ab.getBoneAngleAndMatrix(0).updateMatrix();
doPoseByMatrix(ab);
updateBoneRotationRanges();
hideContextMenu();
}});
*/
}
protected void touchGroundZero() {
//bodyMesh.getGeometry().computeBoundingBox();
//BoundingBox box=bodyMesh.getGeometry().getBoundingBox();
BoundingBox box=SkinningVertexUtils.computeBoundingBox(bodyMesh);
//LogUtils.log("bone-pos");
//ThreeLog.log(ab.getBonePosition(0));
//LogUtils.log("box-min-pos");
//ThreeLog.log(box.getMin());
Vector3 currentRoot=ab.getBonePosition(0);
currentRoot.setY(currentRoot.getY()-box.getMin().getY());
//logger.fine("min:"+ThreeLog.get(box.getMin()));
//logger.fine("max:"+ThreeLog.get(box.getMax()));
ab.getBoneAngleAndMatrix(0).getPosition().setY(currentRoot.getY());
ab.getBoneAngleAndMatrix(0).updateMatrix();
//LogUtils.log("moved-root-pos");
ThreeLog.log(ab.getBoneAngleAndMatrix(0).getPosition());
fitIkOnBone();
doPoseByMatrix(ab);
hideContextMenu();
}
//TODO future
private boolean isShowPrevIk;
private void createContextMenuFrames(MenuBar rootBar){
MenuBar framesBar=new MenuBar(true);
contextMenuShowPrevFrame = new MenuItem("Show Prev Iks",true,new Command(){
@Override
public void execute() {
contextMenuShowPrevFrame.setHTML("<b>"+"Show Prev Iks"+"</b>");
contextMenuHidePrefIks.setHTML("Hide Prev Iks");
isShowPrevIk=true;
hideContextMenu();
}});
framesBar.addItem(contextMenuShowPrevFrame);
contextMenuHidePrefIks = new MenuItem("<b>"+"Hide Prev Iks"+"</b>",true,new Command(){
@Override
public void execute() {
contextMenuShowPrevFrame.setHTML(""+"Show Prev Iks"+"");
contextMenuHidePrefIks.setHTML("<b>"+"Hide Prev Iks"+"</b>");
isShowPrevIk=false;
hideContextMenu();
}});
framesBar.addItem(contextMenuHidePrefIks);
rootBar.addItem("Frames",framesBar);
}
private PoseFrameData getInitialPoseFrameData(){
return initialPoseFrameData;
}
private String lastSelectionIkName;
private long lastClicked;
/*
* to avoid select ik
*/
private boolean selectBoneFirstOnMouseDown;
@Override
public void onMouseDown(MouseDownEvent event) {
logger.fine("onMouse down");
if(event.getNativeButton()==NativeEvent.BUTTON_RIGHT){
// showContextMenu(mouseDownX, mouseDownY);
return;
}else{
hideContextMenu();
}
//middle cant select anything
if(event.getNativeButton()==NativeEvent.BUTTON_MIDDLE){
return;
}
long t=System.currentTimeMillis();
boolean doubleclick=t<lastClicked+300;//onDoubleClick called after mouse up,it's hard to use
doMouseDown(event.getX(),event.getY(),doubleclick||selectBoneFirstOnMouseDown);
lastClicked=t;
}
private void doMouseDown(int x,int y,final boolean selectBoneFirst){
//LogUtils.log("doMouseDown:"+String.valueOf(selectBoneFirst));
mouseDown=true;
mouseDownX=x;
mouseDownY=y;
//log(mouseDownX+","+mouseDownY+":"+screenWidth+"x"+screenHeight);
/*
log("wpos:"+ThreeLog.get(GWTThreeUtils.toPositionVec(camera.getMatrix())));
log("mpos:"+ThreeLog.get(GWTThreeUtils.toPositionVec(camera.getMatrixWorld())));
log("rpos:"+ThreeLog.get(GWTThreeUtils.toPositionVec(camera.getMatrixRotationWorld())));
log("pos:"+ThreeLog.get(camera.getPosition()));
*/
//log("mouse-click:"+event.getX()+"x"+event.getY());
//TODO only call once for speed up
JsArray<Object3D> targets=(JsArray<Object3D>) JsArray.createArray();
JsArray<Object3D> childs=rootObject.getChildren();
for(int i=0;i<childs.length();i++){
//LogUtils.log(childs.get(i).getName());
targets.push(childs.get(i));
}
JsArray<Object3D> bones=bone3D.getChildren();
for(int i=0;i<bones.length();i++){
//LogUtils.log(bones.get(i).getName());
targets.push(bones.get(i));
}
for(int i=0;i<ik3D.getChildren().length();i++){
//LogUtils.log(bones.get(i).getName());
targets.push(ik3D.getChildren().get(i));
}
JsArray<Intersect> intersects=projector.gwtPickIntersects(x, y, screenWidth, screenHeight,camera,targets);
//log("intersects-length:"+intersects.length());
long t=System.currentTimeMillis();
List<Object3D> selections=convertSelections(intersects);
if(selectBoneFirst){//trying every click change ik and bone if both intersected
//check bone first
Object3D lastIk=null;
for(Object3D selection:selections){
if(selection.getName().startsWith("ik:")){
if(selection.getName().equals(lastSelectionIkName)){
lastIk=selection;
}else{
selectIk(selection,x,y,true);
lastSelectionIkName=selection.getName();
updateBoneRanges();
return;
}
}
}
for(Object3D selection:selections){
if(!selection.getName().isEmpty() && !selection.getName().startsWith("ik:")){
selectBone(selection,x,y,true);
return;
}
}
if(lastIk!=null){//when ik selected,select another ik or bone first
selectIk(lastIk,x,y,true);
lastSelectionIkName=lastIk.getName();
updateBoneRanges();
return;
}
}else{
for(Object3D selection:selections){
if(selection.getName().startsWith("ik:")){
selectIk(selection,x,y,true);
lastSelectionIkName=selection.getName();
updateBoneRanges();
return;
}
if(!selection.getName().isEmpty() && !selection.getName().startsWith("ik:")){
selectBone(selection,x,y,true);
return;
}
}
/*
//ik first
for(Object3D selection:selections){
if(selection.getName().startsWith("ik:")){
selectIk(selection,x,y);
lastSelectionIkName=selection.getName();
updateBoneRanges();
return;
}
}
for(Object3D selection:selections){
if(!selection.getName().isEmpty() && !selection.getName().startsWith("ik:")){
selectBone(selection,x,y);
return;
}
}
*/
}
selectBoneFirstOnMouseDown=false;
//LogUtils.log("selectBoneFirstOnMouseDown:"+selectBoneFirstOnMouseDown);
//log("no-selection");
//not select ik or bone
selectedBone=null;
selectionMesh.setVisible(false);
switchSelectionIk(null);
updateIKVisible(ikVisibleCheck.getValue());
logger.fine("onMouse down-end1");
}
private String selectedBone;
private List<Object3D> convertSelections(JsArray<Intersect> intersects){
List<Object3D> selections=new ArrayList<Object3D>();
for(int i=0;i<intersects.length();i++){
selections.add(intersects.get(i).getObject());
}
return selections;
}
private void selectBone(Object3D target,int x,int y,boolean needDrag){
//maybe bone or root-bone
selectedBone=target.getName();
selectionMesh.setVisible(true);
selectionMesh.setPosition(target.getPosition());
selectionMesh.getMaterial().gwtGetColor().setHex(0xff0000);
boolean ikVisible=true;
for(IKData ik:getAvaiableIkdatas()){
if(ik.getLastBoneName().equals(selectedBone)){
ikVisible=false;
break;
}
}
if(ikVisible){
updateIKVisible(ikVisibleCheck.getValue());
selectBoneFirstOnMouseDown=false;
}else{
selectBoneFirstOnMouseDown=true;
updateIKVisible(false);//show guide?
}
switchSelectionIk(null);
if(needDrag){
dragObjectControler.selectObject(target, x,y, screenWidth, screenHeight, camera);
logger.fine("onMouse down-end2");
}
//i guess set pos
//this is same effect as mouse move
positionXBoneRange.setValue((int) (selectionMesh.getPosition().getX()*100));
positionYBoneRange.setValue((int)(selectionMesh.getPosition().getY()*100));
positionZBoneRange.setValue((int)(selectionMesh.getPosition().getZ()*100));
defereredFocusCanvas();
return;
}
private void defereredFocusCanvas(){
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
tabPanel.selectTab(0);//this work reget focus canvas.
canvas.setFocus(true);
}
});
}
private void selectIk(Object3D target,int x,int y,boolean drag){
selectBoneFirstOnMouseDown=false;
String ikBoneName=target.getName().substring(3);//3 is "ik:".length()
for(int j=0;j<ikdatas.size();j++){
if(ikdatas.get(j).getLastBoneName().equals(ikBoneName)){
ikdataIndex=j;//set ikindex here
selectionMesh.setVisible(true);
selectionMesh.setPosition(target.getPosition());
selectionMesh.getMaterial().gwtGetColor().setHex(0x00ff00);
if(!ikBoneName.equals(currentSelectionIkName)){
switchSelectionIk(ikBoneName);
}
selectedBone=null;
if(drag){
dragObjectControler.selectObject(target, x,y, screenWidth, screenHeight, camera);
}
logger.info("onMouse down-end3");
defereredFocusCanvas();
return;//ik selected
}
}
}
@Override
public void onMouseUp(MouseUpEvent event) {
logger.fine("onMouse up");
mouseDown=false;
dragObjectControler.unselectObject();
logger.fine("6 drag");
}
@Override
public void onMouseOut(MouseOutEvent event) {
mouseDown=false;
dragObjectControler.unselectObject();
}
private boolean mouseMoving;
private double lastTime;
private Mesh debugMesh;
//private boolean doSync; never catched
@Override
public void onMouseMove(MouseMoveEvent event) {
if(mouseMoving){
logger.info("conflict-move");
if(lastTime+1000<System.currentTimeMillis()){
logger.info("conflict-move-timeout");//never happen?
}else{
return;
}
}
mouseMoving=true;
lastTime=System.currentTimeMillis();
double diffX=event.getX()-mouseDownX;
double diffY=event.getY()-mouseDownY;
int prevMouseDownX=mouseDownX;
int prevMouseDownY=mouseDownY;
mouseDownX=event.getX();
mouseDownY=event.getY();
double mouseMoved=(Math.abs(diffX)+Math.abs(diffY));
if(mouseMoved==0){
mouseMoving=false;
return;
}
if(event.getNativeEvent().getButton()==NativeEvent.BUTTON_MIDDLE){
changeCamera((int)diffX,(int)diffY,event.isShiftKeyDown(),event.isAltKeyDown(),event.isControlKeyDown());
mouseMoving=false;
return;
}
if(mouseDown){
//log(diffX+","+diffY);
logger.fine("mouse move");
/*
* useless
int limit=10;
int limitX=(int) diffX;
int limitY=(int) diffY;
if(diffX>limit){
limitX=limit;
}
if(diffY>limit){
limitY=limit;
}
if(diffX<-limit){
limitX=-limit;
}
if(diffY<-limit){
limitY=-limit;
}
*/
if(isSelectedIk()){
logger.fine("selected ik");
diffX*=0.1;
diffY*=-0.1;
Vector3 old=getCurrentIkData().getTargetPos().clone();
if(dragObjectControler.isSelected()){
//int eventX=prevMouseDownX+limitX;
//int eventY=prevMouseDownY+limitY;
int eventX=event.getX();
int eventY=event.getY();
logger.fine("selected dragObjectControler");
scene.updateMatrixWorld(true);//very important.sometime selectedDraggablekObject.getParent().getMatrixWorld() return 0.
Vector3 newPos=dragObjectControler.moveSelectionPosition(eventX,eventY, screenWidth, screenHeight, camera);
if(newPos==null){
logger.info("newPos-null:"+ThreeLog.get(dragObjectControler.getDraggableOffset()));
mouseMoving=false;
return;
}
double length=newPos.clone().sub(getCurrentIkData().getTargetPos()).length();
/*
if(newPos.getY()<8){
log("error-newPos:"+ThreeLog.get(newPos));
log("error-mouse:"+eventX+","+eventY);
log("error-diff:"+diffX+","+diffY);
log("error-diff-move:"+length+",mouse:"+mouseMoved+",offset:"+ThreeLog.get(dragObjectControler.getDraggableOffset()));
log("error-log:"+dragObjectControler.getLog());
}else{
log("newPos:"+ThreeLog.get(newPos));
log("mouse:"+eventX+","+eventY);
log("diff:"+diffX+","+diffY);
log("diff-move:"+length+",mouse:"+mouseMoved+",offset:"+ThreeLog.get(dragObjectControler.getDraggableOffset()));
log("log:"+dragObjectControler.getLog());
}
*/
if(length<mouseMoved*2 && mouseMoved!=0){
if(length>mouseMoved || length>5){
logger.fine("diff-move:"+length+",mouse:"+mouseMoved+",offset:"+ThreeLog.get(dragObjectControler.getDraggableOffset()));
logger.fine("mouseDown:"+mouseDownX+","+mouseDownY);
}
//getCurrentIkData().getTargetPos().copy(newPos); // iguess
getCurrentIkData().setTargetPos(newPos);
}else{
logger.fine("diff-error:"+length+",mouse:"+mouseMoved+",offset:"+ThreeLog.get(dragObjectControler.getDraggableOffset()));
if(length>mouseMoved*10 && mouseMoved!=0){//invalid
//dragObjectControler.unselectObject();
//dragObjectControler.selectObject(dragObjectControler.getSelectedDraggablekObject(), event.getX(), event.getY(), screenWidth, screenHeight, camera);
}
}
}
//log("diff-moved:"+ThreeLog.get(old.subSelf(getCurrentIkData().getTargetPos())));
/*
getCurrentIkData().getTargetPos().incrementX(diffX);
getCurrentIkData().getTargetPos().incrementY(diffY);
*/
if(event.isAltKeyDown()){//slow
if(event.isShiftKeyDown()){
doPoseIkk(0,false,1,getCurrentIkData(),1);
for(IKData ik:getAvaiableIkdatas()){
//log("ik:"+ik.getName());
if(ik!=getCurrentIkData()){
doPoseIkk(0,false,5,ik,1);
}
}
}else{
//not work correctly
doPoseIkk(0,false,1,getCurrentIkData(),10);
}
}else if(event.isShiftKeyDown()){//move only
//doPoseIkk(0,true,1,getCurrentIkData(),1);
doPoseByMatrix(ab);
//doPoseIkk(0,false,1,getCurrentIkData(),0);
}else{
doPoseIkk(0,true,1,getCurrentIkData(),5);
}
updateIkPositionLabel();
}else if(isSelectedBone()){
logger.info("selected bone:"+event.isShiftKeyDown());
if(event.isShiftKeyDown()){//move position
int boneIndex=ab.getBoneIndex(selectedBone);
Vector3 pos=null;
if(boneIndex==0){//this version support moving root only
pos=ab.getBonePosition(boneIndex);
}else{
mouseMoving=false;
return;
}
if(dragObjectControler.isSelected()){
Vector3 newPos=dragObjectControler.moveSelectionPosition(event.getX(), event.getY(), screenWidth, screenHeight, camera);
double length=newPos.clone().length();
if(length<mouseMoved*50){
if(length>mouseMoved*10){
logger.fine("diff-move:"+length+",mouse="+mouseMoved);
}
//getCurrentIkData().getTargetPos().copy(newPos);
positionXBoneRange.setValue((int)(newPos.getX()*100)); //what is 100?
positionYBoneRange.setValue((int)(newPos.getY()*100));
positionZBoneRange.setValue((int)(newPos.getZ()*100));
logger.fine("moved:"+length+",mouse="+mouseMoved);
}else{
logger.info("diff-error:"+length+",mouse="+mouseMoved);
}
}
//positionXBoneRange.setValue(positionXBoneRange.getValue()+(int)diffX);
//positionYBoneRange.setValue(positionYBoneRange.getValue()-(int)diffY);
positionToBone();
if(event.isAltKeyDown()){//not follow ik
// switchSelectionIk(null);
//effect-ik
for(IKData ik:getAvaiableIkdatas()){
doPoseIkk(0,false,5,ik,1);
}
}else{ //follow ik
if(boneIndex==0){
/*
Vector3 movedPos=ab.getBonePosition(boneIndex);
movedPos.sub(pos);
for(IKData ik:getAvaiableIkdatas()){
ik.getTargetPos().add(movedPos);
}
*/
fitIkOnBone();
doPoseByMatrix(ab);//redraw
}
}
}else{//change angle
Vector3 angle=ab.getBoneAngleAndMatrix(selectedBone).getDegreeAngle();
rotationBoneXRange.setValue(getRotationRangeValue(rotationBoneXRange.getValue(),(int)diffY));
rotationBoneYRange.setValue(getRotationRangeValue(rotationBoneYRange.getValue(),(int)diffX));
//rotationBoneXRange.setValue(rotationBoneXRange.getValue()+diffY);
//rotationBoneYRange.setValue(rotationBoneYRange.getValue()+diffX);
rotToBone();
if(event.isAltKeyDown()){
// switchSelectionIk(null);
//effect-ik
for(IKData ik:getAvaiableIkdatas()){
doPoseIkk(0,false,5,ik,1);
}
}else{
//Vector3 rootPos=ab.getBonePosition(0);
Vector3 movedAngle=ab.getBoneAngleAndMatrix(selectedBone).getDegreeAngle().clone();
movedAngle.sub(angle);
//log("before:"+ThreeLog.get(angle)+" moved:"+ThreeLog.get(movedAngle));
//Matrix4 mx=GWTThreeUtils.degitRotationToMatrix4(movedAngle);
/*
for(IKData ik:getAvaiableIkdatas()){
String name=ik.getLastBoneName();
//Vector3 pos=ab.getBonePosition(name);
if(existBone(name)){
ik.getTargetPos().copy(getDefaultIkPos(ab.getBoneIndex(name)));
}
//ik.getTargetPos().set(pos.getX(), pos.getY(), pos.getZ());
}
*/
fitIkOnBone();
doPoseByMatrix(ab);//redraw
}
}
}
else{//global
logger.fine("global");
changeCamera((int)diffX,(int)diffY,event.isShiftKeyDown(),event.isAltKeyDown(),event.isControlKeyDown());
}
}else{
/*useless
if(!dragObjectControler.isSelected()){
JsArray<Intersect> intersects=projector.gwtPickIntersects(event.getX(), event.getY(), screenWidth, screenHeight,camera, GWTThreeUtils.toPositionVec(camera.getMatrixWorld()),scene);
//log("intersects-length:"+intersects.length());
for(int i=0;i<intersects.length();i++){
Intersect sect=intersects.get(i);
Object3D target=sect.getObject();
if(!target.getName().isEmpty()){
if(target.getName().startsWith("ik:")){
if(target!=dragObjectControler.getIntersectedDraggablekObject()){
dragObjectControler.setIntersectedDraggablekObject(target);
}
//dragObjectControler.selectObject(target, event.getX(), event.getY(), screenWidth, screenHeight, camera);
}
}
}
}*/
}
mouseMoving=false;
}
private void changeCamera(int diffX,int diffY,boolean shiftKey,boolean AltKey,boolean ctrlKey){
if(shiftKey){
positionXRange.setValue(positionXRange.getValue()+diffX);
positionYRange.setValue(positionYRange.getValue()-diffY);
}else{
rotationXRange.setValue(getRotationRangeValue(rotationXRange.getValue(),diffY));
rotationYRange.setValue(getRotationRangeValue(rotationYRange.getValue(),diffX));
}
}
private int getRotationRangeValue(int oldValue,int diffValue){
int angle=oldValue+diffValue;
if(angle>180){
int over=angle-180;
angle=-180+over;
}
if(angle<-180){
int under=angle+180;
angle=180+under;
}
return angle;
}
private boolean isSelectedBone(){
return !isSelectedIk() && selectedBone!=null;
}
private IKData getCurrentIkData(){
return ikdatas.get(ikdataIndex);
}
@Override
public void onMouseWheel(MouseWheelEvent event) {
if(isSelectedIk()){//IK
double dy=event.getDeltaY()*2.0/posDivided;
getCurrentIkData().getTargetPos().gwtIncrementZ(dy);
if(event.isAltKeyDown()){
if(event.isShiftKeyDown()){//IK selected with ALT+Shift on mouseWheel,? bugs
doPoseIkk(0,false,1,getCurrentIkData(),1);
for(IKData ik:getAvaiableIkdatas()){
if(ik!=getCurrentIkData()){
doPoseIkk(0,false,5,ik,1);
}
}
}else{//IK selected with ALT on mouseWheel,Do move bones mildly.
doPoseIkk(0,false,1,getCurrentIkData(),10);
}
}else if(event.isShiftKeyDown()){//IK selected with Shift on mouseWheel,Do move IK only
//doPoseIkk(0,true,1,getCurrentIkData(),1);
//LogUtils.log("shift-key");
doPoseByMatrix(ab);
}else{
doPoseIkk(0,true,1,getCurrentIkData(),5);
}
updateIkPositionLabel();
}else if(isSelectedBone()){
if(event.isShiftKeyDown()){//move
int diff=event.getDeltaY();
int boneIndex=ab.getBoneIndex(selectedBone);
Vector3 pos=null;
if(boneIndex==0){//this version support moving root only
pos=ab.getBonePosition(boneIndex);
}
positionZBoneRange.setValue(positionZBoneRange.getValue()+diff);
positionToBone();
if(event.isAltKeyDown()){
//switchSelectionIk(null);
//effect-ik
execIk(5, 1);
/*
for(IKData ik:getAvaiableIkdatas()){
if(ik!=getCurrentIkData()){//no need re-ik root?
doPoseIkk(0,false,5,ik,1);
}
}*/
}else{//ik-follow
if(boneIndex==0){
Vector3 moved=ab.getBonePosition(boneIndex);
moved.sub(pos);
for(IKData ik:getAvaiableIkdatas()){
ik.getTargetPos().add(moved);
}
}
}
}else{
int diff=event.getDeltaY();
Vector3 angle=ab.getBoneAngleAndMatrix(selectedBone).getDegreeAngle();
rotationBoneZRange.setValue(getRotationRangeValue(rotationBoneZRange.getValue(),diff));
//rotationBoneZRange.setValue(rotationBoneZRange.getValue()+diff);
rotToBone();
if(event.isAltKeyDown()){
// switchSelectionIk(null);
//effect-ik
/*
for(IKData ik:getAvaiableIkdatas()){
doPoseIkk(0,false,5,ik,1);
}
*/
execIk(5, 1);
}else{
//Vector3 rootPos=ab.getBonePosition(0);
Vector3 movedAngle=ab.getBoneAngleAndMatrix(selectedBone).getDegreeAngle().clone();
movedAngle.sub(angle);
//logger.fine("before:"+ThreeLog.get(angle)+" moved:"+ThreeLog.get(movedAngle));
//Matrix4 mx=GWTThreeUtils.degitRotationToMatrix4(movedAngle);
//move green-ik-indicator
//--this is keep green ik-bone position
/*
for(IKData ik:getAvaiableIkdatas()){
String name=ik.getLastBoneName();
//Vector3 pos=ab.getBonePosition(name);
//ik.getTargetPos().set(pos.getX(), pos.getY(), pos.getZ());
//LogUtils.log("ik:"+name);
//some difference bone call this
if(existBone(name)){//duplicate?
ik.getTargetPos().copy(getDefaultIkPos(ab.getBoneIndex(name)));
}
}
*/
fitIkOnBone();
doPoseByMatrix(ab);//redraw
}
}
}
else{//use small version
double tzoom=5.0/posDivided;
//TODO make class
long t=System.currentTimeMillis();
if(mouseLast+100>t){
czoom*=2;
}else{
czoom=tzoom;
}
//GWT.log("wheel:"+event.getDeltaY());
double tmp=cameraZ+event.getDeltaY()*czoom;
tmp=Math.max(20.0/posDivided, tmp);
tmp=Math.min(4000, tmp);
cameraZ=(double)tmp;
mouseLast=t;
}
}
private double czoom;
private InputRangeWidget positionXRange;
private InputRangeWidget positionYRange;
private InputRangeWidget positionZRange;
//private InputRangeWidget frameRange;
private InputRangeWidget rotationXRange;
private InputRangeWidget rotationYRange;
private InputRangeWidget rotationZRange;
private InputRangeWidget rotationBoneXRange;
private InputRangeWidget rotationBoneYRange;
private InputRangeWidget rotationBoneZRange;
private PopupPanel bottomPanel;
private InputRangeWidget currentFrameRange;
private Label currentFrameLabel;
private InputRangeWidget positionXBoneRange;
private InputRangeWidget positionYBoneRange;
private InputRangeWidget positionZBoneRange;
private CheckBox ylockCheck;
private CheckBox xlockCheck;
private List<String> ikLocks=new ArrayList<String>();
private CheckBox showBonesCheck,ikVisibleCheck,smallCheck,showFingersCheck;
private int posDivided=10; //how small 10 or 100
private CheckBox showBackgroundCheck;
private VerticalPanel ikPositionsPanel;
private Label ikPositionLabelY;
private Label ikPositionLabelZ;
private Label boneRotateLimitXLabel;
private Label boneRotateLimitYLabel;
private Label boneRotateLimitZLabel;
private InputRangeWidget meshRotationYRange;
@Override
public void createControl(DropVerticalPanelBase parent) {
Window.addCloseHandler(new CloseHandler<Window>() {
@Override
public void onClose(CloseEvent<Window> event) {
//this call fix slow closing problem
LogUtils.log("close");
animationHandler.cancel();
}
});
nearCamera=0.0001;//TODO scale up character
HorizontalPanel h1=new HorizontalPanel();
rotationXRange = InputRangeWidget.createInputRange(-180,180,0);
Label rotateXLabel=HTML5Builder.createRangeLabel("X-Rotate:", rotationXRange);
rotateXLabel.setWidth("120px");
parent.add(rotateXLabel);
parent.add(h1);
h1.add(rotationXRange);
Button reset=new Button("Reset");
reset.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
rotationXRange.setValue(0);
}
});
h1.add(reset);
HorizontalPanel h2=new HorizontalPanel();
rotationYRange = InputRangeWidget.createInputRange(-180,180,0);
parent.add(HTML5Builder.createRangeLabel("Y-Rotate:", rotationYRange));
parent.add(h2);
h2.add(rotationYRange);
Button reset2=new Button("Reset");
reset2.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
rotationYRange.setValue(0);
}
});
h2.add(reset2);
HorizontalPanel h3=new HorizontalPanel();
rotationZRange = InputRangeWidget.createInputRange(-180,180,0);
parent.add(HTML5Builder.createRangeLabel("Z-Rotate:", rotationZRange));
parent.add(h3);
h3.add(rotationZRange);
Button reset3=new Button("Reset");
reset3.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
rotationZRange.setValue(0);
}
});
h3.add(reset3);
HorizontalPanel h4=new HorizontalPanel();
positionXRange = InputRangeWidget.createInputRange(-300,300,0);
parent.add(HTML5Builder.createRangeLabel("X-Position:", positionXRange,posDivided));
parent.add(h4);
h4.add(positionXRange);
Button reset4=new Button("Reset");
reset4.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
positionXRange.setValue(0);
}
});
h4.add(reset4);
HorizontalPanel h5=new HorizontalPanel();
positionYRange = InputRangeWidget.createInputRange(-300,300,0);
parent.add(HTML5Builder.createRangeLabel("Y-Position:", positionYRange,posDivided));
parent.add(h5);
h5.add(positionYRange);
Button reset5=new Button("Reset");
reset5.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
positionYRange.setValue(0);
}
});
h5.add(reset5);
//maybe z no need,there are whell-zoom
HorizontalPanel h6=new HorizontalPanel();
positionZRange = InputRangeWidget.createInputRange(-600,600,0);
//parent.add(HTML5Builder.createRangeLabel("Z-Position:", positionZRange,10));
//parent.add(h6);
h6.add(positionZRange);
Button reset6=new Button("Reset");
reset6.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
positionZRange.setValue(0);
}
});
h6.add(reset6);
HorizontalPanel vPanel=new HorizontalPanel();
parent.add(vPanel);
transparentCheck = new CheckBox();
vPanel.add(transparentCheck);
transparentCheck.setText("transparent");
transparentCheck.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
updateMaterial();
}
});
transparentCheck.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
try {
storageControler.setValue(KEY_TRANSPARENT, ""+event.getValue());
} catch (StorageException e) {
//not important
LogUtils.log("storage error:"+e.getMessage());
}
}
});
try {
transparentCheck.setValue(ValuesUtils.toBoolean(storageControler.getValue(KEY_TRANSPARENT, "false"),false));
} catch (StorageException e) {
LogUtils.log("storage error:"+e.getMessage());
}
basicMaterialCheck = new CheckBox();
vPanel.add(basicMaterialCheck);
basicMaterialCheck.setText("BasicMaterial");
basicMaterialCheck.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
updateMaterial();
}
});
try {
basicMaterialCheck.setValue(ValuesUtils.toBoolean(storageControler.getValue(KEY_BASIC_MATERIAL, "false"),false));
} catch (StorageException e) {
LogUtils.log("storage error:"+e.getMessage());
}
basicMaterialCheck.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
try {
storageControler.setValue(KEY_BASIC_MATERIAL, ""+event.getValue());
} catch (StorageException e) {
//not important
LogUtils.log("storage error:"+e.getMessage());
}
}
});
/*
* i tried but behavior totally wrong.i should learn cdd ik again.
HorizontalPanel optionPanel=new HorizontalPanel();
parent.add(optionPanel);
CheckBox useEndsite=new CheckBox("use end-site");
optionPanel.add(useEndsite);
useEndsite.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
isIkTargetEndSite=event.getValue();
fitIkOnBone();
}
});
*/
HorizontalPanel shows=new HorizontalPanel();
parent.add(shows);
showBonesCheck = new CheckBox();
shows.add(showBonesCheck);
showBonesCheck.setText("Show Bones");
showBonesCheck.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
updateBonesVisible(showBonesCheck.getValue());
}
});
showBonesCheck.setValue(true);
showFingersCheck = new CheckBox();
parent.add(showFingersCheck);
showFingersCheck.setText("Show Fingers");//basically no need
showFingersCheck.setValue(false);
showFingersCheck.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
updateFingerBonesVisible(showFingersCheck.getValue());
}
});
smallCheck = new CheckBox();
shows.add(smallCheck);
smallCheck.setText("small");
smallCheck.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
updateBonesSize();
}
});
smallCheck.setValue(false);
ikVisibleCheck = new CheckBox();
shows.add(ikVisibleCheck);
ikVisibleCheck.setText("Iks");
ikVisibleCheck.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
updateIKVisible(ikVisibleCheck.getValue());
}
});
ikVisibleCheck.setValue(true);
final CheckBox showWireframeCheck = new CheckBox("wireframe");
showBackgroundCheck = new CheckBox("Tile");
shows.add(showBackgroundCheck);
showBackgroundCheck.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
boolean allVisible=showBackgroundCheck.getValue();
boolean wire=showWireframeCheck.getValue();
if(allVisible){
updateBackgroundVisible(wire);
planeMesh.setVisible(!wire);
}else{
updateBackgroundVisible(false);
planeMesh.setVisible(false);
}
}
});
showBackgroundCheck.setValue(true);
shows.add(showWireframeCheck);
showWireframeCheck.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
boolean allVisible=showBackgroundCheck.getValue();
boolean wire=showWireframeCheck.getValue();
if(allVisible){
updateBackgroundVisible(wire);
planeMesh.setVisible(!wire);
}else{
updateBackgroundVisible(false);
planeMesh.setVisible(false);
}
}
});
showWireframeCheck.setValue(true);
//dont need now
/*
HorizontalPanel frames=new HorizontalPanel();
frameRange = InputRangeWidget.createInputRange(0,1,0);
parent.add(HTML5Builder.createRangeLabel("Frame:", frameRange));
//parent.add(frames);
frames.add(frameRange);
*/
/*
frameRange.addListener(InputRangeWidget.createInputRangeListener() {
@Override
public void changed(int newValue) {
doPose(frameRange.getValue());
}
});
*/
//
HorizontalPanel boneInfo=new HorizontalPanel();
boneInfo.setWidth("250px");
parent.add(boneInfo);
boneInfo.add(new Label("Bone"));
rotateAndPosList = new ListBox();
boneInfo.add(rotateAndPosList);
rotateAndPosList.addItem("Rotation");
rotateAndPosList.addItem("Position");
rotateAndPosList.setSelectedIndex(0);
rotateAndPosList.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
switchRotateAndPosList();
}
});
HorizontalPanel boneNames=new HorizontalPanel();
parent.add(boneNames);
boneNamesBox = new ListBox();
boneNamesBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
updateBoneRanges();
}
});
boneNames.add(boneNamesBox);
ikLockCheck = new CheckBox("ik-lock");
ikLockCheck.setTitle("add bone to iklocks");
boneNames.add(ikLockCheck);
ikLockCheck.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
//String name=boneNameBox.get
if(ikLockCheck.getValue()){
ikLocks.add(getSelectedBoneName());
}else{
ikLocks.remove(getSelectedBoneName());
}
}
});
//mirror
HorizontalPanel mButtons=new HorizontalPanel();
parent.add(mButtons);
//because it' natural from front-view
Button rightToLeft=new Button("R > L");
rightToLeft.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
doMirror(true);
}
});
mButtons.add(rightToLeft);
Button mirror=new Button("L > R");
mirror.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
doMirror(false);
}
});
mButtons.add(mirror);
Button swap=new Button("do Swap");
swap.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
doSwap();
}
});
mButtons.add(swap);
bonePostionAndRotationContainer = new VerticalPanel();
bonePostionAndRotationContainer.setSize("210px", "150px");
bonePostionAndRotationContainer.setVerticalAlignment(VerticalPanel.ALIGN_MIDDLE);
parent.add(bonePostionAndRotationContainer);
//ikspos
ikPositionsPanel = new VerticalPanel();
bonePostionAndRotationContainer.add(ikPositionsPanel);
ikPositionsPanel.setVisible(false);
ikPositionLabelX = new Label();
ikPositionsPanel.add(ikPositionLabelX);
ikPositionLabelY = new Label();
ikPositionsPanel.add(ikPositionLabelY);
ikPositionLabelZ = new Label();
ikPositionsPanel.add(ikPositionLabelZ);
//positions
bonePositionsPanel = new VerticalPanel();
bonePostionAndRotationContainer.add(bonePositionsPanel);
bonePositionsPanel.setVisible(true);
HorizontalPanel h1bpos=new HorizontalPanel();
positionXBoneRange = InputRangeWidget.createInputRange(-60000/posDivided,60000/posDivided,0);
bonePositionsPanel.add(HTML5Builder.createRangeLabel("X-Pos:", positionXBoneRange,posDivided));
bonePositionsPanel.add(h1bpos);
h1bpos.add(positionXBoneRange);
createPosRangeControlers(positionXBoneRange,h1bpos);
positionXBoneRange.addMouseUpHandler(new MouseUpHandler() {
@Override
public void onMouseUp(MouseUpEvent event) {
positionToBone();
}
});
HorizontalPanel h2bpos=new HorizontalPanel();
positionYBoneRange = InputRangeWidget.createInputRange(-60000/posDivided,60000/posDivided,0);
bonePositionsPanel.add(HTML5Builder.createRangeLabel("Y-Pos:", positionYBoneRange,posDivided));
bonePositionsPanel.add(h2bpos);
h2bpos.add(positionYBoneRange);
createPosRangeControlers(positionYBoneRange,h2bpos);
positionYBoneRange.addMouseUpHandler(new MouseUpHandler() {
@Override
public void onMouseUp(MouseUpEvent event) {
positionToBone();
}
});
HorizontalPanel h3bpos=new HorizontalPanel();
positionZBoneRange = InputRangeWidget.createInputRange(-60000/posDivided,60000/posDivided,0);
bonePositionsPanel.add(HTML5Builder.createRangeLabel("Z-Pos:", positionZBoneRange,posDivided));
bonePositionsPanel.add(h3bpos);
h3bpos.add(positionZBoneRange);
createPosRangeControlers(positionZBoneRange,h3bpos);
/*
Button reset3bpos=new Button("Reset");
reset3bpos.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
positionZBoneRange.setValue(0);
positionToBone();
}
});
h3bpos.add(reset3bpos);
*/
positionZBoneRange.addMouseUpHandler(new MouseUpHandler() {
@Override
public void onMouseUp(MouseUpEvent event) {
positionToBone();
}
});
boneRotationsPanel = new VerticalPanel();
bonePostionAndRotationContainer.add(boneRotationsPanel);
HorizontalPanel rotButtons=new HorizontalPanel();
rotButtons.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
boneRotationsPanel.add(rotButtons);
Button resetAll=new Button("Reset All Rotation",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
rotationBoneXRange.setValue(0);
rotationBoneYRange.setValue(0);
rotationBoneZRange.setValue(0);
rotToBone();
if(event.isAltKeyDown()){
execIk(5,1);
}
}
});
rotButtons.add(resetAll);
//flip angle controler
rotButtons.add(new Label("Flip"));
final ListBox flipBox=new ListBox();
flipBox.addItem("");
flipBox.addItem("X");
flipBox.addItem("Y");
flipBox.addItem("Z");
rotButtons.add(flipBox);
flipBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
int index=flipBox.getSelectedIndex();
if(index==0){
return;
}
if(index==1){
rotationBoneXRange.setValue(-rotationBoneXRange.getValue());
flipBox.setSelectedIndex(0);
}
else if(index==2){
rotationBoneYRange.setValue(-rotationBoneYRange.getValue());
flipBox.setSelectedIndex(0);
}
else if(index==3){
rotationBoneZRange.setValue(-rotationBoneZRange.getValue());
flipBox.setSelectedIndex(0);
}
rotToBone();
}
});
HorizontalPanel h1b=new HorizontalPanel();
xlockCheck = new CheckBox();
h1b.add(xlockCheck);
xlockCheck.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if(xlockCheck.getValue()){
boneLock.setX(getSelectedBoneName(), rotationBoneXRange.getValue());
rotationBoneXRange.setEnabled(false);
}else{
boneLock.clearX(getSelectedBoneName());
rotationBoneXRange.setEnabled(true);
}
}
});
xlockCheck.setTitle("lock this axis");
HorizontalPanel labelPanelX=new HorizontalPanel();
boneRotationsPanel.add(labelPanelX);
rotationBoneXRange = InputRangeWidget.createInputRange(-180,180,0);
Label boneRotateXLabel=HTML5Builder.createRangeLabel("X-Rotate:", rotationBoneXRange);
labelPanelX.add(boneRotateXLabel);
boneRotateXLabel.setWidth("120px");
boneRotateLimitXLabel=new Label();
labelPanelX.add(boneRotateLimitXLabel);
boneRotationsPanel.add(h1b);
h1b.add(rotationBoneXRange);
createRotRangeControlers(rotationBoneXRange,h1b);
rotationBoneXRange.addMouseUpHandler(new MouseUpHandler() {
@Override
public void onMouseUp(MouseUpEvent event) {
rotToBone();
}
});
HorizontalPanel h2b=new HorizontalPanel();
ylockCheck = new CheckBox();
h2b.add(ylockCheck);
ylockCheck.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if(ylockCheck.getValue()){
boneLock.setY(getSelectedBoneName(), rotationBoneYRange.getValue());
rotationBoneYRange.setEnabled(false);
}else{
boneLock.clearY(getSelectedBoneName());
rotationBoneYRange.setEnabled(true);
}
}
});
ylockCheck.setTitle("lock this axis");
HorizontalPanel labelPanelY=new HorizontalPanel();
boneRotationsPanel.add(labelPanelY);
rotationBoneYRange = InputRangeWidget.createInputRange(-180,180,0);
Label boneRotateYLabel=HTML5Builder.createRangeLabel("Y-Rotate:", rotationBoneYRange);
boneRotateYLabel.setWidth("120px");
labelPanelY.add(boneRotateYLabel);
boneRotateLimitYLabel=new Label();
labelPanelY.add(boneRotateLimitYLabel);
boneRotationsPanel.add(h2b);
h2b.add(rotationBoneYRange);
createRotRangeControlers(rotationBoneYRange,h2b);
rotationBoneYRange.addMouseUpHandler(new MouseUpHandler() {
@Override
public void onMouseUp(MouseUpEvent event) {
rotToBone();
}
});
HorizontalPanel h3b=new HorizontalPanel();
zlockCheck = new CheckBox();
h3b.add(zlockCheck);
zlockCheck.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if(zlockCheck.getValue()){
boneLock.setZ(getSelectedBoneName(), rotationBoneZRange.getValue());
rotationBoneZRange.setEnabled(false);
}else{
boneLock.clearZ(getSelectedBoneName());
rotationBoneZRange.setEnabled(true);
}
}
});
zlockCheck.setTitle("lock this axis");
HorizontalPanel labelPanelZ=new HorizontalPanel();
boneRotationsPanel.add(labelPanelZ);
rotationBoneZRange = InputRangeWidget.createInputRange(-180,180,0);
Label boneRotateZLabel=HTML5Builder.createRangeLabel("Z-Rotate:", rotationBoneZRange);
boneRotateZLabel.setWidth("120px");
labelPanelZ.add(boneRotateZLabel);
boneRotateLimitZLabel=new Label();
labelPanelZ.add(boneRotateLimitZLabel);
boneRotationsPanel.add(h3b);
h3b.add(rotationBoneZRange);
createRotRangeControlers(rotationBoneZRange,h3b);
rotationBoneZRange.addMouseUpHandler(new MouseUpHandler() {
@Override
public void onMouseUp(MouseUpEvent event) {
rotToBone();
if(event.isAltKeyDown()){
execIk();
}
}
});
/*
Button test=new Button("image",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
String url=canvas.getRenderer().gwtPngDataUrl();//can'i snap shot with it.
Window.open(url, "test", null);
}
});
parent.add(test);
*/
/*
* crash so oftern if you use don't forget add
* THREE.WebGLRenderer(GWTRenderObject.create().preserveDrawingBuffer());
*
Button test=new Button("screen-shot");
parent.add(test);
test.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
//better screen shot
//Keep before setting
//change setting if
//re render
//back to setting
String url=renderer.gwtPngDataUrl();
//log(url);
//String text="<img style='position:absolute;top:0;left:0' src='"+url+"'>";
//ExportUtils.openTabHtml(text, "screenshot"+screenShotIndex);
ExportUtils.openTabImage(url, "screenshot"+screenShotIndex);
screenShotIndex++;
//Window.open(url, "newwin"+screenShotIndex, null); sometime crash and kill owner
screenShotIndex++;
}
});
*/
/*
parent.add(new Label("Texture Image"));
final FileUploadForm textureUpload=new FileUploadForm();
parent.add(textureUpload);
textureUpload.getFileUpload().addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
JsArray<File> files = FileUtils.toFile(event.getNativeEvent());
final FileReader reader=FileReader.createFileReader();
reader.setOnLoad(new FileHandler() {
@Override
public void onLoad() {
//log("load:"+Benchmark.end("load"));
//GWT.log(reader.getResultAsString());
textureUrl=reader.getResultAsString();
updateMaterial();
}
});
reader.readAsDataURL(files.get(0));
textureUpload.reset();
}
});
*/
/*
CheckBox do1small=new CheckBox("x 0.1");
do1small.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
onTotalSizeChanged(event.getValue());
}
});
parent.add(do1small);
*/
positionYRange.setValue(defaultOffSetY);//for test
//mesh-control
parent.add(createMeshMatrixControlPanel());
updateIkLabels();
createBottomPanel();
showControl();
}
private Panel createMeshMatrixControlPanel(){
VerticalPanel meshControler=new VerticalPanel();
meshControler.add(new Label("Mesh-Matrix"));
meshRotationYRange = InputRangeWidget.createInputRange(-180,180,0);
meshRotationYRange.addInputRangeListener(new InputRangeListener() {
@Override
public void changed(int newValue) {
doPoseByMatrix(ab);
}
});
meshRotationYRange.setWidth(240);
Label meshRotateYLabel=HTML5Builder.createRangeLabel("Y-Rotate:", meshRotationYRange);
meshRotateYLabel.setWidth("120px");
meshControler.add(meshRotateYLabel);
meshControler.add(meshRotationYRange);
//positions
HorizontalPanel xposPanel=new HorizontalPanel();
meshControler.add(xposPanel);
Label xposLabel=new Label("X-Pos:");
xposPanel.add(xposLabel);
meshPositionXBox = new DoubleBox();
meshPositionXBox.setValue(0.0);
xposPanel.add(meshPositionXBox);
meshPositionXBox.addValueChangeHandler(new ValueChangeHandler<Double>() {
@Override
public void onValueChange(ValueChangeEvent<Double> event) {
doPoseByMatrix(ab);
}
});
return meshControler;
}
/*
int upscale=1;
protected void onTotalSizeChanged(Boolean value) {
if(value){
upscale=10;
}else{
upscale=1;
}
List<Mesh> meshs=Lists.newArrayList();
//meshs.add(selectionMesh);
meshs.addAll(vertexMeshs);
meshs.addAll(boneJointMeshs);
meshs.addAll(boneCoreMeshs);
meshs.addAll(endPointMeshs);
if(selectionPointIndicateMesh!=null){
meshs.add(selectionPointIndicateMesh);
}
if(boneSelectionMesh!=null){
meshs.add(boneSelectionMesh);
}
//redo-bone and vertex
for(Mesh mesh:meshs){
double scale=(1.0/upscale);
mesh.getScale().set(scale,scale,scale);
}
}
*/
public int getPosDivided() {
return posDivided;
}
boolean isIkTargetEndSite;
private void createPosRangeControlers(final InputRangeWidget range,HorizontalPanel parent){
Button minus3b=new Button("-");
minus3b.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
range.setValue(range.getValue()-10);
positionToBone();
if(event.isAltKeyDown()){
execIk(5,1);
}
}
});
parent.add(minus3b);
Button minus=new Button("-.");
minus.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
range.setValue(range.getValue()-1);
positionToBone();
if(event.isAltKeyDown()){
execIk(5,1);
}
}
});
parent.add(minus);
Button zero=new Button("0");
zero.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
range.setValue(0);
positionToBone();
if(event.isAltKeyDown()){
execIk(5,1);
}
}
});
parent.add(zero);
Button plus3b=new Button("+.");
plus3b.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
range.setValue(range.getValue()+1);
positionToBone();
if(event.isAltKeyDown()){
execIk(5,1);
}
}
});
parent.add(plus3b);
Button plus=new Button("+");
plus.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
range.setValue(range.getValue()+10);
positionToBone();
if(event.isAltKeyDown()){
execIk(5,1);
}
}
});
parent.add(plus);
}
private void createRotRangeControlers(final InputRangeWidget range,HorizontalPanel parent){
Button minus3b=new Button("-");
minus3b.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
range.setValue(range.getValue()-1);
rotToBone();
if(event.isAltKeyDown()){
execIk(5,1);
}
}
});
parent.add(minus3b);
List<Integer> angles=Lists.newArrayList(-180,-135,-90,-60,-45,0,45,60,90,135,180);
final ValueListBox<Integer> vlist=new ValueListBox<Integer>(new Renderer<Integer>() {
@Override
public String render(Integer object) {
if(object==null){
return "";
}
return String.valueOf(object);
}
@Override
public void render(Integer object, Appendable appendable) throws IOException {
// TODO Auto-generated method stub
}
});
vlist.setValue(null);
vlist.setAcceptableValues(angles);
vlist.addValueChangeHandler(new ValueChangeHandler<Integer>() {
@Override
public void onValueChange(ValueChangeEvent<Integer> event) {
if(event.getValue()==null){
return;
}
range.setValue(event.getValue());
rotToBone();
vlist.setValue(null);//reset to
/*
if(event.isAltKeyDown()){
execIk();
}
*/
}
});
parent.add(vlist);
Button plus3b=new Button("+");
plus3b.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
range.setValue(range.getValue()+1);
rotToBone();
if(event.isAltKeyDown()){
execIk(5,1);
}
}
});
parent.add(plus3b);
}
protected void doSwap() {
List<AngleAndPosition> lastAAP=cloneAngleAndPositions(ab.getBonesAngleAndMatrixs());
if(isSelectedIk() && getSelectedBoneName().isEmpty()){
IKData ik=getCurrentIkData();
for(String name:ik.getBones()){
String targetName=getMirroredName(name);
if(targetName==null){
continue;
}
int index=ab.getBoneIndex(targetName);
int srcIndex=ab.getBoneIndex(name);
if(index!=-1 && srcIndex!=-1){
Vector3 angle1=ab.getBoneAngleAndMatrix(srcIndex).getDegreeAngle();
Vector3 angle=ab.getBoneAngleAndMatrix(index).getDegreeAngle();
rotToBone(name, angle.getX(), -angle.getY(), -angle.getZ(),false);
rotToBone(targetName, angle1.getX(), -angle1.getY(), -angle1.getZ(),true);
}
}
//move ik pos
IKData targetIk=getIk(getMirroredName(ik.getName()));
if(targetIk!=null){
Vector3 root=ab.getBonePosition(0);
Vector3 targetPos=targetIk.getTargetPos().clone().sub(root);
targetPos.setX(targetPos.getX()*-1);
targetPos.add(root);
Vector3 srcPos=ik.getTargetPos().clone().sub(root);
srcPos.setX(srcPos.getX()*-1);
srcPos.add(root);
ik.getTargetPos().set(targetPos.getX(),targetPos.getY(),targetPos.getZ());
targetIk.getTargetPos().set(srcPos.getX(),srcPos.getY(),srcPos.getZ());
doPoseByMatrix(ab);
}
}else{
String name=getSelectedBoneName();
if(name==null){
return;
}
//h mirror
String targetName=getMirroredName(name);
if(targetName==null){
return;
}
int index=ab.getBoneIndex(targetName);
if(index!=-1){
Vector3 targetAngle=ab.getBoneAngleAndMatrix(index).getDegreeAngle();
double x=rotationBoneXRange.getValue();
double y=rotationBoneYRange.getValue()*-1;
double z=rotationBoneZRange.getValue()*-1;
rotationBoneXRange.setValue((int) targetAngle.getX());
rotationBoneYRange.setValue((int) targetAngle.getY()*-1);
rotationBoneZRange.setValue((int) targetAngle.getZ()*-1);
rotToBone(targetName,x,y,z,false);
rotToBone();
}
}
List<AngleAndPosition> newAAP=cloneAngleAndPositions(ab.getBonesAngleAndMatrixs());
AngleAndPositionsCommand command=new AngleAndPositionsCommand(newAAP, lastAAP);
undoControler.addCommand(command);
}
private IKData getIk(String name){
for(IKData ik:ikdatas){
if(ik.getName().equals(name)){
return ik;
}
}
return null;
}
protected void doMirror(boolean rightToLeft) {
List<AngleAndPosition> lastAAP=cloneAngleAndPositions(ab.getBonesAngleAndMatrixs());
if(isSelectedIk() && getSelectedBoneName().isEmpty()){
IKData ik=getCurrentIkData();
for(String name:ik.getBones()){
String targetName=getMirroredName(name);
if(targetName==null){
continue;
}
String srcBoneName;
String destBoneName;
if(isRightBone(name)){
if(rightToLeft){
srcBoneName=name;
destBoneName=targetName;
}else{
srcBoneName=targetName;
destBoneName=name;
}
}else{
if(rightToLeft){
srcBoneName=targetName;
destBoneName=name;
}else{
srcBoneName=name;
destBoneName=targetName;
}
}
int index=ab.getBoneIndex(srcBoneName);
if(index!=-1){
Vector3 angle=ab.getBoneAngleAndMatrix(index).getDegreeAngle();
rotToBone(destBoneName, angle.getX(), -angle.getY(), -angle.getZ(),false);
}
}
//
//Vector3 lastPosition=ik.getTargetPos().clone();
//invoke not call
fitIkOnBone();
doPoseByMatrix(ab);//do it only once
switchSelectionIk(ik.getLastBoneName());//recreate ik pose otherwise use old pose
updateBoneRanges();//possible changed
//ik.getTargetPos().copy(lastPosition);//restore position,usually user continue editing.but i change my mind ,when set opposite selection current ik pos make bad effect
}else{//single selected bone
String name=getSelectedBoneName();
if(name==null){
//somehow not selected
return;
}
String targetName=getMirroredName(name);
LogUtils.log("mirror:"+targetName);
if(targetName==null){
return;
}
int targetBoneIndex=ab.getBoneIndex(targetName);
if(targetBoneIndex!=-1){
if(isRightBone(name)){
if(rightToLeft){
rotToBone(targetName, rotationBoneXRange.getValue(),-rotationBoneYRange.getValue(),-rotationBoneZRange.getValue(),true);
}else{
//copy from left
Vector3 angle=ab.getBoneAngleAndMatrix(targetBoneIndex).getDegreeAngle();
rotationBoneXRange.setValue((int) angle.getX());
rotationBoneYRange.setValue((int) angle.getY()*-1);
rotationBoneZRange.setValue((int) angle.getZ()*-1);
rotToBone();
}
}else{//left bone
if(rightToLeft){
Vector3 angle=ab.getBoneAngleAndMatrix(targetBoneIndex).getDegreeAngle();
rotationBoneXRange.setValue((int) angle.getX());
rotationBoneYRange.setValue((int) angle.getY()*-1);
rotationBoneZRange.setValue((int) angle.getZ()*-1);
rotToBone();
}else{
rotToBone(targetName, rotationBoneXRange.getValue(),-rotationBoneYRange.getValue(),-rotationBoneZRange.getValue(),true);
}
}
}
}
List<AngleAndPosition> newAAP=cloneAngleAndPositions(ab.getBonesAngleAndMatrixs());
AngleAndPositionsCommand command=new AngleAndPositionsCommand(newAAP, lastAAP);
undoControler.addCommand(command);
//no need invoke;
}
protected void updateBonesVisible(boolean value) {
if(bone3D!=null){
Object3DUtils.setVisibleAll(bone3D, value);
if(value){
updateFingerBonesVisible(showFingersCheck.getValue());
}
}
if(skeltonHelper!=null){
skeltonHelper.setVisible(value);
}
}
protected void updateFingerBonesVisible(boolean value) {
List<String> fingerNames=Lists.newArrayList("thumb","index","middle","pinky","ring");
if(bone3D!=null){
for(int i=0;i<bone3D.getChildren().length();i++){
Object3D obj=bone3D.getChildren().get(i);
String name=obj.getName();
for(String fingerName:fingerNames){
if(name.startsWith(fingerName)){
obj.setVisible(value);
}
}
}
}
}
protected void updateBonesSize() {
if(bone3D!=null){
doPoseByMatrix(ab);//re-create
}
}
protected void updateIKVisible(boolean value) {
if(ik3D!=null){
Object3DUtils.setVisibleAll(ik3D, value);
}
}
protected void updateBackgroundVisible(boolean value) {
if(backgroundGrid!=null){
Object3DUtils.setVisibleAll(backgroundGrid,value);
}
}
protected String getMirroredName(String name) {
if(name.endsWith("_R")){
return name.replace("_R", "_L");
}
if(name.endsWith("_L")){
return name.replace("_L", "_R");
}
if(name.indexOf("Right")!=-1){
return name.replace("Right", "Left");
}
if(name.indexOf("right")!=-1){
return name.replace("right", "left");
}
if(name.indexOf("Left")!=-1){
return name.replace("Left", "Right");
}
if(name.indexOf("left")!=-1){
return name.replace("left", "right");
}
//makehuman 19 bones
if(name.startsWith("r")){
return "l"+name.substring(1);
}
else if(name.startsWith("l")){
return "r"+name.substring(1);
}
return null;
}
private boolean isRightBone(String name){
if(name.endsWith("_R")){
return true;
}
if(name.indexOf("Right")!=-1){
return true;
}
if(name.indexOf("right")!=-1){
return true;
}
//makehuman 19 bones
if(name.startsWith("r")){//rethink later
// return true;
}
return false;
}
private JsArray<Material> loadedMaterials;
JSONModelFile lastLoadedModel;
private void fixGeometryWeight(Geometry geometry){
for(int i=0;i<geometry.getSkinWeight().length();i++){
Vector4 vec4=geometry.getSkinWeight().get(i);
double x=vec4.getX();
double y=vec4.getY();
//seems somehow 1.0 weight make problem?
if(x==1){
geometry.getSkinIndices().get(i).setY(geometry.getSkinIndices().get(i).getX());
}else if(y==1){
geometry.getSkinIndices().get(i).setX(geometry.getSkinIndices().get(i).getY());
}else{//total value is under 1.0 bone usually make problem
double total=x+y;
if(total>1){
// LogUtils.log("invalid:"+total);
}
double remain=(1.0-total);
double nx=(x/total)*remain+x;
double ny=1.0-nx;
vec4.setX(nx);
vec4.setY(ny);
//must be 1.0 ?
}
}
}
private void LoadJsonModel(String jsonText){
try {
JSONModelFile model=GWTThreeUtils.parseJsonObject(jsonText);
lastLoadedModel=model;
GWTThreeUtils.loadJsonModel(lastLoadedModel,new JSONLoadHandler() {
@Override
public void loaded(Geometry geometry,JsArray<Material> materials) {
if(bodyMesh!=null){
rootObject.remove(bodyMesh);//for initialzie
bodyMesh=null;
}
//LogUtils.log("material?");
loadedMaterials=materials;
ab=null;//for remake matrix.
LogUtils.log("loadJsonModel:");
LogUtils.log(geometry);
//force scalling up
//test scale up
//TODO support scale on app
double scale=10;
for(int i=0;i<geometry.getVertices().length();i++){
geometry.getVertices().get(i).multiplyScalar(scale);//or another way.
}
if(geometry.getBones()!=null){
for(int i=0;i<geometry.getBones().length();i++){
JsArrayNumber number=geometry.getBones().get(i).getPos();
for(int j=0;j<number.length();j++){
number.set(j, number.get(j)*scale);
}
}
}
//LogUtils.log(geometry);
//fix geometry weight,otherwise broken model
fixGeometryWeight(geometry);//TODO need my calcurate
baseGeometry=geometry;//change body mesh
LogUtils.log(baseGeometry.getBones());
if(baseGeometry.getBones()!=null && baseGeometry.getBones().length()>0){
LogUtils.log("create-bone from geometry:size="+baseGeometry.getBones().length());
setBone(baseGeometry.getBones()); //possible broken bone.TODO test geometry bone
/*
logger.fine("testly use bone from bvh");//TODO move back
AnimationBoneConverter converter=new AnimationBoneConverter();
setBone(converter.convertJsonBone(bvh));
*/
}else{
Window.alert("your loaded model has not contain bone,\nand use default bone.but this make many problem.\nplease export your model with bone");
logger.fine("bvh:"+bvh);
LogUtils.log("use bvh bone:maybe this is problem");
//initialize default bone
AnimationBoneConverter converter=new AnimationBoneConverter();
setBone(converter.convertJsonBone(bvh));
}
//log(""+(baseGeometry.getBones()!=null));
//log(baseGeometry.getBones());
doRePose(0);
//log("snapped");
if(poseEditorDatas.size()==0){//initial new list
initialPoseFrameData=snapCurrentFrameData();//get invalid pose
updateMaterial();
doNewFile();
}
//update texture
if(texture!=null){
if(lastLoadedModel.getMetaData().getFormatVersion()==3){
LogUtils.log("model-format is 3.0 and set flipY=false");
texture.setFlipY(false);
}else{
texture.setFlipY(true);
}
}
}
});//texture tested.
} catch (InvalidModelFormatException e) {
LogUtils.log("LoadJsonModel:"+e.getMessage());
}
}
private String getSelectedBoneName(){
if(boneNamesBox.getSelectedIndex()==-1){
return "";
}
return boneNamesBox.getValue(boneNamesBox.getSelectedIndex());
}
private void positionToBone(String name,double x,double y,double z){
int index=ab.getBoneIndex(name);
if(index!=0){
//limit root only
//TODO limit by bvh channel
return;
}
//LogUtils.log("increment-x:x="+x+",increment="+incrementMeshX);
Vector3 pos=THREE.Vector3(x,y,z
).multiplyScalar(0.01);//what is 100?
/*
Vector3 angles=GWTThreeUtils.rotationToVector3(ab.getBoneAngleAndMatrix(index).getMatrix());
Matrix4 posMx=GWTThreeUtils.translateToMatrix4(pos);
Matrix4 rotMx=GWTThreeUtils.rotationToMatrix4(angles);
rotMx.multiply(posMx,rotMx);
ab.getBoneAngleAndMatrix(index).setMatrix(rotMx);
*/
ab.getBoneAngleAndMatrix(index).setPosition(pos);
ab.getBoneAngleAndMatrix(index).updateMatrix();
doPoseByMatrix(ab);
if( isSelectedBone()){
selectionMesh.setPosition(pos);
}
}
protected void positionToBone() {
positionToBone(boneNamesBox.getItemText(boneNamesBox.getSelectedIndex()),
positionXBoneRange.getValue(),
positionYBoneRange.getValue(),positionZBoneRange.getValue());
}
protected void switchRotateAndPosList() {
int index=rotateAndPosList.getSelectedIndex();
if(index==0){
bonePositionsPanel.setVisible(false);
boneRotationsPanel.setVisible(true);
}else{
bonePositionsPanel.setVisible(true);
boneRotationsPanel.setVisible(false);
}
}
private String getNewName(){
return "Untitled"+(fileIndex);
}
private int fileIndex;
private Button redoButton;
private void createBottomPanel(){
bottomPanel = new PopupPanel();
bottomPanel.setVisible(true);
bottomPanel.setSize("650px", "106px");
TabPanel bottomTab=new TabPanel();
bottomTab.addSelectionHandler(new SelectionHandler<Integer>() {
@Override
public void onSelection(SelectionEvent<Integer> event) {
onBottomTabSelectionChanged(event.getSelectedItem());
}
});
bottomPanel.add(bottomTab);
VerticalPanel main=new VerticalPanel();
bottomTab.add(main,"Edit");
bottomTab.add(createPlayerPanel(),"Play");
bottomTab.selectTab(0);
HorizontalPanel trueTop=new HorizontalPanel();
trueTop.setWidth("100%");
main.add(trueTop);
//upper
HorizontalPanel topPanel=new HorizontalPanel();
trueTop.add(topPanel);
pedSelectionListBox=new ValueListBox<PoseEditorData>(new Renderer<PoseEditorData>() {
@Override
public String render(PoseEditorData object) {
if(object==null){
return "";
}
return object.getName();
}
@Override
public void render(PoseEditorData object, Appendable appendable) throws IOException {
// TODO Auto-generated method stub
}
});
pedSelectionListBox.addValueChangeHandler(new ValueChangeHandler<PoseEditorData>() {
@Override
public void onValueChange(ValueChangeEvent<PoseEditorData> event) {
event.getValue().updateMatrix(ab);//need bone data
updatePoseEditorDatas();
}
});
topPanel.add(pedSelectionListBox);
Button newFile=new Button("New");
topPanel.add(newFile);
newFile.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
doNewFile();
}
});
saveButton = new Button("Save");
topPanel.add(saveButton);
saveButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
doSaveFile();
}
});
Button saveAsButton = new Button("SaveAs");
topPanel.add(saveAsButton);
saveAsButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
doSaveAsFile(getSelectedPoseEditorData());
}
});
undoButton = new Button("Undo");
topPanel.add(undoButton);
undoButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
undoControler.undo();
}
});
undoButton.setEnabled(false);
redoButton = new Button("Redo");
topPanel.add(redoButton);
redoButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
undoControler.redo();
}
});
redoButton.setEnabled(false);
HorizontalPanel rightSide=new HorizontalPanel();
rightSide.setHorizontalAlignment(HorizontalPanel.ALIGN_RIGHT);
rightSide.setWidth("100%");
trueTop.add(rightSide);
Button imageBt=new Button("Screenshot",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
//doScreenShot();
reservedScreenshot=true;
}
});
rightSide.add(imageBt);
gifAnimeBt = new Button("GifAnime",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
reservedCreateGifAnime=true;
}
});
//rightSide.add(gifAnimeBt);//stop support gif-anime
imageLinkContainer = new VerticalPanel();
imageLinkContainer.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
imageLinkContainer.setWidth("100px");
rightSide.add(imageLinkContainer);
HorizontalPanel upperPanel=new HorizontalPanel();
upperPanel.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
main.add(upperPanel);
Button snap=new Button("Add");//TODO before,after
upperPanel.add(snap);
snap.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
insertFrame(getSelectedPoseEditorData().getPoseFrameDatas().size(),false);
}
});
Button replace=new Button("Replace");//TODO before,after
upperPanel.add(replace);
replace.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
insertFrame(currentFrameRange.getValue(),true);
}
});
/*
*should think system
Button cut=new Button("Cut");
upperPanel.add(cut);
cut.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
doCut();
}
});
*/
final Button pasteBefore=new Button("Paste Before");
final Button pasteAfter=new Button("Paste After");
pasteBefore.setEnabled(false);
pasteAfter.setEnabled(false);
Button copy=new Button("Copy");
upperPanel.add(copy);
copy.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
doCopy();
pasteBefore.setEnabled(true);
pasteAfter.setEnabled(true);
}
});
upperPanel.add(pasteBefore);
pasteBefore.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
doPasteBefore();
getSelectedPoseEditorData().setModified(true);
updateSaveButtons();
}
});
upperPanel.add(pasteAfter);
pasteAfter.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
doPasteAfter();
getSelectedPoseEditorData().setModified(true);
updateSaveButtons();
}
});
Button remove=new Button("Remove");
upperPanel.add(remove);
remove.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
getSelectedPoseEditorData().getPoseFrameDatas().remove(poseFrameDataIndex);
getSelectedPoseEditorData().setModified(true);
updatePoseIndex(Math.max(0,poseFrameDataIndex-1));
updateSaveButtons();
}
});
HorizontalPanel pPanel=new HorizontalPanel();
main.add(pPanel);
pPanel.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
currentFrameRange = InputRangeWidget.createInputRange(0,0,0);
currentFrameRange.setWidth(420);
pPanel.add(currentFrameRange);
currentFrameRange.addMouseUpHandler(new MouseUpHandler() {
@Override
public void onMouseUp(MouseUpEvent event) {
FrameMoveCommand command=new FrameMoveCommand(currentFrameRange.getValue(),cloneAngleAndPositions(ab.getBonesAngleAndMatrixs()));
command.invoke();
undoControler.addCommand(command);
}
});
Button prev=new Button("Prev");
pPanel.add(prev);
prev.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
doPrevFrame();
}
});
Button next=new Button("Next");
pPanel.add(next);
next.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
doNextFrame();
}
});
currentFrameLabel = new Label("1/1");//usually this style
currentFrameLabel.setWidth("40px");
pPanel.add(currentFrameLabel);
Button first=new Button("First");
pPanel.add(first);
first.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
doFirstFrame();
}
});
bottomPanel.show();
super.leftBottom(bottomPanel);
}
protected void onBottomTabSelectionChanged(Integer selectedItem) {
if(selectedItem==0){//edit tab
executeStop();
ikVisibleCheck.setValue(true, true);
showBonesCheck.setValue(true, true);
}else if(selectedItem==1){//play tab
executePlayAnimation();
ikVisibleCheck.setValue(false, true);
showBonesCheck.setValue(false, true);
}
//I'm not sure still some objects make each time ,TODO fix this
updateBonesVisible(showBonesCheck.getValue());
if(ik3D!=null){
Object3DUtils.setVisibleAll(ik3D, ikVisibleCheck.getValue());
}
}
private Widget createPlayerPanel() {
VerticalPanel panel=new VerticalPanel();
HorizontalPanel buttons=new HorizontalPanel();
buttons.setVerticalAlignment(VerticalPanel.ALIGN_MIDDLE);
panel.add(buttons);
HorizontalPanel buttons2=new HorizontalPanel();
buttons2.setVerticalAlignment(VerticalPanel.ALIGN_MIDDLE);
panel.add(buttons2);
CheckBox connectFirstCheck=new CheckBox("Connect to first frame");
connectFirstCheck.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
connectFirst=event.getValue();
executeStop();
executePlayAnimation();
}
});
buttons2.add(connectFirstCheck);
CheckBox moveOnlyRootCheck=new CheckBox("Move only root");
moveOnlyRootCheck.setValue(moveOnlyRoot);
moveOnlyRootCheck.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
moveOnlyRoot=event.getValue();
executeStop();
executePlayAnimation();
}
});
buttons2.add(moveOnlyRootCheck);
Button play=new Button("Play Animation",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
executePlayAnimation();
//TODO disable all editor
}
});
buttons.add(play);
Button stop=new Button("Stop",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
executeStop();
}
});
buttons.add(stop);
final HorizontalPanel downloadPanel=new HorizontalPanel();
downloadPanel.setSpacing(4);
Button export=new Button("Export",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
AnimationClip clip=makeFrameAnimation(playAnimationDuration,posDivided,connectFirst,moveOnlyRoot);
JavaScriptObject json=AnimationClip.toJSON(clip);
JSONObject obj=new JSONObject(json);
downloadPanel.clear();
Anchor a=HTML5Download.get().generateTextDownloadLink(obj.toString(), "animation.json", "download",true);
downloadPanel.add(a);
}
});
buttons.add(export);
buttons.add(downloadPanel);
//
return panel;
}
protected void executeStop() {
stopAnimation();
doPoseByMatrix(ab);//recover base pose
}
private double playAnimationDuration=1;
private boolean connectFirst;
/*
* if translate other bone,it's animation only work same bone.
*/
private boolean moveOnlyRoot=true;
protected void executePlayAnimation() {
stopAnimation();
playFrameAnimation(playAnimationDuration,connectFirst,moveOnlyRoot);
}
private List<AngleAndPosition> poseFrameDataToAngleAndPosition(PoseFrameData poseFrameData){
List<AngleAndPosition> list=Lists.newArrayList();
for(int i=0;i<poseFrameData.getAngles().size();i++){
//need add bone-position-data
list.add(new AngleAndPosition(poseFrameData.getAngles().get(i).clone(), poseFrameData.getPositions().get(i).clone().add(ab.getBaseBoneRelativePosition(i)), null));
}
return list;
}
private AnimationClip makeFrameAnimation(double duration,double divided,boolean connectFirst,boolean moveOnlyRoot){
PoseEditorData editorData=getSelectedPoseEditorData();
List<JsArray<KeyframeTrack>> tracksList=Lists.newArrayList();
List<PoseFrameData> frames=Lists.newArrayList(editorData.getPoseFrameDatas());
if(connectFirst){
frames.add(editorData.getPoseFrameDatas().get(0));
}
for(int i=0;i<frames.size();i++){
List<AngleAndPosition> ap=poseFrameDataToAngleAndPosition(frames.get(i));
JsArray<KeyframeTrack> tracks=createAnimationTracks(ap,divided,moveOnlyRoot);
tracksList.add(tracks);
}
JsArray<KeyframeTrack> tracks=mergeAnimationTracks(tracksList, duration);
/*
LogUtils.log("tracks:"+tracks.length());
for(int i=0;i<tracks.length();i++){
LogUtils.log(tracks.get(i));
}
*/
AnimationClip clip=THREE.AnimationClip("play", -1,tracks);
return clip;
}
protected void playFrameAnimation(double duration,boolean connectFirst,boolean moveOnlyRoot) {
AnimationClip clip=makeFrameAnimation(duration,1,connectFirst,moveOnlyRoot);
mixer.stopAllAction();
mixer.uncacheClip(clip);//same name cache that.
mixer.clipAction(clip).play();
}
private boolean isUsingRenderer;//gifanime
private boolean reservedCreateGifAnime;//call start gif-anime
protected void doGifAnime() {
//TODO timer for update ui.
gifAnimeBt.setEnabled(false);
Timer timer=new Timer(){
@Override
public void run() {
try{
isUsingRenderer=true;
int quality=settingPanel.getGifQuality();
int speed=settingPanel.getGifSpeed();
boolean lastBackground=showBackgroundCheck.getValue();
boolean lastIk=ikVisibleCheck.getValue();
boolean lastBone=showBonesCheck.getValue();
showBackgroundCheck.setValue(settingPanel.isGifShowBackground());
ikVisibleCheck.setValue(settingPanel.isGifShowIk());
showBonesCheck.setValue(settingPanel.isGifShowBone());
LogUtils.log(settingPanel.isGifShowIk()+","+settingPanel.isGifShowBone());
Canvas baseCanvas=settingPanel.createBgCanvas();
Canvas gifCanvas=CanvasUtils.createCanvas(baseCanvas.getCoordinateSpaceWidth(),baseCanvas.getCoordinateSpaceHeight());
int lastFrameIndex=currentFrameRange.getValue();
List<ImageElement> elements=Lists.newArrayList();
for(int i=0;i<getCurrentDataSize();i++){
updatePoseIndex(i);
renderer.render(scene, camera);
String url=canvas.getRenderer().gwtPngDataUrl();
ImageElement element=ImageElementUtils.create(url);
CanvasUtils.clear(gifCanvas);
gifCanvas.getContext2d().drawImage(baseCanvas.getCanvasElement(), 0, 0);
CanvasUtils.drawCenter(gifCanvas, element);
elements.add(ImageElementUtils.create(gifCanvas.toDataUrl()));
}
final String gifUrl=GifAnimeBuilder.from(elements).setQuality(quality).loop().delay(speed).toDataUrl();
//becareful somehow rightnow no transparent gif supported.
//even choose choose transparent draw black.
Anchor anchor=HTML5Download.get().generateBase64DownloadLink(gifUrl, "image/gif", getSelectedPoseEditorData().getName()+".gif", "Download", true);
imageLinkContainer.clear();
imageLinkContainer.add(anchor);
/*
Anchor debug=HTML5Download.get().generateBase64DownloadLink(baseCanvas.toDataUrl(), "image/png", "poseeditor.png", "debug", true);
imageLinkContainer.add(debug);
*/
//reset last index
currentFrameRange.setValue(lastFrameIndex);
updatePoseIndex(lastFrameIndex);
showBackgroundCheck.setValue(lastBackground);
ikVisibleCheck.setValue(lastIk);
showBonesCheck.setValue(lastBone);
updateBackgroundVisible(showBackgroundCheck.getValue());
updateIKVisible(ikVisibleCheck.getValue());
updateBonesVisible(showBonesCheck.getValue());
isUsingRenderer=false;
}catch (Exception e) {
Window.alert(e.getMessage());
}finally{
gifAnimeBt.setEnabled(true);
}
}
// TODO Auto-generated method stub
};
timer.schedule(50);//just disable bt;
}
private SimpleUndoControler undoControler=new SimpleUndoControler();
private class SimpleUndoControler{
private ICommand currentCommand;
public void undo(){
if(currentCommand!=null){
currentCommand.undo();
}
undoButton.setEnabled(false);
redoButton.setEnabled(true);
}
public void redo(){
if(currentCommand!=null){
currentCommand.redo();
}
undoButton.setEnabled(true);
redoButton.setEnabled(false);
}
public void addCommand(ICommand command){
currentCommand=command;
undoButton.setEnabled(true);
redoButton.setEnabled(false);
}
}
private List<AngleAndPosition> cloneAngleAndPositions(List<AngleAndPosition> datas){
return AnimationBonesData.cloneAngleAndMatrix(datas);
}
private class AngleAndPositionsCommand implements ICommand{
public AngleAndPositionsCommand(List<AngleAndPosition> newBoneData, List<AngleAndPosition> lastBoneData) {
super();
this.newBoneData = newBoneData;
this.lastBoneData = lastBoneData;
}
private List<AngleAndPosition> newBoneData;
private List<AngleAndPosition> lastBoneData;
@Override
public void invoke() {
selectAnimationDataData(cloneAngleAndPositions(newBoneData));
}
@Override
public void undo() {
selectAnimationDataData(cloneAngleAndPositions(lastBoneData));
}
@Override
public void redo() {
selectAnimationDataData(cloneAngleAndPositions(newBoneData));
}
}
private class FrameMoveCommand implements ICommand{
public FrameMoveCommand(int index,List<AngleAndPosition> boneData){
this.frameIndex=index;
this.boneData=boneData;
this.lastIndex=poseFrameDataIndex;
}
private int lastIndex;
private int frameIndex;
private List<AngleAndPosition> boneData;
@Override
public void invoke() {
currentFrameRange.setValue(frameIndex);
updatePoseIndex(frameIndex);
}
@Override
public void undo() {
currentFrameRange.setValue(lastIndex);
updatePoseIndex(lastIndex);
selectAnimationDataData(AnimationBonesData.cloneAngleAndMatrix(boneData));
}
@Override
public void redo() {
currentFrameRange.setValue(frameIndex);
updatePoseIndex(frameIndex);
}
}
private boolean reservedScreenshot;
private boolean reservedSettingPreview;
protected void doScreenShot(WebGLRenderer renderer) {
if(settingPanel.getScreenshotBackgroundType()==1){
String bgcolor=settingPanel.getScreenshotBackgroundValue();
int clearColor=ColorUtils.toColor(bgcolor);
renderer.setClearColor(clearColor, 1);
//need re-render
renderer.render(scene, camera);
}
String url=canvas.getRenderer().gwtPngDataUrl();
Anchor anchor=HTML5Download.get().generateBase64DownloadLink(url, "image/png", "poseeditor.png", "Download", true);
imageLinkContainer.clear();
imageLinkContainer.add(anchor);
//set back clear
renderer.setClearColor(0, 0);//somehow this is default
}
private void doFirstFrame() {
FrameMoveCommand command=new FrameMoveCommand(0,AnimationBonesData.cloneAngleAndMatrix(ab.getBonesAngleAndMatrixs()));
command.invoke();
undoControler.addCommand(command);
}
private void doPrevFrame(){
int value=currentFrameRange.getValue();
if(value>0){
value--;
FrameMoveCommand command=new FrameMoveCommand(value,AnimationBonesData.cloneAngleAndMatrix(ab.getBonesAngleAndMatrixs()));
command.invoke();
undoControler.addCommand(command);
}
}
private int getCurrentDataSize(){
return getSelectedPoseEditorData().getPoseFrameDatas().size();
}
private void doNextFrame(){
int value=currentFrameRange.getValue();
if(value<getSelectedPoseEditorData().getPoseFrameDatas().size()-1){
value++;
FrameMoveCommand command=new FrameMoveCommand(value,AnimationBonesData.cloneAngleAndMatrix(ab.getBonesAngleAndMatrixs()));
command.invoke();
undoControler.addCommand(command);
}
}
private int getNewDataIndex() throws StorageException{
int dataIndex=0;
dataIndex = storageControler.getValue(KEY_INDEX, 0);
return dataIndex;
}
protected void doSaveAsFile(PoseEditorData pdata) {
String result=Window.prompt("Save File", pdata.getName());
if(result!=null){
pdata.setName(result);
JSONObject data=PoseEditorData.convertToJson(pdata);
updateListBox();
//fileNames.setItemText(poseEditorDataSelection, result);
//TODO
if(!storageControler.isAvailable()){
//TODO just export
Window.alert("not saved because your browser not supoort HTML5 storage");
return;
}
// Window.alert("hello");
//save database
int dataIndex=0;
try {
dataIndex = getNewDataIndex();
} catch (StorageException e) {
alert("save faild:"+e.getMessage());
return;
}
//TODO method?
//Canvas canvas=Canvas.createIfSupported();
/*
int thumbW=32;
int thumbH=32;
canvas.setSize(thumbW+"px", thumbH+"px");
canvas.setCoordinateSpaceWidth(thumbW);
canvas.setCoordinateSpaceHeight(thumbH);
//log(renderer.gwtCanvas());
//now stop write image.
//canvas.getContext2d().drawImage(renderer.gwtCanvas(),0,0,screenWidth,screenHeight,0,0,thumbW,thumbH);
String thumbnail=canvas.toDataUrl();
LogUtils.log(thumbnail);
*/
// Window.alert("hello1");
//Window.alert("hello1");
//Window.open(thumbnail, "tmp", null);
try{
storageControler.setValue(KEY_DATA+dataIndex, data.toString());
// Window.alert("hello2");
//storageControler.setValue(KEY_IMAGE+dataIndex, thumbnail);
storageControler.setValue(KEY_HEAD+dataIndex, pdata.getName()+"\t"+pdata.getCdate());
// Window.alert("hello3:"+dataIndex);
pdata.setFileId(dataIndex);
//increment
dataIndex++;
storageControler.setValue(KEY_INDEX, dataIndex);
pdata.setModified(false);
updateSaveButtons();
updateDatasPanel();
tabPanel.selectTab(1);//datas
}catch(Exception e){
try {
//remove no need values
storageControler.removeValue(KEY_DATA+dataIndex);
storageControler.removeValue(KEY_HEAD+dataIndex);
} catch (StorageException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
alert(e.getMessage());
}
}
}
//TODO more info
public static void alert(String message){
logger.fine(message);
if(message.indexOf("(QUOTA_EXCEEDED_ERR)")!=-1 || message.indexOf("(QuotaExceededError)")!=-1){
String title="QUOTA EXCEEDED_ERR\n";
title+="maybe load texture is succeed.but store data is faild.so this data can't load again.\nbecause over internal HTML5 storage capacity.\n";
title+="please remove unused textures or models from Preference Tab\n";
Window.alert(title);
}else{
Window.alert("Error:"+message);
}
}
protected void doSaveFile() {
PoseEditorData pdata=getSelectedPoseEditorData();
int fileId=pdata.getFileId();
if(fileId!=-1){
JSONObject data=PoseEditorData.convertToJson(pdata);
try{
storageControler.setValue(KEY_DATA+fileId, data.toString());
pdata.setModified(false);
updateSaveButtons();
updateDatasPanel();//
}catch(Exception e){
alert(e.getMessage());
}
}else{
doSaveAsFile(pdata);
}
//log(data.toString());
/*
//test
PoseEditorData readData=PoseEditorData.readData(data.toString());
readData.updateMatrix(ab);
readData.setName("tmp1");
doLoad(readData);
*/
}
//private int poseEditorDataSelection;
List<PoseEditorData> poseEditorDatas=new ArrayList<PoseEditorData>();
public void updatePoseEditorDatas(){
//poseEditorDataSelection=index;
updatePoseIndex(0);
updateSaveButtons();
}
private PoseEditorData getSelectedPoseEditorData(){
return pedSelectionListBox.getValue();
}
protected void doNewFile() {
fileIndex++;
String newName=getNewName();
PoseEditorData ped=new PoseEditorData();
ped.setModified(true);//new always
ped.setName(newName);
ped.setCdate(System.currentTimeMillis());
List<PoseFrameData> pfList=new ArrayList<PoseFrameData>();
ped.setPoseFrameDatas(pfList);
pfList.add(initialPoseFrameData.clone());
ped.setBones(boneList);
poseEditorDatas.add(ped);
pedSelectionListBox.setValue(ped);
//when new file 0 is natural
updatePoseIndex(0);
//updatePoseIndex(Math.max(0,poseFrameDataIndex-1));//new label
updateSaveButtons();
}
private void updateListBox(){//refresh list
pedSelectionListBox.setAcceptableValues(poseEditorDatas);
}
/**
* load frame data
* @param ped
*/
public void doLoad(PoseEditorData ped){
//add to list
poseEditorDatas.add(ped);
updateListBox();
pedSelectionListBox.setValue(ped);//select
ped.updateMatrix(ab);//need bone data
tabPanel.selectTab(0);//datas
updatePoseEditorDatas();
}
protected void doPasteBefore() {
if(clipboard!=null){
int index=Math.max(0, poseFrameDataIndex);//same means insert before
getSelectedPoseEditorData().getPoseFrameDatas().add(index,clipboard.clone());
updatePoseIndex(index);
}
}
protected void doPasteAfter() {
if(clipboard!=null){
getSelectedPoseEditorData().getPoseFrameDatas().add(currentFrameRange.getValue()+1,clipboard.clone());
updatePoseIndex(currentFrameRange.getValue()+1);
}
}
protected void doCut() {
// TODO Auto-generated method stub
}
PoseFrameData clipboard;
private boolean availIk(IKData ik){
if(ab.getBoneIndex(ik.getLastBoneName())==-1){
return false;
}
for(String name:ik.getBones()){
if(ab.getBoneIndex(name)==-1){
return false;
}
}
return true;
}
protected void doCopy() {
// TODO Auto-generated method stub
clipboard=snapCurrentFrameData();
}
private PoseFrameData initialPoseFrameData;
private PoseFrameData snapCurrentFrameData(){
List<AngleAndPosition> matrixs=AnimationBonesData.cloneAngleAndMatrix(ab.getBonesAngleAndMatrixs());
List<Vector3> angles=new ArrayList<Vector3>();
List<Vector3> positions=new ArrayList<Vector3>();
for(int i=0;i<matrixs.size();i++){
Vector3 angle=matrixs.get(i).getDegreeAngle().clone();
angles.add(angle);
Vector3 position=ab.getMatrixPosition(i);//TODO getPosition()?
position.sub(ab.getBaseBoneRelativePosition(i));
positions.add(position);
// log(ab.getBoneName(i)+" pos="+ThreeLog.get(position)+",base="+ThreeLog.get(ab.getBaseBoneRelativePosition(i)));
}
List<Vector3> targets=new ArrayList<Vector3>();
List<String> names=new ArrayList<String>();
Map<String,Vector3> ikDataMap=new LinkedHashMap<String,Vector3>();
for(IKData ikdata:ikdatas){
if(!availIk(ikdata)){//check ik
continue;
}
//some how not work correctly
Vector3 pos=getDefaultIkPos(ab.getBoneIndex(ikdata.getLastBoneName()));
pos.sub(ab.getBonePosition(ikdata.getLastBoneName()));//relative path
//Vector3 pos=
ikDataMap.put(ikdata.getName(), pos);
}
PoseFrameData ps=new PoseFrameData(matrixs, ikDataMap);
ps.setAngles(angles);
ps.setPositions(positions);
return ps;
}
private void insertFrame(int index,boolean overwrite){
if(index<0){
index=0;
}
PoseFrameData ps=snapCurrentFrameData();
if(overwrite){
getSelectedPoseEditorData().getPoseFrameDatas().set(index,ps);
updatePoseIndex(index,false);
}else{
getSelectedPoseEditorData().getPoseFrameDatas().add(index,ps);
updatePoseIndex(getSelectedPoseEditorData().getPoseFrameDatas().size()-1,false);
}
getSelectedPoseEditorData().setModified(true);
updateSaveButtons();
}
private void updateSaveButtons() {
if(getSelectedPoseEditorData().isModified()){
saveButton.setEnabled(true);
}else{
saveButton.setEnabled(false);
}
}
protected String convertBVHText(PoseEditorData ped) {
ped.updateMatrix(ab);//current-bone
BVH exportBVH=new BVH();
BVHConverter converter=new BVHConverter();
BVHNode node=converter.convertBVHNode(animationBones);
exportBVH.setHiearchy(node);
converter.setChannels(node,0,"XYZ"); //TODO support other order
BVHMotion motion=new BVHMotion();
motion.setFrameTime(.25);
//TODO post issue
//this is temporaly fix,first frame contain root pos and must sub position
JsArrayNumber rootPos=animationBones.get(0).getPos();
double rootX=0;
double rootY=0;
double rootZ=0;
if(rootPos!=null && rootPos.length()==3){
rootX=rootPos.get(0);
rootY=rootPos.get(1);
rootZ=rootPos.get(2);
}
//somehow this pos value don't care bone position.
for(PoseFrameData pose:ped.getPoseFrameDatas()){
double[] values=converter.angleAndMatrixsToMotion(pose.getAngleAndMatrixs(),BVHConverter.ROOT_POSITION_ROTATE_ONLY,"XYZ");
//TODO update pos based on channel,but now has no channel data
values[0]=values[0]-rootX;
values[1]=values[1]-rootY;
values[2]=values[2]-rootZ;
motion.add(values);
}
motion.setFrames(motion.getMotions().size());//
exportBVH.setMotion(motion);
//log("frames:"+exportBVH.getFrames());
BVHWriter writer=new BVHWriter();
String bvhText=writer.writeToString(exportBVH);
return bvhText;
/*
//log(bvhText);
ExportUtils.exportTextAsDownloadDataUrl(bvhText, "UTF-8", "poseeditor"+exportIndex);
//ExportUtils.openTabTextChrome(bvhText,"poseeditor"+exportIndex);//
exportIndex++;
*/
}
private int exportIndex=0;
private int poseFrameDataIndex=0;
//private List<PoseFrameData> poseFrameDatas=new ArrayList<PoseFrameData>();
private void updatePoseIndex(int index){
updatePoseIndex(index,true);
}
private void updatePoseIndex(int index,boolean needSelect){
if(index==-1){
currentFrameRange.setMax(0);
currentFrameRange.setValue(0);
currentFrameLabel.setText("");
}else{
//poseIndex=index;
currentFrameRange.setMax(Math.max(0,getSelectedPoseEditorData().getPoseFrameDatas().size()-1));
if(currentFrameRange.getValue()==index){
currentFrameRange.setValue(currentFrameRange.getMin());//on paste before not value change,index
//need change value to update range widget;
}
currentFrameRange.setValue(index);
currentFrameLabel.setText((index+1)+"/"+getSelectedPoseEditorData().getPoseFrameDatas().size());
//currentFrameRange.setFocus(true);
//LogUtils.log(currentFrameRange.getMin()+"/"+currentFrameRange.getMax()+" v="+currentFrameRange.getValue());
if(!needSelect){
poseFrameDataIndex=index; //need set poseFrameDataIndex,
return;//no need select maybe still add or replacing
}
//check in range
if(index<=currentFrameRange.getMax() && getSelectedPoseEditorData().getPoseFrameDatas().size()!=0){//0 is empty no need to set
selectFrameData(index);
}else{
LogUtils.log("call small index:"+index+" of "+currentFrameRange.getMax());
}
}
}
private void selectFrameData(int index) {
poseFrameDataIndex=index;
PoseFrameData pfd=getSelectedPoseEditorData().getPoseFrameDatas().get(index);
if(pfd.getAngleAndMatrixs().size()!=animationBones.length()){
Window.alert("difference- bone.not compatiple this frame.\nso push new button.");
return;
}
selectAnimationDataData(AnimationBonesData.cloneAngleAndMatrix(pfd.getAngleAndMatrixs()));
}
private void selectAnimationDataData(List<AngleAndPosition> angleAndPositions) {
currentMatrixs=angleAndPositions;
ab.setBonesAngleAndMatrixs(currentMatrixs);
//update
fitIkOnBone();
//followTarget();//use initial pos,TODO follow or fit
/*
for(int i=0;i<ikdatas.size();i++){
if(!availIk(ikdatas.get(i))){
continue;
}
int boneIndex=ab.getBoneIndex(ikdatas.get(i).getLastBoneName());
ab.getBonePosition(boneIndex);
//this is load form ikdatas
String ikName=ikdatas.get(i).getName();
Vector3 vec=pfd.getIkTargetPosition(ikName);
if(vec!=null){
vec=vec.clone();
vec.add(ab.getBonePosition(ikdatas.get(i).getLastBoneName()));//relative path
LogUtils.log("ignore ikpos");
ikdatas.get(i).getTargetPos().set(vec.getX(), vec.getY(), vec.getZ());
}
}
*/
if(isSelectedIk()){
switchSelectionIk(getCurrentIkData().getLastBoneName());
}
doPoseByMatrix(ab);
updateBoneRanges();
}
/**
* ik position is same on last ik bone.
* this style usually not so moved when root-bone rotateds
*
* call doPoseByMatrix or syncIkPosition to sync 3d model position
*/
private void fitIkOnBone() {
AnimationBonesData merged=getMergedAnimationBonesData(ab);
for(IKData ik:getAvaiableIkdatas()){
String name=ik.getLastBoneName();
Vector3 pos=merged.getBonePosition(name,isIkTargetEndSite);//try get endsite
ik.getTargetPos().copy(pos);
}
}
private AnimationBonesData getMergedAnimationBonesData(AnimationBonesData origin){
return new AnimationBonesData(animationBones,mergeMeshMatrix(origin));
}
private void rotToBone(String name,double x,double y,double z,boolean doPoseByMatrix){
int index=ab.getBoneIndex(name);
if(index==-1){
LogUtils.log("rotToBone:invalid bone called name="+name);
return ;
}
//for mesh-rotation
if(index==0){
y+=meshRotationYRange.getValue();
}
//Matrix4 mx=ab.getBoneMatrix(name);
Vector3 degAngles=THREE.Vector3(x,y,z);
Vector3 angles=GWTThreeUtils.degreeToRagiant(degAngles);
//log("set-angle:"+ThreeLog.get(GWTThreeUtils.radiantToDegree(angles)));
//mx.setRotationFromEuler(angles, "XYZ");
Vector3 pos=GWTThreeUtils.toPositionVec(ab.getBoneAngleAndMatrix(index).getMatrix());
//log("pos:"+ThreeLog.get(pos));
Matrix4 posMx=GWTThreeUtils.translateToMatrix4(pos);
Matrix4 rotMx=GWTThreeUtils.rotationToMatrix4(angles);
rotMx.multiplyMatrices(posMx,rotMx);
//log("bone-pos:"+ThreeLog.get(bones.get(index).getPos()));
ab.getBoneAngleAndMatrix(index).setMatrix(rotMx);
ab.getBoneAngleAndMatrix(index).setDegreeAngle(degAngles);
if(doPoseByMatrix){
doPoseByMatrix(ab);
}
}
//update current selection angles
private void rotToBone(){
String name=boneNamesBox.getItemText(boneNamesBox.getSelectedIndex());
rotToBone(name,rotationBoneXRange.getValue(),rotationBoneYRange.getValue(),rotationBoneZRange.getValue(),true);
}
/*
private void rotToBone(String boneName,Vector3 degAngles,boolean doPoseByMatrix){
int index=ab.getBoneIndex(boneName);
if(index==-1){
LogUtils.log("rotToBone:invalid bone called name="+boneName);
return ;
}
Vector3 angles=GWTThreeUtils.degreeToRagiant(degAngles);
//log("set-angle:"+ThreeLog.get(GWTThreeUtils.radiantToDegree(angles)));
//mx.setRotationFromEuler(angles, "XYZ");
Vector3 pos=GWTThreeUtils.toPositionVec(ab.getBoneAngleAndMatrix(index).getMatrix());
//log("pos:"+ThreeLog.get(pos));
Matrix4 posMx=GWTThreeUtils.translateToMatrix4(pos);
Matrix4 rotMx=GWTThreeUtils.rotationToMatrix4(angles);
rotMx.multiplyMatrices(posMx,rotMx);
//log("bone-pos:"+ThreeLog.get(bones.get(index).getPos()));
ab.getBoneAngleAndMatrix(index).setMatrix(rotMx);
ab.getBoneAngleAndMatrix(index).setAngle(degAngles);
//log("set angle:"+ThreeLog.get(degAngles));
if(doPoseByMatrix){
doPoseByMatrix(ab);
}
}
*/
private void updateBoneRanges(){
updateBoneRotationRanges();
updateBonePositionRanges();
}
private void updateBoneRotationRanges(){
if(isSelectEmptyBoneListBox()){
setEnableBoneRanges(false,false,true);
boneRotateLimitXLabel.setText("");
boneRotateLimitYLabel.setText("");
boneRotateLimitZLabel.setText("");
return;
}else{
setEnableBoneRanges(true,true,false);
}
String name=boneNamesBox.getItemText(boneNamesBox.getSelectedIndex());
BoneLimit boneLimit=boneLimits.get(name);
if(boneLimit!=null){
boneRotateLimitXLabel.setText("min:"+boneLimit.getMinXDegit()+" - max:"+boneLimit.getMaxXDegit());
boneRotateLimitYLabel.setText("min:"+boneLimit.getMinYDegit()+" - max:"+boneLimit.getMaxYDegit());
boneRotateLimitZLabel.setText("min:"+boneLimit.getMinZDegit()+" - max:"+boneLimit.getMaxZDegit());
}else{
boneRotateLimitXLabel.setText("");
boneRotateLimitYLabel.setText("");
boneRotateLimitZLabel.setText("");
}
if(ikLocks.contains(name)){
ikLockCheck.setValue(true);
}else{
ikLockCheck.setValue(false);
}
int boneIndex=ab.getBoneIndex(name);
if(boneIndex!=0){//only root has position
rotateAndPosList.setSelectedIndex(0);
switchRotateAndPosList();
}
//Quaternion q=GWTThreeUtils.jsArrayToQuaternion(bones.get(boneIndex).getRotq());
//log("bone:"+ThreeLog.get(GWTThreeUtils.radiantToDegree(GWTThreeUtils.rotationToVector3(q))));
Vector3 mAngles=GWTThreeUtils.toDegreeAngle(ab.getBoneAngleAndMatrix(name).getMatrix());
//log("updateBoneRotationRanges():"+ThreeLog.get(mAngles));
Vector3 angles=ab.getBoneAngleAndMatrix(name).getDegreeAngle();
int x=(int) angles.getX();
rotationBoneXRange.setValue(x);
if(boneLock.hasX(name)){
xlockCheck.setValue(true);
rotationBoneXRange.setEnabled(false);
}else{
xlockCheck.setValue(false);
rotationBoneXRange.setEnabled(true);
}
int y=(int) angles.getY();
rotationBoneYRange.setValue(y);
if(boneLock.hasY(name)){
ylockCheck.setValue(true);
rotationBoneYRange.setEnabled(false);
}else{
ylockCheck.setValue(false);
rotationBoneYRange.setEnabled(true);
}
int z=(int) angles.getZ();
rotationBoneZRange.setValue(z);
if(boneLock.hasZ(name)){
zlockCheck.setValue(true);
rotationBoneZRange.setEnabled(false);
}else{
zlockCheck.setValue(false);
rotationBoneZRange.setEnabled(true);
}
}
private boolean isSelectEmptyBoneListBox(){
return boneNamesBox.getSelectedIndex()==-1 || boneNamesBox.getItemText(boneNamesBox.getSelectedIndex()).isEmpty();
}
private void updateBonePositionRanges(){
if(isSelectEmptyBoneListBox()){
return;
}
String name=boneNamesBox.getItemText(boneNamesBox.getSelectedIndex());
Vector3 values=GWTThreeUtils.toPositionVec(ab.getBoneAngleAndMatrix(name).getMatrix());
values.multiplyScalar(100);
int x=(int) values.getX();
positionXBoneRange.setValue(x);
int y=(int) values.getY();
positionYBoneRange.setValue(y);
int z=(int) values.getZ();
positionZBoneRange.setValue(z);
}
private Material bodyMaterial;
private String textureUrl="female001_texture1.jpg";//default
private Texture texture;
protected void updateMaterial() {
if(lastLoadedModel!=null){
if(lastLoadedModel.getMetaData().getFormatVersion()==3){
texture.setFlipY(false);
}else{
texture.setFlipY(true);
}
}
Material material=null;
boolean transparent=transparentCheck.getValue();
double opacity=1;
if(transparent){
opacity=0.75;
}
JSParameter parameter=MeshBasicMaterialParameter.create().map(texture).transparent(true).opacity(opacity).skinning(true);
if(texture==null){//some case happend
material=THREE.MeshBasicMaterial(MeshBasicMaterialParameter.create().transparent(true).opacity(opacity).skinning(true));
//only initial happend,if you set invalid texture
}else{
if(basicMaterialCheck.getValue()){
material=THREE.MeshBasicMaterial(parameter);
}else{
material=THREE.MeshLambertMaterial(parameter);
}
}
bodyMaterial=material;
if(bodyMesh!=null){
bodyMaterial.setNeedsUpdate(true);
bodyMesh.setMaterial(material);
}
/*
if(basicMaterialCheck.getValue()){
if(bodyMesh!=null){
bodyMesh.setMaterial(material);//somehow now works.
}else{
LogUtils.log("materical update called,but body mesh is null");
}
}else{
//not basic material need recreate-model
doPoseByMatrix(ab);
}
*/
//test loaded material
/* i have no idea how to set it.
if(loadedMaterials!=null){
MeshFaceMaterial faceMaterial=THREE.MeshFaceMaterial(loadedMaterials);
LogUtils.log(faceMaterial);
bodyMesh.setMaterial(faceMaterial);
}
*/
}
//TODO use for load bvh
private void loadBVH(String path){
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(path));
try {
builder.sendRequest(null, new RequestCallback() {
@Override
public void onResponseReceived(Request request, Response response) {
String bvhText=response.getText();
parseInitialBVHAndLoadModels(bvhText);
}
@Override
public void onError(Request request, Throwable exception) {
Window.alert("load faild:");
}
});
} catch (RequestException e) {
LogUtils.log(e.getMessage());
e.printStackTrace();
}
}
private Geometry baseGeometry;
private List<String> boneList=new ArrayList<String>();
protected void parseInitialBVHAndLoadModels(String bvhText) {
final BVHParser parser=new BVHParser();
parser.parseAsync(bvhText, new ParserListener() {
@Override
public void onFaild(String message) {
LogUtils.log(message);
}
@Override
public void onSuccess(BVH bv) {
bvh=bv;
//createBonesFromBVH
AnimationBoneConverter converter=new AnimationBoneConverter();
setBone(converter.convertJsonBone(bvh));
if(preferencePanel!=null){
LogUtils.log("load from preference");
preferencePanel.loadSelectionModel();
preferencePanel.loadSelectionTexture();
}
//frameRange.setMax(animationData.getHierarchy().get(0).getKeys().length());
/*
JSONLoader loader=THREE.JSONLoader();
loader.load("men3menb.js", new LoadHandler() {
@Override
public void loaded(Geometry geometry) {
baseGeometry=geometry;
//doPose(0);
doPose(0);//bug i have no idea,need call twice to better initial pose
initialPoseFrameData=snapCurrentFrameData();
doNewFile();
//insertFrame(getSelectedPoseEditorData().getPoseFrameDatas().size(),false);//initial pose-frame
}
});
*/
}
});
}
private void setBone(JsArray<AnimationBone> bo){
animationBones=bo;
AnimationDataConverter dataConverter=new AnimationDataConverter();
dataConverter.setSkipFirst(false);
animationData = dataConverter.convertJsonAnimation(animationBones,bvh);//use for first pose
boneList.clear();
for(int i=0;i<animationBones.length();i++){
boneList.add(animationBones.get(i).getName()); //TODO should i trim?
//log(bones.get(i).getName()+","+ThreeLog.get(GWTThreeUtils.jsArrayToVector3(bones.get(i).getPos())));
}
}
/**
* simple check bone-name;
* @param name
* @return
*/
private boolean existBone(String name){
return boneList.contains(name);
}
public static class MatrixAndVector3{
public MatrixAndVector3(){}
private Vector3 position;
public Vector3 getPosition() {
return position;
}
public void setPosition(Vector3 position) {
this.position = position;
}
private Vector3 absolutePosition;
public Vector3 getAbsolutePosition() {
return absolutePosition;
}
public void setAbsolutePosition(Vector3 absolutePosition) {
this.absolutePosition = absolutePosition;
}
public Matrix4 getMatrix() {
return matrix;
}
public void setMatrix(Matrix4 matrix) {
this.matrix = matrix;
}
private Matrix4 matrix;
}
/*
private Vector3 calculateBonedPos(Vector3 pos,AnimationBone bone,int animationIndex){
}
*/
public static List<MatrixAndVector3> boneToBoneMatrix(JsArray<AnimationBone> bones,AnimationData animationData,int index){
List<MatrixAndVector3> boneMatrix=new ArrayList<MatrixAndVector3>();
//analyze bone matrix
for(int i=0;i<bones.length();i++){
AnimationBone bone=bones.get(i);
AnimationHierarchyItem item=animationData.getHierarchy().get(i);
AnimationKey motion=item.getKeys().get(index);
//log(bone.getName());
Matrix4 mx=THREE.Matrix4().makeRotationFromQuaternion(GWTThreeUtils.jsArrayToQuaternion(motion.getRot()));
Vector3 motionPos=GWTThreeUtils.jsArrayToVector3(motion.getPos());
//seems same as bone
// LogUtils.log(motionPos);
mx.setPosition(motionPos);
//mx.setRotationFromQuaternion();
/*
Matrix4 mx2=THREE.Matrix4();
mx2.setRotationFromQuaternion(motion.getRot());
mx.multiplySelf(mx2);
*/
/*
Vector3 tmpRot=THREE.Vector3();
tmpRot.setRotationFromMatrix(mx);
Vector3 tmpPos=THREE.Vector3();
tmpPos.setPositionFromMatrix(mx);
*/
//LogUtils.log(tmpPos.getX()+","+tmpPos.getY()+","+tmpPos.getZ());
//LogUtils.log(Math.toDegrees(tmpRot.))
MatrixAndVector3 mv=new MatrixAndVector3();
Vector3 bpos=AnimationBone.jsArrayToVector3(bone.getPos());
mv.setPosition(bpos);//not effected self matrix
mv.setMatrix(mx);
if(bone.getParent()!=-1){
MatrixAndVector3 parentMv=boneMatrix.get(bone.getParent());
Vector3 apos=bpos.clone();
apos.add(parentMv.getAbsolutePosition());
mv.setAbsolutePosition(apos);
}else{
//root
mv.setAbsolutePosition(bpos.clone());
}
boneMatrix.add(mv);
}
return boneMatrix;
}
private List<List<Integer>> bonePath;
private boolean hasChild(List<List<Integer>> paths,int target){
boolean ret=false;
for(List<Integer> path:paths){
for(int i=0;i<path.size()-1;i++){//exclude last
if(path.get(i)==target){
return true;
}
}
}
return ret;
}
public static List<List<Integer>> boneToPath(JsArray<AnimationBone> bones){
List<List<Integer>> data=new ArrayList<List<Integer>>();
for(int i=0;i<bones.length();i++){
List<Integer> path=new ArrayList<Integer>();
AnimationBone bone=bones.get(i);
path.add(i);
data.add(path);
while(bone.getParent()!=-1){
//path.add(bone.getParent());
path.add(0,bone.getParent());
bone=bones.get(bone.getParent());
}
}
return data;
}
private JsArray<Vector4> bodyIndices;
private JsArray<Vector4> bodyWeight;
SkinnedMesh bodyMesh;
Object3D rootObject;
Object3D bone3D;
Object3D ik3D;
private CheckBox transparentCheck;
private CheckBox basicMaterialCheck;
/**
* called after load
* @param index
*/
/*
private void doPose(int index){
//initial bone names
log(bones);
for(int i=0;i<bones.length();i++){
//log(bones.get(i).getName());
}
//initializeBodyMesh();
initializeAnimationData(index,false);
//stepCDDIk();
log("do-pose");
doPoseByMatrix(ab);
updateBoneRanges();
/*
* trying to fix leg problem
Vector3 rootOffset=GWTThreeUtils.jsArrayToVector3(animationData.getHierarchy().get(0).getKeys().get(index).getPos());
//initial pose is base for motions
baseGeometry=GeometryUtils.clone(bodyMesh.getGeometry());
for(int i=0;i<baseGeometry.vertices().length();i++){
Vertex vertex=baseGeometry.vertices().get(i);
vertex.getPosition().subSelf(rootOffset);
}
*/
//}
//for after loading
private void doRePose(int index){
//initializeBodyMesh();
initializeAnimationData(index,true);
//stepCDDIk();
doPoseByMatrix(ab);
updateBoneRanges();
LogUtils.log("update-bone-range");
}
AnimationBonesData ab;
List<AngleAndPosition> baseMatrixs;
private void applyMatrix(List<AngleAndPosition> matrix,List<NameAndVector3> samples){
for(NameAndVector3 nv:samples){
int boneIndex=ab.getBoneIndex(nv.getName());
Matrix4 translates=GWTThreeUtils.translateToMatrix4(GWTThreeUtils.toPositionVec(ab.getBoneAngleAndMatrix(boneIndex).getMatrix()));
Matrix4 newMatrix=GWTThreeUtils.rotationToMatrix4(nv.getVector3());
newMatrix.multiplyMatrices(translates,newMatrix);
//log("apply-matrix");
matrix.get(boneIndex).setDegreeAngle(GWTThreeUtils.radiantToDegree(nv.getVector3()));
matrix.get(boneIndex).setMatrix(newMatrix);
}
}
List<List<AngleAndPosition>> candiateAngleAndMatrixs;
/*
private void initializeBodyMesh(){
//initializeBodyMesh
if(bodyMesh==null){//initial Indices & weight,be careful bodyMesh create in doPoseByMatrix
bodyIndices = (JsArray<Vector4>) JsArray.createArray();
bodyWeight = (JsArray<Vector4>) JsArray.createArray();
//geometry initialized 0 indices & weights
if(baseGeometry.getSkinIndices().length()!=0 && baseGeometry.getSkinWeight().length()!=0){
log("auto-weight from geometry:");
WeightBuilder.autoWeight(baseGeometry, bones, WeightBuilder.MODE_FROM_GEOMETRY, bodyIndices, bodyWeight);
}else{
//WeightBuilder.autoWeight(baseGeometry, bones, WeightBuilder.MODE_NearParentAndChildren, bodyIndices, bodyWeight);
WeightBuilder.autoWeight(baseGeometry, bones, WeightBuilder.MODE_NearParentAndChildren, bodyIndices, bodyWeight);
}
//WeightBuilder.autoWeight(baseGeometry, bones, WeightBuilder.MODE_NearAgressive, bodyIndices, bodyWeight);
log("initialized-weight:"+bodyIndices.length());
for(int i=0;i<bodyIndices.length();i++){
log(bodyIndices.get(i).getX()+" x "+bodyIndices.get(i).getY());
}
}else{
root.remove(bodyMesh);
}
}
*/
//initialize & loaded
List<AngleAndPosition> currentMatrixs;
private void initializeAnimationData(int index,boolean resetMatrix){
//initialize AnimationBone
if(ab==null){
baseMatrixs=AnimationBonesData.boneToAngleAndMatrix(animationBones, animationData, index);
ab=new AnimationBonesData(animationBones,AnimationBonesData.cloneAngleAndMatrix(baseMatrixs) );
currentMatrixs=null;
for(int i=0;i<animationBones.length();i++){
// log(bones.get(i).getName()+":"+ThreeLog.get(baseMatrixs.get(i).getPosition()));
}
}
//TODO make automatic
//this is find base matrix ,because sometime cdd-ik faild from some position
//nearMatrix=new ArrayList<List<Matrix4>>();
//nearMatrix.add(AnimationBonesData.cloneMatrix(baseMatrixs));
/*
* for foot
List<NameAndVector3> sample=new ArrayList<NameAndVector3>();
sample.add(new NameAndVector3("RightLeg", GWTThreeUtils.degreeToRagiant(THREE.Vector3(90, 0, 0)), 0));
sample.add(new NameAndVector3("RightUpLeg", GWTThreeUtils.degreeToRagiant(THREE.Vector3(-90, 0, 0)), 0));
List<Matrix4> bm=AnimationBonesData.cloneMatrix(baseMatrixs);
applyMatrix(bm, sample);
nearMatrix.add(bm);
List<NameAndVector3> sample1=new ArrayList<NameAndVector3>();
sample1.add(new NameAndVector3("RightLeg", GWTThreeUtils.degreeToRagiant(THREE.Vector3(0, 0, 0)), 0));
sample1.add(new NameAndVector3("RightUpLeg", GWTThreeUtils.degreeToRagiant(THREE.Vector3(0, 0, 45)), 0));
List<Matrix4> bm1=AnimationBonesData.cloneMatrix(baseMatrixs);
applyMatrix(bm1, sample);
//ab.setBonesMatrixs(findStartMatrix("RightFoot",getCurrentIkData().getTargetPos()));//
*/
if(currentMatrixs!=null && resetMatrix){
if(candiateAngleAndMatrixs!=null){
//need bone limit
ab.setBonesAngleAndMatrixs(AnimationBonesData.cloneAngleAndMatrix(findStartMatrix(getCurrentIkData().getLastBoneName(),getCurrentIkData().getTargetPos())));//)
}else{
ab.setBonesAngleAndMatrixs(AnimationBonesData.cloneAngleAndMatrix(currentMatrixs));
}
//TODO only need?
}else{
}
}
private BoneLockControler boneLock=new BoneLockControler();
//this new position base ikk faild
private Vector3 findNextStep(AnimationBonesData merged,int boneIndex,int lastBoneIndex,Vector3 targetPos){
Vector3 lastTrans=merged.getMatrixPosition(lastBoneIndex);
List<Integer> path=merged.getBonePath(lastBoneIndex);
Matrix4 matrix=THREE.Matrix4();
for(int i=0;i<path.size()-1;i++){
int bindex=path.get(i);
AngleAndPosition am=merged.getBoneAngleAndMatrix(bindex);
matrix.multiply(am.getMatrix());
}
Vector3 base=THREE.Vector3(0,0,0);
Vector3 pos=matrix.multiplyVector3(lastTrans.clone());
double length=pos.sub(targetPos).length();
//log("length:"+length+","+0+"x"+0+"x"+0);
Vector3 tmpVec=THREE.Vector3();
for(int x=-1;x<=1;x++){
for(int y=-1;y<=1;y++){
for(int z=-1;z<=1;z++){
if(x==0 && y==0 && z==0){
continue;
}
tmpVec.set(x*5, y*5, z*5);
matrix=THREE.Matrix4();
for(int i=0;i<path.size()-1;i++){
int bindex=path.get(i);
AngleAndPosition am=merged.getBoneAngleAndMatrix(bindex);
Matrix4 m=am.getMatrix();
if(bindex==boneIndex){
Vector3 newAngle=am.getDegreeAngle().clone().add(tmpVec);
Vector3 pv=GWTThreeUtils.toPositionVec(m);
m=THREE.Matrix4();
m.setPosition(pv);
m.setRotationFromEuler(newAngle, "XYZ");
}
matrix.multiply(m);
}
pos=matrix.multiplyVector3(lastTrans.clone());
double tmpl=pos.sub(targetPos).length();
//log("length:"+tmpl+","+x+"x"+y+"x"+z);
if(tmpl<length){
base.set(x*5,y*5,z*5);
length=tmpl;
}
}
}
}
//log("mutch:"+ThreeLog.get(base));
return base.add(merged.getBoneAngleAndMatrix(boneIndex).getDegreeAngle());
}
private boolean doLimit=true;
private boolean ignorePerLimit=false;
private List<AngleAndPosition> mergeMeshMatrix(AnimationBonesData animationBonesData){
List<AngleAndPosition> mergedMatrix=AnimationBonesData.cloneAngleAndMatrix(animationBonesData.getBonesAngleAndMatrixs());
mergedMatrix.set(0, mergedMatrix.get(0).clone());//temporaly merge
double incrementMeshX=meshPositionXBox.getValue()!=null?meshPositionXBox.getValue():0;
mergedMatrix.get(0).getPosition().gwtIncrementX(incrementMeshX);
mergedMatrix.get(0).getDegreeAngle().gwtIncrementY(meshRotationYRange.getValue());
mergedMatrix.get(0).updateMatrix();
return mergedMatrix;
}
/**
* modify mergedMatrix
* @param mergedMatrix
* @return
*/
private List<AngleAndPosition> unmergeMeshMatrix(List<AngleAndPosition> mergedMatrix){
double incrementMeshX=meshPositionXBox.getValue()!=null?meshPositionXBox.getValue():0;
mergedMatrix.get(0).getPosition().gwtDecrementX(incrementMeshX);
mergedMatrix.get(0).getDegreeAngle().gwtDecrementY(meshRotationYRange.getValue());
mergedMatrix.get(0).updateMatrix();
return mergedMatrix;
}
private void stepCDDIk(int perLimit,IKData ikData,int cddLoop){
//do CDDIK
//doCDDIk();
currentIkJointIndex=0;
AnimationBonesData merged=getMergedAnimationBonesData(ab);
List<AngleAndPosition> minMatrix=mergeMeshMatrix(ab);
double minLength=merged.getBonePosition(ikData.getLastBoneName(),isIkTargetEndSite).clone().sub(ikData.getTargetPos()).length();
for(int i=0;i<ikData.getIteration()*cddLoop;i++){
String targetBoneName=ikData.getBones().get(currentIkJointIndex);
//ik locks not so good than expected
if(ikLocks.contains(targetBoneName)){
currentIkJointIndex++;
if(currentIkJointIndex>=ikData.getBones().size()){
currentIkJointIndex=0;
}
//LogUtils.log("skipped-ik:"+targetBoneName);
continue;
}
//LogUtils.log("do-ik:"+targetBoneName);
int boneIndex=ab.getBoneIndex(targetBoneName);
Vector3 ikkedAngle=null;
Matrix4 jointRot=merged.getBoneAngleAndMatrix(targetBoneName).getMatrix();
Matrix4 translates=GWTThreeUtils.translateToMatrix4(GWTThreeUtils.toPositionVec(jointRot));
Vector3 currentAngle=merged.getBoneAngleAndMatrix(targetBoneName).getDegreeAngle().clone();
//log("current:"+ThreeLog.get(currentAngle));
String beforeAngleLog="";
if(perLimit>0){
Vector3 lastJointPos=merged.getBonePosition(ikData.getLastBoneName(),isIkTargetEndSite);
//Vector3 jointPos=ab.getParentPosition(targetName);
Vector3 jointPos=merged.getBonePosition(targetBoneName);
//Vector3 beforeAngles=GWTThreeUtils.radiantToDegree(GWTThreeUtils.rotationToVector3(jointRot));
//Vector3 beforeAngle=ab.getBoneAngleAndMatrix(targetBoneName).getAngle().clone();
//Matrix4 newMatrix=cddIk.doStep(lastJointPos, jointPos, jointRot, ikData.getTargetPos());
//TODO add parent bone angles
//AngleAndPosition root=ab.getBoneAngleAndMatrix(0);
Vector3 parentAngle=merged.getParentAngles(boneIndex);
Matrix4 newMatrix=cddIk.getStepAngleMatrix(parentAngle,lastJointPos, jointPos, jointRot, ikData.getTargetPos());
beforeAngleLog=targetBoneName+","+"parent:"+ThreeLog.get(parentAngle)+",joint:"+ThreeLog.get(currentAngle);
if(newMatrix==null){//invalid value
if(debug){
LogUtils.log("null matrix");
}
continue;
}
//limit per angles
/*
* if angle value over 90 usually value is invalid.
* but i dont know how to detect or fix it.
*/
ikkedAngle=GWTThreeUtils.rotationToVector3(newMatrix);
//Vector3 diffAngles=GWTThreeUtils.radiantToDegree(ikkedAngle).subSelf(currentAngle);
Vector3 diffAngles=GWTThreeUtils.radiantToDegree(ikkedAngle);
if(perLimit==1){
//diffAngles.normalize();
}
//diffAngles.normalize().addScalar(perLimit);//miss choice
//log("diff:"+ThreeLog.get(diffAngles));
if(!ignorePerLimit){
if(Math.abs(diffAngles.getX())>perLimit){
double diff=perLimit;
if(diffAngles.getX()<0){
diff*=-1;
}
diffAngles.setX(diff);
}
if(Math.abs(diffAngles.getY())>perLimit){
double diff=perLimit;
if(diffAngles.getY()<0){
diff*=-1;
}
diffAngles.setY(diff);
}
if(Math.abs(diffAngles.getZ())>perLimit){
double diff=perLimit;
if(diffAngles.getZ()<0){
diff*=-1;
}
diffAngles.setZ(diff);
}
}
currentAngle.add(diffAngles);
//log("added:"+ThreeLog.get(currentAngle));
//currentAngle.setX(0);//keep x
ikkedAngle=GWTThreeUtils.degreeToRagiant(currentAngle);
}else{
//faild TODO fix it
Vector3 angle=findNextStep(merged,boneIndex, ab.getBoneIndex(ikData.getLastBoneName()), ikData.getTargetPos());
//log(targetBoneName+" before:"+ThreeLog.get(ab.getBoneAngleAndMatrix(boneIndex).getAngle())+" after:"+ThreeLog.get(angle));
ikkedAngle=GWTThreeUtils.degreeToRagiant(angle);
}
//log("before:"+ThreeLog.get(beforeAngle)+" after:"+ThreeLog.get(currentAngle));
//limit max
BoneLimit blimit=boneLimits.get(targetBoneName);
//log(targetBoneName);
//log("before-limit:"+ThreeLog.get(GWTThreeUtils.radiantToDegree(angles)));
if(blimit!=null && doLimit){
blimit.apply(ikkedAngle);
}
//invalid ignore
if("NaN".equals(""+ikkedAngle.getX())){
continue;
}
if("NaN".equals(""+ikkedAngle.getY())){
continue;
}
if("NaN".equals(""+ikkedAngle.getZ())){
continue;
}
if(boneLock.hasX(targetBoneName)){
ikkedAngle.setX(Math.toRadians(boneLock.getX(targetBoneName)));
}
if(boneLock.hasY(targetBoneName)){
ikkedAngle.setY(Math.toRadians(boneLock.getY(targetBoneName)));
}
if(boneLock.hasZ(targetBoneName)){
ikkedAngle.setZ(Math.toRadians(boneLock.getZ(targetBoneName)));
}
//String afterAngleLog=("after-limit:"+ThreeLog.get(GWTThreeUtils.radiantToDegree(ikkedAngle)));
Matrix4 newMatrix=GWTThreeUtils.rotationToMatrix4(ikkedAngle);
newMatrix.multiplyMatrices(translates,newMatrix);
merged.getBoneAngleAndMatrix(boneIndex).setMatrix(newMatrix);
merged.getBoneAngleAndMatrix(boneIndex).setDegreeAngle(GWTThreeUtils.radiantToDegree(ikkedAngle));
//log(targetName+":"+ThreeLog.getAngle(jointRot)+",new"+ThreeLog.getAngle(newMatrix));
//log("parentPos,"+ThreeLog.get(jointPos)+",lastPos,"+ThreeLog.get(lastJointPos));
Vector3 diffPos=merged.getBonePosition(ikData.getLastBoneName()).clone().subSelf(ikData.getTargetPos());
/*
if(diffPos.length()>2){
//usually ivalid
log(i+","+"length="+diffPos.length()+" diff:"+ThreeLog.get(diffPos));
log(beforeAngleLog);
log(afterAngleLog);
}*/
if(diffPos.length()<minLength){
minMatrix=mergeMeshMatrix(ab);//initialize
}
currentIkJointIndex++;
if(currentIkJointIndex>=ikData.getBones().size()){
currentIkJointIndex=0;
}
if(diffPos.length()<0.02){
break;
}
//tmp1=lastJointPos;
//tmp2=jointPos;
}
ab.setBonesAngleAndMatrixs(unmergeMeshMatrix(minMatrix));//use min
}
private void doPoseIkk(int index,boolean resetMatrix,int perLimit,IKData ikdata,int cddLoop){
if(!existBone(ikdata.getLastBoneName())){
return;//some time non exist bone selected.
}
//initializeBodyMesh();
initializeAnimationData(index,resetMatrix);
stepCDDIk(perLimit,ikdata,cddLoop); //not working?
doPoseByMatrix(ab);
updateBoneRanges();
}
private List<AngleAndPosition> findStartMatrix(String boneName,Vector3 targetPos) {
List<AngleAndPosition> retMatrix=candiateAngleAndMatrixs.get(0);
AnimationBonesData tmpData=new AnimationBonesData(animationBones, retMatrix);
//ab.setBonesAngleAndMatrixs(retMatrix);//TODO without set
Vector3 tpos=tmpData.getBonePosition(boneName);
double minlength=targetPos.clone().sub(tpos).length();
for(int i=1;i<candiateAngleAndMatrixs.size();i++){
List<AngleAndPosition> mxs=candiateAngleAndMatrixs.get(i);
tmpData.setBonesAngleAndMatrixs(mxs);//TODO change
Vector3 tmpPos=tmpData.getBonePosition(boneName);
double tmpLength=targetPos.clone().sub(tmpPos).length();
if(tmpLength<minlength){
minlength=tmpLength;
retMatrix=mxs;
}
}
for(String name:getCurrentIkData().getBones()){
//Matrix4 mx=retMatrix.get(ab.getBoneIndex(name));
// log(name+":"+ThreeLog.get(GWTThreeUtils.rotationToVector3(mx)));
// log(name+":"+ThreeLog.get(GWTThreeUtils.toDegreeAngle(mx)));
}
return unmergeMeshMatrix(AnimationBonesData.cloneAngleAndMatrix(retMatrix));
}
/*
private void doCDDIk(){
String targetName=getCurrentIkData().getBones().get(currentIkJointIndex);
int boneIndex=ab.getBoneIndex(targetName);
Vector3 lastJointPos=ab.getPosition("RightFoot");
Vector3 jointPos=ab.getParentPosition(targetName);
Matrix4 jointRot=ab.getBoneMatrix(targetName);
Matrix4 newMatrix=cddIk.doStep(lastJointPos, jointPos, jointRot, getCurrentIkData().getTargetPos());
ab.setBoneMatrix(boneIndex, newMatrix);
//log(targetName+":"+ThreeLog.getAngle(jointRot)+",new"+ThreeLog.getAngle(newMatrix));
//log("parentPos,"+ThreeLog.get(jointPos)+",lastPos,"+ThreeLog.get(lastJointPos));
currentIkJointIndex++;
if(currentIkJointIndex>=getCurrentIkData().getBones().size()){
currentIkJointIndex=0;
}
doPoseByMatrix(ab);
}
*/
CDDIK cddIk=new CDDIK();
int currentIkJointIndex=0;
//private String[] ikTestNames={"RightLeg","RightUpLeg"};
//Vector3 targetPos=THREE.Vector3(-10, -3, 0);
private ListBox boneNamesBox;
private double baseBoneCoreSize=7;
private double baseIkLength=13;
private void doPoseByMatrix(AnimationBonesData animationBonesData){
/*
*
* creating bone and ik everyframe totally waste of fps
*
*/
if(animationBonesData==null){
LogUtils.log("doPoseByMatrix:animationBonesData=null");
return;
}
if(isSelectedBone()){
selectionMesh.setPosition(ab.getBonePosition(selectedBone));
}
List<AngleAndPosition> originalBoneMatrix=animationBonesData.getBonesAngleAndMatrixs();
//temporaly merged root & mesh-matrix
List<AngleAndPosition> boneMatrix=mergeMeshMatrix(animationBonesData);
/*
* merge mesh-matrix here,bone seems follow,beside pos value
*
*/
bonePath=boneToPath(animationBones);
if(bone3D!=null){
group2.remove(bone3D);
}
bone3D=THREE.Object3D();
group2.add(bone3D);
if(ik3D!=null){
group2.remove(ik3D);
}
ik3D=THREE.Object3D();
group2.add(ik3D);
//selection
//test ikk
/*
Mesh cddIk0=THREE.Mesh(THREE.CubeGeometry(1.5, 1.5, 1.5),THREE.MeshLambertMaterial().color(0x00ff00).build());
cddIk0.setPosition(getCurrentIkData().getTargetPos());
bone3D.add(cddIk0);
*/
double bsize=baseBoneCoreSize;
bsize/=posDivided;
if(smallCheck.getValue()){
bsize/=4;
}
BoxGeometry box=THREE.BoxGeometry(bsize,bsize, bsize);
//can't merge material,because each bone has color
List<Matrix4> moveMatrix=new ArrayList<Matrix4>();
List<Vector3> bonePositions=new ArrayList<Vector3>();
for(int i=0;i<animationBones.length();i++){
Matrix4 mv=boneMatrix.get(i).getMatrix();
Mesh mesh=null;
if(i==0){//root is better
mesh=THREE.Mesh(THREE.SphereGeometry(bsize, 4, 4),THREE.MeshLambertMaterial(GWTParamUtils.MeshBasicMaterial().color(0xFF0000)));
}else{
mesh=THREE.Mesh(box,THREE.MeshLambertMaterial(GWTParamUtils.MeshBasicMaterial().color(0xFF0000)));
}
bone3D.add(mesh);
Vector3 pos=THREE.Vector3();
pos.setFromMatrixPosition(mv);
//Vector3 rot=GWTThreeUtils.rotationToVector3(GWTThreeUtils.jsArrayToQuaternion(bones.get(i).getRotq()));
Vector3 rot=GWTThreeUtils.degreeToRagiant(ab.getBoneAngleAndMatrix(i).getDegreeAngle());
List<Integer> path=bonePath.get(i);
String boneName=animationBones.get(i).getName();
//log(boneName);
mesh.setName(boneName);
Matrix4 matrix=THREE.Matrix4();
for(int j=0;j<path.size()-1;j++){//last is boneself,no need for bone-pos
Matrix4 mx=boneMatrix.get(path.get(j)).getMatrix();
matrix.multiplyMatrices(matrix, mx);
}
pos.applyProjection(matrix);//set pos before last bone matrixed.
//matrix.multiplyVector3(pos);
//but need for transform
matrix.multiplyMatrices(matrix, boneMatrix.get(path.get(path.size()-1)).getMatrix());//last one
moveMatrix.add(matrix);
//maybe this position use for end-sites.
if(animationBones.get(i).getParent()!=-1){
Vector3 ppos=bonePositions.get(animationBones.get(i).getParent());
//pos.addSelf(ppos);
//log(boneName+":"+ThreeLog.get(pos)+","+ThreeLog.get(ppos));
//cylinder
/* better bone faild
Vector3 halfPos=pos.clone().subSelf(ppos).multiplyScalar(0.5).addSelf(ppos);
Mesh boneMesh=THREE.Mesh(THREE.CylinderGeometry(.1,.1,.2,6), THREE.MeshLambertMaterial().color(0xffffff).build());
boneMesh.setPosition(halfPos);
boneMesh.setName(boneName);
bone3D.add(boneMesh);
BoxData data=boxDatas.get(boneName);
if(data!=null){
boneMesh.setScale(data.getScaleX(), data.getScaleY(), data.getScaleZ());
boneMesh.getRotation().setZ(Math.toRadians(data.getRotateZ()));
}
*/
for(IKData ik:getAvaiableIkdatas()){
if(ik.getLastBoneName().equals(boneName)){//valid ik
Mesh ikMesh=targetMeshs.get(boneName);
if(ikMesh==null){//at first call this from non-ik stepped.
//log("xxx");
//initial
double ikLength=baseIkLength/posDivided;
if(!hasChild(bonePath,i)){
ikLength=baseIkLength*2/posDivided;
}
double ikCoreSize=baseBoneCoreSize*2/posDivided;
//ThreeLog.log(pos.clone().sub(ppos));
//LogUtils.log(ikLength);
Vector3 ikpos=pos.clone().sub(ppos).multiplyScalar(ikLength).add(pos);
//ThreeLog.log(ikpos);
//ikpos=pos.clone();
//trying transparent
ikMesh=THREE.Mesh(THREE.BoxGeometry(ikCoreSize, ikCoreSize, ikCoreSize),THREE.MeshLambertMaterial(MeshLambertMaterialParameter.create().color(0x00ff00).transparent(true).opacity(0.5)));
ikMesh.setPosition(ikpos);
ikMesh.setName("ik:"+boneName);
// log(boneName+":"+ThreeLog.get(ikpos));
//log(ThreeLog.get(pos));
ik.getTargetPos().set(ikpos.getX(), ikpos.getY(), ikpos.getZ());
targetMeshs.put(boneName, ikMesh);
}else{
ikMesh.getParent().remove(ikMesh);
}
ik3D.add(ikMesh);
ikMesh.setPosition(ik.getTargetPos());
Line ikline=GWTGeometryUtils.createLineMesh(pos, ik.getTargetPos(), 0xffffff);
ik3D.add(ikline);
}
}
}
mesh.setRotation(rot);
mesh.setPosition(pos);
//mesh color
if(pos.getY()<0){
mesh.getMaterial().gwtGetColor().set(0xffee00);//over color
}else if(pos.getY()<10.0/posDivided){
mesh.getMaterial().gwtGetColor().set(0xff8800);//near
}
bonePositions.add(pos);
}
//dont work
updateBonesVisible(showBonesCheck.getValue());
if(selectBoneFirstOnMouseDown){
Object3DUtils.setVisibleAll(ik3D, false);
}else{
Object3DUtils.setVisibleAll(ik3D, ikVisibleCheck.getValue());
}
//bone3D.setVisible(showBonesCheck.getValue());
//Geometry geo=GeometryUtils.clone(baseGeometry);
//initialize AutoWeight
if(bodyMesh==null){//initial
bodyIndices = (JsArray<Vector4>) JsArray.createArray();
bodyWeight = (JsArray<Vector4>) JsArray.createArray();
//geometry initialized 0 indices & weights
if(baseGeometry.getSkinIndices().length()!=0 && baseGeometry.getSkinWeight().length()!=0){
//LogUtils.log(bones);
LogUtils.log("use geometry bone");
//test
/*
LogUtils.log("testlly use root all geometry");
WeightBuilder.autoWeight(baseGeometry, bones, WeightBuilder.MODE_ROOT_ALL, bodyIndices, bodyWeight);
*/
WeightBuilder.autoWeight(baseGeometry, animationBones, WeightBuilder.MODE_FROM_GEOMETRY, bodyIndices, bodyWeight);
/*
List<String> lines=new ArrayList<String>();
for(int i=0;i<bodyWeight.length();i++){
lines.add(i+get(bodyWeight.get(i)));
}
LogUtils.log("after");
LogUtils.log(Joiner.on("\n").join(lines));
*/
}else{
LogUtils.log("auto-weight :sometime this broke models.you can use ModelWeight Apps");
WeightBuilder.autoWeight(baseGeometry, animationBones, WeightBuilder.MODE_NearParentAndChildren, bodyIndices, bodyWeight);
}
}else{
//root.remove(bodyMesh);
}
//Geometry geo=bodyMesh.getGeometry();
//Geometry geo=GeometryUtils.clone(baseGeometry);
//log("bi-length:"+bodyIndices.length());
/*
for(int i=0;i<baseGeometry.vertices().length();i++){
Vector3 baseVertex=baseGeometry.vertices().get(i);
Vector3 vertexPosition=baseVertex.clone();
Vector3 targetVertex=geo.vertices().get(i);
int boneIndex1=(int) bodyIndices.get(i).getX();
int boneIndex2=(int) bodyIndices.get(i).getY();
String name=animationBonesData.getBoneName(boneIndex1);
//log(boneIndex1+"x"+boneIndex2);
*/
/*
*
if(name.equals("RightLeg")){//test parent base
Vector3 parentPos=animationBonesData.getBaseParentBonePosition(boneIndex1);
Matrix4 tmpMatrix=GWTThreeUtils.rotationToMatrix4(GWTThreeUtils.degreeToRagiant(THREE.Vector3(0, 0, 20)));
vertexPosition.subSelf(parentPos);
tmpMatrix.multiplyVector3(vertexPosition);
vertexPosition.addSelf(parentPos);
boneIndex2=boneIndex1; //dont work without this
}*/
/*
Vector3 bonePos=animationBonesData.getBaseBonePosition(boneIndex1);
Vector3 relatePos=bonePos.clone();
relatePos.subVectors(vertexPosition,bonePos);
//double length=relatePos.length();
*
moveMatrix.get(boneIndex1).multiplyVector3(relatePos);
*/
/*
if(name.equals("RightLeg")){
Vector3 parentPos=animationBonesData.getParentPosition(boneIndex1);
relatePos.subSelf(parentPos);
Matrix4 tmpMatrix2=GWTThreeUtils.rotationToMatrix4(GWTThreeUtils.degreeToRagiant(THREE.Vector3(0, 0, -20)));
tmpMatrix2.multiplyVector3(relatePos);
relatePos.addSelf(parentPos);
}*/
//relatePos.addSelf(bonePos);
/*
if(boneIndex2!=boneIndex1){
//what is this?
Vector3 bonePos2=animationBonesData.getBaseBonePosition(boneIndex2);
Vector3 relatePos2=bonePos2.clone();
relatePos2.subVectors(baseVertex,bonePos2);
double length2=relatePos2.length();
moveMatrix.get(boneIndex2).multiplyVector3(relatePos2);
//scalar weight
relatePos.multiplyScalar(bodyWeight.get(i).getX());
relatePos2.multiplyScalar(bodyWeight.get(i).getY());
relatePos.add(relatePos2);
*/
//keep distance1 faild
/*
if(length<1){ //length2
Vector3 abpos=THREE.Vector3();
abpos.sub(relatePos, bonePositions.get(boneIndex1));
double scar=abpos.length()/length;
abpos.multiplyScalar(scar);
abpos.addSelf(bonePositions.get(boneIndex1));
relatePos.set(abpos.getX(), abpos.getY(), abpos.getZ());
}*/
/*//why need this?,anyway this crash models.
if(length2<1){
Vector3 abpos=THREE.Vector3();
abpos.subVectors(relatePos, bonePositions.get(boneIndex2));
double scar=abpos.length()/length2;
abpos.multiplyScalar(scar);
abpos.add(bonePositions.get(boneIndex2));
relatePos.set(abpos.getX(), abpos.getY(), abpos.getZ());
//LogUtils.log("length2<1:"+i);
}
*/
/*
Vector3 diff=THREE.Vector3();
diff.sub(relatePos2, relatePos);
diff.multiplyScalar(bodyWeight.get(i).getY());
relatePos.addSelf(diff);
*/
/*
}else{
if(name.equals("RightLeg")){
// Matrix4 tmpMatrix2=GWTThreeUtils.rotationToMatrix4(GWTThreeUtils.degreeToRagiant(THREE.Vector3(0, 0, -20)));
// tmpMatrix2.multiplyVector3(relatePos);
}
}
targetVertex.set(relatePos.getX(), relatePos.getY(), relatePos.getZ());
}
*/
/*
geo.computeFaceNormals();
geo.computeVertexNormals();
//Material material=THREE.MeshLambertMaterial().map(ImageUtils.loadTexture("men3smart_texture.png")).build();
bodyMesh=THREE.Mesh(geo, bodyMaterial);
root.add(bodyMesh);
*/
if(bodyMesh==null){
createSkinnedMesh();
}
/*//update-mesh-matrix
*
*/
double meshRotationY=meshRotationYRange.getValue().doubleValue();
bodyMesh.getRotation().setY(Math.toRadians(meshRotationY));
bodyMesh.getPosition().setX(meshPositionXBox.getValue());
//make skinning animation
AnimationClip clip=createAnimationClip(animationBonesData.getBonesAngleAndMatrixs());
mixer.stopAllAction();
mixer.uncacheClip(clip);//same name cache that.
mixer.clipAction(clip).play();
//selection
//selectionMesh=THREE.Mesh(THREE.CubeGeometry(2, 2, 2), THREE.MeshBasicMaterial().color(0x00ff00).wireFrame(true).build());
if(isSelectedIk()){
selectionMesh.setPosition(getCurrentIkData().getTargetPos());
}
//bone3D.add(selectionMesh);
//selectionMesh.setVisible(false);
/*
geo.setDynamic(true);
geo.setDirtyVertices(true);
geo.computeBoundingSphere();
*/
//
//bodyMesh.setGeometry(geo);
//bodyMesh.gwtBoundingSphere();
//geo.computeTangents();
/*
geo.setDynamic(true);
geo.setDirtyVertices(true);
geo.computeFaceNormals();
geo.computeVertexNormals();
geo.computeTangents();
*/
}
private AnimationMixer mixer;
public void createSkinnedMesh(){
stopAnimation();
//must be remove by hand
baseGeometry.computeBoundingSphere();
bodyMesh=THREE.SkinnedMesh(baseGeometry, bodyMaterial);
mixer=THREE.AnimationMixer(bodyMesh);//replace mixer
group1.add(bodyMesh);
if(skeltonHelper!=null){
scene.remove(skeltonHelper);//re-create
}
skeltonHelper = THREE.SkeletonHelper(bodyMesh);
scene.add(skeltonHelper);
updateBonesVisible(showBonesCheck.getValue());
touchGroundZero();//
}
public void stopAnimation() {
if(mixer==null){
return;
}
mixer.stopAllAction();
//characterMesh.getGeometry().getBones().get(60).setRotq(q)
}
/*
* radiants
* same as THREE.Quaternion().setFromEuler(euler, false)
*/
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;
}
public Quaternion createAngleQuaternionByAngle(double x,double y,double z){
return createAngleQuaternion(Math.toRadians(x), Math.toRadians(y), Math.toRadians(z));
}
private void concat(JsArrayNumber target,JsArrayNumber values){
for(int i=0;i<values.length();i++){
target.push(values.get(i));
}
}
public AnimationClip createAnimationClip(List<AngleAndPosition> angleAndPositions){
AnimationClip clip=THREE.AnimationClip("pose", -1, createAnimationTracks(angleAndPositions,1,moveOnlyRoot));
return clip;
}
public JsArray<KeyframeTrack> createAnimationTracks(List<AngleAndPosition> angleAndPositions,double posdivided,boolean moveOnlyRoot){
JsArray<KeyframeTrack> tracks=JavaScriptObject.createArray().cast();
for(int i=0;i<angleAndPositions.size();i++){
int index=i;
AngleAndPosition ap=angleAndPositions.get(index);
int boneIndex=index;
JsArrayNumber times=JavaScriptObject.createArray().cast();
times.push(0);
Quaternion q=createAngleQuaternionByAngle(ap.getDegreeAngle().getX(),ap.getDegreeAngle().getY(),ap.getDegreeAngle().getZ());
JsArrayNumber values=JsArray.createArray().cast();
concat(values,q.toArray());
QuaternionKeyframeTrack track=THREE.QuaternionKeyframeTrack(".bones["+boneIndex+"].quaternion", times, values);
tracks.push(track);
}
//position animation
for(int i=0;i<angleAndPositions.size();i++){
int index=i;
if(index!=0 && moveOnlyRoot){
continue;
}
AngleAndPosition ap=angleAndPositions.get(index);
int boneIndex=index;
JsArrayNumber times=JavaScriptObject.createArray().cast();
times.push(0);
JsArrayNumber values=JsArray.createArray().cast();
concat(values,ap.getPosition().clone().divideScalar(posdivided).toArray());
VectorKeyframeTrack track=THREE.VectorKeyframeTrack(".bones["+boneIndex+"].position", times, values);
tracks.push(track);
//position animation
}
return tracks;
}
public JsArray<KeyframeTrack> mergeAnimationTracks(List<JsArray<KeyframeTrack>> trackList,double betweenTime){
final JsArray<KeyframeTrack> tracks=JavaScriptObject.createArray().cast();
for(int i=0;i<trackList.get(0).length();i++){
KeyframeTrack track=trackList.get(0).get(i);
JsArrayNumber times=JavaScriptObject.createArray().cast();
JsArrayNumber values=JavaScriptObject.createArray().cast();
double lastTime=0;
for(int k=0;k<trackList.size();k++){
JsArray<KeyframeTrack> tracksA=trackList.get(k);
KeyframeTrack trackA=tracksA.get(i);
JsArrayNumber timesA=trackA.getTimes().cast();
JsArrayNumber valuesA=trackA.getValues().cast();
concat(values,valuesA);
double time=0;
for(int j=0;j<timesA.length();j++){
time=timesA.get(j)+lastTime;
times.push(time);
}
lastTime=time+betweenTime;
}
//don't check with instanceof
if(track.getValueTypeName().equals("quaternion")){
tracks.push(THREE.QuaternionKeyframeTrack(track.getName(),times,values));
}else if(track.getValueTypeName().equals("vector")){
tracks.push(THREE.VectorKeyframeTrack(track.getName(),times,values));
}else{
LogUtils.log("invalid instance type:mergeAnimationTracks:"+track.getValueTypeName());
}
}
return tracks;
}
/**
*
* my old pose change way
* @deprecated
* @param moveMatrix
* @param animationBonesData
* @return
*/
public Mesh createPosedMesh(List<Matrix4> moveMatrix,AnimationBonesData animationBonesData){
Geometry geo=GeometryUtils.clone(baseGeometry);
//log("bi-length:"+bodyIndices.length());
for(int i=0;i<baseGeometry.vertices().length();i++){
Vector3 baseVertex=baseGeometry.vertices().get(i);
Vector3 vertexPosition=baseVertex.clone();
Vector3 targetVertex=geo.vertices().get(i);
int boneIndex1=(int) bodyIndices.get(i).getX();
int boneIndex2=(int) bodyIndices.get(i).getY();
String name=animationBonesData.getBoneName(boneIndex1);
Vector3 bonePos=animationBonesData.getBaseBonePosition(boneIndex1);
Vector3 relatePos=bonePos.clone();
relatePos.subVectors(vertexPosition,bonePos);
//double length=relatePos.length();
moveMatrix.get(boneIndex1).multiplyVector3(relatePos);
if(boneIndex2!=boneIndex1){
//what is this?
Vector3 bonePos2=animationBonesData.getBaseBonePosition(boneIndex2);
Vector3 relatePos2=bonePos2.clone();
relatePos2.subVectors(baseVertex,bonePos2);
double length2=relatePos2.length();
moveMatrix.get(boneIndex2).multiplyVector3(relatePos2);
//scalar weight
relatePos.multiplyScalar(bodyWeight.get(i).getX());
relatePos2.multiplyScalar(bodyWeight.get(i).getY());
relatePos.add(relatePos2);
//keep distance1 faild
}else{
if(name.equals("RightLeg")){
// Matrix4 tmpMatrix2=GWTThreeUtils.rotationToMatrix4(GWTThreeUtils.degreeToRagiant(THREE.Vector3(0, 0, -20)));
// tmpMatrix2.multiplyVector3(relatePos);
}
}
targetVertex.set(relatePos.getX(), relatePos.getY(), relatePos.getZ());
}
geo.computeFaceNormals();
geo.computeVertexNormals();
//Material material=THREE.MeshLambertMaterial().map(ImageUtils.loadTexture("men3smart_texture.png")).build();
return THREE.Mesh(geo, bodyMaterial);
}
//TODO move to three.js
public static String get(Vector4 vec){
if(vec==null){
return "Null";
}
String ret="x:"+vec.getX();
ret+=",y:"+vec.getY();
ret+=",z:"+vec.getZ();
ret+=",w:"+vec.getW();
return ret;
}
private Vector3 getDefaultIkPos(int index){
AnimationBonesData merged=getMergedAnimationBonesData(ab);
Vector3 pos=merged.getBonePosition(index);
Vector3 ppos=merged.getParentPosition(index);
double ikLength=1.5;
if(!hasChild(bonePath,index)){
ikLength=2.5;
}
return pos.clone().sub(ppos).multiplyScalar(ikLength).add(ppos);
}
private Map<String,Mesh> targetMeshs=new HashMap<String,Mesh>();
private ListBox rotateAndPosList;
private VerticalPanel bonePositionsPanel;
private VerticalPanel boneRotationsPanel;
private CheckBox zlockCheck;
private CheckBox ikLockCheck;
//private ListBox fileNames;
private ValueListBox<PoseEditorData> pedSelectionListBox;
private IStorageControler storageControler;
private VerticalPanel datasPanel;
private Button saveButton;
private VerticalPanel bonePostionAndRotationContainer;
private MenuItem contextMenuShowPrevFrame;
private MenuItem contextMenuHidePrefIks;
private GridHelper backgroundGrid;
private Label ikPositionLabelX;
private MenuBar rootBar;
private VerticalPanel imageLinkContainer;
private Button undoButton;
private Button gifAnimeBt;
private Mesh planeMesh;
private SkeletonHelper skeltonHelper;
private Group group1;
private Group group2;
private DoubleBox meshPositionXBox;
;
/**
* @deprecated
*/
private void doPose(List<MatrixAndVector3> boneMatrix){
bonePath=boneToPath(animationBones);
if(bone3D!=null){
rootObject.remove(bone3D);
}
bone3D=THREE.Object3D();
rootObject.add(bone3D);
//test ikk
Mesh cddIk0=THREE.Mesh(THREE.BoxGeometry(.5, .5, .5),THREE.MeshLambertMaterial(GWTParamUtils.MeshBasicMaterial().color(0x00ff00)));
cddIk0.setPosition(getCurrentIkData().getTargetPos());
bone3D.add(cddIk0);
List<Matrix4> moveMatrix=new ArrayList<Matrix4>();
List<Vector3> bonePositions=new ArrayList<Vector3>();
for(int i=0;i<animationBones.length();i++){
MatrixAndVector3 mv=boneMatrix.get(i);
Mesh mesh=THREE.Mesh(THREE.BoxGeometry(.2, .2, .2),THREE.MeshLambertMaterial(GWTParamUtils.MeshBasicMaterial().color(0xff0000)));
bone3D.add(mesh);
Vector3 pos=mv.getPosition().clone();
List<Integer> path=bonePath.get(i);
String boneName=animationBones.get(i).getName();
//log(boneName);
Matrix4 tmpmx=boneMatrix.get(path.get(path.size()-1)).getMatrix();
Vector3 tmpp=THREE.Vector3();
tmpp.setFromMatrixPosition(tmpmx);
//log(pos.getX()+","+pos.getY()+","+pos.getZ()+":"+tmpp.getX()+","+tmpp.getY()+","+tmpp.getZ());
Matrix4 matrix=THREE.Matrix4();
for(int j=0;j<path.size()-1;j++){//last is boneself
// log(""+path.get(j));
Matrix4 mx=boneMatrix.get(path.get(j)).getMatrix();
matrix.multiplyMatrices(matrix, mx);
}
matrix.multiplyVector3(pos);
matrix.multiplyMatrices(matrix, boneMatrix.get(path.get(path.size()-1)).getMatrix());//last one
moveMatrix.add(matrix);
if(animationBones.get(i).getParent()!=-1){
Vector3 ppos=bonePositions.get(animationBones.get(i).getParent());
//pos.addSelf(ppos);
Line line=GWTGeometryUtils.createLineMesh(pos, ppos, 0xffffff);
bone3D.add(line);
}else{
//root action
Matrix4 mx=boneMatrix.get(0).getMatrix();
mx.multiplyVector3(pos);
}
mesh.setPosition(pos);
bonePositions.add(pos);
}
//Geometry geo=GeometryUtils.clone(baseGeometry);
//Geometry geo=bodyMesh.getGeometry();
Geometry geo=GeometryUtils.clone(baseGeometry);
for(int i=0;i<baseGeometry.vertices().length();i++){
Vector3 baseVertex=baseGeometry.vertices().get(i);
Vector3 targetVertex=geo.vertices().get(i);
int boneIndex1=(int) bodyIndices.get(i).getX();
int boneIndex2=(int) bodyIndices.get(i).getY();
Vector3 bonePos=boneMatrix.get(boneIndex1).getAbsolutePosition();
Vector3 relatePos=bonePos.clone();
relatePos.sub(baseVertex,bonePos);
double length=relatePos.length();
moveMatrix.get(boneIndex1).multiplyVector3(relatePos);
//relatePos.addSelf(bonePos);
if(boneIndex2!=boneIndex1){
Vector3 bonePos2=boneMatrix.get(boneIndex2).getAbsolutePosition();
Vector3 relatePos2=bonePos2.clone();
relatePos2.sub(baseVertex,bonePos2);
double length2=relatePos2.length();
moveMatrix.get(boneIndex2).multiplyVector3(relatePos2);
//scalar weight
relatePos.multiplyScalar(bodyWeight.get(i).getX());
relatePos2.multiplyScalar(bodyWeight.get(i).getY());
relatePos.add(relatePos2);
//keep distance1 faild
/*
if(length<1){ //length2
Vector3 abpos=THREE.Vector3();
abpos.sub(relatePos, bonePositions.get(boneIndex1));
double scar=abpos.length()/length;
abpos.multiplyScalar(scar);
abpos.addSelf(bonePositions.get(boneIndex1));
relatePos.set(abpos.getX(), abpos.getY(), abpos.getZ());
}*/
if(length2<1){
Vector3 abpos=THREE.Vector3();
abpos.sub(relatePos, bonePositions.get(boneIndex2));
double scar=abpos.length()/length2;
abpos.multiplyScalar(scar);
abpos.add(bonePositions.get(boneIndex2));
relatePos.set(abpos.getX(), abpos.getY(), abpos.getZ());
}
/*
Vector3 diff=THREE.Vector3();
diff.sub(relatePos2, relatePos);
diff.multiplyScalar(bodyWeight.get(i).getY());
relatePos.addSelf(diff);
*/
}
targetVertex.set(relatePos.getX(), relatePos.getY(), relatePos.getZ());
}
geo.computeFaceNormals();
geo.computeVertexNormals();
//Material material=THREE.MeshLambertMaterial().map(ImageUtils.loadTexture("men3smart_texture.png")).build();
bodyMesh=THREE.SkinnedMesh(geo, bodyMaterial);
rootObject.add(bodyMesh);
/*
geo.setDynamic(true);
geo.setDirtyVertices(true);
geo.computeBoundingSphere();
*/
//
//bodyMesh.setGeometry(geo);
//bodyMesh.gwtBoundingSphere();
//geo.computeTangents();
/*
geo.setDynamic(true);
geo.setDirtyVertices(true);
geo.computeFaceNormals();
geo.computeVertexNormals();
geo.computeTangents();
*/
}
@Override
public String getHtml(){
String html="Pose Editor ver."+version+" "+super.getHtml()+" 3D-Models created by <a href='http://www.manuelbastioni.com/manuellab.php'>Manuel Bastioni Lab 3D</a> and <a href='http://makehuman.org'>Makehuman</a>";
return html;
}
@Override
public String getTabTitle() {
return "Editor";
}
@Override
public void modelChanged(SimpleTextData model) {
//log("model-load:"+model.getData());
LoadJsonModel(model.getData());
//refresh matrix for new bone-model
pedSelectionListBox.getValue().updateMatrix(ab);
//new model need new initial pose
initialPoseFrameData=snapCurrentFrameData();
try{
//now catch error
selectFrameData(currentFrameRange.getValue());//re pose
}catch (Exception e) {
Window.alert("maybe difference bone model loaded.\nreload app");
}
}
@Override
public void textureChanged(SimpleTextData textureValue) {
textureUrl=textureValue.getData();
generateTexture();
}
private void generateTexture(){
final Image img=new Image(textureUrl);
img.setVisible(false);
RootPanel.get().add(img);
img.addLoadHandler(new com.google.gwt.event.dom.client.LoadHandler() {
@Override
public void onLoad(LoadEvent event) {
Canvas canvas=Canvas.createIfSupported();
canvas.setCoordinateSpaceWidth(img.getWidth());
canvas.setCoordinateSpaceHeight(img.getHeight());
canvas.getContext2d().drawImage(ImageElement.as(img.getElement()),0,0);
texture=THREE.CanvasTexture(canvas.getCanvasElement());
texture.setNeedsUpdate(true);
img.removeFromParent();
//LogUtils.log("generate-texture");
updateMaterial();
}
});
}
//this called after mouse up and it's hard to use
@Override
public void onDoubleClick(DoubleClickEvent event) {
//usually called after onMouseDown?
//doMouseDown(event.getX(),event.getY(),true);
}
}