diff options
author | Joshua Slack <[email protected]> | 2017-06-02 19:02:23 -0500 |
---|---|---|
committer | Joshua Slack <[email protected]> | 2017-06-02 19:02:23 -0500 |
commit | cbc61566689660d39f469efe4c429f8adf5b239e (patch) | |
tree | 7f4811bc04e605f25643d645337f553960b35ba0 /ardor3d-examples | |
parent | 67d620baf5eaf5b0ce66800e468719a9ef79ef25 (diff) |
Updates to allow overriding SpatialState in InteractManager.
Updates to allow easier overriding of input flow in UIHud.
Added new example tying together UI and Interact
Diffstat (limited to 'ardor3d-examples')
-rw-r--r-- | ardor3d-examples/src/main/java/com/ardor3d/example/ui/InteractUIExample.java | 693 |
1 files changed, 693 insertions, 0 deletions
diff --git a/ardor3d-examples/src/main/java/com/ardor3d/example/ui/InteractUIExample.java b/ardor3d-examples/src/main/java/com/ardor3d/example/ui/InteractUIExample.java new file mode 100644 index 0000000..1dfa5b2 --- /dev/null +++ b/ardor3d-examples/src/main/java/com/ardor3d/example/ui/InteractUIExample.java @@ -0,0 +1,693 @@ +/** + * Copyright (c) 2008-2017 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at <http://www.ardor3d.com/LICENSE>. + */ + +package com.ardor3d.example.ui; + +import java.lang.reflect.Field; +import java.util.LinkedList; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.ardor3d.bounding.BoundingBox; +import com.ardor3d.example.ExampleBase; +import com.ardor3d.example.Purpose; +import com.ardor3d.extension.interact.InteractManager; +import com.ardor3d.extension.interact.data.SpatialState; +import com.ardor3d.extension.interact.filter.PlaneBoundaryFilter; +import com.ardor3d.extension.interact.widget.AbstractInteractWidget; +import com.ardor3d.extension.interact.widget.BasicFilterList; +import com.ardor3d.extension.interact.widget.IFilterList; +import com.ardor3d.extension.interact.widget.MovePlanarWidget; +import com.ardor3d.extension.interact.widget.MovePlanarWidget.MovePlane; +import com.ardor3d.extension.ui.FloatingUIContainer; +import com.ardor3d.extension.ui.Orientation; +import com.ardor3d.extension.ui.UIButton; +import com.ardor3d.extension.ui.UIComboBox; +import com.ardor3d.extension.ui.UIContainer; +import com.ardor3d.extension.ui.UIFrame; +import com.ardor3d.extension.ui.UIHud; +import com.ardor3d.extension.ui.UIPanel; +import com.ardor3d.extension.ui.UISlider; +import com.ardor3d.extension.ui.backdrop.EmptyBackdrop; +import com.ardor3d.extension.ui.border.EmptyBorder; +import com.ardor3d.extension.ui.event.ActionEvent; +import com.ardor3d.extension.ui.event.ActionListener; +import com.ardor3d.extension.ui.event.SelectionListener; +import com.ardor3d.extension.ui.layout.BorderLayoutData; +import com.ardor3d.extension.ui.layout.RowLayout; +import com.ardor3d.extension.ui.model.DefaultComboBoxModel; +import com.ardor3d.extension.ui.util.Insets; +import com.ardor3d.framework.Canvas; +import com.ardor3d.image.Texture; +import com.ardor3d.input.Key; +import com.ardor3d.input.logical.InputTrigger; +import com.ardor3d.input.logical.KeyReleasedCondition; +import com.ardor3d.input.logical.TriggerAction; +import com.ardor3d.input.logical.TwoInputStates; +import com.ardor3d.intersection.PickData; +import com.ardor3d.intersection.Pickable; +import com.ardor3d.intersection.PrimitivePickResults; +import com.ardor3d.math.ColorRGBA; +import com.ardor3d.math.MathUtils; +import com.ardor3d.math.Plane; +import com.ardor3d.math.Quaternion; +import com.ardor3d.math.Vector3; +import com.ardor3d.renderer.Camera; +import com.ardor3d.renderer.Renderer; +import com.ardor3d.renderer.state.MaterialState; +import com.ardor3d.renderer.state.MaterialState.ColorMaterial; +import com.ardor3d.renderer.state.TextureState; +import com.ardor3d.scenegraph.Mesh; +import com.ardor3d.scenegraph.Spatial; +import com.ardor3d.scenegraph.controller.SpatialController; +import com.ardor3d.scenegraph.hint.PickingHint; +import com.ardor3d.scenegraph.shape.Box; +import com.ardor3d.scenegraph.shape.Tube; +import com.ardor3d.scenegraph.visitor.Visitor; +import com.ardor3d.util.ReadOnlyTimer; +import com.ardor3d.util.TextureManager; +import com.google.common.collect.Lists; + +/** + * An example illustrating the use of the interact framework. + */ +@Purpose(htmlDescriptionKey = "com.ardor3d.example.interact.InteractUIExample", // +thumbnailPath = "com/ardor3d/example/media/thumbnails/interact_InteractUIExample.jpg", // +maxHeapMemory = 64) +public class InteractUIExample extends ExampleBase { + + final UIHud hud = new UIHud(); + private InteractManager manager; + private MovePlanarWidget moveWidget; + private InsertMarkerUIWidget insertWidget; + private ColorSelectUIWidget colorWidget; + private PulseControlUIWidget pulseWidget; + + final Vector3 tempVec = new Vector3(); + + public static void main(final String[] args) { + start(InteractUIExample.class); + } + + @Override + protected void updateExample(final ReadOnlyTimer timer) { + manager.update(timer); + } + + @Override + protected void renderExample(final Renderer renderer) { + super.renderExample(renderer); + manager.render(renderer); + renderer.renderBuckets(); + hud.draw(renderer); + } + + @Override + protected void updateLogicalLayer(final ReadOnlyTimer timer) { + hud.getLogicalLayer().checkTriggers(timer.getTimePerFrame()); + } + + @Override + protected void initExample() { + _canvas.setTitle("Interact Example"); + + final Camera camera = _canvas.getCanvasRenderer().getCamera(); + camera.setLocation(15, 11, -9); + camera.lookAt(0, 0, 0, Vector3.UNIT_Y); + + // setup our interact controls + addControls(); + + // create a floor to act as a reference. + addFloor(); + + // create a few way-markers to start things off + initPath(); + + } + + private void addFloor() { + final Box floor = new Box("floor", Vector3.ZERO, 100, 5, 100); + floor.setTranslation(0, -5, 0); + final TextureState ts = new TextureState(); + ts.setTexture(TextureManager.load("models/obj/pitcher.jpg", Texture.MinificationFilter.Trilinear, true)); + floor.setRenderState(ts); + floor.getSceneHints().setPickingHint(PickingHint.Pickable, false); + floor.setModelBound(new BoundingBox()); + _root.attachChild(floor); + _root.updateGeometricState(0); + } + + LinkedList<Spatial> path = Lists.newLinkedList(); + + private void initPath() { + final Spatial marker1 = createMarker(); + marker1.setName("marker1"); + final Spatial marker2 = createMarkerAfter(marker1); + marker2.setName("marker2"); + createMarkerBefore(marker2).setName("marker3"); + + // auto select the joint + _root.updateGeometricState(0); + manager.setSpatialTarget(marker1); + } + + private void removeMarker(final Spatial ref) { + final int index = path.indexOf(ref); + if (path.remove(ref)) { + ref.removeFromParent(); + manager.setSpatialTarget(null); + if (path.size() == 0) { + manager.setSpatialTarget(null); + } else if (path.size() <= index) { + manager.setSpatialTarget(path.get(index - 1)); + } else { + manager.setSpatialTarget(path.get(index)); + } + } + } + + private Spatial createMarker() { + final Tube t = new Tube("marker", 1, 0.25, .25); + t.setModelBound(new BoundingBox()); + t.updateGeometricState(0); + t.addTranslation(0, .25, 0); + t.getSceneHints().setPickingHint(PickingHint.Pickable, true); + final MaterialState ms = new MaterialState(); + ms.setColorMaterial(ColorMaterial.AmbientAndDiffuse); + t.setRenderState(ms); + final MarkerData data = new MarkerData(); + t.setUserData(data); + t.addController(new SpatialController<Spatial>() { + private double _scaleTime = 0; + + public void update(final double time, final Spatial caller) { + // update our rotation + final double pulseSpeed = ((MarkerData) t.getUserData()).pulseSpeed; + if (pulseSpeed != 0.0) { + _scaleTime = _scaleTime + (_timer.getTimePerFrame() * pulseSpeed); + final double scale = MathUtils.sin(_scaleTime) * .99 + 1.0; + t.setScale(scale); + } else { + t.setScale(1.0); + } + } + }); + _root.attachChild(t); + path.add(t); + return t; + } + + private Spatial createMarkerAfter(final Spatial ref) { + final Spatial marker = createMarker(); + + // copy transform (orientation and position) of ref + marker.setTranslation(ref.getTranslation()); + marker.setRotation(ref.getRotation()); + + // check if we're moving into place between two points, or just after last point + final int indexOfRef = path.indexOf(ref); + if (indexOfRef == path.size() - 2) { + // we're adding after last point, so no need to move in list + // just translate us in the forward z direction of the last node + final Vector3 fwd = marker.getRotation().applyPost(Vector3.UNIT_Z, null); + marker.addTranslation(fwd.multiplyLocal(8.0)); + } else { + // we're adding a point between two others - get our other ref + final Spatial postRef = path.get(indexOfRef + 1); + + // move new marker into list between the other two points + path.remove(marker); + path.add(indexOfRef + 1, marker); + + // translate and orient between points + marker.setTranslation(ref.getTranslation().add(postRef.getTranslation(), null).divideLocal(2.0)); + + final Quaternion rotHelper1 = new Quaternion(); + rotHelper1.fromRotationMatrix(ref.getRotation()); + final Quaternion rotHelper2 = new Quaternion(); + rotHelper2.fromRotationMatrix(postRef.getRotation()); + marker.setRotation(rotHelper1.slerp(rotHelper2, .5, null)); + } + + manager.setSpatialTarget(marker); + + return marker; + } + + private Spatial createMarkerBefore(final Spatial ref) { + final int indexOfRef = path.indexOf(ref); + if (indexOfRef <= 0) { + return null; + } + + return createMarkerAfter(path.get(indexOfRef - 1)); + } + + private void addControls() { + // create our manager + manager = new InteractManager(new MarkerState()); + manager.setupInput(_canvas, _physicalLayer, _logicalLayer); + + hud.setupInput(_canvas, _physicalLayer, manager.getLogicalLayer()); + hud.setMouseManager(_mouseManager); + + final BasicFilterList filterList = new BasicFilterList(); + + // add some widgets. + insertWidget = new InsertMarkerUIWidget(filterList); + manager.addWidget(insertWidget); + + colorWidget = new ColorSelectUIWidget(filterList); + manager.addWidget(colorWidget); + + pulseWidget = new PulseControlUIWidget(filterList); + manager.addWidget(pulseWidget); + + moveWidget = new MovePlanarWidget(filterList).withPlane(MovePlane.XZ).withDefaultHandle(.33, .33, + ColorRGBA.YELLOW); + manager.addWidget(moveWidget); + + // set the default as current + manager.setActiveWidget(moveWidget); + + // add triggers to change which widget is active + manager.getLogicalLayer().registerTrigger( + new InputTrigger(new KeyReleasedCondition(Key.ONE), new TriggerAction() { + @Override + public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) { + manager.setActiveWidget(moveWidget); + } + })); + manager.getLogicalLayer().registerTrigger( + new InputTrigger(new KeyReleasedCondition(Key.TWO), new TriggerAction() { + @Override + public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) { + manager.setActiveWidget(insertWidget); + } + })); + manager.getLogicalLayer().registerTrigger( + new InputTrigger(new KeyReleasedCondition(Key.THREE), new TriggerAction() { + @Override + public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) { + manager.setActiveWidget(colorWidget); + } + })); + manager.getLogicalLayer().registerTrigger( + new InputTrigger(new KeyReleasedCondition(Key.FOUR), new TriggerAction() { + @Override + public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) { + manager.setActiveWidget(pulseWidget); + } + })); + + // add some filters + manager.addFilter(new PlaneBoundaryFilter(new Plane(Vector3.UNIT_Y, 0))); + } + + @Override + protected void processPicks(final PrimitivePickResults pickResults) { + final PickData pick = pickResults.findFirstIntersectingPickData(); + if (pick != null) { + final Pickable target = pick.getTarget(); + if (target instanceof Spatial) { + manager.setSpatialTarget((Spatial) target); + System.out.println("Setting target to: " + ((Spatial) target).getName()); + return; + } + } + manager.setSpatialTarget(null); + } + + class InsertMarkerUIWidget extends AbstractInteractWidget { + + UIFrame popupFrame; + + public InsertMarkerUIWidget(final IFilterList filterList) { + super(filterList); + + createFrame(); + } + + private void createFrame() { + + final RowLayout rowLay = new RowLayout(true); + final UIPanel centerPanel = new UIPanel(rowLay); + centerPanel.setBackdrop(new EmptyBackdrop()); + centerPanel.setLayoutData(BorderLayoutData.CENTER); + + AddButton(centerPanel, "+", new ActionListener() { + @Override + public void actionPerformed(final ActionEvent event) { + final Spatial spat = manager.getSpatialTarget(); + if (spat == null) { + return; + } + createMarkerBefore(spat); + } + }); + + AddButton(centerPanel, "-", new ActionListener() { + @Override + public void actionPerformed(final ActionEvent event) { + final Spatial spat = manager.getSpatialTarget(); + if (spat == null) { + return; + } + removeMarker(spat); + } + }); + + AddButton(centerPanel, "+", new ActionListener() { + @Override + public void actionPerformed(final ActionEvent event) { + final Spatial spat = manager.getSpatialTarget(); + if (spat == null) { + return; + } + createMarkerAfter(spat); + } + }); + + popupFrame = new FloatingUIContainer(); + popupFrame.getContentPanel().add(centerPanel); + popupFrame.getBasePanel().setBackdrop(new EmptyBackdrop()); + popupFrame.getBasePanel().setBorder(new EmptyBorder()); + + popupFrame.updateMinimumSizeFromContents(); + popupFrame.layout(); + popupFrame.pack(); + + _handle = popupFrame; + } + + private void AddButton(final UIContainer parent, final String label, final ActionListener actionListener) { + final UIButton button = new UIButton(label); + button.setPadding(Insets.EMPTY); + button.setMargin(Insets.EMPTY); + button.setMaximumContentSize(22, 22); + button.setMinimumContentSize(22, 22); + button.addActionListener(actionListener); + parent.add(button); + } + + @Override + public void render(final Renderer renderer, final InteractManager manager) { + final Spatial spat = manager.getSpatialTarget(); + if (spat == null) { + return; + } + + tempVec.zero(); + tempVec.set(Camera.getCurrentCamera().getScreenCoordinates(spat.getWorldTransform().applyForward(tempVec))); + tempVec.setZ(0); + tempVec.subtractLocal(popupFrame.getContentWidth() / 2, -10, 0); + _handle.setTranslation(tempVec); + _handle.updateWorldTransform(true); + } + + @Override + public void receivedControl(final InteractManager manager) { + super.receivedControl(manager); + final Spatial spat = manager.getSpatialTarget(); + if (spat != null) { + hud.add(popupFrame); + } + } + + @Override + public void lostControl(final InteractManager manager) { + super.lostControl(manager); + hud.remove(popupFrame); + } + + @Override + public void targetChanged(final InteractManager manager) { + super.targetChanged(manager); + if (manager.getActiveWidget() == this) { + final Spatial spat = manager.getSpatialTarget(); + if (spat == null) { + hud.remove(popupFrame); + } else { + hud.add(popupFrame); + } + } + } + } + + class ColorSelectUIWidget extends AbstractInteractWidget { + + UIFrame popupFrame; + ColorRGBA unconsumedColor; + + public ColorSelectUIWidget(final IFilterList filterList) { + super(filterList); + + createFrame(); + } + + private void createFrame() { + + final UIPanel centerPanel = new UIPanel(null); + centerPanel.setBackdrop(new EmptyBackdrop()); + centerPanel.setLayoutData(BorderLayoutData.CENTER); + + final UIComboBox combo = new UIComboBox(new DefaultComboBoxModel("White", "Black", "Red", "Green", "Blue", + "Yellow", "Magenta", "Cyan")); + combo.setLocalComponentWidth(100); + combo.addSelectionListener(new SelectionListener<UIComboBox>() { + @Override + public void selectionChanged(final UIComboBox component, final Object newValue) { + try { + final Field field = ColorRGBA.class.getField(newValue.toString().toUpperCase()); + final ColorRGBA color = (ColorRGBA) field.get(null); + if (manager.getSpatialState() instanceof MarkerState) { + unconsumedColor = color; + } + + } catch (final Exception ex) { + ex.printStackTrace(); + } + } + }); + centerPanel.add(combo); + + popupFrame = new FloatingUIContainer(); + popupFrame.getContentPanel().add(centerPanel); + popupFrame.getBasePanel().setBackdrop(new EmptyBackdrop()); + popupFrame.getBasePanel().setBorder(null); + + popupFrame.updateMinimumSizeFromContents(); + popupFrame.layout(); + popupFrame.pack(); + + _handle = popupFrame; + } + + @Override + public void render(final Renderer renderer, final InteractManager manager) { + final Spatial spat = manager.getSpatialTarget(); + if (spat == null) { + return; + } + + tempVec.zero(); + tempVec.set(Camera.getCurrentCamera().getScreenCoordinates(spat.getWorldTransform().applyForward(tempVec))); + tempVec.setZ(0); + tempVec.subtractLocal(popupFrame.getContentWidth() / 2, -20, 0); + _handle.setTranslation(tempVec); + _handle.updateWorldTransform(true); + } + + @Override + public void receivedControl(final InteractManager manager) { + super.receivedControl(manager); + final Spatial spat = manager.getSpatialTarget(); + if (spat != null) { + hud.add(popupFrame); + } + } + + @Override + public void lostControl(final InteractManager manager) { + super.lostControl(manager); + hud.remove(popupFrame); + } + + @Override + public void processInput(final Canvas source, final TwoInputStates inputStates, + final AtomicBoolean inputConsumed, final InteractManager manager) { + super.processInput(source, inputStates, inputConsumed, manager); + if (unconsumedColor != null) { + ((MarkerState) manager.getSpatialState()).data.color.set(unconsumedColor); + inputConsumed.set(true); + unconsumedColor = null; + } + } + + @Override + public void targetChanged(final InteractManager manager) { + super.targetChanged(manager); + if (manager.getActiveWidget() == this) { + final Spatial spat = manager.getSpatialTarget(); + if (spat == null) { + hud.remove(popupFrame); + } else { + hud.add(popupFrame); + } + } + } + } + + class PulseControlUIWidget extends AbstractInteractWidget { + + UIFrame popupFrame; + Double unconsumedPulse; + + public PulseControlUIWidget(final IFilterList filterList) { + super(filterList); + + createFrame(); + } + + private void createFrame() { + + final UIPanel centerPanel = new UIPanel(null); + centerPanel.setBackdrop(new EmptyBackdrop()); + centerPanel.setLayoutData(BorderLayoutData.CENTER); + + final UISlider slider = new UISlider(Orientation.Horizontal, 0, 100, 0); + slider.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent event) { + if (manager.getSpatialTarget() == null) { + return; + } + if (manager.getSpatialState() instanceof MarkerState) { + unconsumedPulse = slider.getValue() * 0.05; + } + } + }); + slider.setLocalComponentWidth(100); + centerPanel.add(slider); + + popupFrame = new FloatingUIContainer(); + popupFrame.getContentPanel().add(centerPanel); + popupFrame.getBasePanel().setBackdrop(new EmptyBackdrop()); + popupFrame.getBasePanel().setBorder(null); + + popupFrame.updateMinimumSizeFromContents(); + popupFrame.layout(); + popupFrame.pack(); + + _handle = popupFrame; + } + + @Override + public void render(final Renderer renderer, final InteractManager manager) { + final Spatial spat = manager.getSpatialTarget(); + if (spat == null) { + return; + } + + tempVec.zero(); + tempVec.set(Camera.getCurrentCamera().getScreenCoordinates(spat.getWorldTransform().applyForward(tempVec))); + tempVec.setZ(0); + tempVec.subtractLocal(popupFrame.getContentWidth() / 2, -20, 0); + _handle.setTranslation(tempVec); + _handle.updateWorldTransform(true); + } + + @Override + public void receivedControl(final InteractManager manager) { + super.receivedControl(manager); + final Spatial spat = manager.getSpatialTarget(); + if (spat != null) { + hud.add(popupFrame); + } + } + + @Override + public void lostControl(final InteractManager manager) { + super.lostControl(manager); + hud.remove(popupFrame); + } + + @Override + public void processInput(final Canvas source, final TwoInputStates inputStates, + final AtomicBoolean inputConsumed, final InteractManager manager) { + super.processInput(source, inputStates, inputConsumed, manager); + if (unconsumedPulse != null) { + ((MarkerState) manager.getSpatialState()).data.pulseSpeed = unconsumedPulse; + inputConsumed.set(true); + unconsumedPulse = null; + } + } + + @Override + public void targetChanged(final InteractManager manager) { + super.targetChanged(manager); + if (manager.getActiveWidget() == this) { + final Spatial spat = manager.getSpatialTarget(); + if (spat == null) { + hud.remove(popupFrame); + } else { + hud.add(popupFrame); + } + } + } + } + + class MarkerData { + public ColorRGBA color = new ColorRGBA(ColorRGBA.WHITE); + public double pulseSpeed; + + public void copy(final MarkerData source) { + color.set(source.color); + pulseSpeed = source.pulseSpeed; + } + } + + class MarkerState extends SpatialState { + public final MarkerData data = new MarkerData(); + + @Override + public void applyState(final Spatial target) { + super.applyState(target); + if (target.getUserData() instanceof MarkerData) { + final MarkerData tData = (MarkerData) target.getUserData(); + if (tData.pulseSpeed != data.pulseSpeed) { + tData.pulseSpeed = data.pulseSpeed; + } + + if (!tData.color.equals(data.color)) { + tData.color.set(data.color); + target.acceptVisitor(new Visitor() { + @Override + public void visit(final Spatial spatial) { + if (spatial instanceof Mesh) { + final Mesh mesh = (Mesh) spatial; + mesh.setDefaultColor(tData.color); + mesh.setSolidColor(tData.color); + } + } + }, true); + } + } + } + + @Override + public void copyState(final Spatial source) { + super.copyState(source); + if (source.getUserData() instanceof MarkerData) { + data.copy((MarkerData) source.getUserData()); + } + } + } +} |