From e7b631c42fc83a6d8a9485a5b6c3d26dbdb5bc76 Mon Sep 17 00:00:00 2001 From: Julien Gouesse Date: Wed, 8 Mar 2023 23:52:22 +0100 Subject: Detects the 64 distinct off keywords in the OFF importer, clarifies which ones are fully supported --- .../ardor3d/example/pipeline/SimpleOffExample.java | 2 +- .../extension/model/off/OffDimensionInfo.java | 614 +++++++++++++++++++++ .../extension/model/off/OffGeometryStore.java | 2 +- .../ardor3d/extension/model/off/OffImporter.java | 444 ++++----------- 4 files changed, 722 insertions(+), 340 deletions(-) create mode 100644 ardor3d-extras/src/main/java/com/ardor3d/extension/model/off/OffDimensionInfo.java diff --git a/ardor3d-examples/src/main/java/com/ardor3d/example/pipeline/SimpleOffExample.java b/ardor3d-examples/src/main/java/com/ardor3d/example/pipeline/SimpleOffExample.java index c8b3f19..4f27d40 100644 --- a/ardor3d-examples/src/main/java/com/ardor3d/example/pipeline/SimpleOffExample.java +++ b/ardor3d-examples/src/main/java/com/ardor3d/example/pipeline/SimpleOffExample.java @@ -36,7 +36,7 @@ public class SimpleOffExample extends ExampleBase { // Load the OFF scene final long time = System.currentTimeMillis(); final OffImporter importer = new OffImporter(); - final OffGeometryStore storage = importer.load("off/cube.off"); + final OffGeometryStore storage = importer.load("off/mushroom.off"); System.out.println("Importing Took " + (System.currentTimeMillis() - time) + " ms"); final Node model = storage.getScene(); diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/off/OffDimensionInfo.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/off/OffDimensionInfo.java new file mode 100644 index 0000000..7f11e2b --- /dev/null +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/off/OffDimensionInfo.java @@ -0,0 +1,614 @@ +/** + * 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 . + */ + +package com.ardor3d.extension.model.off; + +import java.io.IOException; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * off dimension information, i.e sizes of the components + */ +final class OffDimensionInfo { + + enum OffKeywordVertexDataComponentId { + /** + * color + */ + COLOR, + /** + * normal + */ + NORMAL, + /** + * texture + */ + TEXTURE; + } + + /** + * The off keyword indicates which data is available for each vertex except vertex coordinates which are mandatory + */ + private enum OffKeyword { + /** + * 3D + */ + _OFF(3, false, + Stream. empty().collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D + */ + _4OFF(4, true, + Stream. empty().collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D + */ + _nOFF(-1, false, + Stream. empty().collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D + */ + _4nOFF(-1, true, + Stream. empty().collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 3D, colors + */ + _COFF(3, false, + Stream.of(OffKeywordVertexDataComponentId.COLOR).collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D, colors + */ + _C4OFF(4, true, + Stream.of(OffKeywordVertexDataComponentId.COLOR).collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, colors + */ + _CnOFF(-1, false, + Stream.of(OffKeywordVertexDataComponentId.COLOR).collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, colors + */ + _C4nOFF(-1, true, + Stream.of(OffKeywordVertexDataComponentId.COLOR).collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 3D, colors + normals + */ + _CNOFF(3, false, Stream.of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D, colors + normals + */ + _CN4OFF(4, true, Stream.of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, colors + normals + */ + _CNnOFF(-1, false, Stream.of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, colors + normals + */ + _CN4nOFF(-1, true, Stream.of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 3D, colors + normals + textures + */ + _CNSTOFF( + 3, false, Stream + .of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.NORMAL, + OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D, colors + normals + textures + */ + _CNST4OFF( + 4, true, Stream + .of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.NORMAL, + OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, colors + normals + textures + */ + _CNSTnOFF( + -1, false, Stream + .of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.NORMAL, + OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, colors + normals + textures + */ + _CNST4nOFF( + -1, true, Stream + .of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.NORMAL, + OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 3D, colors + textures + */ + _CSTOFF(3, false, Stream.of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D, colors + textures + */ + _CST4OFF(4, true, Stream.of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, colors + textures + */ + _CSTnOFF(-1, false, Stream.of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, colors + textures + */ + _CST4nOFF(-1, true, Stream.of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 3D, colors + textures + normals + */ + _CSTNOFF( + 3, false, Stream + .of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.TEXTURE, + OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D, colors + textures + normals + */ + _CSTN4OFF( + 4, true, Stream + .of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.TEXTURE, + OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, colors + textures + normals + */ + _CSTNnOFF( + -1, false, Stream + .of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.TEXTURE, + OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, colors + textures + normals + */ + _CSTN4nOFF( + -1, true, Stream + .of(OffKeywordVertexDataComponentId.COLOR, OffKeywordVertexDataComponentId.TEXTURE, + OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 3D, normals + */ + _NOFF(3, false, + Stream.of(OffKeywordVertexDataComponentId.NORMAL).collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D, normals + */ + _N4OFF(4, true, + Stream.of(OffKeywordVertexDataComponentId.NORMAL).collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, normals + */ + _NnOFF(-1, false, + Stream.of(OffKeywordVertexDataComponentId.NORMAL).collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, normals + */ + _N4nOFF(-1, true, + Stream.of(OffKeywordVertexDataComponentId.NORMAL).collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 3D, normals + colors + */ + _NCOFF(3, false, Stream.of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D, normals + colors + */ + _NC4OFF(4, true, Stream.of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, normals + colors + */ + _NCnOFF(-1, false, Stream.of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, normals + colors + */ + _NC4nOFF(-1, true, Stream.of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 3D, normals + colors + textures + */ + _NCSTOFF( + 3, false, Stream + .of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.COLOR, + OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D, normals + colors + textures + */ + _NCST4OFF( + 4, true, Stream + .of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.COLOR, + OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, normals + colors + textures + */ + _NCSTnOFF( + -1, false, Stream + .of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.COLOR, + OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, normals + colors + textures + */ + _NCST4nOFF( + -1, true, Stream + .of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.COLOR, + OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 3D, normals + textures + */ + _NSTOFF(3, false, Stream.of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D, normals + textures + */ + _NST4OFF(4, true, Stream.of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, normals + textures + */ + _NSTnOFF(-1, false, Stream.of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, normals + textures + */ + _NST4nOFF(-1, true, Stream.of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 3D, normals + textures + colors + */ + _NSTCOFF( + 3, false, Stream + .of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.TEXTURE, + OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D, normals + textures + colors + */ + _NSTC4OFF( + 4, true, Stream + .of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.TEXTURE, + OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, normals + textures + colors + */ + _NSTCnOFF( + -1, false, Stream + .of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.TEXTURE, + OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, normals + textures + colors + */ + _NSTC4nOFF( + -1, true, Stream + .of(OffKeywordVertexDataComponentId.NORMAL, OffKeywordVertexDataComponentId.TEXTURE, + OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 3D, textures + */ + _STOFF(3, false, Stream.of(OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D, textures + */ + _ST4OFF(4, true, Stream.of(OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, textures + */ + _STnOFF(-1, false, Stream.of(OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, textures + */ + _ST4nOFF(-1, true, Stream.of(OffKeywordVertexDataComponentId.TEXTURE) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 3D, textures + colors + */ + _STCOFF(3, false, Stream.of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D, textures + colors + */ + _STC4OFF(4, true, Stream.of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, textures + colors + */ + _STCnOFF(-1, false, Stream.of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, textures + colors + */ + _STC4nOFF(-1, true, Stream.of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 3D, textures + colors + normals + */ + _STCNOFF( + 3, false, Stream + .of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.COLOR, + OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D, textures + colors + normals + */ + _STCN4OFF( + 4, true, Stream + .of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.COLOR, + OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, textures + colors + normals + */ + _STCNnOFF( + -1, false, Stream + .of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.COLOR, + OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, textures + colors + normals + */ + _STCN4nOFF( + -1, true, Stream + .of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.COLOR, + OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 3D, textures + normals + */ + _STNOFF(3, false, Stream.of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D, textures + normals + */ + _STN4OFF(4, true, Stream.of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, textures + normals + */ + _STNnOFF(-1, false, Stream.of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, textures + normals + */ + _STN4nOFF(-1, true, Stream.of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.NORMAL) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 3D, textures + normals + colors + */ + _STNCOFF( + 3, false, Stream + .of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.NORMAL, + OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * 4D, textures + normals + colors + */ + _STNC4OFF( + 4, true, Stream + .of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.NORMAL, + OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, textures + normals + colors + */ + _STNCnOFF( + -1, false, Stream + .of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.NORMAL, + OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))), + /** + * ?D, textures + normals + colors + */ + _STNC4nOFF( + -1, true, Stream + .of(OffKeywordVertexDataComponentId.TEXTURE, OffKeywordVertexDataComponentId.NORMAL, + OffKeywordVertexDataComponentId.COLOR) + .collect(Collectors.toCollection(LinkedHashSet::new))); + + /** + * set of components indicating whether colors, normals and textures are expected on the vertices + */ + private final LinkedHashSet componentSet; + /** + * text of the keyword as found in the very beginning of the file + */ + private final String keywordText; + + /** + * vertex values per tuple, either 3 or 4, -1 if unknown yet + */ + private final int vertexValuesPerTuple; + + private final boolean homogeneousComponent; + + /** + * minimum color values per tuple, either 0 or 1 + */ + private final int minColorValuesPerTuple; + + /** + * maximum color values per tuple, either 0, 1 or 4, color map, RGB and RGBA colors are supported + */ + private final int maxColorValuesPerTuple; + + /** + * texture values per tuple, either 0 or 2 + */ + private final int textureValuesPerTuple; + + private OffKeyword(final int vertexValuesPerTuple, final boolean homogeneousComponent, + final LinkedHashSet componentSet) { + this.vertexValuesPerTuple = vertexValuesPerTuple; + this.componentSet = componentSet; + // 1 color component for an index in a color map + minColorValuesPerTuple = componentSet.contains(OffKeywordVertexDataComponentId.COLOR) ? 1 : 0; + // 4 color components for RGBA + maxColorValuesPerTuple = componentSet.contains(OffKeywordVertexDataComponentId.COLOR) ? 4 : 0; + // u and v texture coordinates + textureValuesPerTuple = componentSet.contains(OffKeywordVertexDataComponentId.TEXTURE) ? 2 : 0; + keywordText = name().substring(1); + this.homogeneousComponent = homogeneousComponent; + } + + String getKeywordText() { + return keywordText; + } + + int getVertexValuesPerTuple() { + return vertexValuesPerTuple; + } + + int getMinColorValuesPerTuple() { + return minColorValuesPerTuple; + } + + int getMaxColorValuesPerTuple() { + return maxColorValuesPerTuple; + } + + int getTextureValuesPerTuple() { + return textureValuesPerTuple; + } + + LinkedHashSet getComponentSet() { + return componentSet; + } + + boolean hasHomogeneousComponent() { + return homogeneousComponent; + } + } + + private final OffKeyword offKeyword; + + /** + * indicates whether the off keyword has been deduced and is not in the file + */ + private final boolean deduced; + + private int vertexValuesPerTuple; + + private int normalValuesPerTuple; + + private int minValuesPerVertexLine; + + private int maxValuesPerVertexLine; + + /** + * Constructor + * + * @param parsedText + * text of the off keyword + */ + OffDimensionInfo(final String parsedText) { + super(); + final OffKeyword offKeywordInFile = Arrays.stream(OffKeyword.values()) + .filter((final OffKeyword offKeyword) -> offKeyword.getKeywordText().equals(parsedText)).findFirst() + .orElse(null); + if (offKeywordInFile == null) { + deduced = true; + // when there is no off keyword in the file, it takes OFF keyword's behaviour + offKeyword = OffKeyword._OFF; + } else { + deduced = false; + offKeyword = offKeywordInFile; + } + if (offKeyword.getVertexValuesPerTuple() <= 0) { + // it will have to be set later when nDim is known + vertexValuesPerTuple = -1; + } else { + // number of dimensions from the off keyword + vertexValuesPerTuple = offKeyword.getVertexValuesPerTuple(); + compute(); + } + } + + private final void compute() { + normalValuesPerTuple = offKeyword.getComponentSet().contains(OffKeywordVertexDataComponentId.NORMAL) + ? vertexValuesPerTuple + : 0; + final int valuesPerVertexLineExcludingColorValues = vertexValuesPerTuple + normalValuesPerTuple + + offKeyword.getTextureValuesPerTuple(); + minValuesPerVertexLine = valuesPerVertexLineExcludingColorValues + offKeyword.getMinColorValuesPerTuple(); + maxValuesPerVertexLine = valuesPerVertexLineExcludingColorValues + offKeyword.getMaxColorValuesPerTuple(); + } + + boolean needDimensionNumber() { + return vertexValuesPerTuple == -1; + } + + void useDimensionNumber(final int nDim) { + if (vertexValuesPerTuple == -1) { + if (nDim <= 0) { + throw new IllegalArgumentException("Illegal dimension number " + nDim); + } + // if 4nOFF (4 => hasHomogeneousComponent(), nOFF => nDim), each vertex has Ndim+1 components + vertexValuesPerTuple = nDim + (hasHomogeneousComponent() ? 1 : 0); + compute(); + } else { + throw new IllegalArgumentException("Illegal dimension number " + nDim + + ", unneeded when using the off keyword " + offKeyword.getKeywordText()); + } + } + + boolean isDeduced() { + return deduced; + } + + int computeColorValuesPerTuple(final int totalValuesPerVertexLine) throws IOException { + if (totalValuesPerVertexLine < minValuesPerVertexLine) { + throw new IOException("Premature end of (vertex) line, expected at least " + minValuesPerVertexLine + + " values, got " + totalValuesPerVertexLine); + } + if (maxValuesPerVertexLine < totalValuesPerVertexLine) { + throw new IOException("Too much values per (vertex) line, expected at most " + maxValuesPerVertexLine + + " values, got " + totalValuesPerVertexLine); + } + return totalValuesPerVertexLine - vertexValuesPerTuple - normalValuesPerTuple + - offKeyword.textureValuesPerTuple; + } + + int getVertexValuesPerTuple() { + return vertexValuesPerTuple; + } + + int getNormalValuesPerTuple() { + return normalValuesPerTuple; + } + + int getTextureValuesPerTuple() { + return offKeyword.getTextureValuesPerTuple(); + } + + boolean hasHomogeneousComponent() { + return offKeyword.hasHomogeneousComponent(); + } +} diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/off/OffGeometryStore.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/off/OffGeometryStore.java index d884aff..883f068 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/off/OffGeometryStore.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/off/OffGeometryStore.java @@ -240,7 +240,7 @@ public class OffGeometryStore { } if (indexModeList.size() == 1) { - _geometryTool.minimizeVerts(mesh, matchConditions); + // _geometryTool.minimizeVerts(mesh, matchConditions); } else { // FIXME unsure about minimizeVerts preserving the index modes } diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/off/OffImporter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/off/OffImporter.java index 944cdeb..32f1fbb 100644 --- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/off/OffImporter.java +++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/off/OffImporter.java @@ -18,14 +18,11 @@ import java.io.Reader; import java.io.StreamTokenizer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.IntStream; -import java.util.stream.Stream; import com.ardor3d.math.ColorRGBA; import com.ardor3d.math.Vector2; @@ -43,264 +40,10 @@ import com.ardor3d.util.resource.ResourceSource; * * N.B: supports only the ASCII file format as there's a lack of available binary files to make some tests and the * specification mentions some constants in the include file named "off.h" but it's not included in Geomview's source - * code + * code. It supports only 1D, 2D and 3D without homogeneous coordinates. */ public class OffImporter { - private enum OffKeywordVertexDataComponentId { - /** - * color - */ - COLOR("C"), - /** - * normal - */ - NORMAL("N"), - /** - * texture - */ - TEXTURE("ST"); - - private final String keywordText; - - private OffKeywordVertexDataComponentId(final String keywordText) { - this.keywordText = keywordText; - } - - public String getKeywordText() { - return keywordText; - } - } - - static { - // TODO use OffKeywordVertexDataComponentId to generate all possible combinations with OFF and 4OFF - for (final String componentId0 : Stream.concat(Stream.of(" "), Arrays - .stream(OffKeywordVertexDataComponentId.values()).map(OffKeywordVertexDataComponentId::getKeywordText)) - .toArray(String[]::new)) { - for (final String componentId1 : Stream - .concat(Stream.of(" "), Arrays.stream(OffKeywordVertexDataComponentId.values()) - .map(OffKeywordVertexDataComponentId::getKeywordText)) - .toArray(String[]::new)) { - for (final String componentId2 : Stream - .concat(Stream.of(" "), Arrays.stream(OffKeywordVertexDataComponentId.values()) - .map(OffKeywordVertexDataComponentId::getKeywordText)) - .toArray(String[]::new)) { - // FIXME - if (!componentId0.equals(componentId1) && !componentId1.equals(componentId2) - && !componentId0.equals(componentId2)) { - for (final String offWithoutOrWithDim : new String[] { "OFF", "4OFF" }) { - System.out.println( - "_" + componentId0 + componentId1 + componentId2 + offWithoutOrWithDim + ","); - } - } - } - } - } - } - - /** - * The off keyword indicates which data is available for each vertex except vertex coordinates which are mandatory - */ - private enum OffKeyword { - /** - * colors + normals - */ - _CNOFF(), - /** - * colors + normals - */ - _CN4OFF(), - /** - * colors - */ - _COFF(), - /** - * colors - */ - _C4OFF(), - /** - * normals - */ - _NOFF(), - /** - * normals + colors - */ - _NCOFF(), - /** - * normals - */ - _N4OFF(), - /** - * - */ - _OFF(), - /** - * texture coordinates + colors + normals - */ - _STCNOFF(), - /** - * texture coordinates + colors + normals - */ - _STCN4OFF(), - /** - * texture coordinates + colors - */ - _STCOFF(), - /** - * texture coordinates + colors - */ - _STC4OFF(), - /** - * texture coordinates + normals + colors - */ - _STNCOFF(), - /** - * texture coordinates + normals + colors - */ - _STNC4OFF(), - /** - * texture coordinates + normals - */ - _STNOFF(), - /** - * texture coordinates + normals - */ - _STN4OFF(), - /** - * texture coordinates - */ - _STOFF(), - /** - * texture coordinates - */ - _ST4OFF(), - /** - * - */ - _4OFF(); - - /** - * text of the keyword as found in the very beginning of the file - */ - private final String keywordText; - - /** - * vertex values per tuple, either 3 or 4 - */ - private final int vertexValuesPerTuple; - - /** - * normal values per tuple, either 0, 3 or 4 - */ - private final int normalValuesPerTuple; - - /** - * minimum color values per tuple, either 0 or 1 - */ - private final int minColorValuesPerTuple; - - /** - * maximum color values per tuple, either 0, 1 or 4, color map, RGB and RGBA colors are supported - */ - private final int maxColorValuesPerTuple; - - /** - * texture values per tuple, either 0 or 2 - */ - private final int textureValuesPerTuple; - - private OffKeyword() { - final String offSuffix; - if (name().endsWith("nOFF")) { - throw new UnsupportedOperationException("nOFF not supported, must end with either OFF or 4OFF"); - } else if (name().endsWith("4OFF")) { - vertexValuesPerTuple = 4; - offSuffix = "4OFF"; - } else if (name().endsWith("OFF")) { - vertexValuesPerTuple = 3; - offSuffix = "OFF"; - } else { - throw new UnsupportedOperationException( - "Unsupported off keyword " + name() + ", must end with either OFF or 4OFF"); - } - final String vertexDataComponentIds; - if (name().length() == offSuffix.length() + 1) { - vertexDataComponentIds = ""; - } else { - vertexDataComponentIds = name().substring(1, name().length() - offSuffix.length()); - } - if (vertexDataComponentIds.chars() - .anyMatch((final int vertexDataComponentId) -> IntStream.of('N', 'C', 'S', 'T') - .noneMatch((final int supportedChar) -> vertexDataComponentId == supportedChar))) { - throw new UnsupportedOperationException("Unsupported off keyword " + name() - + ", the first part should contain only the characters N, C, S and T at most once each"); - } - int tmpNormalValuesPerTuple = 0; - int tmpMinColorValuesPerTuple = 0; - int tmpMaxColorValuesPerTuple = 0; - int tmpTextureValuesPerTuple = 0; - for (final String vertexDataComponentIdCandidate : new String[] { "N", "C", "ST" }) { - final int firstIndexOfVertexDataComponentIdCandidate = vertexDataComponentIds - .indexOf(vertexDataComponentIdCandidate); - if (firstIndexOfVertexDataComponentIdCandidate != -1) { - final int lastIndexOfVertexDataComponentIdCandidate = vertexDataComponentIds - .lastIndexOf(vertexDataComponentIdCandidate); - // if the candidate appears exactly once - if (firstIndexOfVertexDataComponentIdCandidate == lastIndexOfVertexDataComponentIdCandidate) { - switch (vertexDataComponentIdCandidate) { - case "N": - tmpNormalValuesPerTuple = vertexValuesPerTuple; - break; - case "C": - tmpMinColorValuesPerTuple = 1;// 1 color component for an index in a color map - tmpMaxColorValuesPerTuple = 4;// 4 color components for RGBA - break; - case "ST": - tmpTextureValuesPerTuple = 2;// u and v texture coordinates - break; - default: - // cannot happen - break; - } - } else { - throw new UnsupportedOperationException("Unsupported off keyword " + name() - + ", the first part should contain N, C and ST at most once each. " - + vertexDataComponentIdCandidate + " appears more than once."); - } - } - } - normalValuesPerTuple = tmpNormalValuesPerTuple; - minColorValuesPerTuple = tmpMinColorValuesPerTuple; - maxColorValuesPerTuple = tmpMaxColorValuesPerTuple; - textureValuesPerTuple = tmpTextureValuesPerTuple; - keywordText = name().substring(1); - } - - public String getKeywordText() { - return keywordText; - } - - public int getVertexValuesPerTuple() { - return vertexValuesPerTuple; - } - - public int getNormalValuesPerTuple() { - return normalValuesPerTuple; - } - - public int getMinColorValuesPerTuple() { - return minColorValuesPerTuple; - } - - public int getMaxColorValuesPerTuple() { - return maxColorValuesPerTuple; - } - - public int getTextureValuesPerTuple() { - return textureValuesPerTuple; - } - } - private static final Logger LOGGER = Logger.getLogger(OffImporter.class.getName()); public static class OffFileParser extends StreamTokenizer implements Closeable { @@ -427,11 +170,14 @@ public class OffImporter { "Premature end of file, expected an optional off keyword followed by three integers vertex_count face_count edge_count"); } final String unhandledFirstParsedValue; - final OffKeyword offKeywordInFile = Arrays.stream(OffKeyword.values()) - .filter((final OffKeyword offKeyword) -> offKeyword.getKeywordText().equals(parser.sval)) - .findFirst().orElse(null); // tries to read an (optional) off keyword - if (offKeywordInFile == null) { + // FIXME try to read nDim + final OffDimensionInfo offDimensionInfo = new OffDimensionInfo(parser.sval); + if (offDimensionInfo.hasHomogeneousComponent()) { + throw new IOException( + "Homogeneous coordinates aren't supported whereas " + parser.sval + " requires it"); + } + if (offDimensionInfo.isDeduced()) { // no *off keyword OffImporter.LOGGER.log(Level.INFO, "No off keyword on line " + parser.lineno()); unhandledFirstParsedValue = parser.sval; @@ -461,6 +207,17 @@ public class OffImporter { throw new IOException( "Premature end of line, expected three integers vertex_count face_count edge_count"); } else { + if (offDimensionInfo.needDimensionNumber()) { + offDimensionInfo.useDimensionNumber(Integer.parseInt(parser.sval)); + do { + parser.nextToken(); + } while (parser.ttype != StreamTokenizer.TT_WORD + && parser.ttype != StreamTokenizer.TT_EOF); + if (parser.ttype == StreamTokenizer.TT_EOF) { + throw new IOException( + "Premature end of line, expected three integers vertex_count face_count edge_count"); + } + } numberOfVertices = Integer.valueOf(parser.sval); parser.nextToken(); } @@ -470,6 +227,17 @@ public class OffImporter { } } else { if (unhandledFirstParsedValue == null) { + if (offDimensionInfo.needDimensionNumber()) { + offDimensionInfo.useDimensionNumber(Integer.parseInt(parser.sval)); + do { + parser.nextToken(); + } while (parser.ttype != StreamTokenizer.TT_WORD + && parser.ttype != StreamTokenizer.TT_EOF); + if (parser.ttype == StreamTokenizer.TT_EOF) { + throw new IOException( + "Premature end of line, expected three integers vertex_count face_count edge_count"); + } + } // *off keyword and three integers on the first line, rare scenario: // *OFF vertex_count face_count edge_count // reads the 3 integer values on the first uncommented line, most common scenario @@ -507,15 +275,13 @@ public class OffImporter { if (parser.ttype == StreamTokenizer.TT_EOF) { throw new IOException("Premature end of file, no vertex has been declared"); } - // when there is no off keyword in the file, it takes off keyword's behaviour - final OffKeyword offKeyword = Optional.ofNullable(offKeywordInFile).orElse(OffKeyword._OFF); - // computes the expected value counts per line of vertex definition - final int expectedValueCountPerVertexLineExcludingColorValues = offKeyword.getVertexValuesPerTuple() - + offKeyword.getNormalValuesPerTuple() + offKeyword.getTextureValuesPerTuple(); - final int minExpectedValueCountPerVertexLine = expectedValueCountPerVertexLineExcludingColorValues - + offKeyword.getMinColorValuesPerTuple(); - final int maxExpectedValueCountPerVertexLine = expectedValueCountPerVertexLineExcludingColorValues - + offKeyword.getMaxColorValuesPerTuple(); + // before going further, ensures that the number of components inside the vertices is supported + if (offDimensionInfo.getVertexValuesPerTuple() < 1 + || 3 < offDimensionInfo.getVertexValuesPerTuple()) { + throw new IOException("The off keyword " + parser.sval + + " requires the unsupported number of components per vertex or dimension " + + offDimensionInfo.getVertexValuesPerTuple()); + } // reads the vertices, normals, colors and/or texture coordinates for each vertex in that order // FIXME handle other orders for (int vertexIndex = 0; vertexIndex < numberOfVertices.intValue(); vertexIndex++) { @@ -551,75 +317,77 @@ public class OffImporter { break; } } while (goOn); - if (minExpectedValueCountPerVertexLine <= valueList.size()) { - if (valueList.size() <= maxExpectedValueCountPerVertexLine) { - OffImporter.LOGGER.log(Level.INFO, "Coords: " - + valueList.stream().map(Number::toString).collect(Collectors.joining(" "))); - if (offKeyword.getVertexValuesPerTuple() == 3) { - store.getDataStore().getVertices().add(new Vector3(valueList.get(0).doubleValue(), - valueList.get(1).doubleValue(), valueList.get(2).doubleValue())); - } else { - // TODO 4D - } - int nextIndex = offKeyword.getVertexValuesPerTuple(); - switch (offKeyword.getNormalValuesPerTuple()) { - case 0: - // nothing to do - break; - case 3: - store.getDataStore().getNormals() - .add(new Vector3(valueList.get(nextIndex).doubleValue(), - valueList.get(nextIndex + 1).doubleValue(), - valueList.get(nextIndex + 2).doubleValue())); - break; - case 4: - // TODO 4D - break; - } - nextIndex += offKeyword.getNormalValuesPerTuple(); - final int colorValuesPerTuple = valueList.size() - offKeyword.getVertexValuesPerTuple() - - offKeyword.getNormalValuesPerTuple() - offKeyword.getTextureValuesPerTuple(); - switch (colorValuesPerTuple) { - case 0: - // nothing to do - break; - case 1: - // TODO store the color index somewhere - break; - case 3: - final ColorRGBA rgb = new ColorRGBA(valueList.get(nextIndex).floatValue(), - valueList.get(nextIndex + 1).floatValue(), - valueList.get(nextIndex + 2).floatValue(), 0.0f); - if (valueList.get(nextIndex) instanceof Integer) { - rgb.divideLocal(255.0f); - } - rgb.setAlpha(1.0f); - store.getDataStore().getColors().add(rgb); - break; - case 4: - final ColorRGBA rgba = new ColorRGBA(valueList.get(nextIndex).floatValue(), - valueList.get(nextIndex + 1).floatValue(), - valueList.get(nextIndex + 2).floatValue(), - valueList.get(nextIndex + 3).floatValue()); - if (valueList.get(nextIndex) instanceof Integer) { - rgba.divideLocal(255.0f); - } - store.getDataStore().getColors().add(rgba); - break; + final int colorValuesPerTuple = offDimensionInfo.computeColorValuesPerTuple(valueList.size()); + OffImporter.LOGGER.log(Level.INFO, + "Coords: " + valueList.stream().map(Number::toString).collect(Collectors.joining(" "))); + switch (offDimensionInfo.getVertexValuesPerTuple()) { + case 1: + store.getDataStore().getVertices() + .add(new Vector3(valueList.get(0).doubleValue(), 0.0d, 0.0d)); + case 2: + store.getDataStore().getVertices().add(new Vector3(valueList.get(0).doubleValue(), + valueList.get(1).doubleValue(), 0.0d)); + break; + case 3: + store.getDataStore().getVertices().add(new Vector3(valueList.get(0).doubleValue(), + valueList.get(1).doubleValue(), valueList.get(2).doubleValue())); + break; + } + int nextIndex = offDimensionInfo.getVertexValuesPerTuple(); + switch (offDimensionInfo.getNormalValuesPerTuple()) { + case 0: + // nothing to do + break; + case 1: + store.getDataStore().getNormals() + .add(new Vector3(valueList.get(nextIndex).doubleValue(), 0.0d, 0.0d)); + break; + case 2: + store.getDataStore().getNormals() + .add(new Vector3(valueList.get(nextIndex).doubleValue(), + valueList.get(nextIndex + 1).doubleValue(), 0.0d)); + break; + case 3: + store.getDataStore().getNormals() + .add(new Vector3(valueList.get(nextIndex).doubleValue(), + valueList.get(nextIndex + 1).doubleValue(), + valueList.get(nextIndex + 2).doubleValue())); + break; + } + nextIndex += offDimensionInfo.getNormalValuesPerTuple(); + switch (colorValuesPerTuple) { + case 0: + // nothing to do + break; + case 1: + // TODO store the color index somewhere + break; + case 3: + final ColorRGBA rgb = new ColorRGBA(valueList.get(nextIndex).floatValue(), + valueList.get(nextIndex + 1).floatValue(), + valueList.get(nextIndex + 2).floatValue(), 0.0f); + if (valueList.get(nextIndex) instanceof Integer) { + rgb.divideLocal(255.0f); } - nextIndex += colorValuesPerTuple; - if (offKeyword.getTextureValuesPerTuple() == 2) { - store.getDataStore().getTextureCoordinates() - .add(new Vector2(valueList.get(nextIndex).doubleValue(), - valueList.get(nextIndex + 1).doubleValue())); + rgb.setAlpha(1.0f); + store.getDataStore().getColors().add(rgb); + break; + case 4: + final ColorRGBA rgba = new ColorRGBA(valueList.get(nextIndex).floatValue(), + valueList.get(nextIndex + 1).floatValue(), + valueList.get(nextIndex + 2).floatValue(), + valueList.get(nextIndex + 3).floatValue()); + if (valueList.get(nextIndex) instanceof Integer) { + rgba.divideLocal(255.0f); } - } else { - throw new IOException("Too much values per (vertex) line, expected at most " - + maxExpectedValueCountPerVertexLine + " values, got " + valueList.size()); - } - } else { - throw new IOException("Premature end of (vertex) line, expected at least " - + minExpectedValueCountPerVertexLine + " values, got " + valueList.size()); + store.getDataStore().getColors().add(rgba); + break; + } + nextIndex += colorValuesPerTuple; + if (offDimensionInfo.getTextureValuesPerTuple() == 2) { + store.getDataStore().getTextureCoordinates() + .add(new Vector2(valueList.get(nextIndex).doubleValue(), + valueList.get(nextIndex + 1).doubleValue())); } } for (int faceIndex = 0; faceIndex < numberOfFaces.intValue(); faceIndex++) { -- cgit v1.2.3