Package com.esotericsoftware.spine

Source Code of com.esotericsoftware.spine.SkeletonViewer$UI

/******************************************************************************
* Spine Runtimes Software License
* Version 2.1
*
* Copyright (c) 2013, Esoteric Software
* All rights reserved.
*
* You are granted a perpetual, non-exclusive, non-sublicensable and
* non-transferable license to install, execute and perform the Spine Runtimes
* Software (the "Software") solely for internal use. Without the written
* permission of Esoteric Software (typically granted by licensing Spine), you
* may not (a) modify, translate, adapt or otherwise create derivative works,
* improvements of the Software or develop new applications using the Software
* or (b) remove, delete, alter or obscure any trademarks or any copyright,
* trademark, patent or other intellectual property or proprietary rights
* notices on or in the Software, including any copy thereof. Redistributions
* in binary or source form must include this license and terms.
*
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/

package com.esotericsoftware.spine;

import static com.badlogic.gdx.scenes.scene2d.actions.Actions.*;

import java.awt.FileDialog;
import java.awt.Frame;
import java.io.File;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.TextureAtlasData;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.List;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
import com.badlogic.gdx.scenes.scene2d.ui.Slider;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.WidgetGroup;
import com.badlogic.gdx.scenes.scene2d.ui.Window;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.viewport.ScreenViewport;

public class SkeletonViewer extends ApplicationAdapter {
  static final float checkModifiedInterval = 0.250f;
  static final float reloadDelay = 1;

  UI ui;

  PolygonSpriteBatch batch;
  SkeletonRenderer renderer;
  SkeletonRendererDebug debugRenderer;
  SkeletonData skeletonData;
  Skeleton skeleton;
  AnimationState state;
  int skeletonX, skeletonY;
  FileHandle skeletonFile;
  long lastModified;
  float lastModifiedCheck, reloadTimer;

  public void create () {
    ui = new UI();
    batch = new PolygonSpriteBatch();
    renderer = new SkeletonRenderer();
    debugRenderer = new SkeletonRendererDebug();
    skeletonX = (int)(ui.window.getWidth() + (Gdx.graphics.getWidth() - ui.window.getWidth()) / 2);
    skeletonY = Gdx.graphics.getHeight() / 4;

    loadSkeleton(
      Gdx.files.internal(Gdx.app.getPreferences("spine-skeletontest").getString("lastFile", "spineboy/spineboy.json")), false);
  }

  void loadSkeleton (FileHandle skeletonFile, boolean reload) {
    if (skeletonFile == null) return;

    // A regular texture atlas would normally usually be used. This returns a white image for images not found in the atlas.
    Pixmap pixmap = new Pixmap(32, 32, Format.RGBA8888);
    pixmap.setColor(new Color(1, 1, 1, 0.33f));
    pixmap.fill();
    final AtlasRegion fake = new AtlasRegion(new Texture(pixmap), 0, 0, 32, 32);
    pixmap.dispose();

    String atlasFileName = skeletonFile.nameWithoutExtension();
    if (atlasFileName.endsWith(".json")) atlasFileName = new FileHandle(atlasFileName).nameWithoutExtension();
    FileHandle atlasFile = skeletonFile.sibling(atlasFileName + ".atlas");
    if (!atlasFile.exists()) atlasFile = skeletonFile.sibling(atlasFileName + ".atlas.txt");
    TextureAtlasData data = !atlasFile.exists() ? null : new TextureAtlasData(atlasFile, atlasFile.parent(), false);
    TextureAtlas atlas = new TextureAtlas(data) {
      public AtlasRegion findRegion (String name) {
        AtlasRegion region = super.findRegion(name);
        return region != null ? region : fake;
      }
    };

    try {
      String extension = skeletonFile.extension();
      if (extension.equalsIgnoreCase("json") || extension.equalsIgnoreCase("txt")) {
        SkeletonJson json = new SkeletonJson(atlas);
        json.setScale(ui.scaleSlider.getValue());
        skeletonData = json.readSkeletonData(skeletonFile);
      } else {
        SkeletonBinary binary = new SkeletonBinary(atlas);
        binary.setScale(ui.scaleSlider.getValue());
        skeletonData = binary.readSkeletonData(skeletonFile);
      }
    } catch (Exception ex) {
      ex.printStackTrace();
      ui.toast("Error loading skeleton: " + skeletonFile.name());
      lastModifiedCheck = 5;
      return;
    }

    skeleton = new Skeleton(skeletonData);
    skeleton.setToSetupPose();
    skeleton = new Skeleton(skeleton);
    skeleton.updateWorldTransform();

    state = new AnimationState(new AnimationStateData(skeletonData));

    this.skeletonFile = skeletonFile;
    Preferences prefs = Gdx.app.getPreferences("spine-skeletontest");
    prefs.putString("lastFile", skeletonFile.path());
    prefs.flush();
    lastModified = skeletonFile.lastModified();
    lastModifiedCheck = checkModifiedInterval;

    // Populate UI.

    ui.skeletonLabel.setText(skeletonFile.name());
    {
      Array<String> items = new Array();
      for (Skin skin : skeletonData.getSkins())
        items.add(skin.getName());
      ui.skinList.setItems(items);
    }
    {
      Array<String> items = new Array();
      for (Animation animation : skeletonData.getAnimations())
        items.add(animation.getName());
      ui.animationList.setItems(items);
    }

    // Configure skeleton from UI.

    skeleton.setSkin(ui.skinList.getSelected());
    state.setAnimation(0, ui.animationList.getSelected(), ui.loopCheckbox.isChecked());

    if (reload) ui.toast("Reloaded.");
  }

  public void render () {
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    float delta = Gdx.graphics.getDeltaTime();

    if (skeleton != null) {
      if (reloadTimer <= 0) {
        lastModifiedCheck -= delta;
        if (lastModifiedCheck < 0) {
          lastModifiedCheck = checkModifiedInterval;
          long time = skeletonFile.lastModified();
          if (time != 0 && lastModified != time) reloadTimer = reloadDelay;
        }
      } else {
        reloadTimer -= delta;
        if (reloadTimer <= 0) loadSkeleton(skeletonFile, true);
      }

      state.getData().setDefaultMix(ui.mixSlider.getValue());
      renderer.setPremultipliedAlpha(ui.premultipliedCheckbox.isChecked());

      delta = Math.min(delta, 0.032f) * ui.speedSlider.getValue();
      skeleton.update(delta);
      skeleton.setFlip(ui.flipXCheckbox.isChecked(), ui.flipYCheckbox.isChecked());
      if (!ui.pauseButton.isChecked()) {
        state.update(delta);
        state.apply(skeleton);
      }
      skeleton.setPosition(skeletonX, skeletonY);
      // skeleton.setPosition(0, 0);
      // skeleton.getRootBone().setX(skeletonX);
      // skeleton.getRootBone().setY(skeletonY);
      skeleton.updateWorldTransform();

      batch.begin();
      renderer.draw(batch, skeleton);
      batch.end();

      debugRenderer.setBones(ui.debugBonesCheckbox.isChecked());
      debugRenderer.setRegionAttachments(ui.debugRegionsCheckbox.isChecked());
      debugRenderer.setBoundingBoxes(ui.debugBoundingBoxesCheckbox.isChecked());
      debugRenderer.setMeshHull(ui.debugMeshHullCheckbox.isChecked());
      debugRenderer.setMeshTriangles(ui.debugMeshTrianglesCheckbox.isChecked());
      debugRenderer.draw(skeleton);
    }

    ui.stage.act();
    ui.stage.draw();
  }

  public void resize (int width, int height) {
    batch.getProjectionMatrix().setToOrtho2D(0, 0, width, height);
    debugRenderer.getShapeRenderer().setProjectionMatrix(batch.getProjectionMatrix());
    ui.stage.getViewport().update(width, height, true);
    if (!ui.minimizeButton.isChecked()) ui.window.setHeight(height);
  }

  class UI {
    Stage stage = new Stage(new ScreenViewport());
    com.badlogic.gdx.scenes.scene2d.ui.Skin skin = new com.badlogic.gdx.scenes.scene2d.ui.Skin(
      Gdx.files.internal("skin/skin.json"));

    Window window = new Window("Skeleton", skin);
    Table root = new Table(skin);
    TextButton browseButton = new TextButton("Browse", skin);
    Label skeletonLabel = new Label("", skin);
    List<String> animationList = new List(skin);
    List<String> skinList = new List(skin);
    CheckBox loopCheckbox = new CheckBox(" Loop", skin);
    CheckBox premultipliedCheckbox = new CheckBox(" Premultiplied", skin);
    Slider mixSlider = new Slider(0f, 2, 0.01f, false, skin);
    Label mixLabel = new Label("0.3", skin);
    Slider speedSlider = new Slider(0.1f, 3, 0.01f, false, skin);
    Label speedLabel = new Label("1.0", skin);
    CheckBox flipXCheckbox = new CheckBox(" X", skin);
    CheckBox flipYCheckbox = new CheckBox(" Y", skin);
    CheckBox debugBonesCheckbox = new CheckBox(" Bones", skin);
    CheckBox debugRegionsCheckbox = new CheckBox(" Regions", skin);
    CheckBox debugBoundingBoxesCheckbox = new CheckBox(" Bounds", skin);
    CheckBox debugMeshHullCheckbox = new CheckBox(" Mesh Hull", skin);
    CheckBox debugMeshTrianglesCheckbox = new CheckBox(" Mesh Triangles", skin);
    Slider scaleSlider = new Slider(0.1f, 3, 0.01f, false, skin);
    Label scaleLabel = new Label("1.0", skin);
    TextButton pauseButton = new TextButton("Pause", skin, "toggle");
    TextButton minimizeButton = new TextButton("-", skin);
    TextButton bonesSetupPoseButton = new TextButton("Bones", skin);
    TextButton slotsSetupPoseButton = new TextButton("Slots", skin);
    TextButton setupPoseButton = new TextButton("Both", skin);
    WidgetGroup toasts = new WidgetGroup();

    public UI () {
      // Configure widgets.

      premultipliedCheckbox.setChecked(true);

      loopCheckbox.setChecked(true);

      scaleSlider.setValue(1);
      scaleSlider.setSnapToValues(new float[] {1}, 0.1f);

      mixSlider.setValue(0.3f);

      speedSlider.setValue(1);
      speedSlider.setSnapToValues(new float[] {1}, 0.1f);

      window.setMovable(false);
      window.setResizable(false);

      minimizeButton.padTop(-2).padLeft(5);
      minimizeButton.getColor().a = 0.66f;
      window.getButtonTable().add(minimizeButton).size(20, 20);

      ScrollPane skinScroll = new ScrollPane(skinList, skin);
      skinScroll.setFadeScrollBars(false);

      ScrollPane animationScroll = new ScrollPane(animationList, skin);
      animationScroll.setFadeScrollBars(false);

      // Layout.

      root.pad(2, 4, 4, 4).defaults().space(6);
      root.columnDefaults(0).top().right();
      root.columnDefaults(1).left();
      root.row().padTop(6);
      root.add("Skeleton:");
      {
        Table table = table();
        table.add(skeletonLabel).fillX().expandX();
        table.add(browseButton);
        root.add(table).fill().row();
      }
      root.add("Scale:");
      {
        Table table = table();
        table.add(scaleLabel).width(29);
        table.add(scaleSlider).fillX().expandX();
        root.add(table).fill().row();
      }
      root.add("Flip:");
      root.add(table(flipXCheckbox, flipYCheckbox)).row();
      root.add("Debug:");
      root.add(table(debugBonesCheckbox, debugRegionsCheckbox, debugBoundingBoxesCheckbox)).row();
      root.add();
      root.add(table(debugMeshHullCheckbox, debugMeshTrianglesCheckbox)).row();
      root.add("Alpha:");
      root.add(premultipliedCheckbox).row();
      root.add("Skin:");
      root.add(skinScroll).expand().fill().minHeight(75).row();
      root.add("Setup Pose:");
      root.add(table(bonesSetupPoseButton, slotsSetupPoseButton, setupPoseButton)).row();
      root.add("Animation:");
      root.add(animationScroll).expand().fill().minHeight(75).row();
      root.add("Mix:");
      {
        Table table = table();
        table.add(mixLabel).width(29);
        table.add(mixSlider).fillX().expandX();
        root.add(table).fill().row();
      }
      root.add("Speed:");
      {
        Table table = table();
        table.add(speedLabel).width(29);
        table.add(speedSlider).fillX().expandX();
        root.add(table).fill().row();
      }
      root.add("Playback:");
      root.add(table(pauseButton, loopCheckbox)).row();

      window.add(root).expand().fill();
      window.pack();
      stage.addActor(window);

      {
        Table table = new Table(skin);
        table.setFillParent(true);
        table.setTouchable(Touchable.disabled);
        stage.addActor(table);
        table.pad(10).bottom().right();
        table.add(toasts);
        table.debug();
      }

      // Events.

      window.addListener(new InputListener() {
        public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
          event.cancel();
          return true;
        }
      });

      browseButton.addListener(new ChangeListener() {
        public void changed (ChangeEvent event, Actor actor) {
          FileDialog fileDialog = new FileDialog((Frame)null, "Choose skeleton file");
          fileDialog.setMode(FileDialog.LOAD);
          fileDialog.setVisible(true);
          String name = fileDialog.getFile();
          String dir = fileDialog.getDirectory();
          if (name == null || dir == null) return;
          loadSkeleton(new FileHandle(new File(dir, name).getAbsolutePath()), false);
        }
      });

      setupPoseButton.addListener(new ChangeListener() {
        public void changed (ChangeEvent event, Actor actor) {
          if (skeleton != null) skeleton.setToSetupPose();
        }
      });
      bonesSetupPoseButton.addListener(new ChangeListener() {
        public void changed (ChangeEvent event, Actor actor) {
          if (skeleton != null) skeleton.setBonesToSetupPose();
        }
      });
      slotsSetupPoseButton.addListener(new ChangeListener() {
        public void changed (ChangeEvent event, Actor actor) {
          if (skeleton != null) skeleton.setSlotsToSetupPose();
        }
      });

      minimizeButton.addListener(new ClickListener() {
        public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
          event.cancel();
          return super.touchDown(event, x, y, pointer, button);
        }

        public void clicked (InputEvent event, float x, float y) {
          if (minimizeButton.isChecked()) {
            window.getCells().get(0).setActor(null);
            window.setHeight(20);
            minimizeButton.setText("+");
          } else {
            window.getCells().get(0).setActor(root);
            ui.window.setHeight(Gdx.graphics.getHeight());
            minimizeButton.setText("-");
          }
        }
      });

      scaleSlider.addListener(new ChangeListener() {
        public void changed (ChangeEvent event, Actor actor) {
          scaleLabel.setText(Float.toString((int)(scaleSlider.getValue() * 100) / 100f));
          if (!scaleSlider.isDragging()) loadSkeleton(skeletonFile, false);
        }
      });

      speedSlider.addListener(new ChangeListener() {
        public void changed (ChangeEvent event, Actor actor) {
          speedLabel.setText(Float.toString((int)(speedSlider.getValue() * 100) / 100f));
        }
      });

      mixSlider.addListener(new ChangeListener() {
        public void changed (ChangeEvent event, Actor actor) {
          mixLabel.setText(Float.toString((int)(mixSlider.getValue() * 100) / 100f));
          if (state != null) state.getData().setDefaultMix(mixSlider.getValue());
        }
      });

      animationList.addListener(new ChangeListener() {
        public void changed (ChangeEvent event, Actor actor) {
          if (state != null) state.setAnimation(0, animationList.getSelected(), loopCheckbox.isChecked());
        }
      });

      skinList.addListener(new ChangeListener() {
        public void changed (ChangeEvent event, Actor actor) {
          if (skeleton != null) {
            skeleton.setSkin(skinList.getSelected());
            skeleton.setSlotsToSetupPose();
          }
        }
      });

      Gdx.input.setInputProcessor(new InputMultiplexer(stage, new InputAdapter() {
        public boolean touchDown (int screenX, int screenY, int pointer, int button) {
          touchDragged(screenX, screenY, pointer);
          return false;
        }

        public boolean touchDragged (int screenX, int screenY, int pointer) {
          skeletonX = screenX;
          skeletonY = Gdx.graphics.getHeight() - screenY;
          return false;
        }
      }));
    }

    private Table table (Actor... actors) {
      Table table = new Table();
      table.defaults().space(6);
      table.add(actors);
      return table;
    }

    void toast (String text) {
      Table table = new Table();
      table.add(new Label(text, skin));
      table.getColor().a = 0;
      table.pack();
      table.setPosition(-table.getWidth(), -3 - table.getHeight());
      table.addAction(sequence( //
        parallel(moveBy(0, table.getHeight(), 0.3f), fadeIn(0.3f)), //
        delay(5f), //
        parallel(moveBy(0, table.getHeight(), 0.3f), fadeOut(0.3f)), //
        removeActor() //
        ));
      for (Actor actor : toasts.getChildren())
        actor.addAction(moveBy(0, table.getHeight(), 0.3f));
      toasts.addActor(table);
      toasts.getParent().toFront();
    }
  }

  static public void main (String[] args) throws Exception {
    LwjglApplicationConfiguration.disableAudio = true;
    LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
    config.width = 800;
    config.height = 600;
    config.title = "Skeleton Viewer";
    config.allowSoftwareMode = true;
    new LwjglApplication(new SkeletonViewer(), config);
  }
}
TOP

Related Classes of com.esotericsoftware.spine.SkeletonViewer$UI

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.