package info.u250.c2d.box2deditor.gdx.scenes;
import info.u250.c2d.box2d.Box2dObject;
import info.u250.c2d.box2d.model.b2BodyDefModel;
import info.u250.c2d.box2d.model.b2FixtureDefModel;
import info.u250.c2d.box2d.model.b2JointDefModel;
import info.u250.c2d.box2d.model.fixture.b2CircleFixtureDefModel;
import info.u250.c2d.box2d.model.fixture.b2RectangleFixtureDefModel;
import info.u250.c2d.box2deditor.adapter.DistanceJointDefModel;
import info.u250.c2d.box2deditor.adapter.FrictionJointDefModel;
import info.u250.c2d.box2deditor.adapter.PolygonFixtureDefModel;
import info.u250.c2d.box2deditor.adapter.PrismaticJointDefModel;
import info.u250.c2d.box2deditor.adapter.PulleyJointDefModel;
import info.u250.c2d.box2deditor.adapter.RevoluteJointDefModel;
import info.u250.c2d.box2deditor.adapter.RopeJointDefModel;
import info.u250.c2d.box2deditor.adapter.SceneModelAdapter;
import info.u250.c2d.box2deditor.adapter.WeldJointDefModel;
import info.u250.c2d.box2deditor.adapter.WheelJointDefModel;
import info.u250.c2d.box2deditor.gdx.PhysicalWorld;
import info.u250.c2d.box2deditor.gdx.support.AbstractBox2dHelper;
import info.u250.c2d.box2deditor.gdx.support.DefaultLabel;
import info.u250.c2d.box2deditor.gdx.support.Geometry;
import info.u250.c2d.box2deditor.gdx.support.LeftTopImage;
import info.u250.c2d.engine.Engine;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Buttons;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.QueryCallback;
import com.badlogic.gdx.scenes.scene2d.Group;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.scenes.scene2d.utils.NinePatchDrawable;
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.utils.Scaling;
public class SceneHelper extends AbstractBox2dHelper<SceneModelAdapter> {
private b2BodyDefModel data = null;
private b2BodyDefModel secondData = null;
private Vector2 dragTemp = new Vector2();
private Vector2 startPoint = new Vector2();
final Vector2 box2dAABBTestPoint = new Vector2();
InputMultiplexer input = new InputMultiplexer();
LeftTopImage bodyMode,jointModel;
private boolean rotateOn = false;
private Color colorInner = new Color(1f,1,1f,0.2f);
private Color colorOuter = new Color(0,0,0,0.2f);
QueryCallback callback = new QueryCallback() {
@Override
public boolean reportFixture (Fixture fixture) {
// if the hit point is inside the fixture of the body
// we report it
if (fixture.testPoint(box2dAABBTestPoint.x, box2dAABBTestPoint.y)) {
Body hitBody = fixture.getBody();
for(b2BodyDefModel bodyModel : PhysicalWorld.MODEL.bodyDefModels){
if(hitBody == bodyModel.body){
data = bodyModel;
adapter.callUI.updateToUI(bodyModel);
return false;
}
}
data = null;
secondData = null;
return false;
} else{
secondData = null;
return true;
}
}
};
InputAdapter jointSelectInput = new InputAdapter(){
MakeJointsMenu jointsMenu = new MakeJointsMenu();
QueryCallback callbackSecond = new QueryCallback() {
@Override
public boolean reportFixture (Fixture fixture) {
if(fixture==null)return true;
// if the hit point is inside the fixture of the body
// we report it
if (fixture.testPoint(box2dAABBTestPoint.x, box2dAABBTestPoint.y)) {
Body hitBody = fixture.getBody();
for(b2BodyDefModel bodyModel : PhysicalWorld.MODEL.bodyDefModels){
if(hitBody == bodyModel.body){
//not the same body
if(data==bodyModel)return true;
secondData = bodyModel;
adapter.callUI.updateToUI(secondData);
if(!Gdx.input.isButtonPressed(Buttons.LEFT)){
jointsMenu.setScale(Engine.getDefaultCamera().zoom);
jointsMenu.setPosition(worldCenter.x-100,worldCenter.y-100);
addActor(jointsMenu);
}
return false;
}
}
secondData = null;
return false;
} else{
return true;
}
}
};
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
if(button == Buttons.LEFT){
jointsMenu.remove();
dragTemp.set(Engine.screenToWorld(screenX, screenY));
startPoint.set(dragTemp);
data = null;
secondData = null;
//find the first data //
if(button == Buttons.LEFT){
box2dAABBTestPoint.set(dragTemp).scl(1f/Box2dObject.RADIO);
PhysicalWorld.WORLD.QueryAABB(callback, box2dAABBTestPoint.x - 0.1f, box2dAABBTestPoint.y - 0.1f, box2dAABBTestPoint.x + 0.1f, box2dAABBTestPoint.y + 0.1f);
}
}
return super.touchDown(screenX, screenY, pointer, button);
}
public boolean touchDragged(int screenX, int screenY, int pointer) {
secondData = null;
dragTemp.set(Engine.screenToWorld(screenX, screenY));
//find the first data //
box2dAABBTestPoint.set(dragTemp).scl(1f/Box2dObject.RADIO);
PhysicalWorld.WORLD.QueryAABB(callbackSecond, box2dAABBTestPoint.x - 0.1f, box2dAABBTestPoint.y - 0.1f, box2dAABBTestPoint.x + 0.1f, box2dAABBTestPoint.y + 0.1f);
return super.touchDragged(screenX, screenY, pointer);
};
@Override
public boolean touchUp(int x, int y, int pointer, int button) {
if(button == Buttons.LEFT){
secondData = null;
dragTemp.set(Engine.screenToWorld(x, y));
//find the first data //
box2dAABBTestPoint.set(dragTemp).scl(1f/Box2dObject.RADIO);
PhysicalWorld.WORLD.QueryAABB(callbackSecond, box2dAABBTestPoint.x - 0.1f, box2dAABBTestPoint.y - 0.1f, box2dAABBTestPoint.x + 0.1f, box2dAABBTestPoint.y + 0.1f);
}
return false;
}
public boolean keyDown(int keycode) {
if(keycode==Keys.SPACE){
jointsMenu.remove();
data = null;
secondData = null;
input.clear();
input.addProcessor(SceneHelper.this);
input.addProcessor(bodySelectInput);
jointModel.remove();
addActor(bodyMode);
}else if(keycode == Keys.F5){
jointModel.remove();
adapter.simulation();
}
return super.keyDown(keycode);
}
};
InputAdapter bodySelectInput = new InputAdapter() {
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
dragTemp.set(Engine.screenToWorld(screenX, screenY));
secondData = null;
float dst = 0;
if(data!=null){
dst = data.position.dst(dragTemp);
}
if(dst>100 && dst<150){
rotateOn = true;
}else{
data = null;
//find the first data //
if(button == Buttons.LEFT){
box2dAABBTestPoint.set(dragTemp).scl(1f/Box2dObject.RADIO);
PhysicalWorld.WORLD.QueryAABB(callback, box2dAABBTestPoint.x - 0.1f, box2dAABBTestPoint.y - 0.1f, box2dAABBTestPoint.x + 0.1f, box2dAABBTestPoint.y + 0.1f);
}
}
return super.touchDown(screenX, screenY, pointer, button);
}
@Override
public boolean touchDragged(int x, int y, int pointer) {
if(null!=data){
Vector2 current = Engine.screenToWorld(x, y);
if(rotateOn){
float angle_append = current.cpy().sub(data.position).angle() - dragTemp.sub(data.position).angle();
data.degrees+=angle_append;
adapter.callUI.updateToUI(data);
}else{
secondData = null;
Vector2 offset = current.cpy().sub(dragTemp);
data.position.add(offset.x,offset.y );
adapter.callUI.updateToUI(data);
}
dragTemp.set(current);
}
return super.touchDragged(x, y, pointer);
}
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
rotateOn = false;
return false;
};
public boolean keyDown(int keycode) {
if(keycode==Keys.SPACE){
data = null;
secondData = null;
input.clear();
input.addProcessor(SceneHelper.this);
input.addProcessor(jointSelectInput);
bodyMode.remove();
addActor(jointModel);
}else if(keycode == Keys.F5){
jointModel.remove();
adapter.simulation();
}
return super.keyDown(keycode);
}
};
public SceneHelper(final MainScene adapter) {
super(adapter);
TextureAtlas atlas = Engine.resource("TA");
bodyMode = new LeftTopImage(atlas.findRegion("bodyMode"));
jointModel = new LeftTopImage(atlas.findRegion("jointMode"));
this.addActor(bodyMode);
}
@Override
public void render(float delta) {
boolean bodySelectMode = input.getProcessors().contains(bodySelectInput, true);
Gdx.gl.glEnable(GL20.GL_BLEND);
if(bodySelectMode && null!=data){
render.setColor(colorOuter);
render.begin(ShapeType.Filled);
render.circle(data.position.x, data.position.y, 150);
render.end();
render.setColor(colorInner);
render.begin(ShapeType.Filled);
render.circle(data.position.x, data.position.y, 100);
render.end();
render.setColor(colorOuter);
render.begin(ShapeType.Filled);
render.triangle(
data.position.x+100*MathUtils.cosDeg(data.degrees-30),
data.position.y+100*MathUtils.sinDeg(data.degrees-30),
data.position.x+100*MathUtils.cosDeg(data.degrees+90),
data.position.y+100*MathUtils.sinDeg(data.degrees+90),
data.position.x+100*MathUtils.cosDeg(data.degrees+210),
data.position.y+100*MathUtils.sinDeg(data.degrees+210));
render.end();
}
try{
for(b2BodyDefModel b2:model.bodyDefModels){
final Vector2 position = b2.position;
final float angle = b2.degrees;
for(b2FixtureDefModel obj:b2.fixtures){
if(obj instanceof PolygonFixtureDefModel){
Geometry.renderPolygon(PolygonFixtureDefModel.class.cast(obj), position,angle,!bodySelectMode && b2==data||b2==secondData);
}else if(obj instanceof b2CircleFixtureDefModel){
Geometry.renderCircle(b2CircleFixtureDefModel.class.cast(obj), position,angle,!bodySelectMode &&b2==data||b2==secondData);
}else if(obj instanceof b2RectangleFixtureDefModel){
Geometry.renderBox(b2RectangleFixtureDefModel.class.cast(obj), position,angle,!bodySelectMode &&b2==data||b2==secondData);
}
}
if(null!=b2.body){
render.begin(ShapeType.Filled);
render.setColor(Color.RED);
render.circle(b2.position.x, b2.position.y, 5);
render.end();
}
}
}catch(Exception ex){
Gdx.app.error("Exception", ex.getMessage());
}
// now we draw the joints
for(b2JointDefModel b2Joint:model.jointDefModels){
Geometry.renderJoint(b2Joint);
}
if(!bodySelectMode && Gdx.input.isButtonPressed(Buttons.LEFT)){
render.begin(ShapeType.Line);
render.line(startPoint.x, startPoint.y, dragTemp.x, dragTemp.y);
render.end();
}
Gdx.gl.glDisable(GL20.GL_BLEND);
this.act(delta);
this.draw();
}
@Override
public InputProcessor getInputProcessor() {
input.clear();
input.addProcessor(this);
input.addProcessor(bodySelectInput);
return input;
}
@Override
public Class<SceneModelAdapter> getType() {
return SceneModelAdapter.class;
}
public void setData(b2BodyDefModel data) {
this.data = data;
}
/**
* The menu for make new joints
*
*/
class MakeJointsMenu extends Group {
public MakeJointsMenu(){
TextureAtlas atlas = Engine.resource("TA");
Table menuTable = new Table();
menuTable.pad(20);
menuTable.setBackground(new NinePatchDrawable(atlas.createPatch("dialog")));
this.addActor(menuTable);
addNewHandle(menuTable, "b2DistanceJointDefModel", "DistanceJoint",new ClickListener(){
@Override
public void clicked(InputEvent event, float x, float y) {
DistanceJointDefModel def = new DistanceJointDefModel();
def.bodyA = data;
def.bodyB = secondData;
def.localAnchorA.set(def.bodyA.body.getLocalPoint(startPoint.cpy().scl(1f/Box2dObject.RADIO))).scl(Box2dObject.RADIO);
def.localAnchorB.set(def.bodyB.body.getLocalPoint(dragTemp.cpy().scl(1f/Box2dObject.RADIO))).scl(Box2dObject.RADIO);
def.length = startPoint.dst(dragTemp);
Geometry.ajustJoint(def);
PhysicalWorld.MODEL.addJoint(def);
adapter.callUI.addModelToLeft(def);
remove();
}
});
addNewHandle(menuTable, "b2PrismaticJointDefModel", "PrismaticJoint",new ClickListener(){
@Override
public void clicked(InputEvent event, float x, float y) {
PrismaticJointDefModel def = new PrismaticJointDefModel();
def.bodyA = data;
def.bodyB = secondData;
def.anchor.set(startPoint).add(dragTemp).scl(1f/2);
Geometry.ajustJoint(def);
PhysicalWorld.MODEL.addJoint(def);
adapter.callUI.addModelToLeft(def);
remove();
}
});
addNewHandle(menuTable, "b2RevoluteJointDefModel", "RevoluteJoint",new ClickListener(){
@Override
public void clicked(InputEvent event, float x, float y) {
RevoluteJointDefModel def = new RevoluteJointDefModel();
def.bodyA = data;
def.bodyB = secondData;
def.anchor.set(startPoint).add(dragTemp).scl(1f/2);
Geometry.ajustJoint(def);
PhysicalWorld.MODEL.addJoint(def);
adapter.callUI.addModelToLeft(def);
remove();
}
});
addNewHandle(menuTable, "b2RopeJointDefModel", "RopeJoint",new ClickListener(){
@Override
public void clicked(InputEvent event, float x, float y) {
RopeJointDefModel def = new RopeJointDefModel();
def.bodyA = data;
def.bodyB = secondData;
def.localAnchorA.set(def.bodyA.body.getLocalPoint(startPoint.cpy().scl(1f/Box2dObject.RADIO))).scl(Box2dObject.RADIO);
def.localAnchorB.set(def.bodyB.body.getLocalPoint(dragTemp.cpy().scl(1f/Box2dObject.RADIO))).scl(Box2dObject.RADIO);
Geometry.ajustJoint(def);
PhysicalWorld.MODEL.addJoint(def);
adapter.callUI.addModelToLeft(def);
remove();
}
});
addNewHandle(menuTable, "b2WeldJointDefModel", "WeldJointDefModel",new ClickListener(){
@Override
public void clicked(InputEvent event, float x, float y) {
WeldJointDefModel def = new WeldJointDefModel();
def.bodyA = data;
def.bodyB = secondData;
def.anchor.set(startPoint).add(dragTemp).scl(1f/2);
Geometry.ajustJoint(def);
PhysicalWorld.MODEL.addJoint(def);
adapter.callUI.addModelToLeft(def);
remove();
}
});
addNewHandle(menuTable, "b2WheelJointDefModel", "WheelJointDefModel",new ClickListener(){
@Override
public void clicked(InputEvent event, float x, float y) {
WheelJointDefModel def = new WheelJointDefModel();
def.bodyA = data;
def.bodyB = secondData;
def.anchor.set(startPoint).add(dragTemp).scl(1f/2);
Geometry.ajustJoint(def);
PhysicalWorld.MODEL.addJoint(def);
adapter.callUI.addModelToLeft(def);
remove();
}
});
addNewHandle(menuTable, "b2FrictionJointDefModel", "FrictionJoint",new ClickListener(){
@Override
public void clicked(InputEvent event, float x, float y) {
FrictionJointDefModel def = new FrictionJointDefModel();
def.bodyA = data;
def.bodyB = secondData;
def.anchor.set(startPoint).add(dragTemp).scl(1f/2);
Geometry.ajustJoint(def);
PhysicalWorld.MODEL.addJoint(def);
adapter.callUI.addModelToLeft(def);
remove();
}
});
addNewHandle(menuTable, "b2PulleyJointDefModel", "PulleyJoint",new ClickListener(){
@Override
public void clicked(InputEvent event, float x, float y) {
PulleyJointDefModel def = new PulleyJointDefModel();
def.bodyA = data;
def.bodyB = secondData;
def.localAnchorA.set(def.bodyA.body.getLocalPoint(startPoint.cpy().scl(1f/Box2dObject.RADIO))).scl(Box2dObject.RADIO);
def.localAnchorB.set(def.bodyB.body.getLocalPoint(dragTemp.cpy().scl(1f/Box2dObject.RADIO))).scl(Box2dObject.RADIO);
def.groundAnchorA.set(startPoint.x, 500);
def.groundAnchorB.set(dragTemp.x, 500);
def.lengthA = startPoint.dst(def.groundAnchorA);
def.lengthB = dragTemp.dst(def.groundAnchorB);
Geometry.ajustJoint(def);
PhysicalWorld.MODEL.addJoint(def);
adapter.callUI.addModelToLeft(def);
remove();
}
});
menuTable.pack();
}
void addNewHandle(Table table,String region,String txt,ClickListener l){
TextureAtlas atlas = Engine.resource("TA");
DefaultLabel label = DefaultLabel.getDefaultLabel(txt+" ");
label.addListener(l);
table.add(new Image(new TextureRegionDrawable(atlas.findRegion(region)),Scaling.none)).height(30).spaceRight(10);
table.add(label).right().spaceRight(10);
table.row();
}
}
}