diff options
Diffstat (limited to 'ardor3d-extras/src/main/java/com')
61 files changed, 4214 insertions, 496 deletions
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasNode.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasNode.java index f047859..62efc17 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasNode.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasNode.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasPacker.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasPacker.java index cf11c06..230ad79 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasPacker.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasPacker.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasTextureParameter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasTextureParameter.java index c45b4d6..55869c4 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasTextureParameter.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasTextureParameter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/TexturePacker.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/TexturePacker.java index 0e6771c..36d2025 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/TexturePacker.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/TexturePacker.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * @@ -12,6 +12,8 @@ package com.ardor3d.extension.atlas; import java.nio.ByteBuffer; import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; @@ -31,8 +33,6 @@ import com.ardor3d.renderer.state.TextureState; import com.ardor3d.scenegraph.Mesh; import com.ardor3d.util.TextureManager; import com.ardor3d.util.geom.BufferUtils; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; /** * A tool that uses the AtlasNode/AtlasPacker algorithm to pack textures into texture atlases. It modifies the uv @@ -69,15 +69,15 @@ public class TexturePacker { private final List<AtlasPacker> packers; private final List<ByteBuffer> dataBuffers; - private final List<Texture> textures = Lists.newArrayList(); + private final List<Texture> textures = new ArrayList<>(); public TexturePacker(final int atlasWidth, final int atlasHeight) { this.atlasWidth = atlasWidth; this.atlasHeight = atlasHeight; - cachedAtlases = Maps.newHashMap(); - packers = Lists.newArrayList(); - dataBuffers = Lists.newArrayList(); + cachedAtlases = new HashMap<>(); + packers = new ArrayList<>(); + dataBuffers = new ArrayList<>(); addPacker(); } @@ -168,7 +168,7 @@ public class TexturePacker { return; } - list = Lists.newArrayList(); + list = new ArrayList<>(); cachedAtlases.put(parameterObject, list); list.add(parameterObject); diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/TextureParameter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/TextureParameter.java index 5679195..9cf8e5d 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/TextureParameter.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/TextureParameter.java @@ -1,9 +1,9 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * - * Ardor3D is free software: you can redistribute it and/or modify it + * 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>. */ @@ -11,6 +11,7 @@ package com.ardor3d.extension.atlas; import java.nio.FloatBuffer; +import java.util.Objects; import com.ardor3d.image.Texture; import com.ardor3d.renderer.state.RenderState; @@ -88,10 +89,7 @@ public class TextureParameter { @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (textureKey == null ? 0 : textureKey.hashCode()); - return result; + return Objects.hashCode(textureKey); } @Override @@ -106,14 +104,7 @@ public class TextureParameter { return false; } final TextureParameter other = (TextureParameter) obj; - if (textureKey == null) { - if (other.textureKey != null) { - return false; - } - } else if (!textureKey.equals(other.textureKey)) { - return false; - } - return true; + return Objects.equals(textureKey, other.textureKey); } public int getTargetTextureIndex() { diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/InteractManager.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/InteractManager.java index 447a226..e924ede 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/InteractManager.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/InteractManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * @@ -10,6 +10,7 @@ package com.ardor3d.extension.interact; +import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -28,14 +29,13 @@ import com.ardor3d.renderer.Renderer; import com.ardor3d.scenegraph.Spatial; import com.ardor3d.util.ReadOnlyTimer; import com.google.common.base.Predicate; -import com.google.common.collect.Lists; public class InteractManager { /** * List of widgets currently managed by this manager. */ - protected final List<AbstractInteractWidget> _widgets = Lists.newArrayList(); + protected final List<AbstractInteractWidget> _widgets = new ArrayList<>(); /** * The logical layer used by this manager to receive input events prior to forwarding them to the scene. @@ -66,7 +66,7 @@ public class InteractManager { /** * List of filters to modify state prior to applying to a Spatial target. */ - protected List<UpdateFilter> _filters = Lists.newArrayList(); + protected List<UpdateFilter> _filters = new ArrayList<>(); public InteractManager() { _state = new SpatialState(); @@ -129,11 +129,13 @@ public class InteractManager { */ private void setupLogicalLayer() { _logicalLayer.registerTrigger(new InputTrigger(new Predicate<TwoInputStates>() { + @Override public boolean apply(final TwoInputStates arg0) { // always trigger this. return true; } }, new TriggerAction() { + @Override public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) { if (_spatialTarget != null) { _state.copyState(_spatialTarget); diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/data/SpatialState.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/data/SpatialState.java index e3918d1..aa822a4 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/data/SpatialState.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/data/SpatialState.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/AllowScaleFilter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/AllowScaleFilter.java index a1eae88..2105eaa 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/AllowScaleFilter.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/AllowScaleFilter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/MinMaxScaleFilter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/MinMaxScaleFilter.java index e823403..dab5583 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/MinMaxScaleFilter.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/MinMaxScaleFilter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/PlaneBoundaryFilter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/PlaneBoundaryFilter.java index 7be40f4..100b51e 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/PlaneBoundaryFilter.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/PlaneBoundaryFilter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/UpdateFilter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/UpdateFilter.java index 20d1644..0e4646a 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/UpdateFilter.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/UpdateFilter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/AbstractInteractWidget.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/AbstractInteractWidget.java index 87ffd1b..39c6372 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/AbstractInteractWidget.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/AbstractInteractWidget.java @@ -1,9 +1,9 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * - * Ardor3D is free software: you can redistribute it and/or modify it + * 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>. */ @@ -57,7 +57,7 @@ public abstract class AbstractInteractWidget { /** * Use the given inputstates to determine if and how to activate this widget. If the widget uses the given input, * inputConsumed should be set to "true" and applyFilters should be called by this method. - * + * * @param source * the canvas that is our input source. * @param inputStates @@ -149,7 +149,8 @@ public abstract class AbstractInteractWidget { } protected Vector3 getLastPick() { - if (_results.getNumber() > 0 && _results.getPickData(0).getIntersectionRecord().getNumberOfIntersections() > 0) { + if (_results.getNumber() > 0 + && _results.getPickData(0).getIntersectionRecord().getNumberOfIntersections() > 0) { return _results.getPickData(0).getIntersectionRecord().getIntersectionPoint(0); } return null; diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/BasicFilterList.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/BasicFilterList.java index 3b13b9d..75f3364 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/BasicFilterList.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/BasicFilterList.java @@ -10,15 +10,15 @@ package com.ardor3d.extension.interact.widget; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; import com.ardor3d.extension.interact.InteractManager; import com.ardor3d.extension.interact.filter.UpdateFilter; -import com.google.common.collect.Lists; public class BasicFilterList implements IFilterList { - final List<UpdateFilter> _filters = Lists.newArrayList(); + final List<UpdateFilter> _filters = new ArrayList<>(); public Iterator<UpdateFilter> iterator() { return _filters.iterator(); diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/CompoundInteractWidget.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/CompoundInteractWidget.java index 62a9684..bd45d6e 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/CompoundInteractWidget.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/CompoundInteractWidget.java @@ -1,15 +1,16 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * - * Ardor3D is free software: you can redistribute it and/or modify it + * 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.extension.interact.widget; +import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; @@ -28,7 +29,6 @@ import com.ardor3d.renderer.Renderer; import com.ardor3d.scenegraph.Node; import com.ardor3d.scenegraph.Spatial; import com.ardor3d.util.ReadOnlyTimer; -import com.google.common.collect.Maps; public class CompoundInteractWidget extends AbstractInteractWidget { @@ -39,12 +39,10 @@ public class CompoundInteractWidget extends AbstractInteractWidget { public static double MIN_SCALE = 0.000001; - protected Map<String, AbstractInteractWidget> _widgets = Maps.newHashMap(); + protected Map<String, AbstractInteractWidget> _widgets = new HashMap<>(); protected AbstractInteractWidget _lastInputWidget = null; - protected InteractMatrix _interactMatrix; - public CompoundInteractWidget() { super(new BasicFilterList()); _handle = new Node("handleRoot"); @@ -52,7 +50,7 @@ public class CompoundInteractWidget extends AbstractInteractWidget { @Override public void addFilter(final UpdateFilter filter) { - for(final AbstractInteractWidget widget : _widgets.values()) { + for (final AbstractInteractWidget widget : _widgets.values()) { widget.addFilter(filter); } super.addFilter(filter); @@ -60,7 +58,7 @@ public class CompoundInteractWidget extends AbstractInteractWidget { @Override public void removeFilter(final UpdateFilter filter) { - for(final AbstractInteractWidget widget : _widgets.values()) { + for (final AbstractInteractWidget widget : _widgets.values()) { widget.removeFilter(filter); } super.removeFilter(filter); @@ -68,7 +66,7 @@ public class CompoundInteractWidget extends AbstractInteractWidget { @Override public void clearFilters() { - for(final AbstractInteractWidget widget : _widgets.values()) { + for (final AbstractInteractWidget widget : _widgets.values()) { widget.clearFilters(); } super.clearFilters(); diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractArrow.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractArrow.java index 0fb6fa6..31d14a9 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractArrow.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractArrow.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * @@ -29,7 +29,7 @@ public class InteractArrow extends Arrow { protected double _lengthGap = 0; protected double _tipGap = 0; - protected static final Quaternion rotator = new Quaternion().applyRotationX(MathUtils.HALF_PI); + protected static final Quaternion ROTATOR = new Quaternion().applyRotationX(MathUtils.HALF_PI); public InteractArrow() {} @@ -66,8 +66,8 @@ public class InteractArrow extends Arrow { final double tipLength = _length / 2.0; final Pyramid tip = new Pyramid("tip", 2 * _width, tipLength); tip.getMeshData().translatePoints(0, _tipGap + _length + 0.5 * tipLength, 0); - tip.getMeshData().rotatePoints(InteractArrow.rotator); - tip.getMeshData().rotateNormals(InteractArrow.rotator); + tip.getMeshData().rotatePoints(InteractArrow.ROTATOR); + tip.getMeshData().rotateNormals(InteractArrow.ROTATOR); attachChild(tip); tip.updateModelBound(); diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractMatrix.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractMatrix.java index 944bd9d..6b9e235 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractMatrix.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractMatrix.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractRing.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractRing.java index 983017a..1c3b42b 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractRing.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractRing.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveMultiPlanarWidget.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveMultiPlanarWidget.java index f47be1b..c598e34 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveMultiPlanarWidget.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveMultiPlanarWidget.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MovePlanarWidget.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MovePlanarWidget.java index 9787ea7..16b650f 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MovePlanarWidget.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MovePlanarWidget.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveWidget.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveWidget.java index e03e1bd..c06569d 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveWidget.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveWidget.java @@ -1,9 +1,9 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * - * Ardor3D is free software: you can redistribute it and/or modify it + * 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>. */ @@ -278,16 +278,6 @@ public class MoveWidget extends AbstractInteractWidget { return _calcVec3D.subtractLocal(_calcVec3C); } - @Override - public void setInteractMatrix(final InteractMatrix matrix) { - _interactMatrix = matrix; - } - - @Override - public InteractMatrix getInteractMatrix() { - return _interactMatrix; - } - public InteractArrow getXArrow() { return _xArrow; } diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/RotateWidget.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/RotateWidget.java index a684ef7..03287de 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/RotateWidget.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/RotateWidget.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/SimpleScaleWidget.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/SimpleScaleWidget.java index ffcc8bb..9f5fc3d 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/SimpleScaleWidget.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/SimpleScaleWidget.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2DataStore.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2DataStore.java index a2c12b1..1830aa6 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2DataStore.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2DataStore.java @@ -1,33 +1,35 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * - * Ardor3D is free software: you can redistribute it and/or modify it + * 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.extension.model.md2; +import java.util.ArrayList; import java.util.List; import com.ardor3d.extension.model.util.KeyframeController; import com.ardor3d.scenegraph.Mesh; -import com.google.common.collect.Lists; public class Md2DataStore { private final Mesh _mainMesh; private final KeyframeController<Mesh> _controller; - private final List<String> _frameNames = Lists.newArrayList(); + private final List<String> _frameNames; - private final List<String> _skinNames = Lists.newArrayList(); + private final List<String> _skinNames; public Md2DataStore(final Mesh mainMesh, final KeyframeController<Mesh> controller) { _mainMesh = mainMesh; _controller = controller; + _frameNames = new ArrayList<>(); + _skinNames = new ArrayList<>(); } public Mesh getScene() { diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Frame.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Frame.java index 8c6b8c1..d01871f 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Frame.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Frame.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Header.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Header.java index 2dc7bf7..d520378 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Header.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Header.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Importer.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Importer.java index 6270b66..b9adc18 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Importer.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Importer.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * @@ -11,13 +11,14 @@ package com.ardor3d.extension.model.md2; import java.io.InputStream; +import java.util.ArrayList; import java.util.List; import com.ardor3d.bounding.BoundingBox; import com.ardor3d.extension.model.util.KeyframeController; import com.ardor3d.image.Texture; -import com.ardor3d.image.TextureStoreFormat; import com.ardor3d.image.Texture.MinificationFilter; +import com.ardor3d.image.TextureStoreFormat; import com.ardor3d.math.Vector3; import com.ardor3d.renderer.IndexMode; import com.ardor3d.renderer.state.TextureState; @@ -30,7 +31,6 @@ import com.ardor3d.util.TextureManager; import com.ardor3d.util.resource.ResourceLocator; import com.ardor3d.util.resource.ResourceLocatorTool; import com.ardor3d.util.resource.ResourceSource; -import com.google.common.collect.Lists; public class Md2Importer { @@ -112,10 +112,10 @@ public class Md2Importer { final LittleEndianRandomAccessDataInput bis = new LittleEndianRandomAccessDataInput(md2Stream); // parse the header - final Md2Header header = new Md2Header(bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis - .readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis - .readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis - .readInt()); + final Md2Header header = new Md2Header(bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), + bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), + bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), + bis.readInt()); // Check magic word and version if (header.magic != ('2' << 24) + ('P' << 16) + ('D' << 8) + 'I') { @@ -159,8 +159,8 @@ public class Md2Importer { bis.seek(header.offsetGlCommands); int length, absLength; Md2GlCommand cmd; - final List<Integer> fanIndices = Lists.newArrayList(); - final List<Integer> stripIndices = Lists.newArrayList(); + final List<Integer> fanIndices = new ArrayList<>(); + final List<Integer> stripIndices = new ArrayList<>(); for (int i = 0; i < header.numGlCommands; i++) { length = bis.readInt(); if (length == 0) { @@ -299,7 +299,7 @@ public class Md2Importer { mesh.setName(resource.getName()); // Add controller - final KeyframeController<Mesh> controller = new KeyframeController<Mesh>(); + final KeyframeController<Mesh> controller = new KeyframeController<>(); mesh.addController(controller); controller.setMorphingMesh(mesh); controller.setInterpTex(false); @@ -351,18 +351,19 @@ public class Md2Importer { } private Texture loadTexture(final String name) { - Texture tex = null; + final ResourceSource source; if (_textureLocator == null) { - tex = TextureManager.load(name, getMinificationFilter(), + source = ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_TEXTURE, name); + } else { + source = _textureLocator.locateResource(name); + } + final Texture tex; + if (source == null) { + tex = null; + } else { + tex = TextureManager.load(source, getMinificationFilter(), isUseCompression() ? TextureStoreFormat.GuessCompressedFormat : TextureStoreFormat.GuessNoCompressedFormat, isFlipTextureVertically()); - } else { - final ResourceSource source = _textureLocator.locateResource(name); - if (source != null) { - tex = TextureManager.load(source, getMinificationFilter(), - isUseCompression() ? TextureStoreFormat.GuessCompressedFormat - : TextureStoreFormat.GuessNoCompressedFormat, isFlipTextureVertically()); - } } return tex; } @@ -378,8 +379,7 @@ public class Md2Importer { private void addVert(final Md2GlCommand cmd, final Md2Frame frame, final int vertIndex, final FloatBufferData verts) { final int index = cmd.vertIndices[vertIndex]; final byte[] vertData = frame.vertData; - calcVert.set((vertData[index * 4 + 0] & 0xFF), (vertData[index * 4 + 1] & 0xFF), - (vertData[index * 4 + 2] & 0xFF)); + calcVert.set(vertData[index * 4 + 0] & 0xFF, vertData[index * 4 + 1] & 0xFF, vertData[index * 4 + 2] & 0xFF); calcVert.multiplyLocal(frame.scale).addLocal(frame.translate); verts.getBuffer().put(calcVert.getXf()).put(calcVert.getYf()).put(calcVert.getZf()); } diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Normals.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Normals.java index fcb0f86..da2266c 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Normals.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Normals.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3DataStore.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3DataStore.java new file mode 100644 index 0000000..5e5a954 --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3DataStore.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2008-2014 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.extension.model.md3; + +import java.util.ArrayList; +import java.util.List; + +import com.ardor3d.scenegraph.Node; + +public class Md3DataStore { + + private final Node _mainNode; + + private final List<String> _frameNames; + + private final List<String> _skinNames; + + public Md3DataStore(final Node mainNode) { + super(); + _mainNode = mainNode; + _frameNames = new ArrayList<>(); + _skinNames = new ArrayList<>(); + } + + public Node getScene() { + return _mainNode; + } + + public List<String> getFrameNames() { + return _frameNames; + } + + public int getFrameIndex(final String frameName) { + return _frameNames.indexOf(frameName); + } + + public List<String> getSkinNames() { + return _skinNames; + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3Frame.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3Frame.java new file mode 100644 index 0000000..e24d903 --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3Frame.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2008-2014 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.extension.model.md3; + +import com.ardor3d.math.Vector3; +import com.ardor3d.math.type.ReadOnlyVector3; + +/** + * frame of MD3: http://en.wikipedia.org/wiki/MD3_%28file_format%29#Frame + */ +final class Md3Frame { + + /** First corner of the bounding box. */ + final Vector3 _minBounds; + /** Second corner of the bounding box. */ + final Vector3 _maxBounds; + /** Local origin, usually (0, 0, 0). */ + final Vector3 _localOrigin; + /** Radius of the bounding sphere. */ + final float _radius; + /** name */ + final String _name; + + Md3Frame(final ReadOnlyVector3 minBounds, final ReadOnlyVector3 maxBounds, final ReadOnlyVector3 localOrigin, + final float radius, final String name) { + super(); + _minBounds = new Vector3(); + _maxBounds = new Vector3(); + _localOrigin = new Vector3(); + _minBounds.set(minBounds); + _maxBounds.set(maxBounds); + _localOrigin.set(localOrigin); + _radius = radius; + _name = name; + } + +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3Header.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3Header.java new file mode 100644 index 0000000..b4d4cf6 --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3Header.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2008-2014 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.extension.model.md3; + +/** + * header of MD3: http://en.wikipedia.org/wiki/MD3_%28file_format%29#MD3_header + */ +final class Md3Header { + /** identifier of the file: magic number: "IDP3" */ + final int _magic; + /** version number of the file */ + final int _version; + /** name, usually its pathname in the PK3. ASCII character string, NULL-terminated (C-style) */ + final String _name; + /** flags, unused yet */ + final int _flags; + /** Number of Frame objects, with a maximum of MD3_MAX_FRAMES. Current value of MD3_MAX_FRAMES is 1024. */ + final int _numFrames; + /** + * Number of Tag objects, with a maximum of MD3_MAX_TAGS. Current value of MD3_MAX_TAGS is 16. There is one set of + * tags per frame so the total number of tags to read is (NUM_TAGS * NUM_FRAMES). + */ + final int _numTags; + /** Number of Surface objects, with a maximum of MD3_MAX_SURFACES. Current value of MD3_MAX_SURFACES is 32. */ + final int _numSurfaces; + /** Number of Skin objects, unused */ + final int _numSkins; + /** + * Relative offset from start of MD3 object where Frame objects start. The Frame objects are written sequentially, + * that is, when you read one Frame object, you do not need to seek() for the next object. + */ + final int _offsetFrame; + /** Relative offset from start of MD3 where Tag objects start. Similarly written sequentially. */ + final int _offsetTag; + /** Relative offset from start of MD3 where Surface objects start. Again, written sequentially. */ + final int _offsetSurface; + /** Relative offset from start of MD3 to the end of the MD3 object */ + final int _offsetEnd; + + Md3Header(final int magic, final int version, final String name, final int flags, final int numFrames, + final int numTags, final int numSurfaces, final int numSkins, final int offsetFrame, final int offsetTag, + final int offsetSurface, final int offsetEnd) { + super(); + _magic = magic; + _version = version; + _name = name; + _flags = flags; + _numFrames = numFrames; + _numTags = numTags; + _numSurfaces = numSurfaces; + _numSkins = numSkins; + _offsetFrame = offsetFrame; + _offsetTag = offsetTag; + _offsetSurface = offsetSurface; + _offsetEnd = offsetEnd; + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3Importer.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3Importer.java new file mode 100644 index 0000000..fb2ceea --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3Importer.java @@ -0,0 +1,211 @@ +/** + * Copyright (c) 2008-2014 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.extension.model.md3; + +import java.io.InputStream; + +import com.ardor3d.extension.model.util.KeyframeController; +import com.ardor3d.math.Matrix3; +import com.ardor3d.math.Vector2; +import com.ardor3d.math.Vector3; +import com.ardor3d.scenegraph.Mesh; +import com.ardor3d.scenegraph.Node; +import com.ardor3d.util.Ardor3dException; +import com.ardor3d.util.LittleEndianRandomAccessDataInput; +import com.ardor3d.util.geom.BufferUtils; +import com.ardor3d.util.resource.ResourceLocator; +import com.ardor3d.util.resource.ResourceLocatorTool; +import com.ardor3d.util.resource.ResourceSource; + +/** + * http://education.mit.edu/starlogo-tng/shapes-tutorial/shapetutorial.html + */ +public class Md3Importer { + + private static final float XYZ_SCALE = 1.0f / 64; + + private ResourceLocator _modelLocator; + + public void setModelLocator(final ResourceLocator locator) { + _modelLocator = locator; + } + + /** + * Reads a MD3 file from the given resource + * + * @param resource + * the name of the resource to find. + * @return an ObjGeometryStore data object containing the scene and other useful elements. + */ + public Md3DataStore load(final String resource) { + final ResourceSource source; + if (_modelLocator == null) { + source = ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_MODEL, resource); + } else { + source = _modelLocator.locateResource(resource); + } + + if (source == null) { + throw new Error("Unable to locate '" + resource + "'"); + } + + return load(source); + } + + /** + * Reads an MD3 file from the given resource + * + * @param resource + * a resource pointing to the model we wish to load. + * @return an Md3DataStore data object containing the scene and other useful elements. + */ + public Md3DataStore load(final ResourceSource resource) { + if (resource == null) { + throw new NullPointerException("Unable to load null resource"); + } + + try { + final InputStream md3Stream = resource.openStream(); + if (md3Stream == null) { + throw new NullPointerException("Unable to load null streams"); + } + final LittleEndianRandomAccessDataInput bis = new LittleEndianRandomAccessDataInput(md3Stream); + + // parse the header + final Md3Header header = new Md3Header(bis.readInt(), bis.readInt(), bis.readString(64), bis.readInt(), + bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), + bis.readInt(), bis.readInt()); + + // Check magic word and version + if (header._magic != ('3' << 24) + ('P' << 16) + ('D' << 8) + 'I') { + throw new Ardor3dException("Not an MD3 file."); + } + if (header._version != 15) { + throw new Ardor3dException("Invalid file version (Version not 15)!"); + } + + // Parse out frames + final Md3Frame[] frames = new Md3Frame[header._numFrames]; + bis.seek(header._offsetFrame); + final Vector3 minBounds = new Vector3(); + final Vector3 maxBounds = new Vector3(); + final Vector3 localOrigin = new Vector3(); + for (int i = 0; i < header._numFrames; i++) { + minBounds.set(bis.readFloat(), bis.readFloat(), bis.readFloat()); + maxBounds.set(bis.readFloat(), bis.readFloat(), bis.readFloat()); + localOrigin.set(bis.readFloat(), bis.readFloat(), bis.readFloat()); + frames[i] = new Md3Frame(minBounds, maxBounds, localOrigin, bis.readFloat(), bis.readString(16)); + } + + // Parse out tags + final Md3Tag[][] tags = new Md3Tag[header._numFrames][header._numTags]; + bis.seek(header._offsetTag); + final Vector3 origin = new Vector3(); + final Matrix3 axis = new Matrix3(); + for (int i = 0; i < header._numFrames; i++) { + for (int j = 0; j < header._numTags; j++) { + final String name = bis.readString(64).trim(); + origin.set(bis.readFloat(), bis.readFloat(), bis.readFloat()); + axis.set(bis.readFloat(), bis.readFloat(), bis.readFloat(), bis.readFloat(), bis.readFloat(), + bis.readFloat(), bis.readFloat(), bis.readFloat(), bis.readFloat()); + tags[i][j] = new Md3Tag(name, origin, axis); + } + } + + // Parse out surfaces + final Md3Surface[] surfaces = new Md3Surface[header._numSurfaces]; + bis.seek(header._offsetSurface); + for (int i = 0; i < header._numSurfaces; i++) { + final int surfaceStart = bis.position(); + final int magic = bis.readInt(); + if (magic != ('3' << 24) + ('P' << 16) + ('D' << 8) + 'I') { + throw new Ardor3dException("Not an MD3 surface."); + } + surfaces[i] = new Md3Surface(magic, bis.readString(64), bis.readInt(), bis.readInt(), bis.readInt(), + bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), + bis.readInt()); + // Parse out shaders + bis.seek(surfaceStart + surfaces[i]._offsetShaders); + for (int j = 0; j < surfaces[i]._numShaders; j++) { + // final String name = bis.readString(64); + // final int index = bis.readInt(); + // unused yet + } + // Parse out triangles + bis.seek(surfaceStart + surfaces[i]._offsetTriangles); + for (int j = 0; j < surfaces[i]._triIndexes.length; j++) { + surfaces[i]._triIndexes[j] = bis.readInt(); + } + // Parse out texture coordinates + bis.seek(surfaceStart + surfaces[i]._offsetTexCoords); + for (int j = 0; j < surfaces[i]._texCoords.length; j++) { + surfaces[i]._texCoords[j] = new Vector2(bis.readFloat(), bis.readFloat()); + } + // Parse out vertices + bis.seek(surfaceStart + surfaces[i]._offsetXyzNormals); + for (int j = 0; j < surfaces[i]._numFrames; j++) { + for (int k = 0; k < surfaces[i]._numVerts; k++) { + surfaces[i]._verts[j][k] = new Vector3(bis.readShort(), bis.readShort(), bis.readShort()) + .multiplyLocal(Md3Importer.XYZ_SCALE); + final int zenith = bis.readByte(); + final int azimuth = bis.readByte(); + final float lat = (float) (zenith * 2 * Math.PI / 255); + final float lng = (float) (azimuth * 2 * Math.PI / 255); + surfaces[i]._norms[j][k] = new Vector3(Math.cos(lat) * Math.sin(lng), Math.sin(lat) + * Math.sin(lng), Math.cos(lng)); + } + } + } + + final Node node = new Node(header._name); + for (int i = 0; i < header._numSurfaces; i++) { + final Md3Surface surface = surfaces[i]; + final KeyframeController<Mesh> controller = new KeyframeController<>(); + final Mesh morphingMesh = new Mesh(surface._name); + morphingMesh.getMeshData().setIndexBuffer(BufferUtils.createIntBuffer(surface._triIndexes)); + morphingMesh.getMeshData().setVertexBuffer(BufferUtils.createFloatBuffer(surface._verts[0])); + morphingMesh.getMeshData().setNormalBuffer(BufferUtils.createFloatBuffer(surface._norms[0])); + morphingMesh.getMeshData().setTextureBuffer(BufferUtils.createFloatBuffer(surface._texCoords), 0); + node.attachChild(morphingMesh); + controller.setMorphingMesh(morphingMesh); + for (int j = 0; j < surface._numFrames; j++) { + final Md3Frame frame = frames[j]; + final Mesh mesh = new Mesh(frame._name); + mesh.getMeshData().setVertexBuffer(BufferUtils.createFloatBuffer(surface._verts[j])); + mesh.getMeshData().setNormalBuffer(BufferUtils.createFloatBuffer(surface._norms[j])); + controller.setKeyframe(j, mesh); + } + morphingMesh.addController(controller); + // TODO should I add a controller into the node? + } + + // Make a store object to return + final Md3DataStore store = new Md3DataStore(node); + + // store names + for (final Md3Frame frame : frames) { + store.getFrameNames().add(frame._name); + } + + // TODO load the animation configuration file (animation.cfg): [sex f/m][first frame, num frames, looping + // frames, frames per second] + + /** + * TODO there is one .skin file per MD3 file, it contains at most one line per surface (?) with the name and + * the texture filename (.jpg or .tga) and a tag per attachment to another MD3 file + */ + + return store; + } catch (final Exception e) { + throw new Error("Unable to load md3 resource from URL: " + resource, e); + } + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3Surface.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3Surface.java new file mode 100644 index 0000000..b27dae9 --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3Surface.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2008-2014 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.extension.model.md3; + +import com.ardor3d.math.Vector2; +import com.ardor3d.math.Vector3; + +/** + * Surface of MD3: http://en.wikipedia.org/wiki/MD3_%28file_format%29#Surface + */ +final class Md3Surface { + + final int _magic; + /** name */ + final String _name; + /** flags */ + final int _flags; + /** Number of animation frames. This should match NUM_FRAMES in the MD3 header. */ + final int _numFrames; + /** + * Number of Shader objects defined in this Surface, with a limit of MD3_MAX_SHADERS. Current value of + * MD3_MAX_SHADERS is 256. + */ + final int _numShaders; + /** Number of Vertex objects defined in this Surface, up to MD3_MAX_VERTS. Current value of MD3_MAX_VERTS is 4096. */ + final int _numVerts; + /** + * Number of Triangle objects defined in this Surface, maximum of MD3_MAX_TRIANGLES. Current value of + * MD3_MAX_TRIANGLES is 8192. + */ + final int _numTriangles; + /** Relative offset from SURFACE_START where the list of Triangle objects starts. */ + final int _offsetTriangles; + /** Relative offset from SURFACE_START where the list of Shader objects starts. */ + final int _offsetShaders; + /** Relative offset from SURFACE_START where the list of ST objects (s-t texture coordinates) starts. */ + final int _offsetTexCoords; + /** Relative offset from SURFACE_START where the list of Vertex objects (X-Y-Z-N vertices) starts. */ + final int _offsetXyzNormals; + /** Relative offset from SURFACE_START to where the Surface object ends. */ + final int _offsetEnd; + /** Indices of triangles' vertices */ + final int[] _triIndexes; + /** Texture coordinates of triangles' vertices */ + final Vector2[] _texCoords; + /** Triangles' vertices */ + final Vector3[][] _verts; + /** Triangles' normals */ + final Vector3[][] _norms; + + Md3Surface(final int magic, final String name, final int flags, final int numFrames, final int numShaders, + final int numVerts, final int numTriangles, final int offsetTriangles, final int offsetShaders, + final int offsetTexCoords, final int offsetXyzNormals, final int offsetEnd) { + super(); + _magic = magic; + _name = name; + _flags = flags; + _numFrames = numFrames; + _numShaders = numShaders; + _numVerts = numVerts; + _numTriangles = numTriangles; + _offsetTriangles = offsetTriangles; + _offsetShaders = offsetShaders; + _offsetTexCoords = offsetTexCoords; + _offsetXyzNormals = offsetXyzNormals; + _offsetEnd = offsetEnd; + _triIndexes = new int[_numTriangles * 3]; + _texCoords = new Vector2[_numVerts]; + _verts = new Vector3[_numFrames][_numVerts]; + _norms = new Vector3[_numFrames][_numVerts]; + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3Tag.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3Tag.java new file mode 100644 index 0000000..b4c25c8 --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md3/Md3Tag.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2008-2014 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.extension.model.md3; + +import com.ardor3d.math.Matrix3; +import com.ardor3d.math.Vector3; +import com.ardor3d.math.type.ReadOnlyMatrix3; +import com.ardor3d.math.type.ReadOnlyVector3; + +/** + * Tag of MD3: http://en.wikipedia.org/wiki/MD3_%28file_format%29#Tag + */ +final class Md3Tag { + + /** name */ + final String _name; + /** coordinates */ + final Vector3 _origin; + /** 3x3 rotation matrix */ + final Matrix3 _axis; + + Md3Tag(final String name, final ReadOnlyVector3 origin, final ReadOnlyMatrix3 axis) { + super(); + _origin = new Vector3(); + _axis = new Matrix3(); + _name = name; + _origin.set(origin); + _axis.set(axis); + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjDataStore.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjDataStore.java index 0c9fa91..cc52553 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjDataStore.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjDataStore.java @@ -1,25 +1,33 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * - * Ardor3D is free software: you can redistribute it and/or modify it + * 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.extension.model.obj; +import java.util.ArrayList; import java.util.List; import com.ardor3d.math.Vector3; -import com.google.common.collect.Lists; public class ObjDataStore { - private final List<Vector3> _vertices = Lists.newArrayList(); - private final List<Vector3> _normals = Lists.newArrayList(); - private final List<Vector3> _generatedNormals = Lists.newArrayList(); - private final List<Vector3> _uvs = Lists.newArrayList(); + private final List<Vector3> _vertices; + private final List<Vector3> _normals; + private final List<Vector3> _generatedNormals; + private final List<Vector3> _uvs; + + public ObjDataStore() { + super(); + _vertices = new ArrayList<>(); + _normals = new ArrayList<>(); + _generatedNormals = new ArrayList<>(); + _uvs = new ArrayList<>(); + } public List<Vector3> getVertices() { return _vertices; diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjExporter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjExporter.java index 033727f..2683123 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjExporter.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjExporter.java @@ -1,9 +1,9 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * - * Ardor3D is free software: you can redistribute it and/or modify it + * 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>. */ @@ -16,8 +16,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.logging.Logger; import com.ardor3d.extension.model.util.KeyframeController; @@ -36,7 +36,7 @@ import com.ardor3d.util.TextureKey; * WaveFront OBJ exporter. It supports only the meshes. Several meshes can be exported into the same OBJ file. Only a * few kinds of primitives are supported. N.B: If the texture is flipped in Ardor3D, you will have to flip it manually * when loading the resulting OBJ file. - * + * * @author Julien Gouesse */ public class ObjExporter { @@ -49,7 +49,7 @@ public class ObjExporter { /** * Save a mesh to a single WaveFront OBJ file and a MTL file - * + * * @param mesh * mesh to export * @param objFile @@ -63,7 +63,7 @@ public class ObjExporter { save(mesh, objFile, mtlFile, false, 0, true, null, null); } else { final KeyframeController<?> controller = (KeyframeController<?>) mesh.getController(0); - final ArrayList<Mesh> meshList = new ArrayList<Mesh>(); + final ArrayList<Mesh> meshList = new ArrayList<>(); for (final KeyframeController.PointInTime pit : controller._keyframes) { if (pit != null && pit._newShape != null) { meshList.add(pit._newShape); @@ -75,7 +75,7 @@ public class ObjExporter { /** * Save several meshes to a single WaveFront OBJ file and a MTL file - * + * * @param meshList * meshes to export * @param objFile @@ -91,7 +91,7 @@ public class ObjExporter { if (!meshList.isEmpty()) { int firstVertexIndex = 0; boolean firstFiles = true; - final List<ObjMaterial> materialList = new ArrayList<ObjMaterial>(); + final List<ObjMaterial> materialList = new ArrayList<>(); for (final Mesh mesh : meshList) { if (mesh != null) { if (mesh.getControllerCount() == 0 || !(mesh.getController(0) instanceof KeyframeController)) { @@ -101,7 +101,7 @@ public class ObjExporter { firstVertexIndex += mesh.getMeshData().getVertexCount(); } else { final KeyframeController<?> controller = (KeyframeController<?>) mesh.getController(0); - final ArrayList<Mesh> subMeshList = new ArrayList<Mesh>(); + final ArrayList<Mesh> subMeshList = new ArrayList<>(); for (final KeyframeController.PointInTime pit : controller._keyframes) { if (pit != null && pit._newShape != null) { subMeshList.add(pit._newShape); @@ -122,7 +122,7 @@ public class ObjExporter { /** * Save a mesh to the given files. - * + * * @param mesh * mesh to export * @param objFile @@ -155,199 +155,209 @@ public class ObjExporter { parentDirectory.mkdirs(); } } - PrintWriter objPw = null, mtlPw = null; try { // fills the MTL file final String mtlName; if (mtlFile != null) { - final FileOutputStream mtlOs = new FileOutputStream(mtlFile, append); - mtlPw = new PrintWriter(new BufferedOutputStream(mtlOs)); - // writes some comments - if (firstFiles) { - mtlPw.println("# Ardor3D 1.0 MTL file"); - } - final ObjMaterial currentMtl = new ObjMaterial(null); - final MaterialState mtlState = (MaterialState) mesh.getLocalRenderState(StateType.Material); - if (mtlState != null) { - final ReadOnlyColorRGBA ambientColor = mtlState.getAmbient(); - if (ambientColor != null) { - currentMtl.d = ambientColor.getAlpha(); - currentMtl.Ka = new float[] { ambientColor.getRed(), ambientColor.getGreen(), - ambientColor.getBlue(), ambientColor.getAlpha() }; + try (final FileOutputStream mtlOs = new FileOutputStream(mtlFile, append); + final PrintWriter mtlPw = new PrintWriter(new BufferedOutputStream(mtlOs))) { + // writes some comments + if (firstFiles) { + mtlPw.println("# Ardor3D 1.0 MTL file"); } - final ReadOnlyColorRGBA diffuseColor = mtlState.getDiffuse(); - if (diffuseColor != null) { - currentMtl.Kd = new float[] { diffuseColor.getRed(), diffuseColor.getGreen(), - diffuseColor.getBlue(), diffuseColor.getAlpha() }; - } - final ReadOnlyColorRGBA specularColor = mtlState.getSpecular(); - if (specularColor != null) { - currentMtl.Ks = new float[] { specularColor.getRed(), specularColor.getGreen(), - specularColor.getBlue(), specularColor.getAlpha() }; - } - currentMtl.Ns = mtlState.getShininess(); - } - if (customTextureName == null) { - currentMtl.textureName = getLocalMeshTextureName(mesh); - } else { - currentMtl.textureName = customTextureName; - } - if (mesh.getSceneHints().getLightCombineMode() == LightCombineMode.Off) { - // Color on and Ambient off - currentMtl.illumType = 0; - } else { - // Color on and Ambient on - currentMtl.illumType = 1; - } - ObjMaterial sameObjMtl = null; - if (materialList != null && !materialList.isEmpty()) { - for (final ObjMaterial mtl : materialList) { - if (mtl.illumType == currentMtl.illumType && mtl.Ns == currentMtl.Ns - && mtl.forceBlend == currentMtl.forceBlend && mtl.d == currentMtl.d - && Arrays.equals(mtl.Ka, currentMtl.Ka) && Arrays.equals(mtl.Kd, currentMtl.Kd) - && Arrays.equals(mtl.Ks, currentMtl.Ks) - //&& Objects.equals(mtl.textureName, currentMtl.textureName)) { - && mtl.textureName.equals(currentMtl.textureName)) { - sameObjMtl = mtl; - break; + final ObjMaterial currentMtl = new ObjMaterial(null); + final MaterialState mtlState = (MaterialState) mesh.getLocalRenderState(StateType.Material); + if (mtlState != null) { + final ReadOnlyColorRGBA ambientColor = mtlState.getAmbient(); + if (ambientColor != null) { + currentMtl.setAmbientRed(ambientColor.getRed()); + currentMtl.setAmbientGreen(ambientColor.getGreen()); + currentMtl.setAmbientBlue(ambientColor.getBlue()); + currentMtl.setAmbientAlpha(ambientColor.getAlpha()); + } + final ReadOnlyColorRGBA diffuseColor = mtlState.getDiffuse(); + if (diffuseColor != null) { + currentMtl.setDiffuseRed(diffuseColor.getRed()); + currentMtl.setDiffuseGreen(diffuseColor.getGreen()); + currentMtl.setDiffuseBlue(diffuseColor.getBlue()); + currentMtl.setDiffuseAlpha(diffuseColor.getAlpha()); + } + final ReadOnlyColorRGBA specularColor = mtlState.getSpecular(); + if (specularColor != null) { + currentMtl.setSpecularRed(specularColor.getRed()); + currentMtl.setSpecularGreen(specularColor.getGreen()); + currentMtl.setSpecularBlue(specularColor.getBlue()); + currentMtl.setSpecularAlpha(specularColor.getAlpha()); } + currentMtl.setShininess(mtlState.getShininess()); } - } - if (sameObjMtl == null) { - // writes the new material library - mtlName = mtlFile.getName().trim().replaceAll(" ", "") + "_" - + (materialList == null ? 1 : materialList.size() + 1); - if (materialList != null) { - final ObjMaterial mtl = new ObjMaterial(mtlName); - mtl.illumType = currentMtl.illumType; - mtl.textureName = currentMtl.textureName; - materialList.add(mtl); + if (customTextureName == null) { + currentMtl.setTextureName(getLocalMeshTextureName(mesh)); + } else { + currentMtl.setTextureName(customTextureName); } - mtlPw.println("newmtl " + mtlName); - if (currentMtl.Ns != -1) { - mtlPw.println("Ns " + currentMtl.Ns); + if (mesh.getSceneHints().getLightCombineMode() == LightCombineMode.Off) { + // Color on and Ambient off + currentMtl.setIllumType(0); + } else { + // Color on and Ambient on + currentMtl.setIllumType(1); } - if (currentMtl.Ka != null) { - mtlPw.print("Ka"); - for (final float KaCoef : currentMtl.Ka) { - mtlPw.print(" " + KaCoef); + ObjMaterial sameObjMtl = null; + if (materialList != null && !materialList.isEmpty()) { + for (final ObjMaterial mtl : materialList) { + if (mtl.getIllumType() == currentMtl.getIllumType() + && mtl.getShininess() == currentMtl.getShininess() + && mtl.isForceBlend() == currentMtl.isForceBlend() + && mtl.getAmbientRed() == currentMtl.getAmbientRed() + && mtl.getAmbientGreen() == currentMtl.getAmbientGreen() + && mtl.getAmbientBlue() == currentMtl.getAmbientBlue() + && mtl.getAmbientAlpha() == currentMtl.getAmbientAlpha() + && mtl.getDiffuseRed() == currentMtl.getDiffuseRed() + && mtl.getDiffuseGreen() == currentMtl.getDiffuseGreen() + && mtl.getDiffuseBlue() == currentMtl.getDiffuseBlue() + && mtl.getDiffuseAlpha() == currentMtl.getDiffuseAlpha() + && mtl.getSpecularRed() == currentMtl.getSpecularRed() + && mtl.getSpecularGreen() == currentMtl.getSpecularGreen() + && mtl.getSpecularBlue() == currentMtl.getSpecularBlue() + && mtl.getSpecularAlpha() == currentMtl.getSpecularAlpha() + && mtl.getEmissiveRed() == currentMtl.getEmissiveRed() + && mtl.getEmissiveGreen() == currentMtl.getEmissiveGreen() + && mtl.getEmissiveBlue() == currentMtl.getEmissiveBlue() + && mtl.getEmissiveAlpha() == currentMtl.getEmissiveAlpha() + && Objects.equals(mtl.getTextureName(), currentMtl.getTextureName())) { + sameObjMtl = mtl; + break; + } } - mtlPw.println(); } - if (currentMtl.Kd != null) { - mtlPw.print("Kd"); - for (final float KdCoef : currentMtl.Kd) { - mtlPw.print(" " + KdCoef); + if (sameObjMtl == null) { + // writes the new material library + mtlName = mtlFile.getName().trim().replaceAll(" ", "") + "_" + + (materialList == null ? 1 : materialList.size() + 1); + if (materialList != null) { + final ObjMaterial mtl = new ObjMaterial(mtlName); + mtl.setIllumType(currentMtl.getIllumType()); + mtl.setTextureName(currentMtl.getTextureName()); + materialList.add(mtl); } - mtlPw.println(); - } - if (currentMtl.Ks != null) { - mtlPw.print("Ks"); - for (final float KsCoef : currentMtl.Ks) { - mtlPw.print(" " + KsCoef); + mtlPw.println("newmtl " + mtlName); + if (currentMtl.getShininess() != -1) { + mtlPw.println("Ns " + currentMtl.getShininess()); } - mtlPw.println(); - } - if (currentMtl.d != -1) { - mtlPw.println("d " + currentMtl.d); - } - mtlPw.println("illum " + currentMtl.illumType); - if (currentMtl.textureName != null) { - mtlPw.println("map_Kd " + currentMtl.textureName); + if (currentMtl.getAmbientRed() != -1 && currentMtl.getAmbientGreen() != -1 + && currentMtl.getAmbientBlue() != -1) { + mtlPw.println("Ka " + currentMtl.getAmbientRed() + " " + currentMtl.getAmbientGreen() + " " + + currentMtl.getAmbientBlue()); + } + if (currentMtl.getDiffuseRed() != -1 && currentMtl.getDiffuseGreen() != -1 + && currentMtl.getDiffuseBlue() != -1) { + mtlPw.println("Kd " + currentMtl.getDiffuseRed() + " " + currentMtl.getDiffuseGreen() + " " + + currentMtl.getDiffuseBlue()); + } + if (currentMtl.getSpecularRed() != -1 && currentMtl.getSpecularGreen() != -1 + && currentMtl.getSpecularBlue() != -1) { + mtlPw.println("Ks " + currentMtl.getSpecularRed() + " " + currentMtl.getSpecularGreen() + + " " + currentMtl.getSpecularBlue()); + } + // exports only a consistent dissolve value when all alpha components are equal + if (currentMtl.getAmbientAlpha() != -1 + && currentMtl.getAmbientAlpha() == currentMtl.getDiffuseAlpha() + && currentMtl.getDiffuseAlpha() == currentMtl.getSpecularAlpha() + && currentMtl.getSpecularAlpha() == currentMtl.getEmissiveAlpha()) { + mtlPw.println("d " + currentMtl.getAmbientAlpha()); + } + mtlPw.println("illum " + currentMtl.getIllumType()); + if (currentMtl.getTextureName() != null) { + mtlPw.println("map_Kd " + currentMtl.getTextureName()); + } + } else { + mtlName = sameObjMtl.getName(); } - } else { - mtlName = sameObjMtl.getName(); } } else { mtlName = null; } - final FileOutputStream objOs = new FileOutputStream(objFile, append); - objPw = new PrintWriter(new BufferedOutputStream(objOs)); - // writes some comments - if (firstFiles) { - objPw.println("# Ardor3D 1.0 OBJ file"); - objPw.println("# www.ardor3d.com"); - // writes the material file name if any + try (final FileOutputStream objOs = new FileOutputStream(objFile, append); + final PrintWriter objPw = new PrintWriter(new BufferedOutputStream(objOs))) { + // writes some comments + if (firstFiles) { + objPw.println("# Ardor3D 1.0 OBJ file"); + objPw.println("# www.ardor3d.com"); + // writes the material file name if any + if (mtlFile != null) { + final String mtlLibFilename = mtlFile.getName(); + objPw.println("mtllib " + mtlLibFilename); + } + } + // writes the object name + final String objName; + String meshName = mesh.getName(); + // removes all spaces from the mesh name + if (meshName != null && !meshName.isEmpty()) { + meshName = meshName.trim().replaceAll(" ", ""); + } + if (meshName != null && !meshName.isEmpty()) { + objName = meshName; + } else { + objName = "obj_mesh" + mesh.hashCode(); + } + objPw.println("o " + objName); + final MeshData meshData = mesh.getMeshData(); + // writes the coordinates + final FloatBufferData verticesData = meshData.getVertexCoords(); + if (verticesData == null) { + throw new IllegalArgumentException("cannot export a mesh with no vertices"); + } + final int expectedTupleCount = verticesData.getTupleCount(); + saveFloatBufferData(verticesData, objPw, "v", expectedTupleCount); + final FloatBufferData texCoordsData = meshData.getTextureCoords(0); + saveFloatBufferData(texCoordsData, objPw, "vt", expectedTupleCount); + final FloatBufferData normalsData = meshData.getNormalCoords(); + saveFloatBufferData(normalsData, objPw, "vn", expectedTupleCount); + // writes the used material library if (mtlFile != null) { - final String mtlLibFilename = mtlFile.getName(); - objPw.println("mtllib " + mtlLibFilename); + objPw.println("usemtl " + mtlName); } - } - // writes the object name - final String objName; - String meshName = mesh.getName(); - // removes all spaces from the mesh name - if (meshName != null && !meshName.isEmpty()) { - meshName = meshName.trim().replaceAll(" ", ""); - } - if (meshName != null && !meshName.isEmpty()) { - objName = meshName; - } else { - objName = "obj_mesh" + mesh.hashCode(); - } - objPw.println("o " + objName); - final MeshData meshData = mesh.getMeshData(); - // writes the coordinates - final FloatBufferData verticesData = meshData.getVertexCoords(); - if (verticesData == null) { - throw new IllegalArgumentException("cannot export a mesh with no vertices"); - } - final int expectedTupleCount = verticesData.getTupleCount(); - saveFloatBufferData(verticesData, objPw, "v", expectedTupleCount); - final FloatBufferData texCoordsData = meshData.getTextureCoords(0); - saveFloatBufferData(texCoordsData, objPw, "vt", expectedTupleCount); - final FloatBufferData normalsData = meshData.getNormalCoords(); - saveFloatBufferData(normalsData, objPw, "vn", expectedTupleCount); - // writes the used material library - if (mtlFile != null) { - objPw.println("usemtl " + mtlName); - } - // writes the faces - for (int sectionIndex = 0; sectionIndex < meshData.getSectionCount(); sectionIndex++) { - final IndexMode indexMode = meshData.getIndexMode(sectionIndex); - final int[] indices = new int[indexMode.getVertexCount()]; - switch (indexMode) { - case TriangleFan: - case Triangles: - case TriangleStrip: - case Quads: - for (int primIndex = 0, primCount = meshData.getPrimitiveCount(sectionIndex); primIndex < primCount; primIndex++) { - meshData.getPrimitiveIndices(primIndex, sectionIndex, indices); - objPw.print("f"); - for (int vertexIndex = 0; vertexIndex < indices.length; vertexIndex++) { - // indices start at 1 in the WaveFront OBJ format whereas indices start at 0 in - // Ardor3D - final int shiftedIndex = indices[vertexIndex] + 1 + firstVertexIndex; - // vertex index - objPw.print(" " + shiftedIndex); - // texture coordinate index - if (texCoordsData != null) { - objPw.print("/" + shiftedIndex); - } - // normal coordinate index - if (normalsData != null) { - objPw.print("/" + shiftedIndex); + // writes the faces + for (int sectionIndex = 0; sectionIndex < meshData.getSectionCount(); sectionIndex++) { + final IndexMode indexMode = meshData.getIndexMode(sectionIndex); + final int[] indices = new int[indexMode.getVertexCount()]; + switch (indexMode) { + case TriangleFan: + case Triangles: + case TriangleStrip: + case Quads: + for (int primIndex = 0, primCount = meshData + .getPrimitiveCount(sectionIndex); primIndex < primCount; primIndex++) { + meshData.getPrimitiveIndices(primIndex, sectionIndex, indices); + objPw.print("f"); + for (int vertexIndex = 0; vertexIndex < indices.length; vertexIndex++) { + // indices start at 1 in the WaveFront OBJ format whereas indices start at 0 in + // Ardor3D + final int shiftedIndex = indices[vertexIndex] + 1 + firstVertexIndex; + // vertex index + objPw.print(" " + shiftedIndex); + // texture coordinate index + if (texCoordsData != null) { + objPw.print("/" + shiftedIndex); + } + // normal coordinate index + if (normalsData != null) { + objPw.print("/" + shiftedIndex); + } } + objPw.println(); } - objPw.println(); - } - break; - default: - throw new IllegalArgumentException("index mode " + indexMode + " not supported"); + break; + default: + throw new IllegalArgumentException("index mode " + indexMode + " not supported"); + } } } } catch (final Throwable t) { throw new Error("Unable to save the mesh into an obj", t); - } finally { - if (objPw != null) { - objPw.flush(); - objPw.close(); - } - if (mtlPw != null) { - mtlPw.flush(); - mtlPw.close(); - } } } @@ -357,25 +367,30 @@ public class ObjExporter { final TextureState textureState = (TextureState) mesh.getLocalRenderState(StateType.Texture); if (textureState.isEnabled() && textureState.getTexture() != null) { final TextureKey tKey = textureState.getTexture().getTextureKey(); - final String tmpTextureName = tKey.getSource().getName(); - final int lastIndexOfUnixPathSeparator = tmpTextureName.lastIndexOf('/'); - final int lastIndexOfWindowsPathSeparator = tmpTextureName.lastIndexOf('\\'); - if (lastIndexOfUnixPathSeparator != -1) { - textureName = tmpTextureName.substring(lastIndexOfUnixPathSeparator + 1); - } else { - if (lastIndexOfWindowsPathSeparator != -1) { - textureName = tmpTextureName.substring(lastIndexOfWindowsPathSeparator + 1); + if (tKey != null && tKey.getSource() != null) { + final String tmpTextureName = tKey.getSource().getName(); + final int lastIndexOfUnixPathSeparator = tmpTextureName.lastIndexOf('/'); + final int lastIndexOfWindowsPathSeparator = tmpTextureName.lastIndexOf('\\'); + if (lastIndexOfUnixPathSeparator != -1) { + textureName = tmpTextureName.substring(lastIndexOfUnixPathSeparator + 1); } else { - textureName = tmpTextureName; + if (lastIndexOfWindowsPathSeparator != -1) { + textureName = tmpTextureName.substring(lastIndexOfWindowsPathSeparator + 1); + } else { + textureName = tmpTextureName; + } + } + if (tKey.isFlipped()) { + ObjExporter.logger.warning("The texture " + tmpTextureName + + " will have to be flipped manually when loading this OBJ file"); + } else { + ObjExporter.logger.warning("The texture " + tmpTextureName + + " might need to be flipped manually when loading this OBJ file"); } - } - if (tKey.isFlipped()) { - ObjExporter.logger.warning("The texture " + tmpTextureName - + " will have to be flipped manually when loading this OBJ file"); } else { - ObjExporter.logger.warning("The texture " + tmpTextureName - + " might need to be flipped manually when loading this OBJ file"); + textureName = null; } + } else { textureName = null; } @@ -394,9 +409,9 @@ public class ObjExporter { final int tupleSize = data.getValuesPerTuple(); final int tupleCount = data.getTupleCount(); if (tupleCount < expectedTupleCount) { - throw new IllegalArgumentException("[" + keyword - + "] not enough data to match with the vertex count: " + tupleCount + " < " - + expectedTupleCount); + throw new IllegalArgumentException( + "[" + keyword + "] not enough data to match with the vertex count: " + tupleCount + " < " + + expectedTupleCount); } else { if (tupleCount > expectedTupleCount) { ObjExporter.logger.warning("[" + keyword + "] too much data to match with the vertex count: " diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjGeometryStore.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjGeometryStore.java index bd96824..aced00f 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjGeometryStore.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjGeometryStore.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * @@ -12,7 +12,9 @@ package com.ardor3d.extension.model.obj; import java.nio.Buffer; import java.nio.FloatBuffer; +import java.util.ArrayList; import java.util.EnumSet; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -33,8 +35,6 @@ import com.ardor3d.util.geom.BufferUtils; import com.ardor3d.util.geom.GeometryTool; import com.ardor3d.util.geom.GeometryTool.MatchCondition; import com.ardor3d.util.geom.VertGroupData; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; public class ObjGeometryStore { private static final String DEFAULT_GROUP = "_default_"; @@ -45,7 +45,7 @@ public class ObjGeometryStore { private int _totalLines = 0; private int _totalMeshes = 0; private final Node _root = new Node(); - private final Map<String, Spatial> _groupMap = Maps.newHashMap(); + private final Map<String, Spatial> _groupMap = new HashMap<>(); private ObjMaterial _currentMaterial = new ObjMaterial("default"); private String _currentObjectName; @@ -55,11 +55,18 @@ public class ObjGeometryStore { private ObjSetManager _lineManager; private ObjSetManager _pointManager; - private final Map<String, ObjMaterial> materialLibrary = Maps.newHashMap(); - private final Map<Spatial, String> _materialMap = Maps.newHashMap(); + private final Map<String, ObjMaterial> materialLibrary = new HashMap<>(); + private final Map<Spatial, String> _materialMap = new HashMap<>(); + + private final GeometryTool _geometryTool; public ObjGeometryStore() { + this(new GeometryTool()); + } + + public ObjGeometryStore(final GeometryTool geometryTool) { super(); + _geometryTool = geometryTool; } public Map<String, ObjMaterial> getMaterialLibrary() { @@ -185,14 +192,14 @@ public class ObjGeometryStore { } final Point points = new Point(name, vertices, null, null, null); - final IndexBufferData<? extends Buffer> indexBuffer = BufferUtils.createIndexBufferData(_pointManager - .getIndices().size(), vertices.length - 1); + final IndexBufferData<? extends Buffer> indexBuffer = BufferUtils + .createIndexBufferData(_pointManager.getIndices().size(), vertices.length - 1); for (final int index : _pointManager.getIndices()) { indexBuffer.put(index); } points.getMeshData().setIndices(indexBuffer); - GeometryTool.minimizeVerts(points, EnumSet.noneOf(MatchCondition.class)); + _geometryTool.minimizeVerts(points, EnumSet.noneOf(MatchCondition.class)); applyCurrentMaterial(points); mapToGroups(points); @@ -226,8 +233,8 @@ public class ObjGeometryStore { } final Line line = new Line(name, vertices, null, null, hasUVs ? uvs : null); - final IndexBufferData<? extends Buffer> indexBuffer = BufferUtils.createIndexBufferData(_lineManager - .getIndices().size(), vertices.length - 1); + final IndexBufferData<? extends Buffer> indexBuffer = BufferUtils + .createIndexBufferData(_lineManager.getIndices().size(), vertices.length - 1); for (final int index : _lineManager.getIndices()) { indexBuffer.put(index); } @@ -240,7 +247,7 @@ public class ObjGeometryStore { } line.getMeshData().setIndexLengths(lengths); } - GeometryTool.minimizeVerts(line, EnumSet.of(MatchCondition.UVs)); + _geometryTool.minimizeVerts(line, EnumSet.of(MatchCondition.UVs)); applyCurrentMaterial(line); mapToGroups(line); @@ -267,7 +274,7 @@ public class ObjGeometryStore { int j = 0; final long[] vertGroups = new long[_meshManager.getStore().size()]; - final List<Long> groups = Lists.newArrayList(); + final List<Long> groups = new ArrayList<>(); Vector3 vector; for (final ObjIndexSet set : _meshManager.getStore().keySet()) { vertGroups[j] = set.getSmoothGroup(); @@ -302,8 +309,8 @@ public class ObjGeometryStore { mesh.getMeshData().setTextureBuffer(uvs, 0); } - final IndexBufferData<? extends Buffer> indexBuffer = BufferUtils.createIndexBufferData(_meshManager - .getIndices().size(), _meshManager.getStore().size() - 1); + final IndexBufferData<? extends Buffer> indexBuffer = BufferUtils + .createIndexBufferData(_meshManager.getIndices().size(), _meshManager.getStore().size() - 1); for (final int index : _meshManager.getIndices()) { indexBuffer.put(index); } @@ -319,7 +326,7 @@ public class ObjGeometryStore { groupData.setVertGroups(vertGroups); groupData.setGroupConditions(VertGroupData.DEFAULT_GROUP, EnumSet.of(MatchCondition.Normal, MatchCondition.UVs)); - GeometryTool.minimizeVerts(mesh, groupData); + _geometryTool.minimizeVerts(mesh, groupData); applyCurrentMaterial(mesh); mapToGroups(mesh); @@ -349,7 +356,7 @@ public class ObjGeometryStore { target.getSceneHints().setRenderBucketType(RenderBucketType.Transparent); } - if (_currentMaterial.illumType == 0) { + if (_currentMaterial.getIllumType() == 0) { target.getSceneHints().setLightCombineMode(LightCombineMode.Off); } diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjImporter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjImporter.java index 4328e19..620ae55 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjImporter.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjImporter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * @@ -12,6 +12,7 @@ package com.ardor3d.extension.model.obj; import java.io.BufferedReader; import java.io.InputStreamReader; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.logging.Logger; @@ -21,10 +22,10 @@ import com.ardor3d.image.TextureStoreFormat; import com.ardor3d.math.MathUtils; import com.ardor3d.math.Vector3; import com.ardor3d.util.TextureManager; +import com.ardor3d.util.geom.GeometryTool; import com.ardor3d.util.resource.ResourceLocator; import com.ardor3d.util.resource.ResourceLocatorTool; import com.ardor3d.util.resource.ResourceSource; -import com.google.common.collect.Lists; /** * Wavefront OBJ importer. See <a href="http://local.wasp.uwa.edu.au/~pbourke/dataformats/obj/">the format spec</a> @@ -48,24 +49,20 @@ public class ObjImporter { return _loadTextures; } - public ObjImporter setLoadTextures(final boolean loadTextures) { + public void setLoadTextures(final boolean loadTextures) { _loadTextures = loadTextures; - return this; } - public ObjImporter setTextureLocator(final ResourceLocator locator) { + public void setTextureLocator(final ResourceLocator locator) { _textureLocator = locator; - return this; } - public ObjImporter setModelLocator(final ResourceLocator locator) { + public void setModelLocator(final ResourceLocator locator) { _modelLocator = locator; - return this; } - public ObjImporter setMaterialLocator(final ResourceLocator locator) { + public void setMaterialLocator(final ResourceLocator locator) { _materialLocator = locator; - return this; } public void setFlipTextureVertically(final boolean flipTextureVertically) { @@ -108,6 +105,19 @@ public class ObjImporter { * @return an ObjGeometryStore data object containing the scene and other useful elements. */ public ObjGeometryStore load(final String resource) { + return load(resource, new GeometryTool()); + } + + /** + * Reads a Wavefront OBJ file from the given resource + * + * @param resource + * the name of the resource to find. + * @param geometryTool + * the geometry tool used to minimize the vertex count. + * @return an ObjGeometryStore data object containing the scene and other useful elements. + */ + public ObjGeometryStore load(final String resource, final GeometryTool geometryTool) { final ResourceSource source; if (_modelLocator == null) { source = ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_MODEL, resource); @@ -119,7 +129,7 @@ public class ObjImporter { throw new Error("Unable to locate '" + resource + "'"); } - return load(source); + return load(source, geometryTool); } /** @@ -130,11 +140,22 @@ public class ObjImporter { * @return an ObjGeometryStore data object containing the scene and other useful elements. */ public ObjGeometryStore load(final ResourceSource resource) { - try { - final ObjGeometryStore store = new ObjGeometryStore(); - long currentSmoothGroup = -1; + return load(resource, new GeometryTool()); + } - final BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream())); + /** + * Reads a Wavefront OBJ file from the given resource + * + * @param resource + * the name of the resource to find. + * @param geometryTool + * the geometry tool used to minimize the vertex count. + * @return an ObjGeometryStore data object containing the scene and other useful elements. + */ + public ObjGeometryStore load(final ResourceSource resource, final GeometryTool geometryTool) { + try (final BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream()))) { + final ObjGeometryStore store = new ObjGeometryStore(geometryTool); + long currentSmoothGroup = -1; String line; int lineNo = 0; while ((line = reader.readLine()) != null) { @@ -204,7 +225,7 @@ public class ObjImporter { else if ("cstype".equals(keyword)) { // TODO: Add support for cstype ObjImporter.logger - .warning("ObjModelImporter: cstype not supported. (line " + lineNo + ") " + line); + .warning("ObjModelImporter: cstype not supported. (line " + lineNo + ") " + line); } // if degree @@ -232,7 +253,7 @@ public class ObjImporter { if (tokens.length < 2) { store.setCurrentGroupNames(null); continue; - // throw new Error("wrong number of args. g must have at least 1 argument. (line " + lineNo + // throw new Error("wrong number of args. g must have at least 1 argument. (line " + lineNo // + ") " + line); } @@ -245,7 +266,8 @@ public class ObjImporter { // if smoothing group else if ("s".equals(keyword)) { if (tokens.length != 2) { - throw new Error("wrong number of args. s must have 1 argument. (line " + lineNo + ") " + line); + throw new Error( + "wrong number of args. s must have 1 argument. (line " + lineNo + ") " + line); } if ("off".equalsIgnoreCase(tokens[1])) { @@ -264,7 +286,8 @@ public class ObjImporter { // if object name else if ("o".equals(keyword)) { if (tokens.length < 2) { - throw new Error("wrong number of args. o must have 1 argument. (line " + lineNo + ") " + line); + throw new Error( + "wrong number of args. o must have 1 argument. (line " + lineNo + ") " + line); } store.setCurrentObjectName(tokens[1]); } @@ -287,8 +310,8 @@ public class ObjImporter { // if use material command else if ("usemtl".equals(keyword)) { if (tokens.length != 2) { - throw new Error("wrong number of args. usemtl must have 1 argument. (line " + lineNo + ") " - + line); + throw new Error( + "wrong number of args. usemtl must have 1 argument. (line " + lineNo + ") " + line); } // set new material @@ -300,12 +323,12 @@ public class ObjImporter { // if point else if ("p".equals(keyword) && tokens.length > 1) { if (tokens.length < 2) { - throw new Error("wrong number of args. p must have at least 1 vertex. (line " + lineNo + ") " - + line); + throw new Error( + "wrong number of args. p must have at least 1 vertex. (line " + lineNo + ") " + line); } // Each token corresponds to 1 vertex entry - final List<ObjIndexSet> indices = Lists.newArrayList(); + final List<ObjIndexSet> indices = new ArrayList<>(); for (int i = 1; i < tokens.length; i++) { indices.add(new ObjIndexSet(tokens[i], store.getDataStore(), currentSmoothGroup)); } @@ -320,7 +343,7 @@ public class ObjImporter { } // Each token corresponds to 1 vertex entry and possibly one texture entry - final List<ObjIndexSet> indices = Lists.newArrayList(); + final List<ObjIndexSet> indices = new ArrayList<>(); for (int i = 1; i < tokens.length; i++) { indices.add(new ObjIndexSet(tokens[i], store.getDataStore(), currentSmoothGroup)); } @@ -335,7 +358,7 @@ public class ObjImporter { } // Each token corresponds to 1 vertex entry and possibly one texture entry and normal entry. - final List<ObjIndexSet> indices = Lists.newArrayList(); + final List<ObjIndexSet> indices = new ArrayList<>(); for (int i = 1; i < tokens.length; i++) { indices.add(new ObjIndexSet(tokens[i], store.getDataStore(), currentSmoothGroup)); } @@ -364,8 +387,8 @@ public class ObjImporter { store.commitObjects(); store.cleanup(); return store; - } catch (final Exception e) { - throw new Error("Unable to load obj resource from URL: " + resource, e); + } catch (final Throwable t) { + throw new Error("Unable to load obj resource from URL: " + resource, t); } } @@ -396,7 +419,7 @@ public class ObjImporter { } /** - * Load a .mtl resource + * Load a .mtl resource, see <a href="http://paulbourke.net/dataformats/mtl/">the format specification</a> * * @param resource * the mtl file to load, as a ResourceSource @@ -453,58 +476,98 @@ public class ObjImporter { // if ambient value if ("Ka".equals(keyword)) { - currentMaterial.Ka = new float[] { Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2]), - Float.parseFloat(tokens[3]) }; + currentMaterial.setAmbientRed(Float.parseFloat(tokens[1])); + currentMaterial.setAmbientGreen(Float.parseFloat(tokens[2])); + currentMaterial.setAmbientBlue(Float.parseFloat(tokens[3])); } // if diffuse value else if ("Kd".equals(keyword)) { - currentMaterial.Kd = new float[] { Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2]), - Float.parseFloat(tokens[3]) }; + currentMaterial.setDiffuseRed(Float.parseFloat(tokens[1])); + currentMaterial.setDiffuseGreen(Float.parseFloat(tokens[2])); + currentMaterial.setDiffuseBlue(Float.parseFloat(tokens[3])); } // if specular value else if ("Ks".equals(keyword)) { - currentMaterial.Ks = new float[] { Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2]), - Float.parseFloat(tokens[3]) }; + currentMaterial.setSpecularRed(Float.parseFloat(tokens[1])); + currentMaterial.setSpecularGreen(Float.parseFloat(tokens[2])); + currentMaterial.setSpecularBlue(Float.parseFloat(tokens[3])); + } + // if transmission filter + else if ("Tf".equals(keyword)) { + // TODO: Add support for Tf + } + // if sharpness value + else if ("sharpness".equals(keyword)) { + // TODO: Add support for sharpness + } + // if optical density + else if ("Ni".equals(keyword)) { + // TODO: Add support for Ni + } + // if disp + else if ("disp".equals(keyword)) { + // TODO: Add support for disp + } + // if decal value + else if ("decal".equals(keyword)) { + // TODO: Add support for decal + } + // if bump + else if ("bump".equals(keyword)) { + // TODO: Add support for bump } - // if illumination style else if ("illum".equals(keyword)) { - currentMaterial.illumType = Integer.parseInt(tokens[1]); + currentMaterial.setIllumType(Integer.parseInt(tokens[1])); } // if "dissolve" (alpha) value else if ("d".equals(keyword)) { - currentMaterial.d = Float.parseFloat(tokens[1]); + final float d; + if ("-halo".equalsIgnoreCase(tokens[1])) { + // TODO: Add support for halo + d = Float.parseFloat(tokens[2]); + } else { + d = Float.parseFloat(tokens[1]); + } + currentMaterial.setAmbientAlpha(d); + currentMaterial.setDiffuseAlpha(d); + currentMaterial.setSpecularAlpha(d); + currentMaterial.setEmissiveAlpha(d); } // if ambient value else if ("Ns".equals(keyword)) { final float Ns = Float.parseFloat(tokens[1]); - currentMaterial.Ns = 128 * MathUtils.clamp(Ns, 0, _specularMax) / _specularMax; + currentMaterial.setShininess(128 * MathUtils.clamp(Ns, 0, _specularMax) / _specularMax); } // if we mapped a texture to alpha else if ("map_d".equals(keyword)) { // force blending... probably also used texture in map_Kd, etc. - currentMaterial.forceBlend = true; + currentMaterial.setForceBlend(true); } // if texture else if (isLoadTextures() && "map_Kd".equals(keyword)) { // TODO: it's possible for map_Kd to have arguments, then filename. final String textureName = line.substring("map_Kd".length()).trim(); - currentMaterial.textureName = textureName; + currentMaterial.setTextureName(textureName); if (_textureLocator == null) { - currentMaterial.map_Kd = TextureManager.load(textureName, getMinificationFilter(), - isUseCompression() ? TextureStoreFormat.GuessCompressedFormat - : TextureStoreFormat.GuessNoCompressedFormat, isFlipTextureVertically()); + currentMaterial + .setMap_Kd(TextureManager.load(textureName, getMinificationFilter(), + isUseCompression() ? TextureStoreFormat.GuessCompressedFormat + : TextureStoreFormat.GuessNoCompressedFormat, + isFlipTextureVertically())); } else { final ResourceSource source = _textureLocator.locateResource(textureName); - currentMaterial.map_Kd = TextureManager.load(source, getMinificationFilter(), - isUseCompression() ? TextureStoreFormat.GuessCompressedFormat - : TextureStoreFormat.GuessNoCompressedFormat, isFlipTextureVertically()); + currentMaterial + .setMap_Kd(TextureManager.load(source, getMinificationFilter(), + isUseCompression() ? TextureStoreFormat.GuessCompressedFormat + : TextureStoreFormat.GuessNoCompressedFormat, + isFlipTextureVertically())); } } } diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjIndexSet.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjIndexSet.java index 0e2bec1..ffd7576 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjIndexSet.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjIndexSet.java @@ -1,15 +1,17 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * - * Ardor3D is free software: you can redistribute it and/or modify it + * 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.extension.model.obj; +import java.util.Objects; + public class ObjIndexSet { private final int _vIndex, _vtIndex; private final long _sGroup; @@ -64,12 +66,8 @@ public class ObjIndexSet { @Override public int hashCode() { - int result = 17; - result += 31 * result + _vIndex; - result += 31 * result + _vtIndex; - result += 31 * result + _vnIndex; - result += 31 * result + _sGroup; - return result; + return Objects.hash(Integer.valueOf(getVIndex()), Integer.valueOf(getVtIndex()), Integer.valueOf(getVnIndex()), + Long.valueOf(getSmoothGroup())); } @Override diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjMaterial.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjMaterial.java index f060cec..ccb8a46 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjMaterial.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjMaterial.java @@ -1,54 +1,37 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * - * Ardor3D is free software: you can redistribute it and/or modify it + * 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.extension.model.obj; +import com.ardor3d.extension.model.util.AbstractMaterial; import com.ardor3d.image.Texture; -import com.ardor3d.math.ColorRGBA; -import com.ardor3d.math.MathUtils; -import com.ardor3d.renderer.state.BlendState; -import com.ardor3d.renderer.state.MaterialState; import com.ardor3d.renderer.state.TextureState; -public class ObjMaterial { +/** + * WaveFront OBJ material (MTL). <code>Ns</code> matches with the shininess, <code>d</code> matches with the alpha + * component(s), <code>Ka</code> matches with the ambient RGB components, <code>Kd</code> matches with the diffuse RGB + * components, <code>Ks</code> matches with the specular RGB components. + */ +public class ObjMaterial extends AbstractMaterial { private final String name; - float[] Ka = null; - float[] Kd = null; - float[] Ks = null; - float Ns = -1; - - String textureName; - Texture map_Kd = null; + private String textureName; - int illumType = 2; + private Texture map_Kd; - boolean forceBlend = false; - float d = -1; + private int illumType; public ObjMaterial(final String name) { + super(); this.name = name; - } - - public BlendState getBlendState() { - if (forceBlend || d != -1 && d < 1.0f) { - final BlendState blend = new BlendState(); - blend.setBlendEnabled(true); - blend.setSourceFunction(BlendState.SourceFunction.SourceAlpha); - blend.setDestinationFunction(BlendState.DestinationFunction.OneMinusSourceAlpha); - blend.setTestEnabled(true); - blend.setTestFunction(BlendState.TestFunction.GreaterThan); - blend.setReference(0); - return blend; - } - return null; + illumType = 2; } public TextureState getTextureState() { @@ -60,29 +43,6 @@ public class ObjMaterial { return null; } - public MaterialState getMaterialState() { - if (Ka != null || Kd != null || Ks != null || d != -1 || Ns != -1) { - final MaterialState material = new MaterialState(); - final float alpha = d != -1 ? MathUtils.clamp(d, 0, 1) : 1; - if (Ka != null) { - material.setAmbient(new ColorRGBA(Ka[0], Ka[1], Ka[2], alpha)); - } - if (Kd != null) { - material.setDiffuse(new ColorRGBA(Kd[0], Kd[1], Kd[2], alpha)); - } - if (Ks != null) { - material.setSpecular(new ColorRGBA(Ks[0], Ks[1], Ks[2], alpha)); - } - - if (Ns != -1) { - material.setShininess(Ns); - } - - return material; - } - return null; - } - public String getName() { return name; } @@ -91,7 +51,23 @@ public class ObjMaterial { return textureName; } + public void setTextureName(final String textureName) { + this.textureName = textureName; + } + public Texture getMap_Kd() { return map_Kd; } + + public void setMap_Kd(final Texture map_Kd) { + this.map_Kd = map_Kd; + } + + public int getIllumType() { + return illumType; + } + + public void setIllumType(final int illumType) { + this.illumType = illumType; + } } diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjSetManager.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjSetManager.java index 116371d..e5abb85 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjSetManager.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/obj/ObjSetManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * @@ -10,16 +10,15 @@ package com.ardor3d.extension.model.obj; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - public class ObjSetManager { - private final Map<ObjIndexSet, Integer> _store = Maps.newLinkedHashMap(); - private final List<Integer> _indices = Lists.newArrayList(); - private final List<Integer> _lengths = Lists.newArrayList(); + private final Map<ObjIndexSet, Integer> _store = new LinkedHashMap<>(); + private final List<Integer> _indices = new ArrayList<>(); + private final List<Integer> _lengths = new ArrayList<>(); public int findSet(final ObjIndexSet set) { if (_store.containsKey(set)) { diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyDataStore.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyDataStore.java new file mode 100644 index 0000000..14cd03b --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyDataStore.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2008-2014 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.extension.model.ply; + +import java.util.ArrayList; +import java.util.List; + +import com.ardor3d.math.ColorRGBA; +import com.ardor3d.math.Vector2; +import com.ardor3d.math.Vector3; + +public class PlyDataStore { + + private final List<Vector3> _vertices; + private final List<Vector3> _normals; + private final List<ColorRGBA> _colors; + private final List<Vector2> _textureCoordinates; + + public PlyDataStore() { + super(); + _vertices = new ArrayList<>(); + _normals = new ArrayList<>(); + _colors = new ArrayList<>(); + _textureCoordinates = new ArrayList<>(); + } + + public List<Vector3> getVertices() { + return _vertices; + } + + public List<Vector3> getNormals() { + return _normals; + } + + public List<ColorRGBA> getColors() { + return _colors; + } + + public List<Vector2> getTextureCoordinates() { + return _textureCoordinates; + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyEdgeInfo.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyEdgeInfo.java new file mode 100644 index 0000000..e4f84f6 --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyEdgeInfo.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2008-2014 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.extension.model.ply; + +import com.ardor3d.math.ColorRGBA; + +public class PlyEdgeInfo { + + private Integer index1; + + private Integer index2; + + private ColorRGBA color; + + public PlyEdgeInfo() { + super(); + } + + public Integer getIndex1() { + return index1; + } + + public void setIndex1(final Integer index1) { + this.index1 = index1; + } + + public Integer getIndex2() { + return index2; + } + + public void setIndex2(final Integer index2) { + this.index2 = index2; + } + + public ColorRGBA getColor() { + return color; + } + + public void setColor(final ColorRGBA color) { + this.color = color; + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyFaceInfo.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyFaceInfo.java new file mode 100644 index 0000000..75fc25d --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyFaceInfo.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2008-2014 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.extension.model.ply; + +import java.util.ArrayList; +import java.util.List; + +public class PlyFaceInfo { + + private List<Integer> _vertexIndices; + + private List<Integer> _materialIndices; + + private List<Float> _textureCoordinates; + + public PlyFaceInfo() { + super(); + } + + public void addVertexIndex(final int vertexIndex) { + if (_vertexIndices == null) { + _vertexIndices = new ArrayList<>(); + } + _vertexIndices.add(Integer.valueOf(vertexIndex)); + } + + public List<Integer> getVertexIndices() { + return _vertexIndices; + } + + public void addMaterialIndex(final int materialIndex) { + if (_materialIndices == null) { + _materialIndices = new ArrayList<>(); + } + _materialIndices.add(Integer.valueOf(materialIndex)); + } + + public List<Integer> getMaterialIndices() { + return _materialIndices; + } + + public void addTextureCoordinate(final float textureCoordinate) { + if (_textureCoordinates == null) { + _textureCoordinates = new ArrayList<>(); + } + _textureCoordinates.add(Float.valueOf(textureCoordinate)); + } + + public List<Float> getTextureCoordinates() { + return _textureCoordinates; + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyGeometryStore.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyGeometryStore.java new file mode 100644 index 0000000..624665d --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyGeometryStore.java @@ -0,0 +1,313 @@ +/** + * Copyright (c) 2008-2014 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.extension.model.ply; + +import java.nio.Buffer; +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.ardor3d.image.Texture; +import com.ardor3d.math.ColorRGBA; +import com.ardor3d.math.Vector2; +import com.ardor3d.math.Vector3; +import com.ardor3d.math.type.ReadOnlyColorRGBA; +import com.ardor3d.renderer.IndexMode; +import com.ardor3d.renderer.state.TextureState; +import com.ardor3d.scenegraph.IndexBufferData; +import com.ardor3d.scenegraph.Line; +import com.ardor3d.scenegraph.Mesh; +import com.ardor3d.scenegraph.Node; +import com.ardor3d.util.geom.BufferUtils; +import com.ardor3d.util.geom.GeometryTool; +import com.ardor3d.util.geom.GeometryTool.MatchCondition; + +public class PlyGeometryStore { + + private static final Logger LOGGER = Logger.getLogger(PlyGeometryStore.class.getName()); + + private int _totalMeshes = 0; + + private int _totalLines = 0; + + private final PlyDataStore _dataStore; + + private final Node _root; + + private final List<PlyMaterial> _materialLibrary; + + private List<PlyFaceInfo> _plyFaceInfoList; + + private List<PlyEdgeInfo> _plyEdgeInfoList; + + private Texture _texture; + + private String _textureName; + + private final GeometryTool _geometryTool; + + public PlyGeometryStore() { + this(new GeometryTool()); + } + + public PlyGeometryStore(final GeometryTool geometryTool) { + super(); + _dataStore = new PlyDataStore(); + _root = new Node(); + _materialLibrary = new ArrayList<>(); + _geometryTool = geometryTool; + } + + public PlyDataStore getDataStore() { + return _dataStore; + } + + public Node getScene() { + return _root; + } + + public List<PlyMaterial> getMaterialLibrary() { + return _materialLibrary; + } + + public String getTextureName() { + return _textureName; + } + + public void setTextureName(final String textureName) { + _textureName = textureName; + } + + public Texture getTexture() { + return _texture; + } + + public void setTexture(final Texture texture) { + _texture = texture; + } + + public TextureState getTextureState() { + if (_texture != null) { + final TextureState tState = new TextureState(); + tState.setTexture(_texture, 0); + return tState; + } + return null; + } + + void addLine(final PlyEdgeInfo edgeInfo) { + if (_plyEdgeInfoList == null) { + _plyEdgeInfoList = new ArrayList<>(); + } + _plyEdgeInfoList.add(edgeInfo); + } + + void addFace(final PlyFaceInfo faceInfo) { + if (_plyFaceInfoList == null) { + _plyFaceInfoList = new ArrayList<>(); + } + _plyFaceInfoList.add(faceInfo); + } + + @SuppressWarnings("null") + void commitObjects() { + if (_plyEdgeInfoList != null) { + final String name = "ply_line" + _totalLines; + boolean hasColors = false; + final boolean hasNormals = _dataStore.getNormals() != null && !_dataStore.getNormals().isEmpty(); + final int vertexCount = _plyEdgeInfoList.size() * 2; + final Vector3[] vertices = new Vector3[vertexCount]; + final Vector3[] normals = hasNormals ? null : new Vector3[vertexCount]; + ReadOnlyColorRGBA[] colors = null; + final IndexBufferData<? extends Buffer> indices = BufferUtils.createIndexBufferData(vertexCount, + vertexCount - 1); + int edgeVertexIndex = 0; + for (final PlyEdgeInfo plyEdgeInfo : _plyEdgeInfoList) { + indices.put(edgeVertexIndex).put(edgeVertexIndex + 1); + vertices[edgeVertexIndex] = _dataStore.getVertices().get(plyEdgeInfo.getIndex1()); + vertices[edgeVertexIndex + 1] = _dataStore.getVertices().get(plyEdgeInfo.getIndex2()); + if (hasNormals) { + normals[edgeVertexIndex] = _dataStore.getNormals().get(plyEdgeInfo.getIndex1()); + normals[edgeVertexIndex + 1] = _dataStore.getNormals().get(plyEdgeInfo.getIndex2()); + } + if (plyEdgeInfo.getColor() != null) { + if (colors == null) { + colors = new ReadOnlyColorRGBA[vertexCount]; + } + colors[edgeVertexIndex] = plyEdgeInfo.getColor(); + colors[edgeVertexIndex + 1] = plyEdgeInfo.getColor(); + hasColors = true; + } + edgeVertexIndex += 2; + } + final Line line = new Line(name, vertices, normals, colors, null); + indices.rewind(); + line.getMeshData().setIndices(indices); + final EnumSet<MatchCondition> matchConditions = EnumSet.noneOf(MatchCondition.class); + if (hasNormals) { + matchConditions.add(MatchCondition.Normal); + } + if (hasColors) { + matchConditions.add(MatchCondition.Color); + } + _geometryTool.minimizeVerts(line, matchConditions); + + final TextureState tState = getTextureState(); + if (tState != null) { + line.setRenderState(tState); + } + + line.updateModelBound(); + _totalLines++; + _plyEdgeInfoList = null; + } + if (_plyFaceInfoList != null) { + final String name = "ply_mesh" + _totalMeshes; + final Mesh mesh = new Mesh(name); + boolean hasTexCoordsInFaces = false; + final boolean hasTexCoordsInVertices = _dataStore.getTextureCoordinates() != null + && !_dataStore.getTextureCoordinates().isEmpty(); + final boolean hasNormals = _dataStore.getNormals() != null && !_dataStore.getNormals().isEmpty(); + final boolean hasColors = _dataStore.getColors() != null && !_dataStore.getColors().isEmpty(); + int vertexCount = 0; + for (final PlyFaceInfo plyFaceInfo : _plyFaceInfoList) { + vertexCount += plyFaceInfo.getVertexIndices().size(); + if (plyFaceInfo.getTextureCoordinates() != null && !plyFaceInfo.getTextureCoordinates().isEmpty()) { + hasTexCoordsInFaces = true; + } + } + final FloatBuffer vertices = BufferUtils.createVector3Buffer(vertexCount); + final IndexBufferData<? extends Buffer> indices = BufferUtils.createIndexBufferData(vertexCount, + vertexCount - 1); + + final FloatBuffer normals = hasNormals ? BufferUtils.createFloatBuffer(vertices.capacity()) : null; + final FloatBuffer colors = hasColors ? BufferUtils.createFloatBuffer(vertexCount * 4) : null; + final FloatBuffer uvs = hasTexCoordsInFaces || hasTexCoordsInVertices + ? BufferUtils.createFloatBuffer(vertexCount * 2) : null; + + int dummyVertexIndex = 0; + final List<IndexMode> indexModeList = new ArrayList<>(); + final List<Integer> indexLengthList = new ArrayList<>(); + for (final PlyFaceInfo plyFaceInfo : _plyFaceInfoList) { + final IndexMode previousIndexMode = indexModeList.isEmpty() ? null + : indexModeList.get(indexModeList.size() - 1); + final IndexMode currentIndexMode; + switch (plyFaceInfo.getVertexIndices().size()) { + case 3: { + currentIndexMode = IndexMode.Triangles; + break; + } + case 4: { + currentIndexMode = IndexMode.Quads; + break; + } + default: { + currentIndexMode = null; + break; + } + } + if (currentIndexMode == null) { + PlyGeometryStore.LOGGER.log(Level.SEVERE, + "The index mode cannot be determined for a face containing " + + plyFaceInfo.getVertexIndices().size() + " vertices"); + } else { + if (previousIndexMode == null || currentIndexMode != previousIndexMode) { + indexModeList.add(currentIndexMode); + indexLengthList.add(currentIndexMode.getVertexCount()); + } else { + final int previousIndexLength = indexLengthList.get(indexLengthList.size() - 1).intValue(); + final int currentIndexLength = previousIndexLength + currentIndexMode.getVertexCount(); + indexLengthList.set(indexLengthList.size() - 1, Integer.valueOf(currentIndexLength)); + } + for (final Integer vertexIndex : plyFaceInfo.getVertexIndices()) { + indices.put(dummyVertexIndex); + final Vector3 vertex = _dataStore.getVertices().get(vertexIndex.intValue()); + vertices.put(vertex.getXf()).put(vertex.getYf()).put(vertex.getZf()); + if (hasNormals) { + final Vector3 normal = _dataStore.getNormals().get(vertexIndex.intValue()); + normals.put(normal.getXf()).put(normal.getYf()).put(normal.getZf()); + } + if (hasColors) { + final ColorRGBA color = _dataStore.getColors().get(vertexIndex.intValue()); + colors.put(color.getRed()).put(color.getGreen()).put(color.getBlue()).put(color.getAlpha()); + } + if (hasTexCoordsInVertices) { + final Vector2 texCoords = _dataStore.getTextureCoordinates().get(vertexIndex.intValue()); + uvs.put(texCoords.getXf()).put(texCoords.getYf()); + } + dummyVertexIndex++; + } + if (hasTexCoordsInFaces) { + for (final Float texCoord : plyFaceInfo.getTextureCoordinates()) { + uvs.put(texCoord); + } + } + } + } + + vertices.rewind(); + mesh.getMeshData().setVertexBuffer(vertices); + indices.rewind(); + mesh.getMeshData().setIndices(indices); + if (indexModeList.size() == 1) { + mesh.getMeshData().setIndexMode(indexModeList.get(0)); + mesh.getMeshData().setIndexLengths(null); + } else { + mesh.getMeshData().setIndexModes(indexModeList.toArray(new IndexMode[indexModeList.size()])); + final int[] indexLengths = new int[indexLengthList.size()]; + for (int indexLengthIndex = 0; indexLengthIndex < indexLengths.length; indexLengthIndex++) { + indexLengths[indexLengthIndex] = indexLengthList.get(indexLengthIndex).intValue(); + } + mesh.getMeshData().setIndexLengths(indexLengths); + } + final EnumSet<MatchCondition> matchConditions = EnumSet.noneOf(MatchCondition.class); + if (hasNormals) { + normals.rewind(); + mesh.getMeshData().setNormalBuffer(normals); + matchConditions.add(MatchCondition.Normal); + } + if (hasColors) { + colors.rewind(); + mesh.getMeshData().setColorBuffer(colors); + matchConditions.add(MatchCondition.Color); + } + if (hasTexCoordsInFaces || hasTexCoordsInVertices) { + uvs.rewind(); + mesh.getMeshData().setTextureBuffer(uvs, 0); + matchConditions.add(MatchCondition.UVs); + } + + if (indexModeList.size() == 1) { + _geometryTool.minimizeVerts(mesh, matchConditions); + } else { + // FIXME unsure about minimizeVerts preserving the index modes + } + + final TextureState tState = getTextureState(); + if (tState != null) { + mesh.setRenderState(tState); + } + + mesh.updateModelBound(); + _root.attachChild(mesh); + _totalMeshes++; + _plyFaceInfoList = null; + } + } + + void cleanup() { + _plyFaceInfoList = null; + _plyEdgeInfoList = null; + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyImporter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyImporter.java new file mode 100644 index 0000000..c4901b7 --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyImporter.java @@ -0,0 +1,1643 @@ +/** + * Copyright (c) 2008-2014 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.extension.model.ply; + +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StreamTokenizer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.nio.charset.StandardCharsets; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.ardor3d.image.Texture; +import com.ardor3d.image.Texture.MinificationFilter; +import com.ardor3d.image.TextureStoreFormat; +import com.ardor3d.math.ColorRGBA; +import com.ardor3d.math.Vector2; +import com.ardor3d.math.Vector3; +import com.ardor3d.util.TextureManager; +import com.ardor3d.util.geom.GeometryTool; +import com.ardor3d.util.resource.ResourceLocator; +import com.ardor3d.util.resource.ResourceLocatorTool; +import com.ardor3d.util.resource.ResourceSource; + +/** + * PLY importer. See <a href="http://paulbourke.net/dataformats/ply/">the format spec</a> + * + * Note that the material indices are stored but not used to build the meshes as it's currently impossible to apply a + * material per vertex + */ +public class PlyImporter { + + public static enum Format { + ASCII, BINARY_LITTLE_ENDIAN, BINARY_BIG_ENDIAN; + }; + + public static class FormatWithVersionNumber { + + private final Format format; + + private final double versionNumber; + + public FormatWithVersionNumber(final Format format, final double versionNumber) { + super(); + this.format = format; + this.versionNumber = versionNumber; + } + + public Format getFormat() { + return format; + } + + public double getVersionNumber() { + return versionNumber; + } + } + + public static enum ListProperty { + /** indices of the vertices */ + VERTEX_INDICES(Element.FACE, Element.CUSTOM), + /** indices of materials (not sure that it's really in the specification) */ + MATERIAL_INDICES(Element.FACE, Element.CUSTOM), + /** texture coordinates (probably only supported by MeshLab) */ + TEXCOORD(Element.FACE, Element.CUSTOM), + /** custom, i.e user-defined, not build-in, up to the developer to support it */ + CUSTOM(Element.CUSTOM); + private final Element[] elements; + + private ListProperty(final Element... elements) { + this.elements = elements; + } + + public Element[] getElements() { + return elements; + } + + public static ListProperty get(final String name) { + final String uppercaseName = name.toUpperCase(); + ListProperty result = null; + try { + result = ListProperty.valueOf(uppercaseName); + } catch (final IllegalArgumentException iae) { + if ("VERTEX_INDEX".equals(uppercaseName) || "VERTEX_INDEXES".equals(uppercaseName)) { + result = VERTEX_INDICES; + } else if ("MATERIAL_INDEX".equals(uppercaseName) || "MATERIAL_INDEXES".equals(uppercaseName)) { + result = MATERIAL_INDICES; + } else { + result = CUSTOM; + } + } + return result; + } + } + + public static enum ScalarProperty { + /** abscissa */ + X(Element.VERTEX, Element.CUSTOM), + /** ordinate */ + Y(Element.VERTEX, Element.CUSTOM), + /** applicate */ + Z(Element.VERTEX, Element.CUSTOM), + /** normal x vector coordinate */ + NX(Element.VERTEX, Element.CUSTOM), + /** normal y vector coordinate */ + NY(Element.VERTEX, Element.CUSTOM), + /** normal z vector coordinate */ + NZ(Element.VERTEX, Element.CUSTOM), + /** u texture coordinate */ + S(Element.VERTEX, Element.CUSTOM), + /** v texture coordinate */ + T(Element.VERTEX, Element.CUSTOM), + /** first vertex */ + VERTEX1(Element.EDGE, Element.CUSTOM), + /** second vertex */ + VERTEX2(Element.EDGE, Element.CUSTOM), + /** red color component */ + RED(Element.VERTEX, Element.EDGE, Element.CUSTOM), + /** green color component */ + GREEN(Element.VERTEX, Element.EDGE, Element.CUSTOM), + /** blue color component */ + BLUE(Element.VERTEX, Element.EDGE, Element.CUSTOM), + /** material (ambient light) components */ + AMBIENT_RED(Element.MATERIAL, Element.CUSTOM), AMBIENT_GREEN(Element.MATERIAL, Element.CUSTOM), AMBIENT_BLUE(Element.MATERIAL, Element.CUSTOM), AMBIENT_COEFF(Element.MATERIAL, Element.CUSTOM), + /** material (diffuse light) components */ + DIFFUSE_RED(Element.MATERIAL, Element.CUSTOM), DIFFUSE_GREEN(Element.MATERIAL, Element.CUSTOM), DIFFUSE_BLUE(Element.MATERIAL, Element.CUSTOM), DIFFUSE_COEFF(Element.MATERIAL, Element.CUSTOM), + /** material (emissive light) components */ + EMISSIVE_RED(Element.MATERIAL, Element.CUSTOM), EMISSIVE_GREEN(Element.MATERIAL, Element.CUSTOM), EMISSIVE_BLUE(Element.MATERIAL, Element.CUSTOM), EMISSIVE_COEFF(Element.MATERIAL, Element.CUSTOM), + /** material (specular light) components */ + SPECULAR_RED(Element.MATERIAL, Element.CUSTOM), SPECULAR_GREEN(Element.MATERIAL, Element.CUSTOM), SPECULAR_BLUE(Element.MATERIAL, Element.CUSTOM), SPECULAR_COEFF(Element.MATERIAL, Element.CUSTOM), SPECULAR_POWER(Element.MATERIAL, Element.CUSTOM), + /** custom, i.e user-defined, not build-in, up to the developer to support it */ + CUSTOM(Element.CUSTOM); + private final Element[] elements; + + private ScalarProperty(final Element... elements) { + this.elements = elements; + } + + public Element[] getElements() { + return elements; + } + + public static ScalarProperty get(final String name) { + final String uppercaseName = name.toUpperCase(); + ScalarProperty result = null; + try { + result = ScalarProperty.valueOf(uppercaseName); + } catch (final IllegalArgumentException iae) { + result = CUSTOM; + } + return result; + } + } + + protected static class EnumWithKeyword<T extends Enum<?>> { + + private final T enumKey; + + private final String keyword; + + protected EnumWithKeyword(final T enumKey, final String keyword) { + super(); + this.enumKey = enumKey; + this.keyword = keyword; + } + + @Override + public int hashCode() { + return Objects.hash(enumKey, keyword); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final EnumWithKeyword<?> other = (EnumWithKeyword<?>) obj; + return enumKey == other.enumKey && Objects.equals(keyword, other.keyword); + } + + public T getEnumKey() { + return enumKey; + } + + public String getKeyword() { + return keyword; + } + + @Override + public String toString() { + return "'" + Objects.toString(enumKey) + "[" + Objects.toString(keyword) + "]'"; + } + } + + public static abstract class AbstractPropertyWithKeyword<T extends Enum<?>> extends EnumWithKeyword<T> { + + private final Data data; + + protected AbstractPropertyWithKeyword(final T enumKey, final String keyword, final Data data) { + super(enumKey, keyword); + this.data = data; + } + + public Data getData() { + return data; + } + + public abstract Element[] getElements(); + + @Override + public String toString() { + return "property " + super.toString() + " data type " + Objects.toString(getData()); + } + } + + public static class ScalarPropertyWithKeyword extends AbstractPropertyWithKeyword<ScalarProperty> { + + public ScalarPropertyWithKeyword(final ScalarProperty scalarProperty, final String keyword, final Data data) { + super(scalarProperty, keyword, data); + } + + @Override + public Element[] getElements() { + return getEnumKey() == null ? null : getEnumKey().getElements(); + } + + @Override + public String toString() { + return "scalar " + super.toString(); + } + } + + public static class ListPropertyWithKeyword extends AbstractPropertyWithKeyword<ListProperty> { + + private final Data countData; + + public ListPropertyWithKeyword(final ListProperty listProperty, final String keyword, final Data countData, + final Data data) { + super(listProperty, keyword, data); + this.countData = countData; + } + + @Override + public Element[] getElements() { + return getEnumKey() == null ? null : getEnumKey().getElements(); + } + + public Data getCountData() { + return countData; + } + + @Override + public String toString() { + return "list " + super.toString() + " index data type " + Objects.toString(countData); + } + } + + public static enum Element { + VERTEX, FACE, EDGE, MATERIAL, + /** custom, i.e user-defined, not build-in, up to the developer to support it */ + CUSTOM; + + public static Element get(final String name) { + final String uppercaseName = name.toUpperCase(); + Element result = null; + try { + result = Element.valueOf(uppercaseName); + } catch (final IllegalArgumentException iae) { + result = CUSTOM; + } + return result; + } + }; + + public static class ElementWithKeyword extends EnumWithKeyword<Element> { + + public ElementWithKeyword(final Element element, final String keyword) { + super(element, keyword); + } + + @Override + public String toString() { + return "element " + super.toString(); + } + } + + public static enum Data { + + /** one byte signed integer */ + CHAR, + /** one byte unsigned integer */ + UCHAR, + /** two byte signed integer */ + SHORT, + /** two byte unsigned integer */ + USHORT, + /** four byte signed integer */ + INT, + /** four byte unsigned integer */ + UINT, + /** four byte floating point number */ + FLOAT, + /** eight byte floating point number */ + DOUBLE; + + public static Data get(final String name) throws IllegalArgumentException { + final String uppercaseName = name.toUpperCase(); + Data result = null; + try { + result = Data.valueOf(uppercaseName); + } catch (final IllegalArgumentException iae) { + switch (uppercaseName) { + case "INT8": { + result = CHAR; + break; + } + case "UINT8": { + result = UCHAR; + break; + } + case "INT16": { + result = SHORT; + break; + } + case "UINT16": { + result = USHORT; + break; + } + case "INT32": { + result = INT; + break; + } + case "UINT32": { + result = UINT; + break; + } + case "FLOAT32": { + result = FLOAT; + break; + } + case "FLOAT64": { + result = DOUBLE; + break; + } + default: { + throw new IllegalArgumentException( + "'" + name + "' does not match with any data type supported by the PLY format"); + } + } + } + return result; + } + } + + public static interface PlyReader extends Closeable { + public double read(final Data data) throws IOException; + } + + /** + * largely inspired of jPly's BinaryPlyInputStream, @see + * https://github.com/smurn/jPLY/blob/master/jply/src/main/java/org/smurn/jply/BinaryPlyInputStream.java + */ + public static class BinaryPlyReader implements PlyReader { + + private final ReadableByteChannel channel; + + private final ByteBuffer buffer; + + private static final int BUFFER_SIZE = 1024; + + public BinaryPlyReader(final InputStream inputStream, final ByteOrder byteOrder) { + super(); + channel = Channels.newChannel(inputStream); + buffer = ByteBuffer.allocate(BinaryPlyReader.BUFFER_SIZE); + buffer.order(byteOrder); + buffer.clear(); + buffer.position(buffer.capacity()); + } + + @Override + public double read(final Data data) throws IOException { + switch (data) { + case CHAR: + ensureAvailable(1); + return buffer.get(); + case UCHAR: + ensureAvailable(1); + return buffer.get() & 0x000000FF; + case SHORT: + ensureAvailable(2); + return buffer.getShort(); + case USHORT: + ensureAvailable(2); + return buffer.getShort() & 0x0000FFFF; + case INT: + ensureAvailable(4); + return buffer.getInt(); + case UINT: + ensureAvailable(4); + return ((long) buffer.getShort()) & 0x00000000FFFFFFFF; + case FLOAT: + ensureAvailable(4); + return buffer.getFloat(); + case DOUBLE: + ensureAvailable(8); + return buffer.getDouble(); + default: + throw new IllegalArgumentException("Unsupported type: " + data); + } + } + + /** + * Ensures that a certain amount of bytes are in the buffer, ready to be read. + * + * @param bytes + * Minimal number of unread bytes required in the buffer. + * @see ByteBuffer#remaining() + * @throws IOException + * if reading sufficient more data into the buffer fails. + */ + protected void ensureAvailable(final int bytes) throws IOException { + while (buffer.remaining() < bytes) { + buffer.compact(); + if (channel.read(buffer) < 0) { + throw new EOFException(); + } + buffer.flip(); + } + } + + @Override + public void close() throws IOException { + channel.close(); + } + } + + public static class AsciiPlyReader implements PlyReader { + + private final PlyFileParser parser; + private final BufferedReader reader; + + public AsciiPlyReader(final InputStream stream) { + super(); + reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.US_ASCII)); + parser = new PlyFileParser(reader); + } + + @Override + public double read(final Data data) throws IOException { + do { + parser.nextToken(); + } while (parser.ttype != StreamTokenizer.TT_WORD && parser.ttype != StreamTokenizer.TT_EOF); + if (parser.ttype == StreamTokenizer.TT_WORD) { + try { + parser.nval = Double.valueOf(parser.sval).doubleValue(); + return parser.nval; + } catch (final NumberFormatException nbe) { + throw new IOException("Unparsable string " + parser.sval, nbe); + } + } else { + throw new IOException("No number to read, end of file reached"); + } + } + + @Override + public void close() throws IOException { + reader.close(); + } + } + + private static final Logger LOGGER = Logger.getLogger(PlyImporter.class.getName()); + + public static class PlyFileParser extends StreamTokenizer implements Closeable { + + private final Reader reader; + + /** + * Constructor. + * + * @param reader + * The Reader. + */ + public PlyFileParser(final Reader reader) { + super(reader); + this.reader = reader; + resetSyntax(); + eolIsSignificant(true); + lowerCaseMode(true); + + // all printable ascii characters + wordChars('!', '~'); + + whitespaceChars(' ', ' '); + whitespaceChars('\n', '\n'); + whitespaceChars('\r', '\r'); + whitespaceChars('\t', '\t'); + } + + /** + * Gets a number from the stream. Need to extract numbers since they may be in scientific notation. The number + * is returned in nval. + * + * @return Logical-true if successful, else logical-false. + */ + protected boolean getNumber() { + try { + nextToken(); + if (ttype != StreamTokenizer.TT_WORD) { + return false; + } + nval = Double.valueOf(sval).doubleValue(); + } catch (final IOException e) { + System.err.println(e.getMessage()); + return false; + } catch (final NumberFormatException e) { + System.err.println(e.getMessage()); + return false; + } + return true; + } + + @Override + public void close() throws IOException { + reader.close(); + } + } + + private ResourceLocator _modelLocator; + + private ResourceLocator _textureLocator; + + private boolean _flipTextureVertically; + + private boolean _useCompression; + + private MinificationFilter _minificationFilter; + + /** + * Constructor. + */ + public PlyImporter() { + super(); + _flipTextureVertically = true; + _useCompression = true; + _minificationFilter = MinificationFilter.Trilinear; + } + + /** + * Reads a PLY file from the given resource + * + * @param resource + * the name of the resource to find. + * + * @return a PlyGeometryStore data object containing the scene and other useful elements. + */ + public PlyGeometryStore load(final String resource) { + return load(resource, new GeometryTool()); + } + + /** + * Reads a PLY file from the given resource + * + * @param resource + * the name of the resource to find. + * @param geometryTool + * the geometry tool to optimize the meshes + * + * @return a PlyGeometryStore data object containing the scene and other useful elements. + */ + public PlyGeometryStore load(final String resource, final GeometryTool geometryTool) { + final ResourceSource source; + if (_modelLocator == null) { + source = ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_MODEL, resource); + } else { + source = _modelLocator.locateResource(resource); + } + + if (source == null) { + throw new Error("Unable to locate '" + resource + "'"); + } + + return load(source, geometryTool); + } + + /** + * Reads a PLY file from the given resource + * + * @param resource + * the resource to find. + * + * @return a PlyGeometryStore data object containing the scene and other useful elements. + */ + public PlyGeometryStore load(final ResourceSource resource) { + return load(resource, new GeometryTool()); + } + + /** + * Reads a PLY file from the given resource + * + * @param resource + * the resource to find. + * @param geometryTool + * the geometry tool to optimize the meshes + * + * @return a PlyGeometryStore data object containing the scene and other useful elements. + */ + @SuppressWarnings("resource") + public PlyGeometryStore load(final ResourceSource resource, final GeometryTool geometryTool) { + final PlyGeometryStore store = createGeometryStore(geometryTool); + try { + FormatWithVersionNumber formatWithVersionNumber = null; + final int firstLineOfBody; + final Map<ElementWithKeyword, Map.Entry<Integer, Set<AbstractPropertyWithKeyword<?>>>> elementMap = new LinkedHashMap<>(); + try (final PlyFileParser parser = new PlyFileParser( + new BufferedReader(new InputStreamReader(resource.openStream(), StandardCharsets.US_ASCII)))) { + try { + // starts reading the header + parser.nextToken(); + // reads "ply" + if ("ply".equals(parser.sval)) { + PlyImporter.LOGGER.log(Level.INFO, "ply keyword on line " + parser.lineno()); + } else { + PlyImporter.LOGGER.log(Level.SEVERE, "No ply keyword on line " + parser.lineno()); + } + // reads the EOL for verifying that the file has a correct format + parser.nextToken(); + if (parser.ttype != StreamTokenizer.TT_EOL) { + PlyImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting End Of Line on line " + parser.lineno()); + } + parser.nextToken(); + // reads the rest of the header + while (parser.ttype != StreamTokenizer.TT_EOF && !"end_header".equals(parser.sval)) { + if (parser.ttype == StreamTokenizer.TT_WORD) { + final int currentLineNumber = parser.lineno(); + switch (parser.sval) { + case "comment": { + parser.nextToken(); + if (parser.ttype == StreamTokenizer.TT_WORD) { + if ("TextureFile".equals(parser.sval)) { + parser.nextToken(); + if (parser.ttype == StreamTokenizer.TT_WORD) { + final String textureName = parser.sval; + store.setTextureName(textureName); + final Texture texture; + if (_textureLocator == null) { + texture = TextureManager.load(textureName, getMinificationFilter(), + isUseCompression() + ? TextureStoreFormat.GuessCompressedFormat + : TextureStoreFormat.GuessNoCompressedFormat, + isFlipTextureVertically()); + } else { + final ResourceSource source = _textureLocator + .locateResource(textureName); + texture = TextureManager.load(source, getMinificationFilter(), + isUseCompression() + ? TextureStoreFormat.GuessCompressedFormat + : TextureStoreFormat.GuessNoCompressedFormat, + isFlipTextureVertically()); + } + store.setTexture(texture); + } else { + PlyImporter.LOGGER.log(Level.SEVERE, + "'TextureFile' comment with no texture file on line " + + currentLineNumber); + } + } + } + break; + } + case "format": { + parser.nextToken(); + if (parser.ttype == StreamTokenizer.TT_WORD) { + if (formatWithVersionNumber == null) { + Format format = null; + try { + format = Format.valueOf(parser.sval.toUpperCase()); + } catch (final IllegalArgumentException iae) { + PlyImporter.LOGGER.log(Level.SEVERE, "Unknown format '" + parser.sval + + "' on line " + currentLineNumber + ": " + iae.getMessage()); + } + final double versionNumber; + if (parser.getNumber()) { + versionNumber = parser.nval; + if (Double.compare(versionNumber, 1.0d) != 0) { + PlyImporter.LOGGER.log(Level.WARNING, + "Unsupported format version number '" + parser.nval + + "' on line " + currentLineNumber + + ". This importer supports only PLY 1.0"); + } + parser.nextToken(); + if (parser.ttype != StreamTokenizer.TT_EOL) { + PlyImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting End Of Line on line " + + currentLineNumber); + } + } else { + PlyImporter.LOGGER.log(Level.SEVERE, + "Format version number missing on line " + currentLineNumber + + "\n"); + versionNumber = Double.NaN; + } + formatWithVersionNumber = new FormatWithVersionNumber(format, + versionNumber); + PlyImporter.LOGGER.log(Level.INFO, + "Format '" + (format == null ? "null" : format.name()) + + "' version number '" + versionNumber + + "' detected on line " + currentLineNumber); + } else { + PlyImporter.LOGGER.log(Level.WARNING, + "Format already defined, format declaration ignored on line " + + currentLineNumber); + } + } else { + PlyImporter.LOGGER.log(Level.SEVERE, + "Format type (ascii, binary_big_endian or binary_little_endian) missing on line " + + currentLineNumber); + } + break; + } + case "element": { + parser.nextToken(); + if (parser.ttype == StreamTokenizer.TT_WORD) { + final String elementName = parser.sval; + final Element element = Element.get(elementName); + final ElementWithKeyword elementWithKeyword = new ElementWithKeyword(element, + elementName); + if (elementMap.containsKey(element)) { + PlyImporter.LOGGER.log(Level.WARNING, + elementWithKeyword + + " already defined, element declaration ignored on line " + + currentLineNumber); + } else { + final int elementCount; + if (parser.getNumber()) { + elementCount = (int) parser.nval; + if (elementCount < 0) { + PlyImporter.LOGGER.log(Level.SEVERE, + elementWithKeyword + " count = " + elementCount + + " whereas it should be >= 0 on line " + + currentLineNumber); + } + parser.nextToken(); + if (parser.ttype != StreamTokenizer.TT_EOL) { + PlyImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting End Of Line on line " + + currentLineNumber); + } + } else { + PlyImporter.LOGGER.log(Level.SEVERE, elementWithKeyword + + " count missing on line " + currentLineNumber); + elementCount = 0; + } + elementMap.put(elementWithKeyword, + new AbstractMap.SimpleEntry<Integer, Set<AbstractPropertyWithKeyword<?>>>( + Integer.valueOf(elementCount), null)); + PlyImporter.LOGGER.log(Level.INFO, + elementWithKeyword + " detected on line " + currentLineNumber); + } + } else { + PlyImporter.LOGGER.log(Level.SEVERE, + "Element type (vertex, face or edge) missing on line " + + currentLineNumber); + } + break; + } + case "property": { + ElementWithKeyword latestInsertedElementWithKeyword = null; + for (final ElementWithKeyword elementWithKeyword : elementMap.keySet()) { + latestInsertedElementWithKeyword = elementWithKeyword; + } + if (latestInsertedElementWithKeyword == null) { + PlyImporter.LOGGER.log(Level.SEVERE, + "Property definition not preceded by an element definition on line " + + currentLineNumber); + } else { + parser.nextToken(); + if (parser.ttype == StreamTokenizer.TT_WORD) { + if ("list".equals(parser.sval)) { + // list property, for face elements (vertex indices, texture + // coordinates, ...) + parser.nextToken(); + if (parser.ttype == StreamTokenizer.TT_WORD) { + Data countData = null; + try { + countData = Data.get(parser.sval); + } catch (final IllegalArgumentException iae) { + PlyImporter.LOGGER.log(Level.SEVERE, + "Count data type '" + parser.sval + "' unknown on line " + + currentLineNumber); + } + if (countData != null) { + parser.nextToken(); + if (parser.ttype == StreamTokenizer.TT_WORD) { + Data data = null; + try { + data = Data.get(parser.sval); + } catch (final IllegalArgumentException iae) { + PlyImporter.LOGGER.log(Level.SEVERE, + "Data type '" + parser.sval + + "' unknown on line " + + currentLineNumber); + } + if (data != null) { + parser.nextToken(); + if (parser.ttype == StreamTokenizer.TT_WORD) { + final String listPropertyName = parser.sval; + final ListProperty listProperty = ListProperty + .get(listPropertyName); + final ListPropertyWithKeyword listPropertyWithKeyword = new ListPropertyWithKeyword( + listProperty, listPropertyName, countData, + data); + if (Arrays.asList(listProperty.getElements()) + .contains(latestInsertedElementWithKeyword + .getEnumKey())) { + final Entry<Integer, Set<AbstractPropertyWithKeyword<?>>> elementMapEntry = elementMap + .get(latestInsertedElementWithKeyword); + Set<AbstractPropertyWithKeyword<?>> propertySet = elementMapEntry + .getValue(); + if (propertySet == null) { + propertySet = new LinkedHashSet<>(); + elementMapEntry.setValue(propertySet); + } + propertySet.add(listPropertyWithKeyword); + PlyImporter.LOGGER.log(Level.INFO, + listPropertyWithKeyword + + " detected on line " + + currentLineNumber); + } else { + PlyImporter.LOGGER.log(Level.SEVERE, + "Unexpected " + listPropertyWithKeyword + + " on line " + + currentLineNumber); + } + } else { + PlyImporter.LOGGER.log(Level.SEVERE, + "List property keyword (vertex_indices, texcoord, ...) missing on line " + + currentLineNumber); + } + } + } else { + PlyImporter.LOGGER.log(Level.SEVERE, + "Second data type (float32, int8, ...) missing on line " + + currentLineNumber); + } + } + } else { + PlyImporter.LOGGER.log(Level.SEVERE, + "First data type (float32, int8, ...) missing on line " + + currentLineNumber); + } + } else { + // scalar property (vertex coordinates, normal coordinates, ...) + Data data = null; + try { + data = Data.get(parser.sval); + } catch (final IllegalArgumentException iae) { + PlyImporter.LOGGER.log(Level.SEVERE, "Data type '" + parser.sval + + "' unknown on line " + currentLineNumber); + } + if (data != null) { + parser.nextToken(); + if (parser.ttype == StreamTokenizer.TT_WORD) { + final String scalarPropertyName = parser.sval; + final ScalarProperty scalarProperty = ScalarProperty + .get(scalarPropertyName); + final ScalarPropertyWithKeyword scalarPropertyWithKeyword = new ScalarPropertyWithKeyword( + scalarProperty, scalarPropertyName, data); + if (Arrays.asList(scalarProperty.getElements()).contains( + latestInsertedElementWithKeyword.getEnumKey())) { + final Entry<Integer, Set<AbstractPropertyWithKeyword<?>>> elementMapValue = elementMap + .get(latestInsertedElementWithKeyword); + Set<AbstractPropertyWithKeyword<?>> propertySet = elementMapValue + .getValue(); + if (propertySet == null) { + propertySet = new LinkedHashSet<>(); + elementMapValue.setValue(propertySet); + } + propertySet.add(scalarPropertyWithKeyword); + PlyImporter.LOGGER.log(Level.INFO, scalarPropertyWithKeyword + + " detected on line " + currentLineNumber); + } else { + PlyImporter.LOGGER.log(Level.SEVERE, + "Unexpected " + scalarPropertyWithKeyword + " in a " + + latestInsertedElementWithKeyword + + " on line " + currentLineNumber); + } + } else { + PlyImporter.LOGGER.log(Level.SEVERE, + "Scalar property keyword (x, nx, vertex1, red, ...) missing on line " + + currentLineNumber); + } + } + } + } else { + PlyImporter.LOGGER.log(Level.SEVERE, + "Property type (list) or scalar data type (float32, int8, ...) missing on line " + + currentLineNumber); + } + } + break; + } + default: { + PlyImporter.LOGGER.log(Level.SEVERE, + "Unknown command '" + parser.sval + "' on line " + currentLineNumber); + break; + } + } + } else { + PlyImporter.LOGGER.log(Level.SEVERE, + "No word at the beginning of the line " + parser.lineno()); + } + // reads the whole line, doesn't look at the content + while (parser.ttype != StreamTokenizer.TT_EOL) { + parser.nextToken(); + } + // if there is still something to read, reads the next token + if (parser.ttype != StreamTokenizer.TT_EOF) { + parser.nextToken(); + } + } + if ("end_header".equals(parser.sval)) { + PlyImporter.LOGGER.log(Level.INFO, "End of header on line " + parser.lineno()); + do { + parser.nextToken(); + } while (parser.ttype != StreamTokenizer.TT_EOL); + firstLineOfBody = parser.lineno(); + } else { + throw new Exception("End of header not detected"); + } + } catch (final IOException ioe) { + throw new Exception("IO Error on line " + parser.lineno(), ioe); + } + } + if (formatWithVersionNumber == null || formatWithVersionNumber.getFormat() == null) { + throw new Exception("Missing or malformed format in the header, cannot read the body of the PLY file"); + } else { + // stores the number of the first line of the body, after the header + int currentLineNumber = firstLineOfBody; + // restarts the reading of the file from the beginning + try (final InputStream stream = resource.openStream()) { + // skips the lines of the header + for (int lineIndex = 1; lineIndex < currentLineNumber; lineIndex++) { + while (stream.read() != '\n') { + ; + } + } + // reads the lines after the header, the body + final PlyReader plyReader; + switch (formatWithVersionNumber.getFormat()) { + case ASCII: { + plyReader = new AsciiPlyReader(stream); + break; + } + case BINARY_BIG_ENDIAN: { + plyReader = new BinaryPlyReader(stream, ByteOrder.BIG_ENDIAN); + break; + } + case BINARY_LITTLE_ENDIAN: { + plyReader = new BinaryPlyReader(stream, ByteOrder.LITTLE_ENDIAN); + break; + } + default: { + throw new UnsupportedOperationException( + "Unsupported format " + formatWithVersionNumber.getFormat()); + } + } + try { + for (final Entry<ElementWithKeyword, Entry<Integer, Set<AbstractPropertyWithKeyword<?>>>> elementMapEntry : elementMap + .entrySet()) { + final ElementWithKeyword elementWithKeyword = elementMapEntry.getKey(); + final Set<AbstractPropertyWithKeyword<?>> propertiesWithKeywords = elementMapEntry + .getValue().getValue(); + final int elementCount = elementMapEntry.getValue().getKey().intValue(); + PlyImporter.LOGGER.log(Level.INFO, "Reading of " + elementCount + " elements (" + + elementWithKeyword + ") started on line " + currentLineNumber); + if (propertiesWithKeywords == null || propertiesWithKeywords.isEmpty()) { + PlyImporter.LOGGER.log(Level.SEVERE, elementWithKeyword + + " data with no property skipped on line " + currentLineNumber); + } else { + for (int elementIndex = 0; elementIndex < elementCount; elementIndex++) { + // reads one line + final List<Double> valueList = new ArrayList<>(); + for (final AbstractPropertyWithKeyword<?> propertyWithKeyWord : propertiesWithKeywords) { + final Data scalarValueDataType = propertyWithKeyWord.getData(); + if (propertyWithKeyWord instanceof ScalarPropertyWithKeyword) { + final double scalarValue = plyReader.read(scalarValueDataType); + valueList.add(Double.valueOf(scalarValue)); + } else if (propertyWithKeyWord instanceof ListPropertyWithKeyword) { + final Data valueCountDataType = ((ListPropertyWithKeyword) propertyWithKeyWord) + .getCountData(); + final double rawValueCount = plyReader.read(valueCountDataType); + valueList.add(Double.valueOf(rawValueCount)); + final long valueCount = (long) rawValueCount; + for (int valueIndex = 0; valueIndex < valueCount; valueIndex++) { + final double scalarValue = plyReader.read(scalarValueDataType); + valueList.add(Double.valueOf(scalarValue)); + } + } + } + if (!valueList.isEmpty()) { + // stores the values into an array + final double[] values = new double[valueList.size()]; + for (int valueIndex = 0; valueIndex < values.length; valueIndex++) { + values[valueIndex] = valueList.get(valueIndex).doubleValue(); + } + if (elementWithKeyword.getEnumKey() == Element.CUSTOM) { + processElementCustomData(formatWithVersionNumber, elementWithKeyword, + propertiesWithKeywords, values, currentLineNumber, store); + } else { + boolean hasBuildInProperties = false; + boolean hasCustomProperties = false; + for (final AbstractPropertyWithKeyword<?> currentProperty : propertiesWithKeywords) { + final boolean isCustom = currentProperty + .getEnumKey() == ScalarProperty.CUSTOM + || currentProperty.getEnumKey() == ListProperty.CUSTOM; + if (!hasCustomProperties && isCustom) { + hasCustomProperties = true; + } + if (!hasBuildInProperties && !isCustom) { + hasBuildInProperties = true; + } + if (hasBuildInProperties && hasCustomProperties) { + break; + } + } + if (hasBuildInProperties) { + processElementBuildInData(formatWithVersionNumber, elementWithKeyword, + propertiesWithKeywords, values, currentLineNumber, store); + } + if (hasCustomProperties) { + processElementCustomData(formatWithVersionNumber, elementWithKeyword, + propertiesWithKeywords, values, currentLineNumber, store); + } + } + } + currentLineNumber++; + } + } + } + } finally { + plyReader.close(); + } + } catch (final IOException ioe) { + throw new Exception("IO Error on line " + currentLineNumber, ioe); + } + } + } catch (final Throwable t) { + throw new Error("Unable to load ply resource from URL: " + resource, t); + } + store.commitObjects(); + store.cleanup(); + return store; + } + + protected PlyGeometryStore createGeometryStore(final GeometryTool geometryTool) { + return new PlyGeometryStore(geometryTool); + } + + /** + * Processes the data within a build-in element, handles only the build-in properties whose behaviour is defined in + * the specification + * + * @param formatWithVersionNumber + * format with version number + * @param elementWithKeyword + * element and keyword + * @param elementProperties + * properties of the element + * @param values + * parsed values contained in a single line of file + * @param lineNumber + * number of the line in the PLY file being parsed + * @param store + * geometry store to fill during the process + */ + protected void processElementBuildInData(final FormatWithVersionNumber formatWithVersionNumber, + final ElementWithKeyword elementWithKeyword, final Set<AbstractPropertyWithKeyword<?>> elementProperties, + final double[] values, final int lineNumber, final PlyGeometryStore store) { + Vector3 vertex = null; + Vector3 normal = null; + ColorRGBA color = null; + Vector2 texCoords = null; + PlyMaterial material = null; + PlyEdgeInfo edgeInfo = null; + PlyFaceInfo faceInfo = null; + final Iterator<AbstractPropertyWithKeyword<?>> elementPropertiesIterator = elementProperties.iterator(); + final AbstractPropertyWithKeyword<?>[] valueProperties = new AbstractPropertyWithKeyword<?>[values.length]; + int valueIndex = 0; + // loops on the properties + while (valueIndex < values.length && elementPropertiesIterator.hasNext()) { + final AbstractPropertyWithKeyword<?> elementProperty = elementPropertiesIterator.next(); + if (elementProperty instanceof ScalarPropertyWithKeyword) { + valueProperties[valueIndex] = elementProperty; + valueIndex++; + } else if (elementProperty instanceof ListPropertyWithKeyword) { + // sets it to null so that it's skipped later as the value at this index is only used to know how much + // coordinates are in the list + valueProperties[valueIndex] = null; + // uses the list property in all concerned indices + final int listSize = (int) values[valueIndex]; + for (int listIndex = 1; listIndex <= listSize; listIndex++) { + valueProperties[valueIndex + listIndex] = elementProperty; + } + valueIndex += 1 + listSize; + } + } + valueIndex = 0; + // loops on the real values + for (final double value : values) { + final AbstractPropertyWithKeyword<?> propertyWithKeyWord = valueProperties[valueIndex]; + // it can be null when a value indicates the number of values in a list + if (propertyWithKeyWord != null) { + final Element[] propertyElements = propertyWithKeyWord.getElements(); + if (propertyElements != null && propertyElements.length > 0 + && Arrays.asList(propertyElements).contains(elementWithKeyword.getEnumKey())) { + switch (elementWithKeyword.getEnumKey()) { + case CUSTOM: { + throw new IllegalArgumentException("Custom data of the " + elementWithKeyword + + " passed to the method responsible for treating build-in data"); + } + case MATERIAL: { + if (propertyWithKeyWord instanceof ScalarPropertyWithKeyword) { + final ScalarProperty scalarProperty = ((ScalarPropertyWithKeyword) propertyWithKeyWord) + .getEnumKey(); + switch (scalarProperty) { + case CUSTOM: { + PlyImporter.LOGGER.log(Level.FINE, "Custom data of the " + propertyWithKeyWord + + " skipped in the method responsible for treating build-in data"); + break; + } + case AMBIENT_RED: { + if (material == null) { + material = new PlyMaterial(); + store.getMaterialLibrary().add(material); + } + material.setAmbientRed((float) (value / 255.0d)); + break; + } + case AMBIENT_GREEN: { + if (material == null) { + material = new PlyMaterial(); + store.getMaterialLibrary().add(material); + } + material.setAmbientGreen((float) (value / 255.0d)); + break; + } + case AMBIENT_BLUE: { + if (material == null) { + material = new PlyMaterial(); + store.getMaterialLibrary().add(material); + } + material.setAmbientBlue((float) (value / 255.0d)); + break; + } + case AMBIENT_COEFF: { + if (material == null) { + material = new PlyMaterial(); + store.getMaterialLibrary().add(material); + } + material.setAmbientAlpha((float) value); + break; + } + case DIFFUSE_RED: { + if (material == null) { + material = new PlyMaterial(); + store.getMaterialLibrary().add(material); + } + material.setDiffuseRed((float) (value / 255.0d)); + break; + } + case DIFFUSE_GREEN: { + if (material == null) { + material = new PlyMaterial(); + store.getMaterialLibrary().add(material); + } + material.setDiffuseGreen((float) (value / 255.0d)); + break; + } + case DIFFUSE_BLUE: { + if (material == null) { + material = new PlyMaterial(); + store.getMaterialLibrary().add(material); + } + material.setDiffuseBlue((float) (value / 255.0d)); + break; + } + case DIFFUSE_COEFF: { + if (material == null) { + material = new PlyMaterial(); + store.getMaterialLibrary().add(material); + } + material.setDiffuseAlpha((float) value); + break; + } + case SPECULAR_RED: { + if (material == null) { + material = new PlyMaterial(); + store.getMaterialLibrary().add(material); + } + material.setSpecularRed((float) (value / 255.0d)); + break; + } + case SPECULAR_GREEN: { + if (material == null) { + material = new PlyMaterial(); + store.getMaterialLibrary().add(material); + } + material.setSpecularGreen((float) (value / 255.0d)); + break; + } + case SPECULAR_BLUE: { + if (material == null) { + material = new PlyMaterial(); + store.getMaterialLibrary().add(material); + } + material.setSpecularBlue((float) (value / 255.0d)); + break; + } + case SPECULAR_COEFF: { + if (material == null) { + material = new PlyMaterial(); + store.getMaterialLibrary().add(material); + } + material.setSpecularAlpha((float) value); + break; + } + case SPECULAR_POWER: { + if (material == null) { + material = new PlyMaterial(); + store.getMaterialLibrary().add(material); + } + // not sure how to treat the specular power, 200 is the maximum specular value + // material.setShininess((float) (128 * MathUtils.clamp(value, 0, 200) / 200)); + material.setShininess((float) value); + break; + } + default: + throw new UnsupportedOperationException( + "Missing implementation: the " + propertyWithKeyWord + + " is not supported by the " + elementWithKeyword + " yet"); + } + } else if (propertyWithKeyWord instanceof ListPropertyWithKeyword) { + final ListProperty listProperty = ((ListPropertyWithKeyword) propertyWithKeyWord) + .getEnumKey(); + switch (listProperty) { + case CUSTOM: { + PlyImporter.LOGGER.log(Level.FINE, "Custom data of the " + propertyWithKeyWord + + " skipped in the method responsible for treating build-in data"); + break; + } + default: + throw new UnsupportedOperationException( + "Missing implementation: the " + propertyWithKeyWord + + " is not supported by the " + elementWithKeyword + " yet"); + } + } + break; + } + case EDGE: { + if (propertyWithKeyWord instanceof ScalarPropertyWithKeyword) { + final ScalarProperty scalarProperty = ((ScalarPropertyWithKeyword) propertyWithKeyWord) + .getEnumKey(); + switch (scalarProperty) { + case CUSTOM: { + PlyImporter.LOGGER.log(Level.FINE, "Custom data of the " + propertyWithKeyWord + + " skipped in the method responsible for treating build-in data"); + break; + } + case VERTEX1: { + if (edgeInfo == null) { + edgeInfo = new PlyEdgeInfo(); + store.addLine(edgeInfo); + } + final Integer edgeIndex1 = Integer.valueOf((int) value); + edgeInfo.setIndex1(edgeIndex1); + break; + } + case VERTEX2: { + if (edgeInfo == null) { + edgeInfo = new PlyEdgeInfo(); + store.addLine(edgeInfo); + } + final Integer edgeIndex2 = Integer.valueOf((int) value); + edgeInfo.setIndex2(edgeIndex2); + break; + } + case RED: { + final ColorRGBA edgeColor; + if (edgeInfo == null) { + edgeColor = new ColorRGBA(); + edgeInfo = new PlyEdgeInfo(); + edgeInfo.setColor(edgeColor); + store.addLine(edgeInfo); + } else { + if (edgeInfo.getColor() == null) { + edgeColor = new ColorRGBA(); + edgeInfo.setColor(edgeColor); + } else { + edgeColor = edgeInfo.getColor(); + } + } + edgeColor.setRed((float) (value / 255.0d)); + break; + } + case GREEN: { + final ColorRGBA edgeColor; + if (edgeInfo == null) { + edgeColor = new ColorRGBA(); + edgeInfo = new PlyEdgeInfo(); + edgeInfo.setColor(edgeColor); + store.addLine(edgeInfo); + } else { + if (edgeInfo.getColor() == null) { + edgeColor = new ColorRGBA(); + edgeInfo.setColor(edgeColor); + } else { + edgeColor = edgeInfo.getColor(); + } + } + edgeColor.setGreen((float) (value / 255.0d)); + break; + } + case BLUE: { + final ColorRGBA edgeColor; + if (edgeInfo == null) { + edgeColor = new ColorRGBA(); + edgeInfo = new PlyEdgeInfo(); + edgeInfo.setColor(edgeColor); + store.addLine(edgeInfo); + } else { + if (edgeInfo.getColor() == null) { + edgeColor = new ColorRGBA(); + edgeInfo.setColor(edgeColor); + } else { + edgeColor = edgeInfo.getColor(); + } + } + edgeColor.setBlue((float) (value / 255.0d)); + break; + } + default: + throw new UnsupportedOperationException( + "Missing implementation: the " + propertyWithKeyWord + + " is not supported by the " + elementWithKeyword + " yet"); + } + } else if (propertyWithKeyWord instanceof ListPropertyWithKeyword) { + final ListProperty listProperty = ((ListPropertyWithKeyword) propertyWithKeyWord) + .getEnumKey(); + switch (listProperty) { + case CUSTOM: { + PlyImporter.LOGGER.log(Level.FINE, "Custom data of the " + propertyWithKeyWord + + " skipped in the method responsible for treating build-in data"); + break; + } + default: + throw new UnsupportedOperationException( + "Missing implementation: the " + propertyWithKeyWord + + " is not supported by the " + elementWithKeyword + " yet"); + } + } + break; + } + case FACE: { + if (propertyWithKeyWord instanceof ScalarPropertyWithKeyword) { + final ScalarProperty scalarProperty = ((ScalarPropertyWithKeyword) propertyWithKeyWord) + .getEnumKey(); + switch (scalarProperty) { + case CUSTOM: { + PlyImporter.LOGGER.log(Level.FINE, "Custom data of the " + propertyWithKeyWord + + " skipped in the method responsible for treating build-in data"); + break; + } + default: + throw new UnsupportedOperationException( + "Missing implementation: the " + propertyWithKeyWord + + " is not supported by the " + elementWithKeyword + " yet"); + } + } else if (propertyWithKeyWord instanceof ListPropertyWithKeyword) { + final ListProperty listProperty = ((ListPropertyWithKeyword) propertyWithKeyWord) + .getEnumKey(); + switch (listProperty) { + case CUSTOM: { + PlyImporter.LOGGER.log(Level.FINE, "Custom data of the " + propertyWithKeyWord + + " skipped in the method responsible for treating build-in data"); + break; + } + case VERTEX_INDICES: { + if (faceInfo == null) { + faceInfo = new PlyFaceInfo(); + store.addFace(faceInfo); + } + faceInfo.addVertexIndex((int) value); + break; + } + case TEXCOORD: { + if (faceInfo == null) { + faceInfo = new PlyFaceInfo(); + store.addFace(faceInfo); + } + faceInfo.addTextureCoordinate((float) value); + break; + } + case MATERIAL_INDICES: { + if (faceInfo == null) { + faceInfo = new PlyFaceInfo(); + store.addFace(faceInfo); + } + faceInfo.addMaterialIndex((int) value); + break; + } + default: + throw new UnsupportedOperationException( + "Missing implementation: the " + propertyWithKeyWord + + " is not supported by the " + elementWithKeyword + " yet"); + } + } + break; + } + case VERTEX: { + if (propertyWithKeyWord instanceof ScalarPropertyWithKeyword) { + final ScalarProperty scalarProperty = ((ScalarPropertyWithKeyword) propertyWithKeyWord) + .getEnumKey(); + switch (scalarProperty) { + case CUSTOM: { + PlyImporter.LOGGER.log(Level.FINE, "Custom data of the " + propertyWithKeyWord + + " skipped in the method responsible for treating build-in data"); + break; + } + case X: { + if (vertex == null) { + vertex = new Vector3(); + store.getDataStore().getVertices().add(vertex); + } + vertex.setX(value); + break; + } + case Y: { + if (vertex == null) { + vertex = new Vector3(); + store.getDataStore().getVertices().add(vertex); + } + vertex.setY(value); + break; + } + case Z: { + if (vertex == null) { + vertex = new Vector3(); + store.getDataStore().getVertices().add(vertex); + } + vertex.setZ(value); + break; + } + case NX: { + if (normal == null) { + normal = new Vector3(); + store.getDataStore().getNormals().add(normal); + } + normal.setX(value); + break; + } + case NY: { + if (normal == null) { + normal = new Vector3(); + store.getDataStore().getNormals().add(normal); + } + normal.setY(value); + break; + } + case NZ: { + if (normal == null) { + normal = new Vector3(); + store.getDataStore().getNormals().add(normal); + } + normal.setZ(value); + break; + } + case S: { + if (texCoords == null) { + texCoords = new Vector2(); + store.getDataStore().getTextureCoordinates().add(texCoords); + } + texCoords.setX(value); + break; + } + case T: { + if (texCoords == null) { + texCoords = new Vector2(); + store.getDataStore().getTextureCoordinates().add(texCoords); + } + texCoords.setY(value); + break; + } + case RED: { + if (color == null) { + color = new ColorRGBA(); + store.getDataStore().getColors().add(color); + } + color.setRed((float) (value / 255.0d)); + break; + } + case GREEN: { + if (color == null) { + color = new ColorRGBA(); + store.getDataStore().getColors().add(color); + } + color.setGreen((float) (value / 255.0d)); + break; + } + case BLUE: { + if (color == null) { + color = new ColorRGBA(); + store.getDataStore().getColors().add(color); + } + color.setBlue((float) (value / 255.0d)); + break; + } + default: + throw new UnsupportedOperationException( + "Missing implementation: the " + propertyWithKeyWord + + " is not supported by the " + elementWithKeyword + " yet"); + } + } else if (propertyWithKeyWord instanceof ListPropertyWithKeyword) { + final ListProperty listProperty = ((ListPropertyWithKeyword) propertyWithKeyWord) + .getEnumKey(); + switch (listProperty) { + case CUSTOM: { + PlyImporter.LOGGER.log(Level.FINE, "Custom data of the " + propertyWithKeyWord + + " skipped in the method responsible for treating build-in data"); + break; + } + default: + throw new UnsupportedOperationException( + "Missing implementation: the " + propertyWithKeyWord + + " is not supported by the " + elementWithKeyword + " yet"); + } + } + break; + } + default: + PlyImporter.LOGGER.log(Level.SEVERE, "Element '" + elementWithKeyword.getEnumKey() + "[" + + elementWithKeyword.getKeyword() + "]' unsupported on line " + lineNumber); + } + } else { + throw new IllegalArgumentException( + "The " + propertyWithKeyWord + " is not supported by the " + elementWithKeyword); + } + } + valueIndex++; + } + } + + /** + * Processes the data within a custom element or within a build-in element with custom properties. The default + * implementation just displays a warning message as it's up to the developer to manage the user-defined custom data + * whose behaviour isn't defined in the specification + * + * @param formatWithVersionNumber + * format with version number + * @param elementWithKeyword + * element and keyword + * @param elementProperties + * properties of the element + * @param values + * parsed values contained in a single line of file + * @param lineNumber + * number of the line in the PLY file being parsed + * @param store + * geometry store to fill during the process + */ + protected void processElementCustomData(final FormatWithVersionNumber formatWithVersionNumber, + final ElementWithKeyword elementWithKeyword, final Set<AbstractPropertyWithKeyword<?>> elementProperties, + final double[] values, final int lineNumber, final PlyGeometryStore store) { + PlyImporter.LOGGER.log(Level.WARNING, + "'" + elementWithKeyword.getEnumKey().name() + "[" + elementWithKeyword.getKeyword() + + "]' ignored on line " + lineNumber + ". Override " + getClass().getCanonicalName() + + ".processElementCustomData() to remove this warning"); + } + + public void setModelLocator(final ResourceLocator locator) { + _modelLocator = locator; + } + + public void setTextureLocator(final ResourceLocator locator) { + _textureLocator = locator; + } + + public void setFlipTextureVertically(final boolean flipTextureVertically) { + _flipTextureVertically = flipTextureVertically; + } + + public boolean isFlipTextureVertically() { + return _flipTextureVertically; + } + + public void setUseCompression(final boolean useCompression) { + _useCompression = useCompression; + } + + public boolean isUseCompression() { + return _useCompression; + } + + public void setMinificationFilter(final MinificationFilter minificationFilter) { + _minificationFilter = minificationFilter; + } + + public MinificationFilter getMinificationFilter() { + return _minificationFilter; + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyMaterial.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyMaterial.java new file mode 100644 index 0000000..ffa4f6c --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/ply/PlyMaterial.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2008-2014 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.extension.model.ply; + +import com.ardor3d.extension.model.util.AbstractMaterial; + +public class PlyMaterial extends AbstractMaterial { + + public PlyMaterial() { + super(); + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlDataStore.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlDataStore.java new file mode 100644 index 0000000..52d6d0d --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlDataStore.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2008-2014 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.extension.model.stl; + +import java.util.ArrayList; +import java.util.List; + +import com.ardor3d.math.Vector3; + +public class StlDataStore { + private final List<Vector3> _vertices; + private final List<Vector3> _normals; + + public StlDataStore() { + super(); + _vertices = new ArrayList<>(); + _normals = new ArrayList<>(); + } + + public List<Vector3> getVertices() { + return _vertices; + } + + public List<Vector3> getNormals() { + return _normals; + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlGeometryStore.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlGeometryStore.java new file mode 100644 index 0000000..3e7fe7a --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlGeometryStore.java @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2008-2014 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.extension.model.stl; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +import com.ardor3d.math.ColorRGBA; +import com.ardor3d.math.Vector3; +import com.ardor3d.math.type.ReadOnlyColorRGBA; +import com.ardor3d.scenegraph.Mesh; +import com.ardor3d.scenegraph.MeshData; +import com.ardor3d.scenegraph.Node; +import com.ardor3d.util.geom.BufferUtils; + +public class StlGeometryStore { + + private final StlDataStore _dataStore; + + private final Node _root; + + private String _currentObjectName; + + public StlGeometryStore() { + super(); + _dataStore = new StlDataStore(); + _root = new Node(); + } + + public StlDataStore getDataStore() { + return _dataStore; + } + + public Node getScene() { + return _root; + } + + void setCurrentObjectName(final String name) { + commitObjects(); + _currentObjectName = name; + } + + void commitObjects() { + if (!_dataStore.getNormals().isEmpty()) { + final String name; + if (_currentObjectName == null) { + name = "stl_mesh"; + } else { + name = _currentObjectName; + } + + // mesh object to return + final Mesh mesh = new Mesh(name); + final MeshData meshData = mesh.getMeshData(); + + // allocate buffers + final int numberTriangles = _dataStore.getNormals().size(); + final int vertexBufferSize = 3 * numberTriangles; + final FloatBuffer vertexBuffer = BufferUtils.createVector3Buffer(vertexBufferSize); + final FloatBuffer normalBuffer = BufferUtils.createVector3Buffer(vertexBufferSize); + final FloatBuffer colourBuffer = BufferUtils.createColorBuffer(vertexBufferSize); + + // fill buffers + int vertexCount = 0; + int normalCount = 0; + int colourCount = 0; + Vector3 v0; + Vector3 v1; + Vector3 v2; + Vector3 n; + final ReadOnlyColorRGBA defaultColour = ColorRGBA.WHITE; + for (int i = 0; i < numberTriangles; i++) { + // triangle properties + v0 = _dataStore.getVertices().get(3 * i + 0); + v1 = _dataStore.getVertices().get(3 * i + 1); + v2 = _dataStore.getVertices().get(3 * i + 2); + n = _dataStore.getNormals().get(i); + // vertices + BufferUtils.setInBuffer(v0, vertexBuffer, vertexCount++); + BufferUtils.setInBuffer(v1, vertexBuffer, vertexCount++); + BufferUtils.setInBuffer(v2, vertexBuffer, vertexCount++); + // normals - 1 foreach vertex + BufferUtils.setInBuffer(n, normalBuffer, normalCount++); + BufferUtils.setInBuffer(n, normalBuffer, normalCount++); + BufferUtils.setInBuffer(n, normalBuffer, normalCount++); + // colours - 1 foreach vertex + BufferUtils.setInBuffer(defaultColour, colourBuffer, colourCount++); + BufferUtils.setInBuffer(defaultColour, colourBuffer, colourCount++); + BufferUtils.setInBuffer(defaultColour, colourBuffer, colourCount++); + } + + meshData.setVertexBuffer(vertexBuffer); + meshData.setNormalBuffer(normalBuffer); + meshData.setColorBuffer(colourBuffer); + + // indices buffer + final int[] indices = new int[vertexBufferSize]; + for (int i = 0; i < vertexBufferSize; i++) { + indices[i] = i; + } + final IntBuffer iBuffer = BufferUtils.createIntBuffer(indices.length); + iBuffer.put(indices); + meshData.setIndexBuffer(iBuffer); + + _root.attachChild(mesh); + } + } + + void cleanup() { + _currentObjectName = null; + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlImporter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlImporter.java new file mode 100644 index 0000000..a5aeed9 --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/stl/StlImporter.java @@ -0,0 +1,545 @@ +/** + * Copyright (c) 2008-2016 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.extension.model.stl; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StreamTokenizer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.ardor3d.extension.model.util.FileHelper; +import com.ardor3d.math.Vector3; +import com.ardor3d.util.resource.ResourceLocator; +import com.ardor3d.util.resource.ResourceLocatorTool; +import com.ardor3d.util.resource.ResourceSource; + +/** + * Reads an STL (STereoLithography) file and builds an Ardor3D Mesh. The STL format consists entirely of triangles and + * as a result is a simple format to handle. Also, it is widely supported by the CAD/CAM community. + * + * This class supports both ASCII and Binary formats and files residing either locally or on a network. + * + * Refer to <a href="http://en.wikipedia.org/wiki/STL_(file_format)" target="_blank>Wikipedia</a>. Several STL models + * can be downloaded freely from <a href="http://grabcad.com" target="_blank">GrabCAD</a>. + * + * @author gmseed + * @see StlFileParser + */ +public class StlImporter { + + /** + * Extends StreamTokenizer for parsing STL files. The STL format for Ascii files is as follows: + * + * <pre> + * solid name + * ... + * facet normal ni nj nk + * outer loop + * vertex v1x v1y v1z + * vertex v2x v2y v2z + * vertex v3x v3y v3z + * endloop + * endfacet + * ... + * endsolid name + * </pre> + * + * @author gmseed + */ + public static class StlFileParser extends StreamTokenizer { + + /** + * Constructor. + * + * @param reader + * The Reader. + */ + public StlFileParser(final Reader reader) { + super(reader); + resetSyntax(); + eolIsSignificant(true); + lowerCaseMode(true); + + // all printable ascii characters + wordChars('!', '~'); + + whitespaceChars(' ', ' '); + whitespaceChars('\n', '\n'); + whitespaceChars('\r', '\r'); + whitespaceChars('\t', '\t'); + } + + /** + * Gets a number from the stream. Need to extract numbers since they may be in scientific notation. The number + * is returned in nval. + * + * @return Logical-true if successful, else logical-false. + */ + protected boolean getNumber() { + try { + nextToken(); + if (ttype != StreamTokenizer.TT_WORD) { + return false; + } + nval = Double.valueOf(sval).doubleValue(); + } catch (final IOException e) { + System.err.println(e.getMessage()); + return false; + } catch (final NumberFormatException e) { + System.err.println(e.getMessage()); + return false; + } + return true; + } + + } + + // logger + private static final Logger LOGGER = Logger.getLogger(StlImporter.class.getName()); + + private static final String SOLID_KEYWORD = "solid"; + private static final String[] END_SOLID_KEYWORD_PARTS = new String[] { "end", "solid" }; + private static final String ENDSOLID_KEYWORD = "endsolid"; + private static final String FACET_KEYWORD = "facet"; + private static final String NORMAL_KEYWORD = "normal"; + private static final String[] OUTER_LOOP_KEYWORD_PARTS = new String[] { "outer", "loop" }; + private static final String VERTEX_KEYWORD = "vertex"; + private static final String ENDLOOP_KEYWORD = "endloop"; + private static final String ENDFACET_KEYWORD = "endfacet"; + + private ResourceLocator _modelLocator; + + /** + * Constructor. + */ + public StlImporter() { + super(); + } + + /** + * Reads a STL file from the given resource + * + * @param resource + * the name of the resource to find. + * @return a StlGeometryStore data object containing the scene and other useful elements. + */ + public StlGeometryStore load(final String resource) { + return load(resource, new FileHelper()); + } + + /** + * Reads a STL file from the given resource + * + * @param resource + * the name of the resource to find. + * @param fileHelper + * the file helper used to determine whether the resource is an Ascii file or a binary file + * + * @return a StlGeometryStore data object containing the scene and other useful elements. + */ + public StlGeometryStore load(final String resource, final FileHelper fileHelper) { + final ResourceSource source; + if (_modelLocator == null) { + source = ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_MODEL, resource); + } else { + source = _modelLocator.locateResource(resource); + } + + if (source == null) { + throw new Error("Unable to locate '" + resource + "'"); + } + + return load(source, fileHelper); + } + + /** + * Reads a STL file from the given resource + * + * @param resource + * the resource to find. + * @return a StlGeometryStore data object containing the scene and other useful elements. + */ + public StlGeometryStore load(final ResourceSource resource) { + return load(resource, new FileHelper()); + } + + /** + * Reads a STL file from the given resource + * + * @param resource + * the resource to find. + * @param fileHelper + * the file helper used to determine whether the resource is an Ascii file or a binary file + * + * @return a StlGeometryStore data object containing the scene and other useful elements. + */ + public StlGeometryStore load(final ResourceSource resource, final FileHelper fileHelper) { + final boolean isAscii = fileHelper.isFilePureAscii(resource); + final StlGeometryStore store = new StlGeometryStore(); + if (isAscii) { // Ascii file + try (final BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream()))) { + final StlFileParser parser = new StlFileParser(reader); + try { + parser.nextToken(); + // read "solid" + if (parser.sval != null && StlImporter.SOLID_KEYWORD.equals(parser.sval)) { + StlImporter.LOGGER.log(Level.INFO, "solid keyword on line " + parser.lineno()); + } else { + StlImporter.LOGGER.log(Level.SEVERE, + "Ascii file but no solid keyword on line " + parser.lineno()); + } + parser.nextToken(); + // read object name if any + if (parser.ttype != StreamTokenizer.TT_WORD) { + StlImporter.LOGGER.log(Level.WARNING, + "Format Warning: expecting the (optional) object name on line " + parser.lineno()); + } else { + final String objectName = parser.sval; + store.setCurrentObjectName(objectName); + // Reads the EOL for verifying that the file has a correct format + parser.nextToken(); + if (parser.ttype != StreamTokenizer.TT_EOL) { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting End Of Line on line " + parser.lineno()); + } + } + // read the rest of the file + parser.nextToken(); + boolean endSolidFound = false; + // read all the facets + while (parser.ttype != StreamTokenizer.TT_EOF && !endSolidFound) { + endSolidFound = false; + // reads "endsolid" + if (StlImporter.ENDSOLID_KEYWORD.equals(parser.sval)) { + StlImporter.LOGGER.log(Level.INFO, "endsolid keyword on line " + parser.lineno()); + endSolidFound = true; + } else { + // reads "end solid" + if (StlImporter.END_SOLID_KEYWORD_PARTS[0].equals(parser.sval)) { + parser.nextToken(); + if (parser.ttype != StreamTokenizer.TT_WORD + || !StlImporter.END_SOLID_KEYWORD_PARTS[1].equals(parser.sval)) { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error:expecting 'end solid' on line " + parser.lineno()); + } else { + StlImporter.LOGGER.log(Level.INFO, "end solid keyword on line " + parser.lineno()); + endSolidFound = true; + } + } else { + // Reads "facet" + if (parser.ttype != StreamTokenizer.TT_WORD + || (!StlImporter.FACET_KEYWORD.equals(parser.sval) + && !StlImporter.END_SOLID_KEYWORD_PARTS[0].equals(parser.sval))) { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error:expecting 'facet' on line " + parser.lineno()); + } else { + parser.nextToken(); + // Reads a normal + if (parser.ttype != StreamTokenizer.TT_WORD + || !StlImporter.NORMAL_KEYWORD.equals(parser.sval)) { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error:expecting 'normal' on line " + parser.lineno()); + } else { + if (parser.getNumber()) { + final Vector3 normal = new Vector3(); + normal.setX(parser.nval); + + if (parser.getNumber()) { + normal.setY(parser.nval); + + if (parser.getNumber()) { + normal.setZ(parser.nval); + + store.getDataStore().getNormals().add(normal); + // Reads the EOL for verifying that the file has a correct format + parser.nextToken(); + if (parser.ttype != StreamTokenizer.TT_EOL) { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting End Of Line on line " + + parser.lineno()); + } + } else { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting normal z-component on line " + + parser.lineno()); + } + } else { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting normal y-component on line " + + parser.lineno()); + } + } else { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting normal x-component on line " + + parser.lineno()); + } + } + + parser.nextToken(); + // Reads "outer loop" then EOL + if (parser.ttype != StreamTokenizer.TT_WORD + || !StlImporter.OUTER_LOOP_KEYWORD_PARTS[0].equals(parser.sval)) { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting 'outer' on line " + parser.lineno()); + } else { + parser.nextToken(); + if (parser.ttype != StreamTokenizer.TT_WORD + || !StlImporter.OUTER_LOOP_KEYWORD_PARTS[1].equals(parser.sval)) { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error:expecting 'loop' on line " + parser.lineno()); + } else { + // Reads the EOL for verifying that the file has a correct format + parser.nextToken(); + if (parser.ttype != StreamTokenizer.TT_EOL) { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting End Of Line on line " + + parser.lineno()); + } + } + } + + parser.nextToken(); + // Reads the first vertex + if (parser.ttype != StreamTokenizer.TT_WORD + || !StlImporter.VERTEX_KEYWORD.equals(parser.sval)) { + System.err + .println("Format Error:expecting 'vertex' on line " + parser.lineno()); + } else { + if (parser.getNumber()) { + final Vector3 vertex = new Vector3(); + vertex.setX(parser.nval); + + if (parser.getNumber()) { + vertex.setY(parser.nval); + + if (parser.getNumber()) { + vertex.setZ(parser.nval); + + store.getDataStore().getVertices().add(vertex); + // Reads the EOL for verifying that the file has a correct format + parser.nextToken(); + if (parser.ttype != StreamTokenizer.TT_EOL) { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting End Of Line on line " + + parser.lineno()); + } + } else { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting vertex z-component on line " + + parser.lineno()); + } + } else { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting vertex y-component on line " + + parser.lineno()); + } + } else { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting vertex x-component on line " + + parser.lineno()); + } + } + + parser.nextToken(); + // Reads the second vertex + if (parser.ttype != StreamTokenizer.TT_WORD + || !StlImporter.VERTEX_KEYWORD.equals(parser.sval)) { + System.err + .println("Format Error:expecting 'vertex' on line " + parser.lineno()); + } else { + if (parser.getNumber()) { + final Vector3 vertex = new Vector3(); + vertex.setX(parser.nval); + + if (parser.getNumber()) { + vertex.setY(parser.nval); + + if (parser.getNumber()) { + vertex.setZ(parser.nval); + + store.getDataStore().getVertices().add(vertex); + // Reads the EOL for verifying that the file has a correct format + parser.nextToken(); + if (parser.ttype != StreamTokenizer.TT_EOL) { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting End Of Line on line " + + parser.lineno()); + } + } else { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting vertex z-component on line " + + parser.lineno()); + } + } else { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting vertex y-component on line " + + parser.lineno()); + } + } else { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting vertex x-component on line " + + parser.lineno()); + } + } + + parser.nextToken(); + // Reads the third vertex + if (parser.ttype != StreamTokenizer.TT_WORD + || !StlImporter.VERTEX_KEYWORD.equals(parser.sval)) { + System.err + .println("Format Error:expecting 'vertex' on line " + parser.lineno()); + } else { + if (parser.getNumber()) { + final Vector3 vertex = new Vector3(); + vertex.setX(parser.nval); + + if (parser.getNumber()) { + vertex.setY(parser.nval); + + if (parser.getNumber()) { + vertex.setZ(parser.nval); + + store.getDataStore().getVertices().add(vertex); + // Reads the EOL for verifying that the file has a correct format + parser.nextToken(); + if (parser.ttype != StreamTokenizer.TT_EOL) { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting End Of Line on line " + + parser.lineno()); + } + } else { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting vertex z-component on line " + + parser.lineno()); + } + } else { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting vertex y-component on line " + + parser.lineno()); + } + } else { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting vertex x-component on line " + + parser.lineno()); + } + } + + parser.nextToken(); + // Reads "endloop" then EOL + if (parser.ttype != StreamTokenizer.TT_WORD + || !StlImporter.ENDLOOP_KEYWORD.equals(parser.sval)) { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting 'endloop' on line " + parser.lineno()); + } else { + // Reads the EOL for verifying that the file has a correct format + parser.nextToken(); + if (parser.ttype != StreamTokenizer.TT_EOL) { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting End Of Line on line " + parser.lineno()); + } + } + + parser.nextToken(); + // Reads "endfacet" then EOL + if (parser.ttype != StreamTokenizer.TT_WORD + || !StlImporter.ENDFACET_KEYWORD.equals(parser.sval)) { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error:expecting 'endfacet' on line " + parser.lineno()); + } else { + // Reads the EOL for verifying that the file has a correct format + parser.nextToken(); + if (parser.ttype != StreamTokenizer.TT_EOL) { + StlImporter.LOGGER.log(Level.SEVERE, + "Format Error: expecting End Of Line on line " + parser.lineno()); + } + } + } + } + } + parser.nextToken(); + } + } catch (final IOException e) { + StlImporter.LOGGER.log(Level.SEVERE, "IO Error on line " + parser.lineno() + ": " + e.getMessage()); + } + } catch (final Throwable t) { + throw new Error("Unable to load stl resource from URL: " + resource, t); + } + } else { // Binary file + try (final InputStream data = resource.openStream()) { + ByteBuffer dataBuffer; // To read in the correct endianness + final byte[] info = new byte[80]; // Header data + final byte[] numberFaces = new byte[4]; // the number of faces + byte[] faceData; // face data + int numberTriangles; // First info (after the header) on the file + + // the first 80 bytes aren't important (except if you want to support non standard colors) + if (80 != data.read(info)) { + throw new IOException("Format Error: 80 bytes expected"); + } else { + // read number of faces, setting the correct order + data.read(numberFaces); + dataBuffer = ByteBuffer.wrap(numberFaces); + dataBuffer.order(ByteOrder.nativeOrder()); + + // allocate buffer for face data, with each face requiring 50 bytes + numberTriangles = dataBuffer.getInt(); + faceData = new byte[50 * numberTriangles]; + + // read face data + data.read(faceData); + dataBuffer = ByteBuffer.wrap(faceData); + dataBuffer.order(ByteOrder.nativeOrder()); + + // read each facet noting that after each fact there are 2 bytes without information + // no need to skip for last iteration + for (int index = 0; index < numberTriangles; index++) { + try { + // Reads a facet from a binary file. + // normal + store.getDataStore().getNormals().add( + new Vector3(dataBuffer.getFloat(), dataBuffer.getFloat(), dataBuffer.getFloat())); + // 3 vertices + store.getDataStore().getVertices().add( + new Vector3(dataBuffer.getFloat(), dataBuffer.getFloat(), dataBuffer.getFloat())); + store.getDataStore().getVertices().add( + new Vector3(dataBuffer.getFloat(), dataBuffer.getFloat(), dataBuffer.getFloat())); + store.getDataStore().getVertices().add( + new Vector3(dataBuffer.getFloat(), dataBuffer.getFloat(), dataBuffer.getFloat())); + if (index != numberTriangles - 1) { + dataBuffer.get(); + dataBuffer.get(); + } + } catch (final Throwable t) { + throw new Exception("Format Error: iteration number " + index, t); + } + } + } + } catch (final Throwable t) { + throw new Error("Unable to load stl resource from URL: " + resource, t); + } + } + + store.commitObjects(); + store.cleanup(); + return store; + + } + + public void setModelLocator(final ResourceLocator locator) { + _modelLocator = locator; + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/AbstractMaterial.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/AbstractMaterial.java new file mode 100644 index 0000000..c008479 --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/AbstractMaterial.java @@ -0,0 +1,244 @@ +/** + * Copyright (c) 2008-2014 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.extension.model.util; + +import com.ardor3d.math.ColorRGBA; +import com.ardor3d.math.MathUtils; +import com.ardor3d.renderer.state.BlendState; +import com.ardor3d.renderer.state.MaterialState; + +/** + * common material parameters + */ +public abstract class AbstractMaterial { + + private float ambientRed, ambientGreen, ambientBlue, ambientAlpha; + + private float diffuseRed, diffuseGreen, diffuseBlue, diffuseAlpha; + + private float emissiveRed, emissiveGreen, emissiveBlue, emissiveAlpha; + + private float specularRed, specularGreen, specularBlue, specularAlpha; + + private float shininess; + + private boolean forceBlend; + + protected AbstractMaterial() { + super(); + ambientRed = -1; + ambientGreen = -1; + ambientBlue = -1; + ambientAlpha = -1; + diffuseRed = -1; + diffuseGreen = -1; + diffuseBlue = -1; + diffuseAlpha = -1; + emissiveRed = -1; + emissiveGreen = -1; + emissiveBlue = -1; + emissiveAlpha = -1; + specularRed = -1; + specularGreen = -1; + specularBlue = -1; + specularAlpha = -1; + shininess = -1; + } + + public BlendState getBlendState() { + if (forceBlend || (ambientAlpha != -1 && ambientAlpha < 1.0f) || (diffuseAlpha != -1 && diffuseAlpha < 1.0f) + || (emissiveAlpha != -1 && emissiveAlpha < 1.0f) || (specularAlpha != -1 && specularAlpha < 1.0f)) { + final BlendState blend = new BlendState(); + blend.setBlendEnabled(true); + blend.setSourceFunction(BlendState.SourceFunction.SourceAlpha); + blend.setDestinationFunction(BlendState.DestinationFunction.OneMinusSourceAlpha); + blend.setTestEnabled(true); + blend.setTestFunction(BlendState.TestFunction.GreaterThan); + blend.setReference(0); + return blend; + } + return null; + } + + public MaterialState getMaterialState() { + if ((ambientRed != -1 && ambientGreen != -1 && ambientBlue != -1) + || (diffuseRed != -1 && diffuseGreen != -1 && diffuseBlue != -1) + || (emissiveRed != -1 && emissiveGreen != -1 && emissiveBlue != -1) + || (specularRed != -1 && specularGreen != -1 && specularBlue != -1) || shininess != -1) { + final MaterialState material = new MaterialState(); + if (ambientRed != -1 && ambientGreen != -1 && ambientBlue != -1) { + final float alpha = ambientAlpha == -1 ? 1 : MathUtils.clamp(ambientAlpha, 0, 1); + material.setAmbient(new ColorRGBA(ambientRed, ambientGreen, ambientBlue, alpha)); + } + if (diffuseRed != -1 && diffuseGreen != -1 && diffuseBlue != -1) { + final float alpha = diffuseAlpha == -1 ? 1 : MathUtils.clamp(diffuseAlpha, 0, 1); + material.setDiffuse(new ColorRGBA(diffuseRed, diffuseGreen, diffuseBlue, alpha)); + } + if (emissiveRed != -1 && emissiveGreen != -1 && emissiveBlue != -1) { + final float alpha = emissiveAlpha == -1 ? 1 : MathUtils.clamp(emissiveAlpha, 0, 1); + material.setEmissive(new ColorRGBA(emissiveRed, emissiveGreen, emissiveBlue, alpha)); + } + if (specularRed != -1 && specularGreen != -1 && specularBlue != -1) { + final float alpha = specularAlpha == -1 ? 1 : MathUtils.clamp(specularAlpha, 0, 1); + material.setSpecular(new ColorRGBA(specularRed, specularGreen, specularBlue, alpha)); + } + if (shininess != -1) { + material.setShininess(shininess); + } + return material; + } + return null; + } + + public float getAmbientRed() { + return ambientRed; + } + + public void setAmbientRed(final float ambientRed) { + this.ambientRed = ambientRed; + } + + public float getAmbientGreen() { + return ambientGreen; + } + + public void setAmbientGreen(final float ambientGreen) { + this.ambientGreen = ambientGreen; + } + + public float getAmbientBlue() { + return ambientBlue; + } + + public void setAmbientBlue(final float ambientBlue) { + this.ambientBlue = ambientBlue; + } + + public float getDiffuseRed() { + return diffuseRed; + } + + public void setDiffuseRed(final float diffuseRed) { + this.diffuseRed = diffuseRed; + } + + public float getDiffuseGreen() { + return diffuseGreen; + } + + public void setDiffuseGreen(final float diffuseGreen) { + this.diffuseGreen = diffuseGreen; + } + + public float getDiffuseBlue() { + return diffuseBlue; + } + + public void setDiffuseBlue(final float diffuseBlue) { + this.diffuseBlue = diffuseBlue; + } + + public float getEmissiveRed() { + return emissiveRed; + } + + public void setEmissiveRed(final float emissiveRed) { + this.emissiveRed = emissiveRed; + } + + public float getEmissiveGreen() { + return emissiveGreen; + } + + public void setEmissiveGreen(final float emissiveGreen) { + this.emissiveGreen = emissiveGreen; + } + + public float getEmissiveBlue() { + return emissiveBlue; + } + + public void setEmissiveBlue(final float emissiveBlue) { + this.emissiveBlue = emissiveBlue; + } + + public float getSpecularRed() { + return specularRed; + } + + public void setSpecularRed(final float specularRed) { + this.specularRed = specularRed; + } + + public float getSpecularGreen() { + return specularGreen; + } + + public void setSpecularGreen(final float specularGreen) { + this.specularGreen = specularGreen; + } + + public float getSpecularBlue() { + return specularBlue; + } + + public void setSpecularBlue(final float specularBlue) { + this.specularBlue = specularBlue; + } + + public float getAmbientAlpha() { + return ambientAlpha; + } + + public void setAmbientAlpha(final float ambientAlpha) { + this.ambientAlpha = ambientAlpha; + } + + public float getDiffuseAlpha() { + return diffuseAlpha; + } + + public void setDiffuseAlpha(final float diffuseAlpha) { + this.diffuseAlpha = diffuseAlpha; + } + + public float getEmissiveAlpha() { + return emissiveAlpha; + } + + public void setEmissiveAlpha(final float emissiveAlpha) { + this.emissiveAlpha = emissiveAlpha; + } + + public float getSpecularAlpha() { + return specularAlpha; + } + + public void setSpecularAlpha(final float specularAlpha) { + this.specularAlpha = specularAlpha; + } + + public float getShininess() { + return shininess; + } + + public void setShininess(final float shininess) { + this.shininess = shininess; + } + + public boolean isForceBlend() { + return forceBlend; + } + + public void setForceBlend(final boolean forceBlend) { + this.forceBlend = forceBlend; + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/FileHelper.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/FileHelper.java new file mode 100644 index 0000000..8a9c5cf --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/FileHelper.java @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2008-2016 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.extension.model.util; + +import java.io.BufferedReader; +import java.io.DataInputStream; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.StandardCharsets; + +import com.ardor3d.util.resource.ResourceSource; + +public class FileHelper { + + /** + * Tests whether or not the specified string is pure ASCII. Uses the method discussed at: + * + * <pre> + * http://www.rgagnon.com/javadetails/java-0536.html + * http://stackoverflow.com/questions/3585053/in-java-is-it-possible-to-check-if-a-string-is-only-ascii + * </pre> + * + * @param string + * String to test. + * @return Logical-true if pure ASCII, else logical-false. + */ + public boolean isStringPureAscii(final String string) { + final byte bytearray[] = string.getBytes(); + final CharsetDecoder d = StandardCharsets.US_ASCII.newDecoder(); + try { + final CharBuffer r = d.decode(ByteBuffer.wrap(bytearray)); + r.toString(); + } catch (final CharacterCodingException e) { + return false; + } + return true; + } + + /** + * Tests whether or not the file with the specified filename is pure ASCII. The method used is to read the file a + * line at a time and test if each line is ASCII. + * + * @param filename + * File name. + * @return Logical-true if pure ASCII, else logical-false. + */ + public boolean isFilePureAscii(final String filename) { + try (final FileInputStream fstream = new FileInputStream(filename); + final DataInputStream in = new DataInputStream(fstream); + final BufferedReader br = new BufferedReader(new InputStreamReader(in))) { + String strLine; + // read file a line at a time + while ((strLine = br.readLine()) != null) { + final boolean isAscii = isStringPureAscii(strLine); + if (!isAscii) { + return false; + } + } + } catch (final Exception e) { + return false; + } + return true; + } + + /** + * Tests whether or not the file with the specified filename is pure ASCII. The method used is to read the file a + * line at a time and test if each line is ASCII. + * + * @param resource + * the name of the resource to find. + * @return Logical-true if pure ASCII, else logical-false. + */ + public boolean isFilePureAscii(final ResourceSource resource) { + try (final InputStream inputStream = resource.openStream(); + final DataInputStream in = new DataInputStream(inputStream); + final BufferedReader br = new BufferedReader(new InputStreamReader(in))) { + String strLine; + // read file a line at a time + while ((strLine = br.readLine()) != null) { + final boolean isAscii = isStringPureAscii(strLine); + if (!isAscii) { + return false; + } + } + } catch (final Exception e) { + return false; + } + return true; + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/KeyframeController.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/KeyframeController.java index 15c810a..e84db4f 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/KeyframeController.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/KeyframeController.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * @@ -127,7 +127,7 @@ public class KeyframeController<T extends Spatial> extends ComplexSpatialControl */ public KeyframeController() { setSpeed(1); - _keyframes = new ArrayList<PointInTime>(); + _keyframes = new ArrayList<>(); _curFrame = 0; setRepeatType(ComplexSpatialController.RepeatType.WRAP); setMinTime(0); @@ -245,7 +245,7 @@ public class KeyframeController<T extends Spatial> extends ComplexSpatialControl } Mesh begin = null, end = null; if (_prevKeyframes == null) { - _prevKeyframes = new ArrayList<PointInTime>(); + _prevKeyframes = new ArrayList<>(); begin = new Mesh(); end = new Mesh(); } else { diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvEdgeInfo.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvEdgeInfo.java index ee49d56..9190145 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvEdgeInfo.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvEdgeInfo.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvFaceInfo.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvFaceInfo.java index 688c73d..3f847ca 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvFaceInfo.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvFaceInfo.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripInfo.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripInfo.java index 2cf0e18..575cd87 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripInfo.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripInfo.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * @@ -10,13 +10,12 @@ package com.ardor3d.extension.model.util.nvtristrip; +import java.util.ArrayList; import java.util.List; -import com.google.common.collect.Lists; - final class NvStripInfo { NvStripStartInfo _startInfo; - List<NvFaceInfo> _faces = Lists.newArrayList(); + List<NvFaceInfo> _faces = new ArrayList<>(); int _stripId; int _experimentId; @@ -177,11 +176,11 @@ final class NvStripInfo { */ void build(final List<NvEdgeInfo> edgeInfos, final List<NvFaceInfo> faceInfos) { // used in building the strips forward and backward - final List<Integer> scratchIndices = Lists.newArrayList(); + final List<Integer> scratchIndices = new ArrayList<>(); // build forward... start with the initial face - final List<NvFaceInfo> forwardFaces = Lists.newArrayList(); - final List<NvFaceInfo> backwardFaces = Lists.newArrayList(); + final List<NvFaceInfo> forwardFaces = new ArrayList<>(); + final List<NvFaceInfo> backwardFaces = new ArrayList<>(); forwardFaces.add(_startInfo._startFace); markTriangle(_startInfo._startFace); @@ -251,7 +250,7 @@ final class NvStripInfo { // tempAllFaces is going to be forwardFaces + backwardFaces // it's used for Unique() - final List<NvFaceInfo> tempAllFaces = Lists.newArrayList(); + final List<NvFaceInfo> tempAllFaces = new ArrayList<>(); for (int i = 0; i < forwardFaces.size(); i++) { tempAllFaces.add(forwardFaces.get(i)); } diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripStartInfo.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripStartInfo.java index cecb0d3..7629d6d 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripStartInfo.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripStartInfo.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripifier.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripifier.java index 8779e5e..fbdf921 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripifier.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripifier.java @@ -1,22 +1,21 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * - * Ardor3D is free software: you can redistribute it and/or modify it + * 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.extension.model.util.nvtristrip; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.logging.Logger; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - /** * Ported from <a href="http://developer.nvidia.com/object/nvtristrip_library.html">NVIDIA's NvTriStrip Library</a> */ @@ -25,14 +24,14 @@ final class NvStripifier { public static int CACHE_INEFFICIENCY = 6; - protected List<Integer> _indices = Lists.newArrayList(); + protected List<Integer> _indices = new ArrayList<>(); protected int _cacheSize; protected int _minStripLength; protected float _meshJump; protected boolean _firstTimeResetPoint; /** - * + * * @param in_indices * the input indices of the mesh to stripify * @param in_cacheSize @@ -59,12 +58,12 @@ final class NvStripifier { _indices = in_indices; // build the stripification info - final List<NvFaceInfo> allFaceInfos = Lists.newArrayList(); - final List<NvEdgeInfo> allEdgeInfos = Lists.newArrayList(); + final List<NvFaceInfo> allFaceInfos = new ArrayList<>(); + final List<NvEdgeInfo> allEdgeInfos = new ArrayList<>(); buildStripifyInfo(allFaceInfos, allEdgeInfos, maxIndex); - final List<NvStripInfo> allStrips = Lists.newArrayList(); + final List<NvStripInfo> allStrips = new ArrayList<>(); // stripify findAllStrips(allStrips, allFaceInfos, allEdgeInfos, numSamples); @@ -75,7 +74,7 @@ final class NvStripifier { /** * Generates actual strips from the list-in-strip-order. - * + * * @param allStrips * @param stripIndices * @param bStitchStrips @@ -150,8 +149,8 @@ final class NvStripifier { stripIndices.add(tFirstFace._v0); // Check CW/CCW ordering - if (NvStripifier.nextIsCW(stripIndices.size() - accountForNegatives) != NvStripifier.isCW( - strip._faces.get(0), tFirstFace._v0, tFirstFace._v1)) { + if (NvStripifier.nextIsCW(stripIndices.size() - accountForNegatives) != NvStripifier + .isCW(strip._faces.get(0), tFirstFace._v0, tFirstFace._v1)) { stripIndices.add(tFirstFace._v0); } } @@ -325,7 +324,7 @@ final class NvStripifier { } /** - * + * * @param numIndices * @return true if the next face should be ordered in CW fashion */ @@ -385,7 +384,7 @@ final class NvStripifier { /** * find the edge info for these two indices - * + * * @param edgeInfos * @param v0 * @param v1 @@ -416,13 +415,14 @@ final class NvStripifier { /** * find the other face sharing these vertices - * + * * @param edgeInfos * @param v0 * @param v1 * @param faceInfo * @return */ + @SuppressWarnings("null") static NvFaceInfo findOtherFace(final List<NvEdgeInfo> edgeInfos, final int v0, final int v1, final NvFaceInfo faceInfo) { final NvEdgeInfo edgeInfo = NvStripifier.findEdgeInfo(edgeInfos, v0, v1); @@ -439,7 +439,7 @@ final class NvStripifier { /** * A good reset point is one near other committed areas so that we know that when we've made the longest strips its * because we're stripifying in the same general orientation. - * + * * @param faceInfos * @param edgeInfos * @return @@ -494,11 +494,11 @@ final class NvStripifier { /** * Does the stripification, puts output strips into vector allStrips - * + * * Works by setting running a number of experiments in different areas of the mesh, and accepting the one which * results in the longest strips. It then accepts this, and moves on to a different area of the mesh. We try to jump * around the mesh some, to ensure that large open spans of strips get generated. - * + * * @param allStrips * @param allFaceInfos * @param allEdgeInfos @@ -519,11 +519,11 @@ final class NvStripifier { // final List<NvStripInfo>[] experiments = new List[numSamples * 6]; for (int i = 0; i < experiments.length; i++) { - experiments[i] = Lists.newArrayList(); + experiments[i] = new ArrayList<>(); } int experimentIndex = 0; - final Set<NvFaceInfo> resetPoints = Sets.newHashSet(); + final Set<NvFaceInfo> resetPoints = new HashSet<>(); for (int i = 0; i < numSamples; i++) { // Try to find another good reset point. @@ -641,7 +641,7 @@ final class NvStripifier { /** * Splits the input vector of strips (allBigStrips) into smaller, cache friendly pieces, then reorders these pieces * to maximize cache hits. The final strips are stored in outStrips - * + * * @param allStrips * @param outStrips * @param edgeInfos @@ -650,7 +650,7 @@ final class NvStripifier { void splitUpStripsAndOptimize(final List<NvStripInfo> allStrips, final List<NvStripInfo> outStrips, final List<NvEdgeInfo> edgeInfos, final List<NvFaceInfo> outFaceList) { final int threshold = _cacheSize; - final List<NvStripInfo> tempStrips = Lists.newArrayList(); + final List<NvStripInfo> tempStrips = new ArrayList<>(); // split up strips into threshold-sized pieces for (int i = 0; i < allStrips.size(); i++) { @@ -681,9 +681,8 @@ final class NvStripifier { degenerateCount++; // last time or first time through, no need for a degenerate - if ((faceCtr + 1 != threshold + j * threshold + degenerateCount || j == numTimes - 1 - && numLeftover < 4 && numLeftover > 0) - && !bFirstTime) { + if ((faceCtr + 1 != threshold + j * threshold + degenerateCount + || j == numTimes - 1 && numLeftover < 4 && numLeftover > 0) && !bFirstTime) { currentStrip._faces.add(allStripI._faces.get(faceCtr++)); } else { ++faceCtr; @@ -747,7 +746,7 @@ final class NvStripifier { } // add small strips to face list - final List<NvStripInfo> tempStrips2 = Lists.newArrayList(); + final List<NvStripInfo> tempStrips2 = new ArrayList<>(); removeSmallStrips(tempStrips, tempStrips2, outFaceList); outStrips.clear(); @@ -868,7 +867,7 @@ final class NvStripifier { final List<NvFaceInfo> faceList) { faceList.clear(); allBigStrips.clear(); // make sure these are empty - final List<NvFaceInfo> tempFaceList = Lists.newArrayList(); + final List<NvFaceInfo> tempFaceList = new ArrayList<>(); for (int i = 0; i < allStrips.size(); i++) { final NvStripInfo allStripI = allStrips.get(i); @@ -918,7 +917,7 @@ final class NvStripifier { /** * Finds the next face to start the next strip on. - * + * * @param faceInfos * @param edgeInfos * @param strip @@ -962,7 +961,7 @@ final class NvStripifier { /** * "Commits" the input strips by setting their m_experimentId to -1 and adding to the allStrips vector - * + * * @param allStrips * @param strips */ @@ -985,7 +984,7 @@ final class NvStripifier { } /** - * + * * @param strips * @return the average strip size of the input vector of strips */ @@ -1000,7 +999,7 @@ final class NvStripifier { /** * Finds a good starting point, namely one which has only one neighbor - * + * * @param faceInfos * @param edgeInfos * @return @@ -1038,7 +1037,7 @@ final class NvStripifier { /** * Updates the input vertex cache with this strip's vertices - * + * * @param vcache * @param strip */ @@ -1050,7 +1049,7 @@ final class NvStripifier { /** * Updates the input vertex cache with this face's vertices - * + * * @param vcache * @param face */ @@ -1120,7 +1119,7 @@ final class NvStripifier { } /** - * + * * @param face * @param edgeInfoVec * @return the number of neighbors that this face has @@ -1145,7 +1144,7 @@ final class NvStripifier { /** * Builds the list of all face and edge infos - * + * * @param faceInfos * @param edgeInfos * @param maxIndex diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvTriangleStripper.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvTriangleStripper.java index c12d86d..71f8870 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvTriangleStripper.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvTriangleStripper.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * @@ -10,6 +10,7 @@ package com.ardor3d.extension.model.util.nvtristrip; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicReference; @@ -22,7 +23,6 @@ import com.ardor3d.scenegraph.MeshData; import com.ardor3d.scenegraph.Spatial; import com.ardor3d.scenegraph.visitor.Visitor; import com.ardor3d.util.geom.BufferUtils; -import com.google.common.collect.Lists; /** * Ported from <a href="http://developer.nvidia.com/object/nvtristrip_library.html">NVIDIA's NvTriStrip Library</a> @@ -241,7 +241,7 @@ public class NvTriangleStripper implements Visitor { PrimitiveGroup[] primGroups; // put data in format that the stripifier likes - final List<Integer> tempIndices = Lists.newArrayList(); + final List<Integer> tempIndices = new ArrayList<>(); int maxIndex = 0; for (int i = 0; i < in_indices.length; i++) { tempIndices.add(in_indices[i]); @@ -249,8 +249,8 @@ public class NvTriangleStripper implements Visitor { maxIndex = in_indices[i]; } } - final List<NvStripInfo> tempStrips = Lists.newArrayList(); - final List<NvFaceInfo> tempFaces = Lists.newArrayList(); + final List<NvStripInfo> tempStrips = new ArrayList<>(); + final List<NvFaceInfo> tempFaces = new ArrayList<>(); final NvStripifier stripifier = new NvStripifier(); @@ -258,7 +258,7 @@ public class NvTriangleStripper implements Visitor { stripifier.stripify(tempIndices, _cacheSize, _minStripSize, maxIndex, tempStrips, tempFaces); // stitch strips together - final List<Integer> stripIndices = Lists.newArrayList(); + final List<Integer> stripIndices = new ArrayList<>(); int numSeparateStrips = 0; if (_listsOnly) { @@ -374,7 +374,7 @@ public class NvTriangleStripper implements Visitor { final List<NvFaceInfo> in_bins[] = new List[NUMBINS]; for (int i = 0; i < NUMBINS; i++) { - in_bins[i] = Lists.newArrayList(); + in_bins[i] = new ArrayList<>(); } // hash input indices on first index @@ -525,7 +525,7 @@ public class NvTriangleStripper implements Visitor { PrimitiveGroup[] strips = generateStrips(indices, false); if (_reorderVertices) { - final AtomicReference<int[]> newOrder = new AtomicReference<int[]>(); + final AtomicReference<int[]> newOrder = new AtomicReference<>(); strips = remapIndices(strips, newOrder, md.getVertexCount()); // ask mesh to apply new vertex order diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/PrimitiveGroup.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/PrimitiveGroup.java index 4ba92c2..1886f11 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/PrimitiveGroup.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/PrimitiveGroup.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/VertexCache.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/VertexCache.java index ba93840..ae3a7ef 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/VertexCache.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/VertexCache.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/useful/TrailMesh.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/useful/TrailMesh.java index 558d9fb..da73655 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/useful/TrailMesh.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/useful/TrailMesh.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2008-2012 Ardor Labs, Inc. + * Copyright (c) 2008-2014 Ardor Labs, Inc. * * This file is part of Ardor3D. * @@ -90,7 +90,7 @@ public class TrailMesh extends Mesh { this.nrTrailSections = nrTrailSections; trailVertices = nrTrailSections * 2; - trailVectors = new LinkedList<TrailData>(); + trailVectors = new LinkedList<>(); for (int i = 0; i < nrTrailSections; i++) { trailVectors.add(new TrailData()); } |