diff --git a/core/.gitignore b/common/.gitignore similarity index 100% rename from core/.gitignore rename to common/.gitignore diff --git a/core/LICENSE b/common/LICENSE similarity index 100% rename from core/LICENSE rename to common/LICENSE diff --git a/core/README.md b/common/README.md similarity index 100% rename from core/README.md rename to common/README.md diff --git a/core/build.gradle b/common/build.gradle similarity index 99% rename from core/build.gradle rename to common/build.gradle index cdf8751e..03cd8778 100644 --- a/core/build.gradle +++ b/common/build.gradle @@ -3,7 +3,7 @@ plugins { } group = 'com.gaia3d' -version "1.4.1" +version "1.4.4" project.ext.lwjglVersion = "3.3.2" switch ( org.gradle.internal.os.OperatingSystem.current() ) { diff --git a/core/gradle/wrapper/gradle-wrapper.jar b/common/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from core/gradle/wrapper/gradle-wrapper.jar rename to common/gradle/wrapper/gradle-wrapper.jar diff --git a/core/gradle/wrapper/gradle-wrapper.properties b/common/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from core/gradle/wrapper/gradle-wrapper.properties rename to common/gradle/wrapper/gradle-wrapper.properties diff --git a/core/gradlew b/common/gradlew similarity index 100% rename from core/gradlew rename to common/gradlew diff --git a/core/gradlew.bat b/common/gradlew.bat similarity index 100% rename from core/gradlew.bat rename to common/gradlew.bat diff --git a/common/settings.gradle b/common/settings.gradle new file mode 100644 index 00000000..47cc8b24 --- /dev/null +++ b/common/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'mago-common' \ No newline at end of file diff --git a/core/src/main/java/com/gaia3d/basic/exception/PostProcessingException.java b/common/src/main/java/com/gaia3d/basic/exception/PostProcessingException.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/exception/PostProcessingException.java rename to common/src/main/java/com/gaia3d/basic/exception/PostProcessingException.java diff --git a/core/src/main/java/com/gaia3d/basic/exception/PreprocessingException.java b/common/src/main/java/com/gaia3d/basic/exception/PreprocessingException.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/exception/PreprocessingException.java rename to common/src/main/java/com/gaia3d/basic/exception/PreprocessingException.java diff --git a/core/src/main/java/com/gaia3d/basic/exception/TileProcessingException.java b/common/src/main/java/com/gaia3d/basic/exception/TileProcessingException.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/exception/TileProcessingException.java rename to common/src/main/java/com/gaia3d/basic/exception/TileProcessingException.java diff --git a/core/src/main/java/com/gaia3d/basic/exchangable/GaiaBuffer.java b/common/src/main/java/com/gaia3d/basic/exchangable/GaiaBuffer.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/exchangable/GaiaBuffer.java rename to common/src/main/java/com/gaia3d/basic/exchangable/GaiaBuffer.java diff --git a/core/src/main/java/com/gaia3d/basic/exchangable/GaiaBufferDataSet.java b/common/src/main/java/com/gaia3d/basic/exchangable/GaiaBufferDataSet.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/exchangable/GaiaBufferDataSet.java rename to common/src/main/java/com/gaia3d/basic/exchangable/GaiaBufferDataSet.java diff --git a/core/src/main/java/com/gaia3d/basic/exchangable/GaiaSet.java b/common/src/main/java/com/gaia3d/basic/exchangable/GaiaSet.java similarity index 97% rename from core/src/main/java/com/gaia3d/basic/exchangable/GaiaSet.java rename to common/src/main/java/com/gaia3d/basic/exchangable/GaiaSet.java index 7a10a17f..936f21db 100644 --- a/core/src/main/java/com/gaia3d/basic/exchangable/GaiaSet.java +++ b/common/src/main/java/com/gaia3d/basic/exchangable/GaiaSet.java @@ -110,7 +110,12 @@ public Path writeFile(Path path, int serial) { File inputPathFile = new File(imagePath); inputPathFile = ImageUtils.getChildFile(parentPath.toFile(), diffusePath); File outputPathFile = outputPath.toFile(); - if (!outputPathFile.exists()) { + + if (inputPathFile == null) { + log.error("Texture Image Path is null. {}", imagePath); + } else if (outputPathFile.exists()) { + log.error("already outputPathFile exists"); + } else { FileUtils.copyFile(inputPathFile, outputPath.toFile()); } } diff --git a/core/src/main/java/com/gaia3d/basic/exchangable/GaiaTextureArchive.java b/common/src/main/java/com/gaia3d/basic/exchangable/GaiaTextureArchive.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/exchangable/GaiaTextureArchive.java rename to common/src/main/java/com/gaia3d/basic/exchangable/GaiaTextureArchive.java diff --git a/core/src/main/java/com/gaia3d/basic/geometry/GaiaBoundingBox.java b/common/src/main/java/com/gaia3d/basic/geometry/GaiaBoundingBox.java similarity index 94% rename from core/src/main/java/com/gaia3d/basic/geometry/GaiaBoundingBox.java rename to common/src/main/java/com/gaia3d/basic/geometry/GaiaBoundingBox.java index 48321f99..4a0bb13f 100644 --- a/core/src/main/java/com/gaia3d/basic/geometry/GaiaBoundingBox.java +++ b/common/src/main/java/com/gaia3d/basic/geometry/GaiaBoundingBox.java @@ -126,6 +126,11 @@ public double getLongestDistance() { return Math.sqrt(volume.x * volume.x + volume.y * volume.y + volume.z * volume.z); } + public boolean contains(GaiaBoundingBox boundingBox) { + return minX <= boundingBox.minX && minY <= boundingBox.minY && minZ <= boundingBox.minZ + && maxX >= boundingBox.maxX && maxY >= boundingBox.maxY && maxZ >= boundingBox.maxZ; + } + public GaiaBoundingBox clone() { return new GaiaBoundingBox(minX, minY, minZ, maxX, maxY, maxZ, isInit); } diff --git a/core/src/main/java/com/gaia3d/basic/geometry/GaiaColor.java b/common/src/main/java/com/gaia3d/basic/geometry/GaiaColor.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/geometry/GaiaColor.java rename to common/src/main/java/com/gaia3d/basic/geometry/GaiaColor.java diff --git a/core/src/main/java/com/gaia3d/basic/geometry/GaiaRectangle.java b/common/src/main/java/com/gaia3d/basic/geometry/GaiaRectangle.java similarity index 97% rename from core/src/main/java/com/gaia3d/basic/geometry/GaiaRectangle.java rename to common/src/main/java/com/gaia3d/basic/geometry/GaiaRectangle.java index cb8a5ac4..26502142 100644 --- a/core/src/main/java/com/gaia3d/basic/geometry/GaiaRectangle.java +++ b/common/src/main/java/com/gaia3d/basic/geometry/GaiaRectangle.java @@ -60,6 +60,10 @@ public Vector2d getRightTopPoint() { return (maxX * maxY); }*/ + public Vector2d getCenter() { + return new Vector2d((minX + maxX) / 2, (minY + maxY) / 2); + } + public double getArea() { return ((maxX - minX) * (maxY - minY)); } diff --git a/core/src/main/java/com/gaia3d/basic/geometry/GaiaVBO.java b/common/src/main/java/com/gaia3d/basic/geometry/GaiaVBO.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/geometry/GaiaVBO.java rename to common/src/main/java/com/gaia3d/basic/geometry/GaiaVBO.java diff --git a/core/src/main/java/com/gaia3d/basic/pointcloud/GaiaPoint.java b/common/src/main/java/com/gaia3d/basic/pointcloud/GaiaPoint.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/pointcloud/GaiaPoint.java rename to common/src/main/java/com/gaia3d/basic/pointcloud/GaiaPoint.java diff --git a/core/src/main/java/com/gaia3d/basic/pointcloud/GaiaPointCloud.java b/common/src/main/java/com/gaia3d/basic/pointcloud/GaiaPointCloud.java similarity index 94% rename from core/src/main/java/com/gaia3d/basic/pointcloud/GaiaPointCloud.java rename to common/src/main/java/com/gaia3d/basic/pointcloud/GaiaPointCloud.java index d7af2c4c..28188866 100644 --- a/core/src/main/java/com/gaia3d/basic/pointcloud/GaiaPointCloud.java +++ b/common/src/main/java/com/gaia3d/basic/pointcloud/GaiaPointCloud.java @@ -25,8 +25,27 @@ public class GaiaPointCloud implements Serializable { private GaiaBoundingBox gaiaBoundingBox = new GaiaBoundingBox(); List vertices = new ArrayList<>(); - // Quarter based on the bounding box public List distribute() { + double minX = gaiaBoundingBox.getMinX(); + double minY = gaiaBoundingBox.getMinY(); + double minZ = gaiaBoundingBox.getMinZ(); + double maxX = gaiaBoundingBox.getMaxX(); + double maxY = gaiaBoundingBox.getMaxY(); + double maxZ = gaiaBoundingBox.getMaxZ(); + + double offsetX = maxX - minX; + double offsetY = maxY - minY; + double offsetZ = maxZ - minZ; + + if (offsetZ < offsetX || offsetZ < offsetY) { + return distributeQuad(); + } else { + return distributeOct(); + } + } + + // Quarter based on the bounding box + public List distributeQuad() { List pointClouds = new ArrayList<>(); GaiaBoundingBox gaiaBoundingBoxA = new GaiaBoundingBox(); diff --git a/core/src/main/java/com/gaia3d/basic/structure/GaiaFace.java b/common/src/main/java/com/gaia3d/basic/structure/GaiaFace.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/structure/GaiaFace.java rename to common/src/main/java/com/gaia3d/basic/structure/GaiaFace.java diff --git a/core/src/main/java/com/gaia3d/basic/structure/GaiaMaterial.java b/common/src/main/java/com/gaia3d/basic/structure/GaiaMaterial.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/structure/GaiaMaterial.java rename to common/src/main/java/com/gaia3d/basic/structure/GaiaMaterial.java diff --git a/core/src/main/java/com/gaia3d/basic/structure/GaiaMesh.java b/common/src/main/java/com/gaia3d/basic/structure/GaiaMesh.java similarity index 96% rename from core/src/main/java/com/gaia3d/basic/structure/GaiaMesh.java rename to common/src/main/java/com/gaia3d/basic/structure/GaiaMesh.java index b01e86ca..5dbe80f2 100644 --- a/core/src/main/java/com/gaia3d/basic/structure/GaiaMesh.java +++ b/common/src/main/java/com/gaia3d/basic/structure/GaiaMesh.java @@ -27,7 +27,7 @@ @NoArgsConstructor @AllArgsConstructor public class GaiaMesh implements Serializable { - private ArrayList primitives = new ArrayList<>(); + private List primitives = new ArrayList<>(); public GaiaBoundingBox getBoundingBox(Matrix4d transform) { GaiaBoundingBox boundingBox = null; @@ -42,6 +42,14 @@ public GaiaBoundingBox getBoundingBox(Matrix4d transform) { return boundingBox; } + public long getTriangleCount() { + long count = 0; + for (GaiaPrimitive primitive : primitives) { + count += getIndicesCount() / 3; + } + return count; + } + public int[] getIndices() { int[] totalIndices = new int[getIndicesCount()]; int index = 0; @@ -221,7 +229,7 @@ public void translate(Vector3d translation) { public void clear() { this.primitives.forEach(GaiaPrimitive::clear); - this.primitives.clear(); + //this.primitives.clear(); } public GaiaMesh clone() { diff --git a/core/src/main/java/com/gaia3d/basic/structure/GaiaNode.java b/common/src/main/java/com/gaia3d/basic/structure/GaiaNode.java similarity index 96% rename from core/src/main/java/com/gaia3d/basic/structure/GaiaNode.java rename to common/src/main/java/com/gaia3d/basic/structure/GaiaNode.java index d6077137..372c89d0 100644 --- a/core/src/main/java/com/gaia3d/basic/structure/GaiaNode.java +++ b/common/src/main/java/com/gaia3d/basic/structure/GaiaNode.java @@ -169,6 +169,17 @@ public void toGaiaBufferSets(List bufferSets, Matrix4d parent } } + public long getTriangleCount() { + long count = 0; + for (GaiaMesh mesh : this.getMeshes()) { + count += mesh.getTriangleCount(); + } + for (GaiaNode child : this.getChildren()) { + count += child.getTriangleCount(); + } + return count; + } + public void translate(Vector3d translation) { for (GaiaMesh mesh : this.getMeshes()) { mesh.translate(translation); diff --git a/core/src/main/java/com/gaia3d/basic/structure/GaiaPrimitive.java b/common/src/main/java/com/gaia3d/basic/structure/GaiaPrimitive.java similarity index 97% rename from core/src/main/java/com/gaia3d/basic/structure/GaiaPrimitive.java rename to common/src/main/java/com/gaia3d/basic/structure/GaiaPrimitive.java index 2574770c..bd401f4b 100644 --- a/core/src/main/java/com/gaia3d/basic/structure/GaiaPrimitive.java +++ b/common/src/main/java/com/gaia3d/basic/structure/GaiaPrimitive.java @@ -275,10 +275,14 @@ public void translate(Vector3d translation) { } public void clear() { - this.vertices.forEach(GaiaVertex::clear); - this.surfaces.forEach(GaiaSurface::clear); - this.vertices.clear(); - this.surfaces.clear(); + if (this.vertices != null) { + this.vertices.forEach(GaiaVertex::clear); + this.vertices.clear(); + } + if (this.surfaces != null) { + this.surfaces.forEach(GaiaSurface::clear); + this.surfaces.clear(); + } } public GaiaPrimitive clone() { diff --git a/core/src/main/java/com/gaia3d/basic/structure/GaiaScene.java b/common/src/main/java/com/gaia3d/basic/structure/GaiaScene.java similarity index 92% rename from core/src/main/java/com/gaia3d/basic/structure/GaiaScene.java rename to common/src/main/java/com/gaia3d/basic/structure/GaiaScene.java index 77adc0df..52ac994e 100644 --- a/core/src/main/java/com/gaia3d/basic/structure/GaiaScene.java +++ b/common/src/main/java/com/gaia3d/basic/structure/GaiaScene.java @@ -80,4 +80,12 @@ public GaiaScene clone() { clone.setGaiaBoundingBox(this.gaiaBoundingBox); return clone; } + + public long calcTriangleCount() { + long triangleCount = 0; + for (GaiaNode node : this.nodes) { + triangleCount += node.getTriangleCount(); + } + return triangleCount; + } } diff --git a/core/src/main/java/com/gaia3d/basic/structure/GaiaSurface.java b/common/src/main/java/com/gaia3d/basic/structure/GaiaSurface.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/structure/GaiaSurface.java rename to common/src/main/java/com/gaia3d/basic/structure/GaiaSurface.java diff --git a/core/src/main/java/com/gaia3d/basic/structure/GaiaTexture.java b/common/src/main/java/com/gaia3d/basic/structure/GaiaTexture.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/structure/GaiaTexture.java rename to common/src/main/java/com/gaia3d/basic/structure/GaiaTexture.java diff --git a/core/src/main/java/com/gaia3d/basic/structure/GaiaVertex.java b/common/src/main/java/com/gaia3d/basic/structure/GaiaVertex.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/structure/GaiaVertex.java rename to common/src/main/java/com/gaia3d/basic/structure/GaiaVertex.java diff --git a/core/src/main/java/com/gaia3d/basic/types/AccessorType.java b/common/src/main/java/com/gaia3d/basic/types/AccessorType.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/types/AccessorType.java rename to common/src/main/java/com/gaia3d/basic/types/AccessorType.java diff --git a/core/src/main/java/com/gaia3d/basic/types/AttributeType.java b/common/src/main/java/com/gaia3d/basic/types/AttributeType.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/types/AttributeType.java rename to common/src/main/java/com/gaia3d/basic/types/AttributeType.java diff --git a/common/src/main/java/com/gaia3d/basic/types/FormatType.java b/common/src/main/java/com/gaia3d/basic/types/FormatType.java new file mode 100644 index 00000000..1d1b2417 --- /dev/null +++ b/common/src/main/java/com/gaia3d/basic/types/FormatType.java @@ -0,0 +1,66 @@ +package com.gaia3d.basic.types; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * Enum for the different types of formats that can be used in the application. + * Each format has a corresponding extension. + * The extension is used to determine the type of the file. + * The extension is also used to determine the type of the file that is being downloaded. + * @Author znkim + * @Since 1.0.1 + * @See GaiaSet + */ +@Getter +@RequiredArgsConstructor +public enum FormatType { + // 3D Formats + KML("kml", "kml", false), + GLTF("gltf", "glb", false), + GLB("glb", "gltf", false), + COLLADA("dae", "dae", true), + MAX_3DS("3ds", "3ds",false), + MAX_ASE("ase", "ase", false), + FBX("fbx", "fbx", true), + OBJ("obj","obj", false), + IFC("ifc", "ifc",false), + CITYGML("gml","xml", false), + INDOORGML("gml","xml", false), + MODO("lxo", "lxo", false), + LWO("lwo", "lwo", false), + LWS("lws", "lws", false), + DirectX("x", "x", false), + // 2D Formats, + SHP("shp", "shp",false), + GEOJSON("geojson", "json", false), + //JSON("json", "", false), + LAS("las", "laz", false), + LAZ("laz", "las", false), + // OUTPUT Formats + B3DM("b3dm", "gltf", true), + I3DM("i3dm", "gltf",true), + PNTS("pnts", "gltf",true), + TEMP("tmp", "tmp", false); + + private final String extension; + private final String subExtension; + private final boolean yUpAxis; + + public static FormatType fromExtension(String extension) { + if (extension == null || extension.isEmpty()) { + return null; + } + return Arrays.stream(FormatType.values()) + .filter((type) -> { + boolean compareName = type.name().equalsIgnoreCase(extension); + boolean compareExtension = type.getExtension().equals(extension.toLowerCase()); + boolean compareSubExtension = type.getSubExtension().equals(extension.toLowerCase()); + return compareName || compareExtension || compareSubExtension; + }) + .findFirst() + .orElse(null); + } +} diff --git a/core/src/main/java/com/gaia3d/basic/types/TextureType.java b/common/src/main/java/com/gaia3d/basic/types/TextureType.java similarity index 100% rename from core/src/main/java/com/gaia3d/basic/types/TextureType.java rename to common/src/main/java/com/gaia3d/basic/types/TextureType.java diff --git a/core/src/main/java/com/gaia3d/util/DecimalUtils.java b/common/src/main/java/com/gaia3d/util/DecimalUtils.java similarity index 100% rename from core/src/main/java/com/gaia3d/util/DecimalUtils.java rename to common/src/main/java/com/gaia3d/util/DecimalUtils.java diff --git a/core/src/main/java/com/gaia3d/util/GaiaFileUtils.java b/common/src/main/java/com/gaia3d/util/GaiaFileUtils.java similarity index 100% rename from core/src/main/java/com/gaia3d/util/GaiaFileUtils.java rename to common/src/main/java/com/gaia3d/util/GaiaFileUtils.java diff --git a/core/src/main/java/com/gaia3d/util/GeometryUtils.java b/common/src/main/java/com/gaia3d/util/GeometryUtils.java similarity index 100% rename from core/src/main/java/com/gaia3d/util/GeometryUtils.java rename to common/src/main/java/com/gaia3d/util/GeometryUtils.java diff --git a/core/src/main/java/com/gaia3d/util/GlobeUtils.java b/common/src/main/java/com/gaia3d/util/GlobeUtils.java similarity index 100% rename from core/src/main/java/com/gaia3d/util/GlobeUtils.java rename to common/src/main/java/com/gaia3d/util/GlobeUtils.java diff --git a/core/src/main/java/com/gaia3d/util/ImageResizer.java b/common/src/main/java/com/gaia3d/util/ImageResizer.java similarity index 87% rename from core/src/main/java/com/gaia3d/util/ImageResizer.java rename to common/src/main/java/com/gaia3d/util/ImageResizer.java index 2a78038c..3114595b 100644 --- a/core/src/main/java/com/gaia3d/util/ImageResizer.java +++ b/common/src/main/java/com/gaia3d/util/ImageResizer.java @@ -12,7 +12,7 @@ public class ImageResizer { public BufferedImage resizeImageGraphic2D(BufferedImage originalImage, int width, int height) { BufferedImage outputImage = new BufferedImage(width, height, originalImage.getType()); Graphics2D graphics2D = outputImage.createGraphics(); - graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); // RenderingHints.VALUE_INTERPOLATION_BILINEAR + graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); // RenderingHints.VALUE_INTERPOLATION_BILINEAR graphics2D.setComposite(AlphaComposite.Src); graphics2D.drawImage(originalImage, 0, 0, width, height, null); return outputImage; diff --git a/core/src/main/java/com/gaia3d/util/ImageUtils.java b/common/src/main/java/com/gaia3d/util/ImageUtils.java similarity index 100% rename from core/src/main/java/com/gaia3d/util/ImageUtils.java rename to common/src/main/java/com/gaia3d/util/ImageUtils.java diff --git a/core/src/main/java/com/gaia3d/util/StringUtils.java b/common/src/main/java/com/gaia3d/util/StringUtils.java similarity index 86% rename from core/src/main/java/com/gaia3d/util/StringUtils.java rename to common/src/main/java/com/gaia3d/util/StringUtils.java index a17119e7..df5e3e28 100644 --- a/core/src/main/java/com/gaia3d/util/StringUtils.java +++ b/common/src/main/java/com/gaia3d/util/StringUtils.java @@ -4,6 +4,7 @@ import java.util.Optional; import java.util.Vector; +import java.util.stream.Collectors; @Slf4j public class StringUtils { @@ -53,4 +54,12 @@ public static Optional getExtensionByStringHandling(String filename) { // https://www.baeldung.com/java-file-extension return Optional.ofNullable(filename).filter(f -> f.contains(".")).map(f -> f.substring(filename.lastIndexOf(".") + 1)); } + + public static String convertUTF8(String ascii) { + return ascii.chars() + .mapToObj(c -> (char) c) + .map(c -> c < 128 ? c : '_') + .map(String::valueOf) + .collect(Collectors.joining()); + } } diff --git a/common/src/main/java/com/gaia3d/util/VectorUtils.java b/common/src/main/java/com/gaia3d/util/VectorUtils.java new file mode 100644 index 00000000..85614a25 --- /dev/null +++ b/common/src/main/java/com/gaia3d/util/VectorUtils.java @@ -0,0 +1,71 @@ +package com.gaia3d.util; + +import com.gaia3d.basic.geometry.GaiaBoundingBox; +import com.gaia3d.basic.geometry.GaiaRectangle; +import lombok.extern.slf4j.Slf4j; +import org.joml.Vector2d; +import org.joml.Vector3d; + +@Slf4j +public class VectorUtils { + public static double cross(Vector2d v1, Vector2d v2, Vector2d v3) { + Vector2d v1v2 = v1.sub(v2, new Vector2d()); + v1v2.normalize(); + Vector2d v2v3 = v2.sub(v3, new Vector2d()); + v2v3.normalize(); + return cross(v1v2, v2v3); + } + + public static double cross(Vector2d a, Vector2d b) { + return (a.x * b.y) - (a.y * b.x); + } + + public static boolean isIntersection(Vector2d a, Vector2d b, Vector2d u, Vector2d v) { + GaiaRectangle rect1 = new GaiaRectangle(a, b); + GaiaRectangle rect2 = new GaiaRectangle(u, v); + if (!rect1.intersects(rect2, 0.0)) { + // Intersection check with bounding box + return false; + } else if (a.equals(u) && b.equals(v) || a.equals(v) && b.equals(u)){ + // Same line case; + return true; + } else if (a.equals(u) || a.equals(v) || b.equals(u) || b.equals(v)) { + // Same point case; + return false; + } + + + double cross1 = cross(a, b, u); + double cross2 = cross(a, b, v); + if (cross1 == 0 && cross2 == 0) { + return true; + } + boolean isIntersectA = cross1 * cross2 < 0; + + double cross3 = cross(u, v, a); + double cross4 = cross(u, v, b); + if (cross3 == 0 && cross4 == 0) { + return true; + } + boolean isIntersectB = cross3 * cross4 < 0; + return isIntersectA == isIntersectB; + } + + public static boolean isIntersection(Vector2d v1, Vector2d v2, Vector2d v3) { + if (v1.equals(v3) || v2.equals(v3)) { + return false; + } + double cross1 = cross(v1, v2, v3); + return cross1 == 0; + } + + public static double calcAngle(Vector2d a, Vector2d b, Vector2d c) { + Vector2d v1 = new Vector2d(); + Vector2d v2 = new Vector2d(); + b.sub(a, v1); + c.sub(b, v2); + v1.normalize(); + v2.normalize(); + return Math.toDegrees(v1.angle(v2)); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/gaia3d/util/io/BigEndianDataInputStream.java b/common/src/main/java/com/gaia3d/util/io/BigEndianDataInputStream.java similarity index 100% rename from core/src/main/java/com/gaia3d/util/io/BigEndianDataInputStream.java rename to common/src/main/java/com/gaia3d/util/io/BigEndianDataInputStream.java diff --git a/core/src/main/java/com/gaia3d/util/io/BigEndianDataOutputStream.java b/common/src/main/java/com/gaia3d/util/io/BigEndianDataOutputStream.java similarity index 100% rename from core/src/main/java/com/gaia3d/util/io/BigEndianDataOutputStream.java rename to common/src/main/java/com/gaia3d/util/io/BigEndianDataOutputStream.java diff --git a/core/src/main/java/com/gaia3d/util/io/LittleEndianDataInputStream.java b/common/src/main/java/com/gaia3d/util/io/LittleEndianDataInputStream.java similarity index 100% rename from core/src/main/java/com/gaia3d/util/io/LittleEndianDataInputStream.java rename to common/src/main/java/com/gaia3d/util/io/LittleEndianDataInputStream.java diff --git a/core/src/main/java/com/gaia3d/util/io/LittleEndianDataOutputStream.java b/common/src/main/java/com/gaia3d/util/io/LittleEndianDataOutputStream.java similarity index 100% rename from core/src/main/java/com/gaia3d/util/io/LittleEndianDataOutputStream.java rename to common/src/main/java/com/gaia3d/util/io/LittleEndianDataOutputStream.java diff --git a/core/settings.gradle b/core/settings.gradle deleted file mode 100644 index 4255538d..00000000 --- a/core/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'mago-core' \ No newline at end of file diff --git a/core/src/main/java/com/gaia3d/basic/types/FormatType.java b/core/src/main/java/com/gaia3d/basic/types/FormatType.java deleted file mode 100644 index bc79defd..00000000 --- a/core/src/main/java/com/gaia3d/basic/types/FormatType.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.gaia3d.basic.types; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.Arrays; - -/** - * Enum for the different types of formats that can be used in the application. - * Each format has a corresponding extension. - * The extension is used to determine the type of the file. - * The extension is also used to determine the type of the file that is being downloaded. - * @Author znkim - * @Since 1.0.1 - * @See GaiaSet - */ -@Getter -@RequiredArgsConstructor -public enum FormatType { - // 3D Formats - KML("kml", false), - GLTF("gltf", false), - GLB("glb", false), - COLLADA("dae", true), - MAX_3DS("3ds", false), - MAX_ASE("ase", false), - FBX("fbx", true), - OBJ("obj", false), - IFC("ifc", false), - CITY_GML("gml", false), - MODO("lxo", false), - LWO("lwo", false), - LWS("lws", false), - DirectX("x", false), - // 2D Formats, - SHP("shp", false), - GEOJSON("geojson", false), - JSON("json", false), - LAS("las", false), - LAZ("laz", false), - // OUTPUT Formats - B3DM("b3dm", true), - I3DM("i3dm", true), - PNTS("pnts", true), - TEMP("tmp", false); - - private final String extension; - private final boolean yUpAxis; - - public static FormatType fromExtension(String extension) { - if (extension == null) { - return null; - } - return Arrays.stream(FormatType.values()) - .filter(type -> type.getExtension().equals(extension.toLowerCase())) - .findFirst() - .orElse(null); - } -} diff --git a/tiler/build.gradle b/tiler/build.gradle index 2a8b56e8..1013d8ad 100644 --- a/tiler/build.gradle +++ b/tiler/build.gradle @@ -7,7 +7,7 @@ plugins { } group "com.gaia3d" -version "1.4.4" +version "1.5.0" project.ext.lwjglVersion = "3.3.3" switch ( OperatingSystem.current() ) { @@ -89,9 +89,10 @@ dependencies { implementation 'org.citygml4j:citygml4j-xml:3.0.0' implementation 'org.citygml4j:citygml4j-cityjson:3.0.0' - implementation 'com.github.mreutegg:laszip4j:0.15' + implementation 'com.github.mreutegg:laszip4j:0.17' - implementation project(":mago-core") + implementation project(":mago-common") + implementation files("libs/Indoor4J.jar") runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives" runtimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives" diff --git a/tiler/build/tmp/compileJava/previous-compilation-data.bin b/tiler/build/tmp/compileJava/previous-compilation-data.bin index 49b7445b..c0fb5db2 100644 Binary files a/tiler/build/tmp/compileJava/previous-compilation-data.bin and b/tiler/build/tmp/compileJava/previous-compilation-data.bin differ diff --git a/tiler/dist/mago-3d-tiler-1.4.4-natives-linux.jar b/tiler/dist/mago-3d-tiler-1.5.0-natives-linux.jar similarity index 96% rename from tiler/dist/mago-3d-tiler-1.4.4-natives-linux.jar rename to tiler/dist/mago-3d-tiler-1.5.0-natives-linux.jar index 395fd737..d42cb6be 100644 Binary files a/tiler/dist/mago-3d-tiler-1.4.4-natives-linux.jar and b/tiler/dist/mago-3d-tiler-1.5.0-natives-linux.jar differ diff --git a/tiler/dist/mago-3d-tiler-1.4.4-natives-macos.jar b/tiler/dist/mago-3d-tiler-1.5.0-natives-macos.jar similarity index 96% rename from tiler/dist/mago-3d-tiler-1.4.4-natives-macos.jar rename to tiler/dist/mago-3d-tiler-1.5.0-natives-macos.jar index f957d1f8..915959fb 100644 Binary files a/tiler/dist/mago-3d-tiler-1.4.4-natives-macos.jar and b/tiler/dist/mago-3d-tiler-1.5.0-natives-macos.jar differ diff --git a/tiler/dist/mago-3d-tiler-1.4.4-natives-windows.jar b/tiler/dist/mago-3d-tiler-1.5.0-natives-windows.jar similarity index 96% rename from tiler/dist/mago-3d-tiler-1.4.4-natives-windows.jar rename to tiler/dist/mago-3d-tiler-1.5.0-natives-windows.jar index c0a504bc..22747a07 100644 Binary files a/tiler/dist/mago-3d-tiler-1.4.4-natives-windows.jar and b/tiler/dist/mago-3d-tiler-1.5.0-natives-windows.jar differ diff --git a/tiler/libs/Indoor4J.jar b/tiler/libs/Indoor4J.jar new file mode 100644 index 00000000..13978dfa Binary files /dev/null and b/tiler/libs/Indoor4J.jar differ diff --git a/tiler/settings.gradle b/tiler/settings.gradle index aca16d7f..9aa087e4 100644 --- a/tiler/settings.gradle +++ b/tiler/settings.gradle @@ -1,3 +1,3 @@ rootProject.name = 'mago-3d-tiler' -include ':mago-core' -project(':mago-core').projectDir = new File(settingsDir, '../core') \ No newline at end of file +include ':mago-common' +project(':mago-common').projectDir = new File(settingsDir, '../common') \ No newline at end of file diff --git a/tiler/src/main/java/com/gaia3d/command/BatchedProcessModel.java b/tiler/src/main/java/com/gaia3d/command/BatchedProcessModel.java index 1658dfb3..a657df83 100644 --- a/tiler/src/main/java/com/gaia3d/command/BatchedProcessModel.java +++ b/tiler/src/main/java/com/gaia3d/command/BatchedProcessModel.java @@ -7,6 +7,7 @@ import com.gaia3d.converter.assimp.AssimpConverter; import com.gaia3d.converter.geometry.citygml.CityGmlConverter; import com.gaia3d.converter.geometry.geojson.GeoJsonConverter; +import com.gaia3d.converter.geometry.indoorgml.IndoorGmlConverter; import com.gaia3d.converter.geometry.shape.ShapeConverter; import com.gaia3d.converter.kml.FastKmlReader; import com.gaia3d.converter.kml.JacksonKmlReader; @@ -21,13 +22,9 @@ import com.gaia3d.process.tileprocess.TilingProcess; import com.gaia3d.process.tileprocess.tile.Batched3DModelTiler; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.FileExistsException; import org.geotools.coverage.grid.GridCoverage2D; -import java.io.File; import java.io.IOException; -import java.nio.file.NotDirectoryException; -import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -35,8 +32,7 @@ public class BatchedProcessModel implements ProcessFlowModel { public void run() throws IOException { GlobalOptions globalOptions = GlobalOptions.getInstance(); - String inputExtension = globalOptions.getInputFormat(); - FormatType inputFormat = FormatType.fromExtension(inputExtension); + FormatType inputFormat = globalOptions.getInputFormat(); boolean isYUpAxis = getYUpAxis(inputFormat, globalOptions.isYUpAxis()); Converter converter = getConverter(inputFormat); AttributeReader kmlReader = new JacksonKmlReader(); @@ -69,18 +65,20 @@ public void run() throws IOException { } private boolean getYUpAxis(FormatType formatType, boolean isYUpAxis) { - if (formatType == FormatType.CITY_GML || formatType == FormatType.SHP || formatType == FormatType.GEOJSON || formatType == FormatType.JSON) { + if (formatType == FormatType.CITYGML || formatType == FormatType.INDOORGML || formatType == FormatType.SHP || formatType == FormatType.GEOJSON) { isYUpAxis = true; } return isYUpAxis; } private Converter getConverter(FormatType formatType) { Converter converter; - if (formatType == FormatType.CITY_GML) { + if (formatType == FormatType.CITYGML) { converter = new CityGmlConverter(); + } else if (formatType == FormatType.INDOORGML) { + converter = new IndoorGmlConverter(); } else if (formatType == FormatType.SHP) { converter = new ShapeConverter(); - } else if (formatType == FormatType.GEOJSON || formatType == FormatType.JSON) { + } else if (formatType == FormatType.GEOJSON) { converter = new GeoJsonConverter(); } else { converter = new AssimpConverter(); @@ -92,25 +90,4 @@ private Converter getConverter(FormatType formatType) { public String getModelName() { return "BatchedProcessModel"; } - - protected static boolean validate(Path outputPath) throws IOException { - File output = outputPath.toFile(); - if (!output.exists()) { - throw new FileExistsException("Output path is not exist."); - } else if (!output.isDirectory()) { - throw new NotDirectoryException("Output path is not directory."); - } else if (!output.canWrite()) { - throw new IOException("Output path is not writable."); - } - return true; - } - - protected static Path createPath(File file) { - Path path = file.toPath(); - boolean result = file.mkdir(); - if (result) { - log.info("Created new directory: {}", path); - } - return path; - } } diff --git a/tiler/src/main/java/com/gaia3d/command/InstancedProcessModel.java b/tiler/src/main/java/com/gaia3d/command/InstancedProcessModel.java index fd1ffe9a..5681e90e 100644 --- a/tiler/src/main/java/com/gaia3d/command/InstancedProcessModel.java +++ b/tiler/src/main/java/com/gaia3d/command/InstancedProcessModel.java @@ -6,9 +6,6 @@ import com.gaia3d.converter.FileLoader; import com.gaia3d.converter.InstancedFileLoader; import com.gaia3d.converter.assimp.AssimpConverter; -import com.gaia3d.converter.geometry.citygml.CityGmlConverter; -import com.gaia3d.converter.geometry.geojson.GeoJsonConverter; -import com.gaia3d.converter.geometry.shape.ShapeConverter; import com.gaia3d.converter.kml.AttributeReader; import com.gaia3d.converter.kml.JacksonKmlReader; import com.gaia3d.converter.kml.ShapeReader; @@ -30,10 +27,9 @@ public class InstancedProcessModel implements ProcessFlowModel{ public void run() throws IOException { GlobalOptions globalOptions = GlobalOptions.getInstance(); - String inputExtension = globalOptions.getInputFormat(); - FormatType inputFormat = FormatType.fromExtension(inputExtension); + FormatType inputFormat = globalOptions.getInputFormat(); boolean isYUpAxis = getYUpAxis(inputFormat, globalOptions.isYUpAxis()); - Converter converter = new AssimpConverter(); + Converter converter = getConverter(inputFormat); AttributeReader kmlReader = getAttributeReader(inputFormat); FileLoader fileLoader = new InstancedFileLoader(converter, kmlReader); @@ -58,11 +54,11 @@ public void run() throws IOException { private AttributeReader getAttributeReader(FormatType formatType) { AttributeReader reader = null; - if (formatType == FormatType.CITY_GML) { + if (formatType == FormatType.CITYGML) { //reader = new CityGmlConverter(); } else if (formatType == FormatType.SHP) { reader = new ShapeReader(); - } else if (formatType == FormatType.GEOJSON || formatType == FormatType.JSON) { + } else if (formatType == FormatType.GEOJSON) { //reader = new GeoJsonConverter(); } else { reader = new JacksonKmlReader(); @@ -71,16 +67,7 @@ private AttributeReader getAttributeReader(FormatType formatType) { } private Converter getConverter(FormatType formatType) { - Converter converter; - if (formatType == FormatType.CITY_GML) { - converter = new CityGmlConverter(); - } else if (formatType == FormatType.SHP) { - converter = new ShapeConverter(); - } else if (formatType == FormatType.GEOJSON || formatType == FormatType.JSON) { - converter = new GeoJsonConverter(); - } else { - converter = new AssimpConverter(); - } + Converter converter = new AssimpConverter(); return converter; } @@ -90,7 +77,7 @@ public String getModelName() { } private boolean getYUpAxis(FormatType formatType, boolean isYUpAxis) { - if (formatType == FormatType.CITY_GML || formatType == FormatType.SHP || formatType == FormatType.GEOJSON || formatType == FormatType.JSON) { + if (formatType == FormatType.CITYGML || formatType == FormatType.SHP || formatType == FormatType.GEOJSON) { isYUpAxis = true; } return isYUpAxis; diff --git a/tiler/src/main/java/com/gaia3d/command/PointCloudProcessModel.java b/tiler/src/main/java/com/gaia3d/command/PointCloudProcessModel.java index 2edbaec1..913bacc0 100644 --- a/tiler/src/main/java/com/gaia3d/command/PointCloudProcessModel.java +++ b/tiler/src/main/java/com/gaia3d/command/PointCloudProcessModel.java @@ -40,24 +40,4 @@ public String getModelName() { return "PointCloudProcessModel"; } - protected static boolean validate(Path outputPath) throws IOException { - File output = outputPath.toFile(); - if (!output.exists()) { - throw new FileExistsException("Output path is not exist."); - } else if (!output.isDirectory()) { - throw new NotDirectoryException("Output path is not directory."); - } else if (!output.canWrite()) { - throw new IOException("Output path is not writable."); - } - return true; - } - - protected static Path createPath(File file) { - Path path = file.toPath(); - boolean result = file.mkdir(); - if (result) { - log.info("Created new directory: {}", path); - } - return path; - } } diff --git a/tiler/src/main/java/com/gaia3d/command/mago/GlobalOptions.java b/tiler/src/main/java/com/gaia3d/command/mago/GlobalOptions.java index fb96df75..1fc86841 100644 --- a/tiler/src/main/java/com/gaia3d/command/mago/GlobalOptions.java +++ b/tiler/src/main/java/com/gaia3d/command/mago/GlobalOptions.java @@ -1,6 +1,7 @@ package com.gaia3d.command.mago; import com.gaia3d.basic.exchangable.GaiaTextureArchive; +import com.gaia3d.basic.types.FormatType; import com.gaia3d.process.ProcessOptions; import lombok.Getter; import lombok.NoArgsConstructor; @@ -28,20 +29,24 @@ public class GlobalOptions { private static final GlobalOptions instance = new GlobalOptions(); // volatile? private static final String DEFAULT_INPUT_FORMAT = "kml"; - private static final String DEFAULT_OUTPUT_FORMAT = "b3dm"; - //private static final int DEFAULT_NODE_LIMIT = 1024; + private static final String DEFAULT_INSTANCE_FILE = "instance.dae"; private static final int DEFAULT_MIN_LOD = 0; private static final int DEFAULT_MAX_LOD = 3; - private static final int DEFAULT_POINT_LIMIT = 20000; - private static final byte DEFAULT_MULTI_THREAD_COUNT = 8; + private static final int DEFAULT_POINT_LIMIT = 65536; + private static final int DEFAULT_POINT_SCALE = 2; + private static final int DEFAULT_POINT_SKIP = 4; + + private static final byte DEFAULT_MULTI_THREAD_COUNT = 4; private static final String DEFAULT_CRS = "4326"; private static final String DEFAULT_NAME_COLUMN = "name"; private static final String DEFAULT_HEIGHT_COLUMN = "height"; private static final String DEFAULT_ALTITUDE_COLUMN = "altitude"; private static final double DEFAULT_ABSOLUTE_ALTITUDE = 0.0d; private static final double DEFAULT_MINIMUM_HEIGHT = 1.0d; + private static final double DEFAULT_SKIRT_HEIGHT = 4.0d; private static final boolean DEFAULT_DEBUG_LOD = false; + private String version; // version flag private String javaVersionInfo; // java version flag private String programInfo; // program info flag @@ -57,22 +62,26 @@ public class GlobalOptions { private String inputPath; // input file or dir path private String outputPath; // output dir path private String logPath; // log file path + private String terrainPath; // terrain file path - private String inputFormat; // input file format - private String outputFormat; // output file format + private String instancePath; // instance file path + + private FormatType inputFormat; // input file format + private FormatType outputFormat; // output file format private CoordinateReferenceSystem crs; private String proj; // default projection private int pointLimit; // point limit per tile + private int pointScale; // point scale + private int pointSkip; // skip points value + private int nodeLimit; // node limit per tile private int minLod; // minimum level of detail private int maxLod; // maximum level of detail private boolean debug = false; // debug mode flag private boolean debugLod = false; // debug lod flag - private boolean quiet = false; // quiet mode flag - private boolean help = false; // help flag private boolean gltf = false; // gltf flag private boolean glb = false; // glb flag @@ -82,14 +91,12 @@ public class GlobalOptions { /* 3D Data Options */ private boolean recursive = false; // recursive flag - private boolean useMultiThread = false; // use multi thread flag private boolean yUpAxis = false; // y up axis flag private boolean refineAdd = false; // 3dTiles refine option ADD fix flag private boolean flipCoordinate = false; // flip coordinate flag for 2D Data private boolean zeroOrigin = false; // data origin to zero point flag private boolean autoUpAxis = false; // automatically assign 3D matrix axes flag private boolean ignoreTextures = false; // ignore textures flag - private boolean reverseTextureCoordinate = false; // reverse texture coordinate flag /* 2D Data Column Options */ private String nameColumn; @@ -97,6 +104,7 @@ public class GlobalOptions { private String altitudeColumn; private double absoluteAltitude; private double minimumHeight; + private double skirtHeight; public static GlobalOptions getInstance() { if (instance.javaVersionInfo == null) { @@ -112,24 +120,57 @@ public static void init(CommandLine command) throws IOException { } else { throw new IllegalArgumentException("Please enter the value of the input argument."); } + if (command.hasOption(ProcessOptions.OUTPUT.getArgName())) { instance.setOutputPath(command.getOptionValue(ProcessOptions.OUTPUT.getArgName())); validateOutputPath(new File(instance.getOutputPath()).toPath()); } else { throw new IllegalArgumentException("Please enter the value of the output argument."); } + + String inputType = command.hasOption(ProcessOptions.INPUT_TYPE.getArgName()) ? command.getOptionValue(ProcessOptions.INPUT_TYPE.getArgName()) : DEFAULT_INPUT_FORMAT; + FormatType formatType = FormatType.fromExtension(inputType); + if (formatType == null) { + throw new IllegalArgumentException("Invalid input format: " + inputType); + } else { + instance.setInputFormat(formatType); + } + + FormatType outputFormat = null; + String outputType = command.hasOption(ProcessOptions.OUTPUT_TYPE.getArgName()) ? command.getOptionValue(ProcessOptions.OUTPUT_TYPE.getArgName()) : null; + if (outputType == null) { + outputFormat = inferOutputFormat(instance.getInputFormat()); + } else { + outputFormat = FormatType.fromExtension(outputType); + } + + if (outputFormat == null) { + throw new IllegalArgumentException("Invalid output format: " + outputType); + } else { + instance.setOutputFormat(outputFormat); + } + instance.setLogPath(command.hasOption(ProcessOptions.LOG.getArgName()) ? command.getOptionValue(ProcessOptions.LOG.getArgName()) : null); - instance.setInputFormat(command.hasOption(ProcessOptions.INPUT_TYPE.getArgName()) ? command.getOptionValue(ProcessOptions.INPUT_TYPE.getArgName()) : DEFAULT_INPUT_FORMAT); - instance.setOutputFormat(command.hasOption(ProcessOptions.OUTPUT_TYPE.getArgName()) ? command.getOptionValue(ProcessOptions.OUTPUT_TYPE.getArgName()) : null); + if (command.hasOption(ProcessOptions.TERRAIN.getArgName())) { instance.setTerrainPath(command.getOptionValue(ProcessOptions.TERRAIN.getArgName())); validateInputPath(new File(instance.getTerrainPath()).toPath()); } + + if (command.hasOption(ProcessOptions.INSTANCE_FILE.getArgName())) { + instance.setInstancePath(command.getOptionValue(ProcessOptions.INSTANCE_FILE.getArgName())); + validateInputPath(new File(instance.getInstancePath()).toPath()); + } else { + String instancePath = instance.getInputPath() + File.separator + DEFAULT_INSTANCE_FILE; + instance.setInstancePath(instancePath); + //validateInputPath(new File(instancePath).toPath()); + } + if (command.hasOption(ProcessOptions.PROJ4.getArgName())) { instance.setProj(command.hasOption(ProcessOptions.PROJ4.getArgName()) ? command.getOptionValue(ProcessOptions.PROJ4.getArgName()) : null); CoordinateReferenceSystem crs = null; if (instance.getProj() != null && !instance.getProj().isEmpty()) { - crs = new CRSFactory().createFromParameters("CUSTOM_CRS", instance.getProj()); + crs = new CRSFactory().createFromParameters("CUSTOM_CRS_PROJ", instance.getProj()); } instance.setCrs(crs); } @@ -141,7 +182,7 @@ public static void init(CommandLine command) throws IOException { // proj code is first priority if (proj != null && !proj.isEmpty()) { - source = factory.createFromParameters("CUSTOM", proj); + source = factory.createFromParameters("CUSTOM_CRS_PROJ", proj); } else if (crsString != null && !crsString.isEmpty()) { source = factory.createFromName("EPSG:" + crsString); } @@ -149,7 +190,6 @@ public static void init(CommandLine command) throws IOException { } /* 3D Data Options */ - //instance.setNodeLimit(command.hasOption(ProcessOptions.MAX_COUNT.getArgName()) ? Integer.parseInt(command.getOptionValue(ProcessOptions.MAX_COUNT.getArgName())) : DEFAULT_NODE_LIMIT); instance.setNodeLimit(command.hasOption(ProcessOptions.MAX_COUNT.getArgName()) ? Integer.parseInt(command.getOptionValue(ProcessOptions.MAX_COUNT.getArgName())) : -1); instance.setMinLod(command.hasOption(ProcessOptions.MIN_LOD.getArgName()) ? Integer.parseInt(command.getOptionValue(ProcessOptions.MIN_LOD.getArgName())) : DEFAULT_MIN_LOD); instance.setMaxLod(command.hasOption(ProcessOptions.MAX_LOD.getArgName()) ? Integer.parseInt(command.getOptionValue(ProcessOptions.MAX_LOD.getArgName())) : DEFAULT_MAX_LOD); @@ -157,6 +197,8 @@ public static void init(CommandLine command) throws IOException { /* Point Cloud Options */ instance.setPointLimit(command.hasOption(ProcessOptions.MAX_POINTS.getArgName()) ? Integer.parseInt(command.getOptionValue(ProcessOptions.MAX_POINTS.getArgName())) : DEFAULT_POINT_LIMIT); + instance.setPointScale(command.hasOption(ProcessOptions.POINT_SCALE.getArgName()) ? Integer.parseInt(command.getOptionValue(ProcessOptions.POINT_SCALE.getArgName())) : DEFAULT_POINT_SCALE); + instance.setPointSkip(command.hasOption(ProcessOptions.POINT_SKIP.getArgName()) ? Integer.parseInt(command.getOptionValue(ProcessOptions.POINT_SKIP.getArgName())) : DEFAULT_POINT_SKIP); /* 2D Data Column Options */ instance.setNameColumn(command.hasOption(ProcessOptions.NAME_COLUMN.getArgName()) ? command.getOptionValue(ProcessOptions.NAME_COLUMN.getArgName()) : DEFAULT_NAME_COLUMN); @@ -164,24 +206,29 @@ public static void init(CommandLine command) throws IOException { instance.setAltitudeColumn(command.hasOption(ProcessOptions.ALTITUDE_COLUMN.getArgName()) ? command.getOptionValue(ProcessOptions.ALTITUDE_COLUMN.getArgName()) : DEFAULT_ALTITUDE_COLUMN); instance.setAbsoluteAltitude(command.hasOption(ProcessOptions.ABSOLUTE_ALTITUDE.getArgName()) ? Double.parseDouble(command.getOptionValue(ProcessOptions.ABSOLUTE_ALTITUDE.getArgName())) : DEFAULT_ABSOLUTE_ALTITUDE); instance.setMinimumHeight(command.hasOption(ProcessOptions.MINIMUM_HEIGHT.getArgName()) ? Double.parseDouble(command.getOptionValue(ProcessOptions.MINIMUM_HEIGHT.getArgName())) : DEFAULT_MINIMUM_HEIGHT); + instance.setSkirtHeight(command.hasOption(ProcessOptions.SKIRT_HEIGHT.getArgName()) ? Double.parseDouble(command.getOptionValue(ProcessOptions.SKIRT_HEIGHT.getArgName())) : DEFAULT_SKIRT_HEIGHT); instance.setDebug(command.hasOption(ProcessOptions.DEBUG.getArgName())); instance.setDebugLod(DEFAULT_DEBUG_LOD); - instance.setQuiet(command.hasOption(ProcessOptions.QUIET.getArgName())); - instance.setHelp(command.hasOption(ProcessOptions.HELP.getArgName())); - instance.setUseMultiThread(command.hasOption(ProcessOptions.MULTI_THREAD.getArgName())); instance.setYUpAxis(command.hasOption(ProcessOptions.Y_UP_AXIS.getArgName())); instance.setRecursive(command.hasOption(ProcessOptions.RECURSIVE.getArgName())); - instance.setRefineAdd(command.hasOption(ProcessOptions.REFINE_ADD.getArgName())); - instance.setGltf(command.hasOption(ProcessOptions.DEBUG_GLTF.getArgName())); + + if (command.hasOption(ProcessOptions.REFINE_ADD.getArgName())) { + instance.setRefineAdd(true); + } else { + if (instance.getInputFormat().equals(FormatType.GEOJSON) || instance.getInputFormat().equals(FormatType.SHP) || instance.getInputFormat().equals(FormatType.CITYGML) || instance.getInputFormat().equals(FormatType.INDOORGML)) { + instance.setRefineAdd(true); + } + } instance.setGlb(command.hasOption(ProcessOptions.DEBUG_GLB.getArgName())); instance.setFlipCoordinate(command.hasOption(ProcessOptions.FLIP_COORDINATE.getArgName())); instance.setMultiThreadCount(command.hasOption(ProcessOptions.MULTI_THREAD_COUNT.getArgName()) ? Byte.parseByte(command.getOptionValue(ProcessOptions.MULTI_THREAD_COUNT.getArgName())) : DEFAULT_MULTI_THREAD_COUNT); instance.setZeroOrigin(command.hasOption(ProcessOptions.ZERO_ORIGIN.getArgName())); instance.setAutoUpAxis(command.hasOption(ProcessOptions.AUTO_UP_AXIS.getArgName())); - instance.setReverseTextureCoordinate(command.hasOption(ProcessOptions.REVERSE_TEXCOORD.getArgName())); + + instance.printDebugOptions(); } private static void initVersionInfo() { @@ -225,4 +272,56 @@ protected static void validateOutputPath(Path path) throws IOException { throw new IOException(String.format("%s path is not writable.", path)); } } + + private static FormatType inferOutputFormat(FormatType inputFormat) { + if (FormatType.LAS == inputFormat || FormatType.LAZ == inputFormat) { + return FormatType.PNTS; + } else { + return FormatType.B3DM; + } + } + + public void printDebugOptions() { + if (!debug) { + return; + } + + log.debug("========================================"); + log.debug("inputPath: {}", inputPath); + log.debug("outputPath: {}", outputPath); + log.debug("inputFormat: {}", inputFormat); + log.debug("outputFormat: {}", outputFormat); + log.debug("terrainPath: {}", terrainPath); + log.debug("instancePath: {}", instancePath); + log.debug("logPath: {}", logPath); + log.debug("crs: {}", crs); + log.debug("proj: {}", proj); + log.debug("tileCount: {}", tileCount); + log.debug("fileCount: {}", fileCount); + log.debug("pointLimit: {}", pointLimit); + log.debug("pointScale: {}", pointScale); + log.debug("pointSkip: {}", pointSkip); + log.debug("nodeLimit: {}", nodeLimit); + log.debug("minLod: {}", minLod); + log.debug("maxLod: {}", maxLod); + log.debug("debug: {}", debug); + log.debug("debugLod: {}", debugLod); + log.debug("glb: {}", glb); + log.debug("classicTransformMatrix: {}", classicTransformMatrix); + log.debug("multiThreadCount: {}", multiThreadCount); + log.debug("recursive: {}", recursive); + log.debug("yUpAxis: {}", yUpAxis); + log.debug("refineAdd: {}", refineAdd); + log.debug("flipCoordinate: {}", flipCoordinate); + log.debug("zeroOrigin: {}", zeroOrigin); + log.debug("autoUpAxis: {}", autoUpAxis); + log.debug("ignoreTextures: {}", ignoreTextures); + log.debug("nameColumn: {}", nameColumn); + log.debug("heightColumn: {}", heightColumn); + log.debug("altitudeColumn: {}", altitudeColumn); + log.debug("absoluteAltitude: {}", absoluteAltitude); + log.debug("minimumHeight: {}", minimumHeight); + log.debug("skirtHeight: {}", skirtHeight); + log.debug("========================================"); + } } diff --git a/tiler/src/main/java/com/gaia3d/command/mago/Mago3DTiler.java b/tiler/src/main/java/com/gaia3d/command/mago/Mago3DTiler.java index 6edff704..2bd36227 100644 --- a/tiler/src/main/java/com/gaia3d/command/mago/Mago3DTiler.java +++ b/tiler/src/main/java/com/gaia3d/command/mago/Mago3DTiler.java @@ -15,10 +15,9 @@ public class Mago3DTiler { public void execute() { - String inputType = GlobalOptions.getInstance().getInputFormat(); - String outputType = GlobalOptions.getInstance().getOutputFormat(); - FormatType inputFormat = FormatType.fromExtension(inputType); - FormatType outputFormat = FormatType.fromExtension(outputType); + GlobalOptions globalOptions = GlobalOptions.getInstance(); + FormatType inputFormat = globalOptions.getInputFormat(); + FormatType outputFormat = globalOptions.getOutputFormat(); try { ProcessFlowModel processFlow = getProcessModel(inputFormat, outputFormat); log.info("Starting process flow: {}", processFlow.getModelName()); @@ -43,11 +42,7 @@ private ProcessFlowModel getProcessModel(FormatType inputFormat, FormatType outp } else if (FormatType.PNTS == outputFormat) { processFlow = new PointCloudProcessModel(); } else { - if (FormatType.LAS == inputFormat || FormatType.LAZ == inputFormat) { - processFlow = new PointCloudProcessModel(); - } else { - processFlow = new BatchedProcessModel(); - } + throw new IllegalArgumentException("Unsupported output format: " + outputFormat); } return processFlow; } diff --git a/tiler/src/main/java/com/gaia3d/converter/BatchedFileLoader.java b/tiler/src/main/java/com/gaia3d/converter/BatchedFileLoader.java index b775e914..47f95729 100644 --- a/tiler/src/main/java/com/gaia3d/converter/BatchedFileLoader.java +++ b/tiler/src/main/java/com/gaia3d/converter/BatchedFileLoader.java @@ -65,9 +65,8 @@ public List loadGridCoverages(List coverages) { public List loadFiles() { GlobalOptions globalOptions = GlobalOptions.getInstance(); File inputFile = new File(globalOptions.getInputPath()); - String inputExtension = globalOptions.getInputFormat(); boolean recursive = globalOptions.isRecursive(); - FormatType formatType = FormatType.fromExtension(inputExtension); + FormatType formatType = globalOptions.getInputFormat(); String[] extensions = getExtensions(formatType); return (List) FileUtils.listFiles(inputFile, extensions, recursive); } @@ -76,8 +75,7 @@ public List loadFiles() { public List loadTileInfo(File file) { GlobalOptions globalOptions = GlobalOptions.getInstance(); Path outputPath = new File(globalOptions.getOutputPath()).toPath(); - String inputExtension = globalOptions.getInputFormat(); - FormatType formatType = FormatType.fromExtension(inputExtension); + FormatType formatType = globalOptions.getInputFormat(); List tileInfos = new ArrayList<>(); KmlInfo kmlInfo = null; @@ -128,6 +126,11 @@ public List loadTileInfo(File file) { } private String[] getExtensions(FormatType formatType) { - return new String[]{formatType.getExtension().toLowerCase(), formatType.getExtension().toUpperCase()}; + String[] extensions = new String[4]; + extensions[0] = formatType.getExtension().toLowerCase(); + extensions[1] = formatType.getExtension().toUpperCase(); + extensions[2] = formatType.getSubExtension().toLowerCase(); + extensions[3] = formatType.getSubExtension().toUpperCase(); + return extensions; } } diff --git a/tiler/src/main/java/com/gaia3d/converter/InstancedFileLoader.java b/tiler/src/main/java/com/gaia3d/converter/InstancedFileLoader.java index 03f0fa91..394055ca 100644 --- a/tiler/src/main/java/com/gaia3d/converter/InstancedFileLoader.java +++ b/tiler/src/main/java/com/gaia3d/converter/InstancedFileLoader.java @@ -74,9 +74,8 @@ public List loadGridCoverages(List coverages) { public List loadFiles() { GlobalOptions globalOptions = GlobalOptions.getInstance(); File inputFile = new File(globalOptions.getInputPath()); - String inputExtension = globalOptions.getInputFormat(); boolean recursive = globalOptions.isRecursive(); - FormatType formatType = FormatType.fromExtension(inputExtension); + FormatType formatType = globalOptions.getInputFormat(); String[] extensions = getExtensions(formatType); return (List) FileUtils.listFiles(inputFile, extensions, recursive); } @@ -85,8 +84,7 @@ public List loadFiles() { public List loadTileInfo(File file) { GlobalOptions globalOptions = GlobalOptions.getInstance(); Path outputPath = new File(globalOptions.getOutputPath()).toPath(); - String inputExtension = globalOptions.getInputFormat(); - FormatType formatType = FormatType.fromExtension(inputExtension); + FormatType formatType = globalOptions.getInputFormat(); List tileInfos = new ArrayList<>(); if (FormatType.KML == formatType) { @@ -112,16 +110,13 @@ public List loadTileInfo(File file) { } } } else { - Path meshPath = file.toPath().getParent(); - File meshData = meshPath.resolve("tree.dae").toFile(); // TODO - + File meshData = new File(globalOptions.getInstancePath()); List scenes = loadScene(meshData); for (GaiaScene scene : scenes) { if (instanceScene == null) { instanceScene = scene; } } - // geojson, shape type List kmlInfos = kmlReader.readAll(file); if (kmlInfos != null) { diff --git a/tiler/src/main/java/com/gaia3d/converter/PointCloudFileLoader.java b/tiler/src/main/java/com/gaia3d/converter/PointCloudFileLoader.java index 73439f2f..f2c75f9a 100644 --- a/tiler/src/main/java/com/gaia3d/converter/PointCloudFileLoader.java +++ b/tiler/src/main/java/com/gaia3d/converter/PointCloudFileLoader.java @@ -32,9 +32,8 @@ public List loadPointCloud(File input) { public List loadFiles() { GlobalOptions globalOptions = GlobalOptions.getInstance(); File inputFile = new File(globalOptions.getInputPath()); - String inputExtension = globalOptions.getInputFormat(); boolean recursive = globalOptions.isRecursive(); - FormatType formatType = FormatType.fromExtension(inputExtension); + FormatType formatType = globalOptions.getInputFormat(); String[] extensions = getExtensions(formatType); return (ArrayList) FileUtils.listFiles(inputFile, extensions, recursive); } diff --git a/tiler/src/main/java/com/gaia3d/converter/PolygonFilter.java b/tiler/src/main/java/com/gaia3d/converter/PolygonFilter.java new file mode 100644 index 00000000..97b08aad --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/PolygonFilter.java @@ -0,0 +1,48 @@ +package com.gaia3d.converter; + +import com.gaia3d.util.VectorUtils; +import lombok.extern.slf4j.Slf4j; +import org.joml.Vector2d; +import org.locationtech.jts.geom.Coordinate; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +public class PolygonFilter { + + public List filter(List input) { + /* Remove last point if it is same as first point */ + if (input.get(0).equals(input.get(input.size() - 1))) { + input.remove(input.size() - 1); + } + + List result = new ArrayList<>(); + int length = input.size(); + for (int i = 0; i < length; i++) { + int index1 = (i - 1) % length < 0 ? length - 1 : (i - 1) % length; + int index2 = i % length; + int index3 = (i + 1) % length; + Vector2d prev = input.get(index1); + Vector2d crnt = input.get(index2); + Vector2d next = input.get(index3); + + double cross = VectorUtils.cross(prev, crnt, next); + if (crnt.equals(prev) || crnt.equals(next)) { + continue; + } else if (Double.isNaN(cross)) { + continue; + } else if (cross == 0) { + continue; + } else if (Math.abs(cross) < 0.000001) { + continue; + } + result.add(crnt); + } + if (!result.isEmpty()) { + result.add(new Vector2d(result.get(0))); + } + return result; + } +} \ No newline at end of file diff --git a/tiler/src/main/java/com/gaia3d/converter/assimp/AssimpConverter.java b/tiler/src/main/java/com/gaia3d/converter/assimp/AssimpConverter.java index 1ab23d90..79424578 100644 --- a/tiler/src/main/java/com/gaia3d/converter/assimp/AssimpConverter.java +++ b/tiler/src/main/java/com/gaia3d/converter/assimp/AssimpConverter.java @@ -193,13 +193,14 @@ private GaiaScene convertScene(AIScene aiScene, String filePath, String fileName } private List getEmbeddedTexturePath(AIScene aiScene, String filePath, String fileName) { + String EMBEDDED_TEXTURES_DIR = "embedded_textures"; List embeddedTextures = new ArrayList<>(); // embedded textures PointerBuffer aiTextures = aiScene.mTextures(); String fileNameWithoutExtension = FilenameUtils.removeExtension(fileName); int numTextures = aiScene.mNumTextures(); for (int i = 0; i < numTextures; i++) { - File path = new File(filePath, "embedded_textures"); + File path = new File(filePath, EMBEDDED_TEXTURES_DIR); path.mkdirs(); assert aiTextures != null; @@ -282,7 +283,7 @@ private GaiaMaterial processMaterial(AIMaterial aiMaterial, String path, List positions, List triangles) { GaiaNode node = new GaiaNode(); node.setTransformMatrix(new Matrix4d().identity()); @@ -97,9 +97,6 @@ protected GaiaPrimitive createPrimitives(GaiaMaterial material, List p GaiaVertex vertex = new GaiaVertex(); vertex.setPosition(new Vector3d(position.x, position.y, position.z)); - if (normal == null) { - log.error("why"); - } vertex.setNormal(normal); vertices.add(vertex); } @@ -108,7 +105,14 @@ protected GaiaPrimitive createPrimitives(GaiaMaterial material, List p } protected int indexOf(List positions, Vector3d item) { - return IntStream.range(0, positions.size()).filter(i -> positions.get(i) == item).findFirst().orElse(-1); + //return positions.indexOf(item); + IntStream intStream = IntStream.range(0, positions.size()); + int result = intStream + .filter(i -> positions.get(i) == item) + .findFirst() + .orElse(-1); + intStream.close(); + return result; } protected double getHeight(SimpleFeature feature, String column, double minimumHeight) { @@ -189,4 +193,146 @@ protected double getAltitude(SimpleFeature feature, String column) { } return result; } + + protected GaiaPrimitive createPrimitiveFromPolygons(List> polygons) { + GaiaTessellator tessellator = new GaiaTessellator(); + + GaiaPrimitive primitive = new GaiaPrimitive(); + List vertexList = new ArrayList<>(); + //Map vertexMap = new HashMap<>(); + Map pointsMap = new HashMap<>(); + + int polygonCount = polygons.size(); + for (List polygon : polygons) { + + Vector3d normal = new Vector3d(); + tessellator.calculateNormal3D(polygon, normal); + + for (Vector3d vector3d : polygon) { + GaiaVertex vertex = new GaiaVertex(); + vertex.setPosition(vector3d); + vertex.setNormal(normal); + vertexList.add(vertex); + } + } + + int vertexCount = vertexList.size(); + for (int m = 0; m < vertexCount; m++) { + GaiaVertex vertex = vertexList.get(m); + pointsMap.put(vertex.getPosition(), m); + } + + primitive.setVertices(vertexList); // total vertex list.*** + + List resultTrianglesIndices = new ArrayList<>(); + + for (int m = 0; m < polygonCount; m++) { + GaiaSurface surface = new GaiaSurface(); + primitive.getSurfaces().add(surface); + + int idx1Local = -1; + int idx2Local = -1; + int idx3Local = -1; + + List polygon = polygons.get(m); + resultTrianglesIndices.clear(); + tessellator.tessellate3D(polygon, resultTrianglesIndices); + + int indicesCount = resultTrianglesIndices.size(); + int trianglesCount = indicesCount / 3; + for (int n = 0; n < trianglesCount; n++) { + idx1Local = resultTrianglesIndices.get(n * 3); + idx2Local = resultTrianglesIndices.get(n * 3 + 1); + idx3Local = resultTrianglesIndices.get(n * 3 + 2); + + Vector3d point1 = polygon.get(idx1Local); + Vector3d point2 = polygon.get(idx2Local); + Vector3d point3 = polygon.get(idx3Local); + + int idx1 = pointsMap.get(point1); + int idx2 = pointsMap.get(point2); + int idx3 = pointsMap.get(point3); + + GaiaFace face = new GaiaFace(); + int[] indicesArray = new int[3]; + indicesArray[0] = idx1; + indicesArray[1] = idx2; + indicesArray[2] = idx3; + face.setIndices(indicesArray); + surface.getFaces().add(face); + } + } + + return primitive; + } + + protected GaiaPrimitive createPrimitiveFromGaiaExtrusionSurfaces(List surfaces) { + GaiaTessellator tessellator = new GaiaTessellator(); + GaiaPrimitive primitive = new GaiaPrimitive(); + List vertexList = new ArrayList<>(); + Map pointsMap = new HashMap<>(); + + for (GaiaExtrusionSurface extrusionSurface : surfaces) { + List polygon = extrusionSurface.getVertices(); + + Vector3d normal = new Vector3d(); + tessellator.calculateNormal3D(polygon, normal); + + for (Vector3d vector3d : polygon) { + GaiaVertex vertex = new GaiaVertex(); + vertex.setPosition(vector3d); + vertex.setNormal(normal); + vertexList.add(vertex); + } + } + + int vertexCount = vertexList.size(); + for (int m = 0; m < vertexCount; m++) { + GaiaVertex vertex = vertexList.get(m); + pointsMap.put(vertex.getPosition(), m); + } + + primitive.setVertices(vertexList); // total vertex list.*** + + List resultTrianglesIndices = new ArrayList<>(); + + for (GaiaExtrusionSurface extrusionSurface : surfaces) { + List polygon = extrusionSurface.getVertices(); + GaiaSurface gaiaSurface = new GaiaSurface(); + primitive.getSurfaces().add(gaiaSurface); + + int idx1Local = -1; + int idx2Local = -1; + int idx3Local = -1; + + resultTrianglesIndices.clear(); + tessellator.tessellate3D(polygon, resultTrianglesIndices); + + int indicesCount = resultTrianglesIndices.size(); + int trianglesCount = indicesCount / 3; + for (int n = 0; n < trianglesCount; n++) { + idx1Local = resultTrianglesIndices.get(n * 3); + idx2Local = resultTrianglesIndices.get(n * 3 + 1); + idx3Local = resultTrianglesIndices.get(n * 3 + 2); + + Vector3d point1 = polygon.get(idx1Local); + Vector3d point2 = polygon.get(idx2Local); + Vector3d point3 = polygon.get(idx3Local); + + int idx1 = pointsMap.get(point1); + int idx2 = pointsMap.get(point2); + int idx3 = pointsMap.get(point3); + + GaiaFace face = new GaiaFace(); + int[] indicesArray = new int[3]; + indicesArray[0] = idx1; + indicesArray[1] = idx2; + indicesArray[2] = idx3; + face.setIndices(indicesArray); + gaiaSurface.getFaces().add(face); + } + } + + return primitive; + } } diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/ConvexHullTessellator.java b/tiler/src/main/java/com/gaia3d/converter/geometry/ConvexHullTessellator.java new file mode 100644 index 00000000..4d4780ba --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/ConvexHullTessellator.java @@ -0,0 +1,26 @@ +package com.gaia3d.converter.geometry; + +import lombok.extern.slf4j.Slf4j; +import org.joml.Vector3d; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +public class ConvexHullTessellator { + + public List tessellate(List positions) { + List result = new ArrayList<>(); + int start = 1; + int size = positions.size() - 1; + Vector3d p0 = positions.get(0); + Vector3d normal = null; + for (int i = start; i < size; i ++) { + Vector3d p1 = positions.get(i); + Vector3d p2 = positions.get(i + 1); + GaiaTriangle triangle = new GaiaTriangle(p0, p1, p2); + result.add(triangle); + } + return result; + } +} diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/Extruder2D.java b/tiler/src/main/java/com/gaia3d/converter/geometry/Extruder2D.java new file mode 100644 index 00000000..1a1cb034 --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/Extruder2D.java @@ -0,0 +1,80 @@ +package com.gaia3d.converter.geometry; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.joml.Vector3d; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@AllArgsConstructor +public class Extruder2D { + + private final Tessellator2D tessellator; + + public Extrusion extrude(List positions, double roofHeight, double floorHeight) { + List resultTriangles = new ArrayList<>(); + List resultPositions = new ArrayList<>(); + Extrusion result = new Extrusion(resultTriangles, resultPositions); + + List roofPositions = resetPositionHeight(positions, roofHeight); + List floorPositions = resetPositionHeight(positions, floorHeight); + + List triangleRoof = tessellator.tessellate(roofPositions, roofHeight); + List triangleFloor = tessellator.tessellate(floorPositions, floorHeight); + + List wallPositions = createWallPositions(roofPositions, floorPositions); + List wallTriangles = createWallTriangles(wallPositions); + + resultTriangles.addAll(wallTriangles); + resultTriangles.addAll(triangleRoof); + resultPositions.addAll(wallPositions); + resultPositions.addAll(roofPositions); + + return result; + } + + private List createWallPositions(List roofPosition, List floorPosition) { + List result = new ArrayList<>(); + int size = roofPosition.size(); + for (int index = 0; index < size; index++) { + int crntIndex = index % size; + int nextIndex = (index + 1) % size; + Vector3d roofTriangle = roofPosition.get(crntIndex); + Vector3d roofTriangleNext = roofPosition.get(nextIndex); + Vector3d floorTriangle = floorPosition.get(crntIndex); + Vector3d floorTriangleNext = floorPosition.get(nextIndex); + + result.add(new Vector3d(roofTriangle)); + result.add(new Vector3d(roofTriangleNext)); + result.add(new Vector3d(floorTriangle)); + result.add(new Vector3d(floorTriangleNext)); + } + return result; + } + + private List createWallTriangles(List wallPositions) { + List result = new ArrayList<>(); + for (int index = 0; index < wallPositions.size(); index += 4) { + Vector3d positionA = wallPositions.get(index); + Vector3d positionB = wallPositions.get(index + 1); + Vector3d positionC = wallPositions.get(index + 2); + Vector3d positionD = wallPositions.get(index + 3); + + GaiaTriangle wallTriangle1 = new GaiaTriangle(positionA, positionC, positionD); + GaiaTriangle wallTriangle2 = new GaiaTriangle(positionA, positionD, positionB); + + result.add(wallTriangle1); + result.add(wallTriangle2); + } + return result; + } + + private List resetPositionHeight(List positions, double height) { + return positions.stream().map((gaiaTriangle) -> { + return new Vector3d(gaiaTriangle.x, gaiaTriangle.y, height); + }).collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/GaiaBuildingSurface.java b/tiler/src/main/java/com/gaia3d/converter/geometry/GaiaBuildingSurface.java new file mode 100644 index 00000000..4cce8be4 --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/GaiaBuildingSurface.java @@ -0,0 +1,19 @@ +package com.gaia3d.converter.geometry; + +import com.gaia3d.basic.geometry.GaiaBoundingBox; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import org.joml.Vector3d; + +import java.util.List; + +@Getter +@Setter +@Builder +public class GaiaBuildingSurface { + private String id; + private String name; + List positions; + private GaiaBoundingBox boundingBox; +} diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/GaiaBuilding.java b/tiler/src/main/java/com/gaia3d/converter/geometry/GaiaExtrusionBuilding.java similarity index 91% rename from tiler/src/main/java/com/gaia3d/converter/geometry/GaiaBuilding.java rename to tiler/src/main/java/com/gaia3d/converter/geometry/GaiaExtrusionBuilding.java index 9fdd0806..0d5c40e5 100644 --- a/tiler/src/main/java/com/gaia3d/converter/geometry/GaiaBuilding.java +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/GaiaExtrusionBuilding.java @@ -11,8 +11,7 @@ @Getter @Setter @Builder -public class GaiaBuilding { - +public class GaiaExtrusionBuilding { private String id; private String name; private double roofHeight; diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/GaiaTriangle.java b/tiler/src/main/java/com/gaia3d/converter/geometry/GaiaTriangle.java index 673256e3..b8a6f94a 100644 --- a/tiler/src/main/java/com/gaia3d/converter/geometry/GaiaTriangle.java +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/GaiaTriangle.java @@ -2,13 +2,15 @@ import lombok.Getter; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; import org.joml.Vector3d; @Getter @Setter +@Slf4j public class GaiaTriangle { private final Vector3d[] positions; - private final Vector3d normal = new Vector3d(); + private Vector3d normal = new Vector3d(); public GaiaTriangle(Vector3d position1, Vector3d position2, Vector3d position3) { this.positions = new Vector3d[3]; @@ -19,11 +21,27 @@ public GaiaTriangle(Vector3d position1, Vector3d position2, Vector3d position3) } private void calcNormal() { + if (positions[0].equals(positions[1]) || positions[1].equals(positions[2]) || positions[2].equals(positions[0])) { + log.warn("Degenerate triangle detected"); + this.normal = new Vector3d(0, 0, 1); + return; + } + Vector3d v1 = new Vector3d(); Vector3d v2 = new Vector3d(); positions[1].sub(positions[0], v1); positions[2].sub(positions[1], v2); + v1.normalize(); + v2.normalize(); v1.cross(v2, this.normal); + + double z = this.normal.z(); + if (z < 0.0) { + this.normal.negate(); + } + this.normal.normalize(); + + log.debug("Normal: {}", this.normal.z()); } } diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/GaiaTriangle2D.java b/tiler/src/main/java/com/gaia3d/converter/geometry/GaiaTriangle2D.java new file mode 100644 index 00000000..936202b9 --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/GaiaTriangle2D.java @@ -0,0 +1,30 @@ +package com.gaia3d.converter.geometry; + +import com.gaia3d.util.VectorUtils; +import lombok.Getter; +import lombok.Setter; +import org.joml.Vector2d; + +@Getter +@Setter +public class GaiaTriangle2D { + private final Vector2d[] positions; + private final Vector2d normal = new Vector2d(); + + public GaiaTriangle2D(Vector2d position1, Vector2d position2) { + this.positions = new Vector2d[3]; + this.positions[0] = position1; + this.positions[1] = position2; + calcNormal(); + } + + private void calcNormal() { + Vector2d v1 = new Vector2d(); + Vector2d v2 = new Vector2d(); + positions[1].sub(positions[0], v1); + positions[2].sub(positions[1], v2); + + VectorUtils.cross(v1, v2, this.normal); + this.normal.normalize(); + } +} \ No newline at end of file diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/InnerRing.java b/tiler/src/main/java/com/gaia3d/converter/geometry/InnerRing.java new file mode 100644 index 00000000..aa812549 --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/InnerRing.java @@ -0,0 +1,56 @@ +package com.gaia3d.converter.geometry; + +import lombok.Getter; +import lombok.Setter; +import org.joml.Vector2d; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@Getter +@Setter +public class InnerRing { + private List coordinates; + private Vector2d leftDown; + private double minValue; + + public InnerRing(List coordinates) { + this.coordinates = coordinates; + this.leftDown = findLeftDown(); + this.minValue = leftDown.x + leftDown.y; + this.coordinates = this.changeOrder(coordinates, coordinates.indexOf(leftDown)); + //this.coordinates.add(this.leftDown); + } + + public double cross(Vector2d a, Vector2d b, Vector2d c) { + Vector2d ab = a.sub(b, new Vector2d()); + Vector2d bc = b.sub(c, new Vector2d()); + return cross(ab, bc); + } + public double cross(Vector2d a, Vector2d b) { + return (a.x * b.y) - (a.y * b.x); + } + + private Vector2d findLeftDown() { + return coordinates.stream().sorted((a, b) -> { + double positionA = a.x + a.y; + double positionB = b.x + b.y; + return Double.compare(positionA, positionB); + }).findFirst().orElse(null); + } + + public List changeOrder(List list, int index) { + if (list.get(0).equals(list.get(list.size() - 1))) { + list.remove(list.size() - 1); + } + + List result = list.subList(index, list.size()); + result.addAll(list.subList(0, index)); + + if (!result.isEmpty()) { + result.add(new Vector2d(result.get(0))); + } + return result; + } +} diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/InnerRingRemover.java b/tiler/src/main/java/com/gaia3d/converter/geometry/InnerRingRemover.java new file mode 100644 index 00000000..42ce2fbe --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/InnerRingRemover.java @@ -0,0 +1,163 @@ +package com.gaia3d.converter.geometry; + +import com.gaia3d.converter.PolygonFilter; +import com.gaia3d.converter.geometry.tessellator.Point2DTess; +import com.gaia3d.converter.geometry.tessellator.Segment2DTess; +import com.gaia3d.util.VectorUtils; +import lombok.extern.slf4j.Slf4j; +import org.joml.Vector2d; +import org.locationtech.jts.geom.Coordinate; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +public class InnerRingRemover { + + public Coordinate[] removeAll(Coordinate[] outerRing, List innerRings) { + PolygonFilter polygonFilter = new PolygonFilter(); + + List outerRingList = List.of(outerRing); + List outerRingVector = outerRingList.stream() + .map(c -> new Vector2d(c.x, c.y)) + .collect(Collectors.toList()); + outerRingVector = polygonFilter.filter(outerRingVector); + + List innerRingObjects = innerRings.stream().map(innerRing -> { + List innerRingVector = List.of(innerRing).stream() + .map(c -> new Vector2d(c.x, c.y)) + .collect(Collectors.toList()); + innerRingVector = polygonFilter.filter(innerRingVector); + return new InnerRing(innerRingVector); + }).collect(Collectors.toList()); + + innerRingObjects = innerRingObjects.stream().sorted((a, b) -> { + return Double.compare(a.getMinValue(), b.getMinValue()); + }).collect(Collectors.toList()); + + for (InnerRing innerRingObject : innerRingObjects) { + outerRingVector = removeRing(outerRingVector, innerRingObject); + } + + List result = outerRingVector.stream() + .map(v -> new Coordinate(v.x, v.y)) + .collect(Collectors.toList()); + outerRing = result.toArray(new Coordinate[0]); + + return outerRing; + } + + public List removeRing(List outerRingVector, InnerRing innerRing) { + List innerRingVector = innerRing.getCoordinates(); + Vector2d inneringLeftDown = innerRing.getLeftDown(); + + List nearestOuterRings = outerRingVector.stream().sorted((a, b) -> { + double distanceA = a.distance(inneringLeftDown); + double distanceB = b.distance(inneringLeftDown); + return Double.compare(distanceA, distanceB); + }).collect(Collectors.toList()); + + + Point2DTess leftDownPoint = new Point2DTess(inneringLeftDown, null); + double error = 1E-10; + + + Vector2d nearestOuterRing = null; + for (int i = 0; i < nearestOuterRings.size(); i++) { + nearestOuterRing = nearestOuterRings.get(i); + + Point2DTess nearestOuterPoint = new Point2DTess(nearestOuterRing, null); + Segment2DTess cuttingSegment = new Segment2DTess(leftDownPoint, nearestOuterPoint); + + Point2DTess vectorA, vectorB; + Segment2DTess innerRingSegement; + Point2DTess intersectionPoint; + + boolean isIntersect = false; + + for (int j = 0; j < innerRingVector.size() - 1; j++) { + Vector2d innerRingVectorA = innerRingVector.get(j); + Vector2d innerRingVectorB = innerRingVector.get(j + 1); + + vectorA = new Point2DTess(innerRingVectorA, null); + vectorB = new Point2DTess(innerRingVectorB, null); + innerRingSegement = new Segment2DTess(vectorA, vectorB); + intersectionPoint = new Point2DTess(null, null); + if (cuttingSegment.intersectionWithSegment(innerRingSegement, intersectionPoint, error) == 1) { + isIntersect = true; + } + } + + Vector2d innerRingVectorA = innerRingVector.get(innerRingVector.size() - 1); + Vector2d innerRingVectorB = innerRingVector.get(0); + vectorA = new Point2DTess(innerRingVectorA, null); + vectorB = new Point2DTess(innerRingVectorB, null); + innerRingSegement = new Segment2DTess(vectorA, vectorB); + intersectionPoint = new Point2DTess(null, null); + if (cuttingSegment.intersectionWithSegment(innerRingSegement, intersectionPoint, error) == 1) { + isIntersect = true; + } + + for (int j = 0; j < outerRingVector.size() - 1; j++) { + Vector2d outerRingVectorA = outerRingVector.get(j); + Vector2d outerRingVectorB = outerRingVector.get(j + 1); + + vectorA = new Point2DTess(outerRingVectorA, null); + vectorB = new Point2DTess(outerRingVectorB, null); + innerRingSegement = new Segment2DTess(vectorA, vectorB); + intersectionPoint = new Point2DTess(null, null); + if (cuttingSegment.intersectionWithSegment(innerRingSegement, intersectionPoint, error) == 1) { + isIntersect = true; + } + } + + Vector2d outerRingVectorA = outerRingVector.get(outerRingVector.size() - 1); + Vector2d outerRingVectorB = outerRingVector.get(0); + + vectorA = new Point2DTess(outerRingVectorA, null); + vectorB = new Point2DTess(outerRingVectorB, null); + innerRingSegement = new Segment2DTess(vectorA, vectorB); + intersectionPoint = new Point2DTess(null, null); + if (cuttingSegment.intersectionWithSegment(innerRingSegement, intersectionPoint, error) == 1) { + isIntersect = true; + } + + if (!isIntersect) { + break; + } + } + outerRingVector = changeOrder(outerRingVector, outerRingVector.indexOf(nearestOuterRing)); + return combine(outerRingVector, innerRingVector); + } + + public List combine(List outerRing, List innerRing) { + List result = new ArrayList<>(); + result.addAll(outerRing); + result.addAll(innerRing); + result.add(new Vector2d(outerRing.get(0))); + return result; + } + + public List changeOrder(List list, int index) { + if (list.get(0).equals(list.get(list.size() - 1))) { + list.remove(list.size() - 1); + } + + List result = list.subList(index, list.size()); + result.addAll(list.subList(0, index)); + + if (!result.isEmpty()) { + result.add(new Vector2d(result.get(0))); + } + return result; + } + + public double cross(Vector2d a, Vector2d b, Vector2d c) { + Vector2d ab = a.sub(b, new Vector2d()); + ab.normalize(); + Vector2d bc = b.sub(c, new Vector2d()); + bc.normalize(); + return VectorUtils.cross(ab, bc); + } +} diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/Tessellator.java b/tiler/src/main/java/com/gaia3d/converter/geometry/Tessellator.java index fe57812f..8f8848a7 100644 --- a/tiler/src/main/java/com/gaia3d/converter/geometry/Tessellator.java +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/Tessellator.java @@ -1,5 +1,6 @@ package com.gaia3d.converter.geometry; +import com.gaia3d.basic.geometry.GaiaBoundingBox; import lombok.extern.slf4j.Slf4j; import org.joml.Vector3d; @@ -15,7 +16,6 @@ public List tessellate(List positions) { boolean isCCW = this.validateAngle(positions); if (!isCCW) { - // reverse array Collections.reverse(positions); } @@ -25,6 +25,7 @@ public List tessellate(List positions) { List triangles = convertTriangles(convex); result.addAll(triangles); }); + return result; } @@ -52,20 +53,17 @@ private List> convertConvex(List> result, List(); } - //log.warn("new"); if (isConvex(positions)) { - //log.warn("isConvex"); result.add(positions); } else { - //log.warn("isConcave"); Vector3d clockWisePosition = getClockWisePosition(positions); if (clockWisePosition == null) { return result; } int clockWiseIndex = positions.indexOf(clockWisePosition); - List nearestPositions = sortNearest(positions, clockWiseIndex); + for (Vector3d nearestPosition : nearestPositions) { List> polygons = splitConvex(positions, clockWisePosition, nearestPosition); @@ -81,9 +79,6 @@ private List> convertConvex(List> result, List convex = positions; // temp only convex for test - //result.add(convex); return result; } @@ -127,17 +122,6 @@ private List sortNearest(List positions, int clockWiseIndex) } } - /*Collections.sort(result, (p1, p2) -> { - double d1 = clockWisePosition.distanceSquared(p1); - double d2 = clockWisePosition.distanceSquared(p2); - if (d1 < d2) { - return -1; - } else if (d1 > d2) { - return 1; - } else { - return 0; - } - });*/ result.sort((p1, p2) -> { double d1 = clockWisePosition.distanceSquared(p1); double d2 = clockWisePosition.distanceSquared(p2); @@ -168,11 +152,11 @@ private boolean isConvex(List positions) { int index1 = (i - 1) % positions.size() < 0 ? positions.size() - 1 : (i - 1) % positions.size(); int index2 = i % positions.size(); int index3 = (i + 1) % positions.size(); - //log.info("index1: {}, index2: {}, index3: {}", index1, index2, index3); Vector3d position1 = positions.get(index1); Vector3d position2 = positions.get(index2); Vector3d position3 = positions.get(index3); + Vector3d normal = calcNormal(position1, position2, position3); if (isClockWise(normal)) { return false; @@ -208,6 +192,8 @@ private double calcAngle(Vector3d a, Vector3d b, Vector3d c) { Vector3d v2 = new Vector3d(); b.sub(a, v1); c.sub(b, v2); + v1.normalize(); + v2.normalize(); return Math.toDegrees(v1.angle(v2)); } @@ -222,8 +208,12 @@ private Vector3d cross(Vector3d a, Vector3d b, Vector3d c) { Vector3d v2 = new Vector3d(); b.sub(a, v1); c.sub(b, v2); + v1.normalize(); + v2.normalize(); Vector3d c1 = new Vector3d(); v1.cross(v2, c1); + v1 = null; + v2 = null; return c1; } @@ -232,10 +222,17 @@ private boolean findIntersection(List positions, Vector3d startPositio int next = (index + 1) % positions.size(); Vector3d crntPosition = positions.get(index); Vector3d nextPosition = positions.get(next); - if (isIntersection(startPosition, endPosition, crntPosition, nextPosition)) { + + if (startPosition.equals(crntPosition) && endPosition.equals(nextPosition)) { + return true; + } + if (startPosition.equals(nextPosition) && endPosition.equals(crntPosition)) { return true; } + if (isIntersection(startPosition, endPosition, crntPosition, nextPosition)) { + return true; + } if (isIntersection(startPosition, endPosition, crntPosition)) { return true; } @@ -250,6 +247,7 @@ private boolean isIntersection(Vector3d a1, Vector3d a2, Vector3d b1, Vector3d b Vector3d c4 = cross(b1, b2, a2); double d1 = c1.dot(c2); double d2 = c3.dot(c4); + if (d1 == 0 && d2 == 0) { return false; } else { @@ -265,4 +263,13 @@ private boolean isIntersection(Vector3d a1, Vector3d a2, Vector3d p1) { Vector3d c1 = cross(a1, a2, p1); return c1.z() == 0; } + + private double dot(Vector3d v1, Vector3d v2) { + return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; + } + + + private Vector3d subtract(Vector3d v1, Vector3d v2) { + return new Vector3d(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); + } } diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/Tessellator2D.java b/tiler/src/main/java/com/gaia3d/converter/geometry/Tessellator2D.java new file mode 100644 index 00000000..43c0d088 --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/Tessellator2D.java @@ -0,0 +1,227 @@ +package com.gaia3d.converter.geometry; + +import com.gaia3d.util.VectorUtils; +import lombok.extern.slf4j.Slf4j; +import org.joml.Vector2d; +import org.joml.Vector2d; +import org.joml.Vector3d; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +public class Tessellator2D { + + public List tessellate(List positions3D, double altitude) { + List positions = positions3D.stream() + .map(p -> new Vector2d(p.x, p.y)) + .collect(Collectors.toList()); + + startEnd(positions); + boolean isCCW = this.validateAngle(positions); + if (!isCCW) { + log.warn("IS CCW POLYGON"); + Collections.reverse(positions); + } + + List result = new ArrayList<>(); + List> convexes = convertConvex(null, positions); + convexes.forEach((convex) -> { + List triangles = convertTriangles(convex, altitude); + result.addAll(triangles); + }); + + return result; + } + + private void startEnd(List positions) { + Vector2d start = positions.get(0); + Vector2d end = positions.get(positions.size() - 1); + if (start.equals(end)) { + positions.remove(positions.size() - 1); + } + } + + private List convertTriangles(List positions, double height) { + List result = new ArrayList<>(); + for (int i = 0; i < positions.size() - 2; i++) { + Vector3d vec3A = new Vector3d(positions.get(0).x, positions.get(0).y, height); + Vector3d vec3B = new Vector3d(positions.get(i + 1).x, positions.get(i + 1).y, height); + Vector3d vec3C = new Vector3d(positions.get(i + 2).x, positions.get(i + 2).y, height); + GaiaTriangle triangle = new GaiaTriangle(vec3A, vec3B, vec3C); + result.add(triangle); + } + return result; + } + + private List> convertConvex(List> result, List positions) { + if (result == null) { + result = new ArrayList<>(); + } + if (isConvex(positions)) { + result.add(positions); + } else { + Vector2d clockWisePosition = getClockWisePosition(positions); + if (clockWisePosition == null) { + return result; + } + + int clockWiseIndex = positions.indexOf(clockWisePosition); + List nearestPositions = sortNearest(positions, clockWiseIndex); + + boolean isSuccess = false; + for (Vector2d nearestPosition : nearestPositions) { + List> polygons = splitConvex(positions, clockWisePosition, nearestPosition); + List splitA = polygons.get(0); + List splitB = polygons.get(1); + + boolean isIntersection = findIntersection(positions, clockWisePosition, nearestPosition); + if (!isIntersection) { + boolean angleA = this.validateAngle(polygons.get(0)); + boolean angleB = this.validateAngle(polygons.get(1)); + if (angleA == angleB) { + convertConvex(result, polygons.get(0)); + convertConvex(result, polygons.get(1)); + isSuccess = true; + break; + } + } + } + + if (!isSuccess) { + log.warn("is FAILED"); + } + } + return result; + } + + private List> splitConvex(List positions, Vector2d positionA, Vector2d positionB) { + List> result = new ArrayList<>(); + result.add(createSplits(positions, positionA, positionB)); + result.add(createSplits(positions, positionB, positionA)); + return result; + } + + private List createSplits(List positions, Vector2d startPosition, Vector2d endPosition) { + List result = new ArrayList<>(); + result.add(startPosition); + result.add(endPosition); + int index = positions.indexOf(endPosition); + for (int i = 0; i < positions.size() - 1; i++) { + int crnt = index % positions.size(); + int next = (index + 1) % positions.size(); + Vector2d crntPosition = positions.get(crnt); + Vector2d nextPosition = positions.get(next); + if (nextPosition == startPosition || nextPosition == endPosition) { + break; + } else if (!crntPosition.equals(nextPosition)) { + result.add(nextPosition); + } + index++; + } + return result; + } + + private List sortNearest(List positions, int clockWiseIndex) { + Vector2d prevPosition = positions.get((clockWiseIndex - 1) % positions.size() < 0 ? positions.size() - 1 : (clockWiseIndex - 1) % positions.size()); + Vector2d clockWisePosition = positions.get(clockWiseIndex); + Vector2d nextPosition = positions.get((clockWiseIndex + 1) % positions.size()); + + List result = new ArrayList<>(); + + for (Vector2d position : positions) { + if (position != prevPosition && position != clockWisePosition && position != nextPosition) { + result.add(position); + } + } + + result.sort((p1, p2) -> { + double d1 = clockWisePosition.distanceSquared(p1); + double d2 = clockWisePosition.distanceSquared(p2); + return Double.compare(d1, d2); + }); + + return result; + } + + private Vector2d getClockWisePosition(List positions) { + for (int i = 0; i < positions.size(); i++) { + int index1 = (i - 1) % positions.size() < 0 ? positions.size() - 1 : (i - 1) % positions.size(); + int index2 = i % positions.size(); + int index3 = (i + 1) % positions.size(); + Vector2d position1 = positions.get(index1); + Vector2d position2 = positions.get(index2); + Vector2d position3 = positions.get(index3); + double normal = VectorUtils.cross(position1, position2, position3); + if (normal < 0) { + return position2; + } + } + return null; + } + + private boolean isConvex(List positions) { + for (int i = 0; i < positions.size(); i++) { + int index1 = (i - 1) % positions.size() < 0 ? positions.size() - 1 : (i - 1) % positions.size(); + int index2 = i % positions.size(); + int index3 = (i + 1) % positions.size(); + + Vector2d position1 = positions.get(index1); + Vector2d position2 = positions.get(index2); + Vector2d position3 = positions.get(index3); + + double normal = VectorUtils.cross(position1, position2, position3); + if (normal < 0) { + return false; + } + } + return true; + } + + private boolean validateAngle(List positions) { + double angleSum = 0; + double resverseAngleSum = 0; + for (int i = 0; i < positions.size() - 1; i++) { + int index1 = (i - 1) % positions.size() < 0 ? positions.size() - 1 : (i - 1) % positions.size(); + int index2 = i % positions.size(); + int index3 = (i + 1) % positions.size(); + double normal = VectorUtils.cross(positions.get(index1), positions.get(index2), positions.get(index3)); + double angle = VectorUtils.calcAngle(positions.get(index1), positions.get(index2), positions.get(index3)); + if (normal > 0) { + angleSum += angle; + } else { + resverseAngleSum += angle; + } + } + return angleSum > resverseAngleSum; + } + + + private boolean findIntersection(List positions, Vector2d startPosition, Vector2d endPosition) { + for (int index = 0; index < positions.size(); index++) { + int next = (index + 1) % positions.size(); + Vector2d crntPosition = positions.get(index); + Vector2d nextPosition = positions.get(next); + + if (startPosition.equals(crntPosition) || endPosition.equals(nextPosition)) { + log.info("SAME INTERSECT1, {}:{}:{}:{}", startPosition, endPosition, crntPosition, nextPosition); + return true; + } + if (startPosition.equals(nextPosition) || endPosition.equals(crntPosition)) { + log.info("SAME INTERSECT2, {}:{}:{}:{}", startPosition, endPosition, crntPosition, nextPosition); + return true; + } + + + if (VectorUtils.isIntersection(startPosition, endPosition, crntPosition, nextPosition)) { + return true; + } + if (VectorUtils.isIntersection(startPosition, endPosition, crntPosition)) { + return true; + } + } + return false; + } +} diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/Vector3dsOnlyHashEquals.java b/tiler/src/main/java/com/gaia3d/converter/geometry/Vector3dsOnlyHashEquals.java new file mode 100644 index 00000000..c07a283d --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/Vector3dsOnlyHashEquals.java @@ -0,0 +1,28 @@ +package com.gaia3d.converter.geometry; + +import lombok.extern.slf4j.Slf4j; +import org.joml.Vector3d; + +@Slf4j +public class Vector3dsOnlyHashEquals extends Vector3d { + public Vector3dsOnlyHashEquals(Vector3d localPosition) { + super(localPosition); + } + + public Vector3dsOnlyHashEquals() { + super(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj == null) { + return false; + } else if (this.getClass() != obj.getClass()) { + return false; + } else { + return false; + } + } +} diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/citygml/CityGmlConverter.java b/tiler/src/main/java/com/gaia3d/converter/geometry/citygml/CityGmlConverter.java index 72c5b49f..a200f444 100644 --- a/tiler/src/main/java/com/gaia3d/converter/geometry/citygml/CityGmlConverter.java +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/citygml/CityGmlConverter.java @@ -1,19 +1,18 @@ package com.gaia3d.converter.geometry.citygml; import com.gaia3d.basic.geometry.GaiaBoundingBox; -import com.gaia3d.basic.structure.GaiaMaterial; -import com.gaia3d.basic.structure.GaiaNode; -import com.gaia3d.basic.structure.GaiaScene; +import com.gaia3d.basic.structure.*; import com.gaia3d.command.mago.GlobalOptions; import com.gaia3d.converter.Converter; import com.gaia3d.converter.geometry.*; +import com.gaia3d.converter.geometry.tessellator.GaiaTessellator; import com.gaia3d.util.GlobeUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.citygml4j.core.model.building.Building; -import org.citygml4j.core.model.core.AbstractCityObject; -import org.citygml4j.core.model.core.AbstractCityObjectProperty; -import org.citygml4j.core.model.core.CityModel; +import org.citygml4j.core.model.building.BuildingRoom; +import org.citygml4j.core.model.construction.*; +import org.citygml4j.core.model.core.*; import org.citygml4j.xml.CityGMLContext; import org.citygml4j.xml.CityGMLContextException; import org.citygml4j.xml.reader.CityGMLInputFactory; @@ -21,18 +20,21 @@ import org.citygml4j.xml.reader.CityGMLReader; import org.joml.Matrix4d; import org.joml.Vector3d; +import org.locationtech.proj4j.CoordinateReferenceSystem; +import org.locationtech.proj4j.ProjCoordinate; import org.xmlobjects.gml.model.geometry.DirectPositionList; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty; import org.xmlobjects.gml.model.geometry.primitives.*; import java.io.File; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Vector; +import java.util.*; @Slf4j @RequiredArgsConstructor public class CityGmlConverter extends AbstractGeometryConverter implements Converter { + private final GlobalOptions globalOptions = GlobalOptions.getInstance(); @Override public List load(String path) { @@ -52,9 +54,6 @@ public List load(Path path) { protected List convert(File file) { List scenes = new ArrayList<>(); - GlobalOptions globalOptions = GlobalOptions.getInstance(); - boolean flipCoordinate = globalOptions.isFlipCoordinate(); - try { Tessellator tessellator = new Tessellator(); Extruder extruder = new Extruder(tessellator); @@ -64,67 +63,68 @@ protected List convert(File file) { CityGMLReader reader = factory.createCityGMLReader(file); CityModel cityModel = (CityModel) reader.next(); - List gaiaBuildings = new ArrayList<>(); + double skirtHeight = globalOptions.getSkirtHeight(); + + List buildingList = new ArrayList<>(); + List> buildingSurfacesList = new ArrayList<>(); List cityObjectMembers = cityModel.getCityObjectMembers(); for (AbstractCityObjectProperty cityObjectProperty : cityObjectMembers) { AbstractCityObject cityObject = cityObjectProperty.getObject(); - Building building = (Building) cityObject; - SolidProperty solidProperty= building.getLod1Solid(); - AbstractSolid solid = solidProperty.getObject(); - - Shell shell = ((Solid) solid).getExterior().getObject(); - List surfaceProperties = shell.getSurfaceMembers(); - - if (building.getHeights().isEmpty()) { - continue; - } + if (cityObject instanceof BuildingRoom) { + BuildingRoom buildingRoom = (BuildingRoom) cityObject; - double height = getHeight(building); - GaiaBuilding gaiaBuilding = GaiaBuilding.builder() - .id(cityObject.getId()) - .floorHeight(0) - .roofHeight(height) - .build(); - - GaiaBoundingBox boundingBox = new GaiaBoundingBox(); - for (SurfaceProperty surfaceProperty : surfaceProperties) { - List polygon = new Vector<>(); - - Polygon surface = (Polygon) surfaceProperty.getObject(); - LinearRing linearRing = (LinearRing)surface.getExterior().getObject(); - DirectPositionList directPositionList = linearRing.getControlPoints().getPosList(); - List values = directPositionList.getValue(); - - double value = 0d; - for (int i = 0; i < values.size(); i+=3) { - double x, y, z; - if (flipCoordinate) { - x = values.get(i + 1); - y = values.get(i); + List abstractSpaceBoundaryProperties = buildingRoom.getBoundaries(); + for (AbstractSpaceBoundaryProperty boundaryProperty : abstractSpaceBoundaryProperties) { + AbstractSpaceBoundary spaceBoundary = boundaryProperty.getObject(); + if (spaceBoundary instanceof CeilingSurface) { + CeilingSurface ceilingSurface = (CeilingSurface) spaceBoundary; + MultiSurfaceProperty multiSurfaceProperty = ceilingSurface.getLod2MultiSurface(); + buildingSurfacesList.add(convertMultiSurfaceProperty(cityObject, multiSurfaceProperty)); + } else if (spaceBoundary instanceof InteriorWallSurface) { + InteriorWallSurface interiorWallSurface = (InteriorWallSurface) spaceBoundary; + MultiSurfaceProperty multiSurfaceProperty = interiorWallSurface.getLod2MultiSurface(); + buildingSurfacesList.add(convertMultiSurfaceProperty(cityObject, multiSurfaceProperty)); + } else if (spaceBoundary instanceof FloorSurface) { + FloorSurface floorSurface = (FloorSurface) spaceBoundary; + MultiSurfaceProperty multiSurfaceProperty = floorSurface.getLod2MultiSurface(); + buildingSurfacesList.add(convertMultiSurfaceProperty(cityObject, multiSurfaceProperty)); } else { - x = values.get(i); - y = values.get(i + 1); + log.error("Unsupported space boundary: {}", spaceBoundary.getClass().getName()); } - z = 0.0d; - value += values.get(i + 2); - Vector3d position = new Vector3d(x, y, z); - polygon.add(position); - boundingBox.addPoint(position); } + } else { + log.error("Unsupported city object: {}", cityObject.getClass().getName()); + } + + + // LOD1 Solid + GaiaExtrusionBuilding building = convertLod1Solid(cityObject); + if (building != null) { + buildingList.add(building); + } + + // LOD1 MultiSurface + List lod1Surfaces = convertLod1(cityObject); + if (!lod1Surfaces.isEmpty()) { + buildingSurfacesList.add(lod1Surfaces); + } - double floorHeight = value / values.size(); - gaiaBuilding.setPositions(polygon); - gaiaBuilding.setFloorHeight(floorHeight); - gaiaBuilding.setRoofHeight(floorHeight + height); - break; + // LOD2 MultiSurface + List lod2Surfaces = convertLod2(cityObject); + if (!lod2Surfaces.isEmpty()) { + buildingSurfacesList.add(lod2Surfaces); + } + + // LOD3 MultiSurface + List lod3Surfaces = convertLod3(cityObject); + if (!lod3Surfaces.isEmpty()) { + buildingSurfacesList.add(lod3Surfaces); } - gaiaBuilding.setBoundingBox(boundingBox); - gaiaBuildings.add(gaiaBuilding); } - for (GaiaBuilding gaiaBuilding : gaiaBuildings) { + for (GaiaExtrusionBuilding gaiaBuilding : buildingList) { GaiaScene scene = initScene(); scene.setOriginalPath(file.toPath()); GaiaMaterial material = scene.getMaterials().get(0); @@ -132,6 +132,7 @@ protected List convert(File file) { GaiaBoundingBox boundingBox = gaiaBuilding.getBoundingBox(); Vector3d center = boundingBox.getCenter(); + center.z = center.z - skirtHeight; Vector3d centerWorldCoordinate = GlobeUtils.geographicToCartesianWgs84(center); Matrix4d transformMatrix = GlobeUtils.transformMatrixAtCartesianPointWgs84(centerWorldCoordinate); @@ -154,6 +155,53 @@ protected List convert(File file) { rootNode.setTransformMatrix(rootTransformMatrix); scenes.add(scene); } + + for (List surfaces : buildingSurfacesList) { + GaiaScene scene = initScene(); + scene.setOriginalPath(file.toPath()); + GaiaNode rootNode = scene.getNodes().get(0); + + GaiaBoundingBox globalBoundingBox = new GaiaBoundingBox(); + for (GaiaBuildingSurface buildingSurface : surfaces) { + GaiaBoundingBox localBoundingBox = buildingSurface.getBoundingBox(); + globalBoundingBox.addBoundingBox(localBoundingBox); + } + + Vector3d center = globalBoundingBox.getCenter(); + Vector3d centerWorldCoordinate = GlobeUtils.geographicToCartesianWgs84(center); + Matrix4d transformMatrix = GlobeUtils.transformMatrixAtCartesianPointWgs84(centerWorldCoordinate); + Matrix4d transfromMatrixInv = new Matrix4d(transformMatrix).invert(); + + List> polygons = new ArrayList<>(); + for (GaiaBuildingSurface buildingSurface : surfaces) { + List polygon = new ArrayList<>(); + + List localPositions = new ArrayList<>(); + for (Vector3d position : buildingSurface.getPositions()) { + Vector3d positionWorldCoordinate = GlobeUtils.geographicToCartesianWgs84(position); + Vector3d localPosition = positionWorldCoordinate.mulPosition(transfromMatrixInv); + //localPosition.z = position.z; + localPositions.add(localPosition); + polygon.add(new Vector3dsOnlyHashEquals(localPosition)); + } + polygons.add(polygon); + } + + GaiaNode node = new GaiaNode(); + node.setTransformMatrix(new Matrix4d().identity()); + GaiaMesh mesh = new GaiaMesh(); + node.getMeshes().add(mesh); + GaiaPrimitive primitive = createPrimitiveFromPolygons(polygons); + primitive.setMaterialIndex(0); + mesh.getPrimitives().add(primitive); + rootNode.getChildren().add(node); + + Matrix4d rootTransformMatrix = new Matrix4d().identity(); + rootTransformMatrix.translate(center, rootTransformMatrix); + rootNode.setTransformMatrix(rootTransformMatrix); + scenes.add(scene); + } + } catch (CityGMLContextException | CityGMLReadException e) { throw new RuntimeException(e); } @@ -161,7 +209,228 @@ protected List convert(File file) { return scenes; } + private GaiaExtrusionBuilding convertLod1Solid(AbstractCityObject cityObject) { + boolean flipCoordinate = globalOptions.isFlipCoordinate(); + double skirtHeight = globalOptions.getSkirtHeight(); + + // Building DoorSurface WindowSurface BuildingRoom + + double height = 1.0d; + SolidProperty lod1SolidProperty = null; + if (cityObject instanceof Building) { + Building building = (Building) cityObject; + lod1SolidProperty = building.getLod1Solid(); + height = getHeight(building); + } else if (cityObject instanceof BuildingRoom) { + BuildingRoom buildingRoom = (BuildingRoom) cityObject; + lod1SolidProperty = buildingRoom.getLod1Solid(); + height = getHeight(buildingRoom); + } else if (cityObject instanceof DoorSurface) { + + } else if (cityObject instanceof WindowSurface) { + + } else { + log.error("Unsupported city object: {}", cityObject.getClass().getName()); + } + + if (lod1SolidProperty == null) { + return null; + } + + AbstractSolid solid = lod1SolidProperty.getObject(); + + Shell shell = ((Solid) solid).getExterior().getObject(); + List surfaceProperties = shell.getSurfaceMembers(); + + GaiaExtrusionBuilding gaiaBuilding = GaiaExtrusionBuilding.builder() + .id(cityObject.getId()) + .name(cityObject.getId()) + .floorHeight(0) + .roofHeight(height) + .build(); + + GaiaBoundingBox boundingBox = new GaiaBoundingBox(); + for (SurfaceProperty surfaceProperty : surfaceProperties) { + List polygon = new Vector<>(); + + Polygon surface = (Polygon) surfaceProperty.getObject(); + LinearRing linearRing = (LinearRing)surface.getExterior().getObject(); + DirectPositionList directPositions = linearRing.getControlPoints().getPosList(); + List positions = directPositions.getValue(); + + double heightSum = 0d; + for (int i = 0; i < positions.size(); i+=3) { + double x, y, z = 0.0d; + if (flipCoordinate) { + x = positions.get(i + 1); + y = positions.get(i); + } else { + x = positions.get(i); + y = positions.get(i + 1); + } + heightSum += positions.get(i + 2); + Vector3d position = new Vector3d(x, y, z); + CoordinateReferenceSystem crs = globalOptions.getCrs(); + if (crs != null) { + ProjCoordinate projCoordinate = new ProjCoordinate(x, y, boundingBox.getMinZ()); + ProjCoordinate centerWgs84 = GlobeUtils.transform(crs, projCoordinate); + position = new Vector3d(centerWgs84.x, centerWgs84.y, z); + } + polygon.add(position); + boundingBox.addPoint(position); + } + + double floorHeight = heightSum / positions.size(); + gaiaBuilding.setPositions(polygon); + gaiaBuilding.setFloorHeight(floorHeight); + gaiaBuilding.setRoofHeight(floorHeight + height + skirtHeight); + } + gaiaBuilding.setBoundingBox(boundingBox); + return gaiaBuilding; + } + + private List convertLod1(AbstractCityObject cityObject) { + //MultiSurfaceProperty lod2MultiSurface = building.getLod2MultiSurface(); + List buildingSurfaces = new ArrayList<>(); + + MultiSurfaceProperty multiSurfaceProperty = null; + if (cityObject instanceof Building) { + return buildingSurfaces; + } else if (cityObject instanceof DoorSurface) { + DoorSurface doorSurface = (DoorSurface) cityObject; + multiSurfaceProperty = doorSurface.getLod1MultiSurface(); + } else if (cityObject instanceof WindowSurface) { + WindowSurface windowSurface = (WindowSurface) cityObject; + multiSurfaceProperty = windowSurface.getLod1MultiSurface(); + } else if (cityObject instanceof Building) { + + } else if (cityObject instanceof BuildingRoom) { + + } else { + log.error("Unsupported city lod1 object: {}", cityObject.getClass().getName()); + } + + if (multiSurfaceProperty == null) { + return buildingSurfaces; + } + + return convertMultiSurfaceProperty(cityObject, multiSurfaceProperty); + } + + private List convertLod2(AbstractCityObject cityObject) { + //MultiSurfaceProperty lod2MultiSurface = building.getLod2MultiSurface(); + List buildingSurfaces = new ArrayList<>(); + + MultiSurfaceProperty multiSurfaceProperty = null; + if (cityObject instanceof Building) { + Building building = (Building) cityObject; + multiSurfaceProperty = building.getLod2MultiSurface(); + } else if (cityObject instanceof DoorSurface) { + DoorSurface doorSurface = (DoorSurface) cityObject; + multiSurfaceProperty = doorSurface.getLod2MultiSurface(); + } else if (cityObject instanceof WindowSurface) { + WindowSurface windowSurface = (WindowSurface) cityObject; + multiSurfaceProperty = windowSurface.getLod2MultiSurface(); + } else if (cityObject instanceof BuildingRoom) { + BuildingRoom buildingRoom = (BuildingRoom) cityObject; + multiSurfaceProperty = buildingRoom.getLod2MultiSurface(); + } else { + log.error("Unsupported city lod2 object: {}", cityObject.getClass().getName()); + } + + if (multiSurfaceProperty == null) { + return buildingSurfaces; + } + + return convertMultiSurfaceProperty(cityObject, multiSurfaceProperty); + } + + private List convertMultiSurfaceProperty(AbstractCityObject cityObject, MultiSurfaceProperty multiSurfaceProperty) { + List buildingSurfaces = new ArrayList<>(); + + MultiSurface multiSurface = multiSurfaceProperty.getObject(); + List surfaceProperties = multiSurface.getSurfaceMember(); + if (surfaceProperties.size() < 1) { + log.error("No surface properties found for city object: {}", cityObject.getId()); + return buildingSurfaces; + } + + for (SurfaceProperty surfaceProperty : surfaceProperties) { + Polygon polygon = (Polygon) surfaceProperty.getObject(); + LinearRing linearRing = (LinearRing) polygon.getExterior().getObject(); + + List vec3Polygon = new ArrayList<>(); + GaiaBoundingBox boundingBox = new GaiaBoundingBox(); + + DirectPositionList directPositionList = linearRing.getControlPoints().getPosList(); + List positions = directPositionList.getValue(); + for (int i = 0; i < positions.size(); i+=3) { + double x, y, z; + x = positions.get(i); + y = positions.get(i + 1); + z = positions.get(i + 2); + Vector3d position = new Vector3d(x, y, z); + CoordinateReferenceSystem crs = globalOptions.getCrs(); + if (crs != null) { + ProjCoordinate projCoordinate = new ProjCoordinate(x, y, boundingBox.getMinZ()); + ProjCoordinate centerWgs84 = GlobeUtils.transform(crs, projCoordinate); + position = new Vector3d(centerWgs84.x, centerWgs84.y, z); + } + vec3Polygon.add(position); + boundingBox.addPoint(position); + } + + GaiaBuildingSurface gaiaBuildingSurface = GaiaBuildingSurface.builder() + .id(cityObject.getId()) + .name(cityObject.getId()) + .positions(vec3Polygon) + .boundingBox(boundingBox) + .build(); + buildingSurfaces.add(gaiaBuildingSurface); + } + return buildingSurfaces; + } + + + private List convertLod3(AbstractCityObject cityObject) { + MultiSurfaceProperty multiSurfaceProperty = null; + if (cityObject instanceof Building) { + Building building = (Building) cityObject; + multiSurfaceProperty = building.getLod3MultiSurface(); + } else if (cityObject instanceof DoorSurface) { + DoorSurface doorSurface = (DoorSurface) cityObject; + multiSurfaceProperty = doorSurface.getLod3MultiSurface(); + } else if (cityObject instanceof WindowSurface) { + WindowSurface windowSurface = (WindowSurface) cityObject; + multiSurfaceProperty = windowSurface.getLod3MultiSurface(); + } else if (cityObject instanceof BuildingRoom) { + BuildingRoom buildingRoom = (BuildingRoom) cityObject; + multiSurfaceProperty = buildingRoom.getLod3MultiSurface(); + } else { + log.error("Unsupported city object: {}", cityObject.getClass().getName()); + } + + List buildingSurfaces = new ArrayList<>(); + if (multiSurfaceProperty == null) { + return buildingSurfaces; + } + + return convertMultiSurfaceProperty(cityObject, multiSurfaceProperty); + } + protected double getHeight(Building building) { - return building.getHeights().get(0).getObject().getValue().getValue(); + if (building.getHeights().size() > 1) { + return building.getHeights().get(0).getObject().getValue().getValue(); + } else { + return 0.0d; + } + } + + protected double getHeight(BuildingRoom buildingRoom) { + if (buildingRoom.getRoomHeights().size() > 0) { + return buildingRoom.getRoomHeights().get(0).getObject().getValue().getValue(); + } else { + return 0.0d; + } } } \ No newline at end of file diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/geojson/GeoJsonConverter.java b/tiler/src/main/java/com/gaia3d/converter/geometry/geojson/GeoJsonConverter.java index 2fc3b66e..5cad5ad1 100644 --- a/tiler/src/main/java/com/gaia3d/converter/geometry/geojson/GeoJsonConverter.java +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/geojson/GeoJsonConverter.java @@ -1,12 +1,15 @@ package com.gaia3d.converter.geometry.geojson; import com.gaia3d.basic.geometry.GaiaBoundingBox; -import com.gaia3d.basic.structure.GaiaMaterial; -import com.gaia3d.basic.structure.GaiaNode; -import com.gaia3d.basic.structure.GaiaScene; +import com.gaia3d.basic.structure.*; import com.gaia3d.command.mago.GlobalOptions; import com.gaia3d.converter.Converter; -import com.gaia3d.converter.geometry.*; +import com.gaia3d.converter.geometry.AbstractGeometryConverter; +import com.gaia3d.converter.geometry.GaiaExtrusionBuilding; +import com.gaia3d.converter.geometry.InnerRingRemover; +import com.gaia3d.converter.geometry.Vector3dsOnlyHashEquals; +import com.gaia3d.converter.geometry.tessellator.GaiaExtruder; +import com.gaia3d.converter.geometry.tessellator.GaiaExtrusionSurface; import com.gaia3d.util.GlobeUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -27,6 +30,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.List; @Slf4j @@ -51,8 +55,8 @@ public List load(Path path) { @Override protected List convert(File file) { List scenes = new ArrayList<>(); - Tessellator tessellator = new Tessellator(); - Extruder extruder = new Extruder(tessellator); + GaiaExtruder gaiaExtruder = new GaiaExtruder(); + InnerRingRemover innerRingRemover = new InnerRingRemover(); GlobalOptions globalOptions = GlobalOptions.getInstance(); boolean flipCoordinate = globalOptions.isFlipCoordinate(); @@ -62,6 +66,7 @@ protected List convert(File file) { double absoluteAltitudeValue = globalOptions.getAbsoluteAltitude(); double minimumHeightValue = globalOptions.getMinimumHeight(); + double skirtHeight = globalOptions.getSkirtHeight(); try { FeatureJSON gjson = new FeatureJSON(); @@ -70,88 +75,103 @@ protected List convert(File file) { SimpleFeatureCollection featureCollection = (SimpleFeatureCollection) gjson.readFeatureCollection(new StringReader(json)); FeatureIterator iterator = featureCollection.features(); - List buildings = new ArrayList<>(); + List buildings = new ArrayList<>(); while (iterator.hasNext()) { SimpleFeature feature = iterator.next(); Geometry geom = (Geometry) feature.getDefaultGeometry(); - Polygon polygon = null; - LineString lineString = null; + if (geom == null) { + log.warn("Is Null Geometry : {}", feature.getID()); + continue; + } + + List polygons = new ArrayList<>(); if (geom instanceof MultiPolygon) { - polygon = (Polygon) geom.getGeometryN(0); - lineString = polygon.getExteriorRing(); + int count = geom.getNumGeometries(); + for (int i = 0; i < count; i++) { + Polygon polygon = (Polygon) geom.getGeometryN(i); + polygons.add(polygon); + } } else if (geom instanceof Polygon) { - polygon = (Polygon) geom; - lineString = polygon.getExteriorRing(); - } else if (geom instanceof MultiLineString) { - lineString = (LineString) geom.getGeometryN(0); - } else if (geom instanceof LineString) { - lineString = (LineString) geom; + polygons.add((Polygon) geom); } else { log.warn("Is Not Supported Geometry Type : {}", geom.getGeometryType()); continue; } - if (!lineString.isValid()) { - log.warn("Invalid : {}", feature.getID()); - continue; - } - GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(); - Coordinate[] coordinates = lineString.getCoordinates(); + for (Polygon polygon : polygons) { + if (!polygon.isValid()) { + log.warn("Is Invalid Polygon. : {}", feature.getID()); + continue; + } - GaiaBoundingBox boundingBox = new GaiaBoundingBox(); - List positions = new ArrayList<>(); + LineString lineString = polygon.getExteriorRing(); + GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(); + Coordinate[] outerCoordinates = lineString.getCoordinates(); - Vector3d firstPosition = null; - for (Coordinate coordinate : coordinates) { - Point point = geometryFactory.createPoint(coordinate); - double x, y; - if (flipCoordinate) { - x = point.getY(); - y = point.getX(); - } else { - x = point.getX(); - y = point.getY(); + int innerRingCount = polygon.getNumInteriorRing(); + List innerCoordinates = new ArrayList<>(); + for (int i = 0; i < innerRingCount; i++) { + LineString innerRing = polygon.getInteriorRingN(i); + Coordinate[] innerCoordinatesArray = innerRing.getCoordinates(); + innerCoordinates.add(innerCoordinatesArray); } - Vector3d position; - CoordinateReferenceSystem crs = globalOptions.getCrs(); - if (crs != null) { - ProjCoordinate projCoordinate = new ProjCoordinate(x, y, boundingBox.getMinZ()); - ProjCoordinate centerWgs84 = GlobeUtils.transform(crs, projCoordinate); - position = new Vector3d(centerWgs84.x, centerWgs84.y, 0.0d); - } else { - position = new Vector3d(x, y, 0.0d); + outerCoordinates = innerRingRemover.removeAll(outerCoordinates, innerCoordinates); + GaiaBoundingBox boundingBox = new GaiaBoundingBox(); + List positions = new ArrayList<>(); + + for (Coordinate coordinate : outerCoordinates) { + Point point = geometryFactory.createPoint(coordinate); + + double x, y; + if (flipCoordinate) { + x = point.getY(); + y = point.getX(); + } else { + x = point.getX(); + y = point.getY(); + } + + Vector3d position; + CoordinateReferenceSystem crs = globalOptions.getCrs(); + if (crs != null && !crs.getName().equals("EPSG:4326")) { + ProjCoordinate projCoordinate = new ProjCoordinate(x, y, boundingBox.getMinZ()); + ProjCoordinate centerWgs84 = GlobeUtils.transform(crs, projCoordinate); + position = new Vector3d(centerWgs84.x, centerWgs84.y, 0.0d); + } else { + position = new Vector3d(x, y, 0.0d); + } + + positions.add(position); + boundingBox.addPoint(position); } - if (firstPosition == null) { - firstPosition = position; - } else if (firstPosition.equals(position)) { - break; + if (positions.size() >= 3) { + String name = getAttribute(feature, nameColumnName); + double height = getHeight(feature, heightColumnName, minimumHeightValue); + double altitude = absoluteAltitudeValue; + if (altitudeColumnName != null) { + altitude = getAltitude(feature, altitudeColumnName); + } + GaiaExtrusionBuilding building = GaiaExtrusionBuilding.builder() + .id(feature.getID()) + .name(name) + .boundingBox(boundingBox) + .floorHeight(altitude) + .roofHeight(height + skirtHeight) + .positions(positions) + .build(); + buildings.add(building); + } else { + String name = getAttribute(feature, nameColumnName); + log.warn("Invalid Geometry : {}, {}", feature.getID(), name); } - positions.add(position); - boundingBox.addPoint(position); - } - - String name = getAttribute(feature, nameColumnName); - double height = getHeight(feature, heightColumnName, minimumHeightValue); - double altitude = absoluteAltitudeValue; - if (altitudeColumnName != null) { - altitude = getAltitude(feature, altitudeColumnName); } - GaiaBuilding building = GaiaBuilding.builder() - .id(feature.getID()) - .name(name) - .boundingBox(boundingBox) - .floorHeight(altitude) - .roofHeight(altitude + height) - .positions(positions) - .build(); - buildings.add(building); } iterator.close(); - for (GaiaBuilding building : buildings) { + for (GaiaExtrusionBuilding building : buildings) { GaiaScene scene = initScene(); scene.setOriginalPath(file.toPath()); @@ -160,21 +180,33 @@ protected List convert(File file) { rootNode.setName(building.getName()); Vector3d center = building.getBoundingBox().getCenter(); + center.z = center.z - skirtHeight; Vector3d centerWorldCoordinate = GlobeUtils.geographicToCartesianWgs84(center); Matrix4d transformMatrix = GlobeUtils.transformMatrixAtCartesianPointWgs84(centerWorldCoordinate); - Matrix4d transformMatrixInv = new Matrix4d(transformMatrix).invert(); + Matrix4d transfromMatrixInv = new Matrix4d(transformMatrix).invert(); List localPositions = new ArrayList<>(); for (Vector3d position : building.getPositions()) { Vector3d positionWorldCoordinate = GlobeUtils.geographicToCartesianWgs84(position); - Vector3d localPosition = positionWorldCoordinate.mulPosition(transformMatrixInv, new Vector3d()); + Vector3d localPosition = positionWorldCoordinate.mulPosition(transfromMatrixInv); localPosition.z = 0.0d; - localPositions.add(localPosition); + localPositions.add(new Vector3dsOnlyHashEquals(localPosition)); } + Collections.reverse(localPositions); + + List extrusionSurfaces = gaiaExtruder.extrude(localPositions, building.getRoofHeight(), building.getFloorHeight()); + + GaiaNode node = new GaiaNode(); + node.setTransformMatrix(new Matrix4d().identity()); + GaiaMesh mesh = new GaiaMesh(); + node.getMeshes().add(mesh); + + GaiaPrimitive primitive = createPrimitiveFromGaiaExtrusionSurfaces(extrusionSurfaces); + + primitive.setMaterialIndex(0); + mesh.getPrimitives().add(primitive); - Extrusion extrusion = extruder.extrude(localPositions, building.getRoofHeight(), building.getFloorHeight()); - GaiaNode node = createNode(material, extrusion.getPositions(), extrusion.getTriangles()); rootNode.getChildren().add(node); Matrix4d rootTransformMatrix = new Matrix4d().identity(); diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/indoorgml/IndoorGmlConverter.java b/tiler/src/main/java/com/gaia3d/converter/geometry/indoorgml/IndoorGmlConverter.java new file mode 100644 index 00000000..ba0b4c85 --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/indoorgml/IndoorGmlConverter.java @@ -0,0 +1,169 @@ +package com.gaia3d.converter.geometry.indoorgml; + +import com.gaia3d.basic.geometry.GaiaBoundingBox; +import com.gaia3d.basic.structure.*; +import com.gaia3d.command.mago.GlobalOptions; +import com.gaia3d.converter.Converter; +import com.gaia3d.converter.geometry.*; +import com.gaia3d.converter.geometry.tessellator.GaiaTessellator; +import com.gaia3d.util.GlobeUtils; +import edu.stem.indoor.IndoorFeatures; +import edu.stem.space.*; +import lombok.extern.slf4j.Slf4j; +import org.joml.Matrix4d; +import org.joml.Vector3d; +import org.locationtech.proj4j.CoordinateReferenceSystem; +import org.locationtech.proj4j.ProjCoordinate; + +import javax.xml.bind.JAXBContext; +import java.io.File; +import java.io.FileReader; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Slf4j +public class IndoorGmlConverter extends AbstractGeometryConverter implements Converter { + @Override + public List load(String path) { + return convert(new File(path)); + } + + @Override + public List load(File file) { + return convert(file); + } + + @Override + public List load(Path path) { + return convert(path.toFile()); + } + + @Override + protected List convert(File file) { + List scenes = new ArrayList<>(); + GlobalOptions globalOptions = GlobalOptions.getInstance(); + //ConvexHullTessellator tessellator = new ConvexHullTessellator(); + GaiaTessellator tessellator = new GaiaTessellator(); + + try { + JAXBContext context = JAXBContext.newInstance(IndoorFeatures.class); + IndoorFeatures indoorFeatures = (IndoorFeatures) context.createUnmarshaller().unmarshal(new FileReader(file)); + + + List> buildingSurfacesList = new ArrayList<>(); + + PrimalSpaceFeatures primalSpaceFeatures = indoorFeatures.getPrimalSpaceFeatures(); + PrimalSpaceFeatures primalSpaceFeaturesChild = primalSpaceFeatures.getPrimalSpaceFeatures(); + List cellSpaceMembers = primalSpaceFeaturesChild.getCellSpaceMember(); + for (CellSpaceMember cellSpaceMember : cellSpaceMembers) { + + //log.info("CellSpaceMember: {}", cellSpaceMember.getCellSpace().getId()); + CellSpace cellSpace = cellSpaceMember.getCellSpace(); + CellSpaceGeometry cellSpaceGeometry = cellSpace.getCellSpaceGeometry(); + Geometry3D geometry3D = cellSpaceGeometry.getGeometry3d(); + Solid solid = geometry3D.getSolid(); + Exterior exterior = solid.getExterior(); + Shell shell = exterior.getShell(); + List surfaceMembers = shell.getSurfaceMembers(); + + List gaiaBuildingSurfaces = new ArrayList<>(); + + for (SurfaceMember surfaceMember : surfaceMembers) { + GaiaBoundingBox boundingBox = new GaiaBoundingBox(); + List vertices = new ArrayList<>(); + Polygon polygon = surfaceMember.getPolygon(); + List posList = polygon.getExterior().getPos(); + for (Pos pos : posList) { + String[] vectors = pos.getVector().split(" "); + + //double scale = 0.0254d; // inch to meter + double scale = 1.0d; + double x = Double.parseDouble(vectors[0]) * scale; + double y = Double.parseDouble(vectors[1]) * scale; + double z = Double.parseDouble(vectors[2]) * scale; + + Vector3d wgs84Position = new Vector3d(x, y, z); + CoordinateReferenceSystem crs = globalOptions.getCrs(); + if (crs != null) { + ProjCoordinate projCoordinate = new ProjCoordinate(x, y, boundingBox.getMinZ()); + ProjCoordinate centerWgs84 = GlobeUtils.transform(crs, projCoordinate); + wgs84Position = new Vector3d(centerWgs84.x, centerWgs84.y, z); + } + vertices.add(wgs84Position); + boundingBox.addPoint(wgs84Position); + } + + // use the first point as the last point to close the polygon + if (!vertices.get(0).equals(vertices.get(vertices.size() - 1))) { + vertices.add(vertices.get(0)); + log.info("Polygon is not closed. Adding the first point to the end of the list."); + } + + GaiaBuildingSurface buildingSurface = GaiaBuildingSurface.builder().id(cellSpace.getId()).name(cellSpace.getName()).boundingBox(boundingBox).positions(vertices).build(); + + gaiaBuildingSurfaces.add(buildingSurface); + } + + if (!gaiaBuildingSurfaces.isEmpty()) { + buildingSurfacesList.add(gaiaBuildingSurfaces); + } + } + + GaiaScene scene = initScene(); + scene.setOriginalPath(file.toPath()); + GaiaNode rootNode = scene.getNodes().get(0); + + GaiaBoundingBox globalBoundingBox = new GaiaBoundingBox(); + for (List surfaces : buildingSurfacesList) { + for (GaiaBuildingSurface buildingSurface : surfaces) { + GaiaBoundingBox localBoundingBox = buildingSurface.getBoundingBox(); + globalBoundingBox.addBoundingBox(localBoundingBox); + } + } + Vector3d center = globalBoundingBox.getCenter(); + Vector3d centerWorldCoordinate = GlobeUtils.geographicToCartesianWgs84(center); + Matrix4d transformMatrix = GlobeUtils.transformMatrixAtCartesianPointWgs84(centerWorldCoordinate); + Matrix4d transfromMatrixInv = new Matrix4d(transformMatrix).invert(); + + for (List surfaces : buildingSurfacesList) { + List> polygons = new ArrayList<>(); + for (GaiaBuildingSurface buildingSurface : surfaces) { + List polygon = new ArrayList<>(); + + List localPositions = new ArrayList<>(); + for (Vector3d position : buildingSurface.getPositions()) { + Vector3d positionWorldCoordinate = GlobeUtils.geographicToCartesianWgs84(position); + Vector3d localPosition = positionWorldCoordinate.mulPosition(transfromMatrixInv); + localPosition.z = position.z; + localPositions.add(localPosition); + polygon.add(new Vector3dsOnlyHashEquals(localPosition)); + } + polygons.add(polygon); + } + + GaiaNode node = new GaiaNode(); + node.setTransformMatrix(new Matrix4d().identity()); + GaiaMesh mesh = new GaiaMesh(); + node.getMeshes().add(mesh); + + GaiaPrimitive primitive = createPrimitiveFromPolygons(polygons); + primitive.setMaterialIndex(0); + mesh.getPrimitives().add(primitive); + + rootNode.getChildren().add(node); + } + Matrix4d rootTransformMatrix = new Matrix4d().identity(); + rootTransformMatrix.translate(center, rootTransformMatrix); + rootNode.setTransformMatrix(rootTransformMatrix); + scenes.add(scene); + } catch (Exception e) { + log.info("Failed to load IndoorGML file: {}", file.getAbsolutePath()); + e.printStackTrace(); + } + + return scenes; + } +} diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/shape/ShapeConverter.java b/tiler/src/main/java/com/gaia3d/converter/geometry/shape/ShapeConverter.java index 8eed89be..a549cc63 100644 --- a/tiler/src/main/java/com/gaia3d/converter/geometry/shape/ShapeConverter.java +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/shape/ShapeConverter.java @@ -1,12 +1,18 @@ package com.gaia3d.converter.geometry.shape; import com.gaia3d.basic.geometry.GaiaBoundingBox; -import com.gaia3d.basic.structure.GaiaMaterial; +import com.gaia3d.basic.structure.GaiaMesh; import com.gaia3d.basic.structure.GaiaNode; +import com.gaia3d.basic.structure.GaiaPrimitive; import com.gaia3d.basic.structure.GaiaScene; import com.gaia3d.command.mago.GlobalOptions; import com.gaia3d.converter.Converter; -import com.gaia3d.converter.geometry.*; +import com.gaia3d.converter.geometry.AbstractGeometryConverter; +import com.gaia3d.converter.geometry.GaiaExtrusionBuilding; +import com.gaia3d.converter.geometry.InnerRingRemover; +import com.gaia3d.converter.geometry.Vector3dsOnlyHashEquals; +import com.gaia3d.converter.geometry.tessellator.GaiaExtruder; +import com.gaia3d.converter.geometry.tessellator.GaiaExtrusionSurface; import com.gaia3d.util.GlobeUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -16,7 +22,7 @@ import org.geotools.data.shapefile.files.ShpFiles; import org.geotools.data.shapefile.shp.ShapefileReader; import org.geotools.data.simple.SimpleFeatureCollection; -import org.geotools.data.simple.SimpleFeatureSource; +import org.geotools.data.store.ContentFeatureSource; import org.geotools.feature.FeatureIterator; import org.geotools.geometry.jts.JTSFactoryFinder; import org.geotools.util.factory.Hints; @@ -32,6 +38,7 @@ import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.List; @Slf4j @@ -55,8 +62,8 @@ public List load(Path path) { protected List convert(File file) { List scenes = new ArrayList<>(); - Tessellator tessellator = new Tessellator(); - Extruder extruder = new Extruder(tessellator); + GaiaExtruder gaiaExtruder = new GaiaExtruder(); + InnerRingRemover innerRingRemover = new InnerRingRemover(); GlobalOptions globalOptions = GlobalOptions.getInstance(); boolean flipCoordinate = globalOptions.isFlipCoordinate(); @@ -66,6 +73,7 @@ protected List convert(File file) { double absoluteAltitudeValue = globalOptions.getAbsoluteAltitude(); double minimumHeightValue = globalOptions.getMinimumHeight(); + double skirtHeight = globalOptions.getSkirtHeight(); ShpFiles shpFiles = null; ShapefileReader reader = null; @@ -74,116 +82,120 @@ protected List convert(File file) { reader = new ShapefileReader(shpFiles, true, true, new GeometryFactory()); DataStore dataStore = new ShapefileDataStore(file.toURI().toURL()); String typeName = dataStore.getTypeNames()[0]; - SimpleFeatureSource source = dataStore.getFeatureSource(typeName); - - source = dataStore.getFeatureSource(typeName); + ContentFeatureSource source = (ContentFeatureSource) dataStore.getFeatureSource(typeName); var query = new Query(typeName, Filter.INCLUDE); - query.getHints().add(new Hints(Hints.FEATURE_2D, true)); // for 3d + query.getHints().add(new Hints(Hints.FEATURE_2D, true)); SimpleFeatureCollection features = source.getFeatures(query); - FeatureIterator iterator = features.features(); - List buildings = new ArrayList<>(); + List buildings = new ArrayList<>(); while (iterator.hasNext()) { SimpleFeature feature = iterator.next(); Geometry geom = (Geometry) feature.getDefaultGeometry(); - Polygon polygon = null; - LineString lineString = null; + if (geom == null) { + log.warn("Is Null Geometry : {}", feature.getID()); + continue; + } + + List polygons = new ArrayList<>(); if (geom instanceof MultiPolygon) { - polygon = (Polygon) geom.getGeometryN(0); - lineString = polygon.getExteriorRing(); + int count = geom.getNumGeometries(); + for (int i = 0; i < count; i++) { + Polygon polygon = (Polygon) geom.getGeometryN(i); + polygons.add(polygon); + } } else if (geom instanceof Polygon) { - polygon = (Polygon) geom; - lineString = polygon.getExteriorRing(); - } else if (geom instanceof MultiLineString) { - lineString = (LineString) geom.getGeometryN(0); - } else if (geom instanceof LineString) { - lineString = (LineString) geom; + polygons.add((Polygon) geom); } else { log.warn("Is Not Supported Geometry Type : {}", geom.getGeometryType()); continue; } - if (!lineString.isValid()) { - log.warn("Invalid : {}", feature.getID()); - continue; - } - //log.info("{}", feature.getID()); - - GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(); - Coordinate[] coordinates = lineString.getCoordinates(); - GaiaBoundingBox boundingBox = new GaiaBoundingBox(); - List positions = new ArrayList<>(); - - Vector3d firstPosition = null; - for (Coordinate coordinate : coordinates) { - Point point = geometryFactory.createPoint(coordinate); - - double x, y; - if (flipCoordinate) { - x = point.getY(); - y = point.getX(); - } else { - x = point.getX(); - y = point.getY(); + for (Polygon polygon : polygons) { + if (!polygon.isValid()) { + log.warn("Is Invalid Polygon. : {}", feature.getID()); + continue; } - Vector3d position; - CoordinateReferenceSystem crs = globalOptions.getCrs(); - if (crs != null) { - ProjCoordinate projCoordinate = new ProjCoordinate(x, y, boundingBox.getMinZ()); - ProjCoordinate centerWgs84 = GlobeUtils.transform(crs, projCoordinate); - position = new Vector3d(centerWgs84.x, centerWgs84.y, 0.0d); - } else { - position = new Vector3d(x, y, 0.0d); + LineString lineString = polygon.getExteriorRing(); + GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(); + Coordinate[] outerCoordinates = lineString.getCoordinates(); + + int innerRingCount = polygon.getNumInteriorRing(); + List innerCoordinates = new ArrayList<>(); + for (int i = 0; i < innerRingCount; i++) { + LineString innerRing = polygon.getInteriorRingN(i); + Coordinate[] innerCoordinatesArray = innerRing.getCoordinates(); + innerCoordinates.add(innerCoordinatesArray); } - if (firstPosition == null) { - firstPosition = position; - } else if (firstPosition.equals(position)) { - break; + outerCoordinates = innerRingRemover.removeAll(outerCoordinates, innerCoordinates); + GaiaBoundingBox boundingBox = new GaiaBoundingBox(); + List positions = new ArrayList<>(); + + for (Coordinate coordinate : outerCoordinates) { + Point point = geometryFactory.createPoint(coordinate); + + double x, y; + if (flipCoordinate) { + x = point.getY(); + y = point.getX(); + } else { + x = point.getX(); + y = point.getY(); + } + + Vector3d position; + CoordinateReferenceSystem crs = globalOptions.getCrs(); + if (crs != null && !crs.getName().equals("EPSG:4326")) { + ProjCoordinate projCoordinate = new ProjCoordinate(x, y, boundingBox.getMinZ()); + ProjCoordinate centerWgs84 = GlobeUtils.transform(crs, projCoordinate); + position = new Vector3d(centerWgs84.x, centerWgs84.y, 0.0d); + } else { + position = new Vector3d(x, y, 0.0d); + } + + positions.add(position); + boundingBox.addPoint(position); } - positions.add(position); - boundingBox.addPoint(position); - } - if (positions.size() >= 3) { - String name = getAttribute(feature, nameColumnName); - double height = getHeight(feature, heightColumnName, minimumHeightValue); - double altitude = absoluteAltitudeValue; - if (altitudeColumnName != null) { - altitude = getAltitude(feature, altitudeColumnName); + if (positions.size() >= 3) { + String name = getAttribute(feature, nameColumnName); + double height = getHeight(feature, heightColumnName, minimumHeightValue); + double altitude = absoluteAltitudeValue; + if (altitudeColumnName != null) { + altitude = getAltitude(feature, altitudeColumnName); + } + GaiaExtrusionBuilding building = GaiaExtrusionBuilding.builder() + .id(feature.getID()) + .name(name) + .boundingBox(boundingBox) + .floorHeight(altitude) + .roofHeight(height + skirtHeight) + .positions(positions) + .build(); + buildings.add(building); + } else { + String name = getAttribute(feature, nameColumnName); + log.warn("Invalid Geometry : {}, {}", feature.getID(), name); } - GaiaBuilding building = GaiaBuilding.builder() - .id(feature.getID()) - .name(name) - .boundingBox(boundingBox) - .floorHeight(altitude) - .roofHeight(height) - .positions(positions) - .build(); - buildings.add(building); - } else { - String name = getAttribute(feature, nameColumnName); - log.warn("Invalid Geometry : {}, {}", feature.getID(), name); } } - iterator.close(); reader.close(); shpFiles.dispose(); dataStore.dispose(); - for (GaiaBuilding building : buildings) { + for (GaiaExtrusionBuilding building : buildings) { GaiaScene scene = initScene(); scene.setOriginalPath(file.toPath()); - GaiaMaterial material = scene.getMaterials().get(0); GaiaNode rootNode = scene.getNodes().get(0); rootNode.setName(building.getName()); Vector3d center = building.getBoundingBox().getCenter(); + center.z = center.z - skirtHeight; Vector3d centerWorldCoordinate = GlobeUtils.geographicToCartesianWgs84(center); Matrix4d transformMatrix = GlobeUtils.transformMatrixAtCartesianPointWgs84(centerWorldCoordinate); @@ -194,11 +206,23 @@ protected List convert(File file) { Vector3d positionWorldCoordinate = GlobeUtils.geographicToCartesianWgs84(position); Vector3d localPosition = positionWorldCoordinate.mulPosition(transfromMatrixInv); localPosition.z = 0.0d; - localPositions.add(localPosition); + localPositions.add(new Vector3dsOnlyHashEquals(localPosition)); } + Collections.reverse(localPositions); + localPositions.remove(localPositions.size() - 1); + + List extrusionSurfaces = gaiaExtruder.extrude(localPositions, building.getRoofHeight(), building.getFloorHeight()); + + GaiaNode node = new GaiaNode(); + node.setTransformMatrix(new Matrix4d().identity()); + GaiaMesh mesh = new GaiaMesh(); + node.getMeshes().add(mesh); + + GaiaPrimitive primitive = createPrimitiveFromGaiaExtrusionSurfaces(extrusionSurfaces); + + primitive.setMaterialIndex(0); + mesh.getPrimitives().add(primitive); - Extrusion extrusion = extruder.extrude(localPositions, building.getRoofHeight(), building.getFloorHeight()); - GaiaNode node = createNode(material, extrusion.getPositions(), extrusion.getTriangles()); rootNode.getChildren().add(node); Matrix4d rootTransformMatrix = new Matrix4d().identity(); @@ -206,6 +230,7 @@ protected List convert(File file) { rootNode.setTransformMatrix(rootTransformMatrix); scenes.add(scene); } + dataStore.dispose(); reader.close(); } catch (IOException e) { diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/GaiaExtruder.java b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/GaiaExtruder.java new file mode 100644 index 00000000..33c81778 --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/GaiaExtruder.java @@ -0,0 +1,53 @@ +package com.gaia3d.converter.geometry.tessellator; + +import com.gaia3d.converter.geometry.Vector3dsOnlyHashEquals; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.joml.Vector3d; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@AllArgsConstructor +public class GaiaExtruder { + public List extrude(List positions, double roofHeight, double floorHeight) { + List result = new ArrayList<>(); + List roofPositions = resetHeight(positions, roofHeight); + List floorPositions = resetHeight(positions, floorHeight); + + GaiaExtrusionSurface roof = new GaiaExtrusionSurface(roofPositions); + List wallPositions = createWallPositions(roofPositions, floorPositions); + + result.add(roof); + result.addAll(wallPositions); + return result; + } + + private List createWallPositions(List roofPosition, List floorPosition) { + List result = new ArrayList<>(); + int size = roofPosition.size(); + for (int index = 0; index < size; index++) { + int crntIndex = index % size; + int nextIndex = (index + 1) % size; + Vector3d roofTriangle = roofPosition.get(crntIndex); + Vector3d roofTriangleNext = roofPosition.get(nextIndex); + Vector3d floorTriangle = floorPosition.get(crntIndex); + Vector3d floorTriangleNext = floorPosition.get(nextIndex); + + List wallPositions = new ArrayList<>(); + wallPositions.add(new Vector3dsOnlyHashEquals(roofTriangle)); + wallPositions.add(new Vector3dsOnlyHashEquals(floorTriangle)); + wallPositions.add(new Vector3dsOnlyHashEquals(floorTriangleNext)); + wallPositions.add(new Vector3dsOnlyHashEquals(roofTriangleNext)); + //wallPositions.add(new Vector3dsOnlyHashEquals(roofTriangle)); + result.add(new GaiaExtrusionSurface(wallPositions)); + } + return result; + } + + private List resetHeight(List positions, double height) { + return positions.stream().map(position -> new Vector3dsOnlyHashEquals(new Vector3d(position.x, position.y, height))).collect(Collectors.toList()); + } +} diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/GaiaExtrusionSurface.java b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/GaiaExtrusionSurface.java new file mode 100644 index 00000000..6e09c124 --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/GaiaExtrusionSurface.java @@ -0,0 +1,17 @@ +package com.gaia3d.converter.geometry.tessellator; + +import com.gaia3d.basic.structure.GaiaVertex; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.joml.Vector3d; + +import java.util.ArrayList; +import java.util.List; + +@AllArgsConstructor +@Getter +public class GaiaExtrusionSurface { + //private final List indices = new ArrayList<>(); + private final List vertices; +} \ No newline at end of file diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/GaiaTessellator.java b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/GaiaTessellator.java new file mode 100644 index 00000000..15174f4d --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/GaiaTessellator.java @@ -0,0 +1,325 @@ +package com.gaia3d.converter.geometry.tessellator; + +import lombok.NoArgsConstructor; +import org.joml.Vector2d; +import org.joml.Vector3d; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@NoArgsConstructor +public class GaiaTessellator { + // tessellate.*** + public void tessellate3D(List points3dArray, List resultTrianglesIndices) { + // 1rst, must know the normal of the polygon to project the polygon to a plane and resilve the tessellation in 2d.*** + Vector3d normal = new Vector3d(); + calculateFastNormal3D(points3dArray, normal); // the normal can be reversed.*** + + Map mapPoints3dIndices = new HashMap<>(); + int pointsCount = points3dArray.size(); + for (int i = 0; i < pointsCount; i++) { + mapPoints3dIndices.put(points3dArray.get(i), i); + } + + // now, with the normal, find the best plane axis aligned to project the polygon.*** + // possible planes : XY, XZ, YZ.*** + // the best plane is the plane that has the normal more aligned to the plane normal.*** + List projectedPoints2D = new ArrayList<>(); + String bestPlane = getBestPlaneToProject(normal); + + if (bestPlane.equals("YZ")) { + // the best plane is the YZ plane.*** + for (Vector3d vertex : points3dArray) { + projectedPoints2D.add(new Point2DTess(new Vector2d(vertex.y, vertex.z), vertex)); + } + } else if (bestPlane.equals("XZ")) { + // the best plane is the XZ plane.*** + for (Vector3d vertex : points3dArray) { + projectedPoints2D.add(new Point2DTess(new Vector2d(vertex.x, vertex.z), vertex)); + } + } else { + // the best plane is the XY plane.*** + for (Vector3d vertex : points3dArray) { + projectedPoints2D.add(new Point2DTess(new Vector2d(vertex.x, vertex.y), vertex)); + } + } + + // now, must resolve the tessellation in 2d.*** + Polygon2DTess polygon2D = new Polygon2DTess(projectedPoints2D); + List resultConvexPolygons = new ArrayList<>(); + tessellate2D(polygon2D, resultConvexPolygons); + + int convexPolygonsCount = resultConvexPolygons.size(); + for (int i = 0; i < convexPolygonsCount; i++) { + Polygon2DTess convexPolygon = resultConvexPolygons.get(i); + List convexIndices = new ArrayList<>(); + convexPolygon.getTrianglesIndicesAsConvexPolygon(convexIndices); + + int convexIndicesCount = convexIndices.size(); + for (int j = 0; j < convexIndicesCount; j += 1) { + int idx = convexIndices.get(j); + Point2DTess point2D = convexPolygon.getPoint(idx); + Vector3d parentVertex = point2D.getParentPoint(); + int parentVertexIndex = mapPoints3dIndices.get(parentVertex); + resultTrianglesIndices.add(parentVertexIndex); + } + } + + polygon2D = null; + resultConvexPolygons.clear(); + } + + public void getPointsIdxSortedByDistToPoint(Point2DTess point, List points, List resultIndices) { + // get the points sorted by distance to the point.*** + int pointsCount = points.size(); + if (pointsCount == 0) { + return; + } + + Map mapIdxDist = new HashMap<>(); + for (int i = 0; i < pointsCount; i++) { + Point2DTess point2D = points.get(i); + if (point2D == point) { + // skip the same point.*** + continue; + } + double dist = point.squareDistanceTo(point2D); + mapIdxDist.put(i, dist); + } + + // sort the map.*** + mapIdxDist.entrySet().stream().sorted(Map.Entry.comparingByValue()).forEachOrdered(x -> resultIndices.add(x.getKey())); + } + + public void tessellate2D(Polygon2DTess polygon2D, List resultConvexPolygons) { + // 1rst, must know the normal of the polygon to project the polygon to a plane and resolve the tessellation in 2d.*** + List concaveIndices = new ArrayList<>(); + float normal = polygon2D.calculateNormal2D(concaveIndices); + + int concaveIndicesCount = concaveIndices.size(); + if (concaveIndicesCount == 0) { + // the polygon is convex.*** + resultConvexPolygons.add(polygon2D); + return; + } + + // the polygon is concave.*** + // now, for any concave vertex, find the closest vertex to split the polygon.*** + boolean finded = false; + int idxB = -1; + int i = 0; + double error = 1E-10; + while (!finded && i < concaveIndicesCount) { + int idxA = concaveIndices.get(i); + Point2DTess pointA = polygon2D.getPoint(idxA); + + List sortedIndices = new ArrayList<>(); + getPointsIdxSortedByDistToPoint(pointA, polygon2D.getPoints(), sortedIndices); + + int sortedIndicesCount = sortedIndices.size(); + for (int j = 0; j < sortedIndicesCount; j++) { + idxB = sortedIndices.get(j); + if (idxA == idxB) { + // skip the same point.*** + continue; + } + + // skip adjacent points.*** + if (idxA == polygon2D.getPrevIdx(idxB) || idxA == polygon2D.getNextIdx(idxB)) { + continue; + } + + Point2DTess pointB = polygon2D.getPoint(idxB); + Segment2DTess segment = new Segment2DTess(pointA, pointB); + if (polygon2D.isSegmentIntersectingPolygon(segment, error)) { + // the segment is intersecting the polygon.*** + continue; + } + + List resultSplittedPolygons = new ArrayList<>(); + polygon2D.splitPolygon(idxA, idxB, resultSplittedPolygons); + + if (resultSplittedPolygons.size() < 2) { + // the polygon failed to split.*** + continue; + } + + Polygon2DTess polygonA = resultSplittedPolygons.get(0); + Polygon2DTess polygonB = resultSplittedPolygons.get(1); + List concaveIndicesA = new ArrayList<>(); + List concaveIndicesB = new ArrayList<>(); + float normalA = polygonA.calculateNormal2D(concaveIndicesA); + float normalB = polygonB.calculateNormal2D(concaveIndicesB); + + if (normalA == normal && normalB == normal) { + // the polygon was splitted successfully.*** + // now check if the polygonA is convex.*** + if (concaveIndicesA.size() == 0) { + resultConvexPolygons.add(polygonA); + } else { + tessellate2D(polygonA, resultConvexPolygons); + } + + // now check if the polygonB is convex.*** + if (concaveIndicesB.size() == 0) { + resultConvexPolygons.add(polygonB); + } else { + tessellate2D(polygonB, resultConvexPolygons); + } + + finded = true; + break; + } + } + i++; + } + } + + private String getBestPlaneToProject(Vector3d normal) { + float absX = Math.abs((float) normal.x); + float absY = Math.abs((float) normal.y); + float absZ = Math.abs((float) normal.z); + + if (absX > absY && absX > absZ) { + // the best plane is the YZ plane.*** + return "YZ"; + } else if (absY > absX && absY > absZ) { + // the best plane is the XZ plane.*** + return "XZ"; + } else { + // the best plane is the XY plane.*** + return "XY"; + } + } + + private boolean isValidVector(Vector3d vector) { + boolean valid = true; + if (!Double.isNaN(vector.get(0)) && !Double.isNaN(vector.get(1)) && !Double.isNaN(vector.get(2))) { + // check if vector is zero.*** + valid = vector.x != 0.0 || vector.y != 0.0 || vector.z != 0.0; + } else { + valid = false; + } + + return valid; + } + + private boolean isValidVector(Vector2d vector) { + boolean valid = true; + if (!Double.isNaN(vector.get(0)) && !Double.isNaN(vector.get(1))) { + // check if vector is zero.*** + valid = vector.x != 0.0 || vector.y != 0.0; + } else { + valid = false; + } + + return valid; + } + + public int getNextIdx(int idx, int pointsCount) { + return (idx + 1) % pointsCount; + } + + public int getPrevIdx(int idx, int pointsCount) { + return (idx + pointsCount - 1) % pointsCount; + } + + public void calculateFastNormal3D(List polygon, Vector3d resultNormal) { + // ************************************* + // take the 1rst valid cross product.*** + // ************************************* + int pointsCount = polygon.size(); + if (pointsCount < 3) { + return; + } + + + for (int i = 0; i < pointsCount; i++) { + int idxNext = getNextIdx(i, pointsCount); + int idxPrev = getPrevIdx(i, pointsCount); + Vector3d currPoint = polygon.get(i); + Vector3d nextPoint = polygon.get(idxNext); + Vector3d prevPoint = polygon.get(idxPrev); + + Vector3d v1 = new Vector3d(); + Vector3d v2 = new Vector3d(); + currPoint.sub(prevPoint, v1); + // check if v1 valid.*** + if (!isValidVector(v1)) { + // v1 is invalid.*** + continue; + } + v1.normalize(); + + nextPoint.sub(currPoint, v2); + // check if v2 valid.*** + if (!isValidVector(v2)) { + // v2 is invalid.*** + continue; + } + v2.normalize(); + + Vector3d cross = new Vector3d(); + v1.cross(v2, cross); + if (isValidVector(cross)) { + // cross is valid.*** + cross.normalize(); + resultNormal.set(cross); + return; + } + } + } + + public void calculateNormal3D(List polygon, Vector3d resultNormal) { + // calculate the normal of the polygon.*** + int pointsCount = polygon.size(); + if (pointsCount < 3) { + return; + } + + for (int i = 0; i < pointsCount; i++) { + int idxNext = getNextIdx(i, pointsCount); + int idxPrev = getPrevIdx(i, pointsCount); + Vector3d currPoint = polygon.get(i); + Vector3d nextPoint = polygon.get(idxNext); + Vector3d prevPoint = polygon.get(idxPrev); + + Vector3d v1 = new Vector3d(); + Vector3d v2 = new Vector3d(); + currPoint.sub(prevPoint, v1); + // check if v1 valid.*** + if (!isValidVector(v1)) { + // v1 is invalid.*** + continue; + } + v1.normalize(); + + nextPoint.sub(currPoint, v2); + // check if v2 valid.*** + if (!isValidVector(v2)) { + // v2 is invalid.*** + continue; + } + v2.normalize(); + + Vector3d cross = new Vector3d(); + v1.cross(v2, cross); + + if (!isValidVector(cross)) { + // cross is invalid.*** + continue; + } + + cross.normalize(); + + double dotProd = v1.dot(v2); + double angRad = Math.acos(dotProd); // because v1 and v2 are normalized.*** + + resultNormal.add(cross.x * angRad, cross.y * angRad, cross.z * angRad); + } + + resultNormal.normalize(); + } +} diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/GaiaTriangle.java b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/GaiaTriangle.java new file mode 100644 index 00000000..384733c7 --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/GaiaTriangle.java @@ -0,0 +1,29 @@ +package com.gaia3d.converter.geometry.tessellator; + +import lombok.Getter; +import lombok.Setter; +import org.joml.Vector3d; + +@Getter +@Setter +public class GaiaTriangle { + private final Vector3d[] positions; + private final Vector3d normal = new Vector3d(); + + public GaiaTriangle(Vector3d position1, Vector3d position2, Vector3d position3) { + this.positions = new Vector3d[3]; + this.positions[0] = position1; + this.positions[1] = position2; + this.positions[2] = position3; + calcNormal(); + } + + private void calcNormal() { + Vector3d v1 = new Vector3d(); + Vector3d v2 = new Vector3d(); + positions[1].sub(positions[0], v1); + positions[2].sub(positions[1], v2); + v1.cross(v2, this.normal); + this.normal.normalize(); + } +} diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/Line2D.java b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/Line2D.java new file mode 100644 index 00000000..4af4227c --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/Line2D.java @@ -0,0 +1,40 @@ +package com.gaia3d.converter.geometry.tessellator; + +import org.joml.Vector2d; + +public class Line2D { + // (x,y) = (x0,y0) + lambda * (u, v); + public Vector2d point = new Vector2d(); + public Vector2d direction = new Vector2d(); + + public Line2D(Vector2d point, Vector2d direction) { + this.point = point; + this.direction = direction; + } + + public void setBy2Points(Vector2d point1, Vector2d point2) { + this.point = point1; + this.direction = new Vector2d(point2.x - point1.x, point2.y - point1.y); + this.direction.normalize(); + } + + public boolean isParallel(Line2D line) { + double error = 1.0e-10; + return Math.abs(this.direction.x * line.direction.y - this.direction.y * line.direction.x) < error; + } + + public boolean intersectionWithLine(Line2D line, Vector2d intersectionPoint) { + double error = 1.0e-10; + if (this.isParallel(line)) { + return false; + } + double det = this.direction.x * line.direction.y - this.direction.y * line.direction.x; + double lambda = (line.direction.y * (line.point.x - this.point.x) - line.direction.x * (line.point.y - this.point.y)) / det; + if (Math.abs(lambda) < error) { + return false; + } + intersectionPoint.x = this.point.x + lambda * this.direction.x; + intersectionPoint.y = this.point.y + lambda * this.direction.y; + return true; + } +} diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/Point2DTess.java b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/Point2DTess.java new file mode 100644 index 00000000..96951706 --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/Point2DTess.java @@ -0,0 +1,28 @@ +package com.gaia3d.converter.geometry.tessellator; + +import lombok.Getter; +import lombok.Setter; +import org.joml.Vector2d; +import org.joml.Vector3d; + +@Getter +@Setter +public class Point2DTess { + public Vector2d point; + public Vector3d parentPoint; + + public Point2DTess(Vector2d point, Vector3d parentPoint) { + this.point = point; + this.parentPoint = parentPoint; + } + + public double squareDistanceTo(Point2DTess point) { + double dx = this.point.x - point.point.x; + double dy = this.point.y - point.point.y; + return dx * dx + dy * dy; + } + + public double distanceTo(Point2DTess point) { + return Math.sqrt(squareDistanceTo(point)); + } +} diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/Polygon2DTess.java b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/Polygon2DTess.java new file mode 100644 index 00000000..f882c42d --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/Polygon2DTess.java @@ -0,0 +1,235 @@ +package com.gaia3d.converter.geometry.tessellator; + +import lombok.Getter; +import lombok.Setter; +import org.joml.Vector2d; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +public class Polygon2DTess { + public List points; + + public Polygon2DTess(List points) { + this.points = points; + } + + public Point2DTess getPoint(int index) { + return points.get(index); + } + + public void getTrianglesIndicesAsConvexPolygon(List resultConvexIndices) { + // get the triangles indices as convex polygon.*** + int pointsCount = points.size(); + if (pointsCount < 3) { + return; + } + + // in a convexPolygon, the triangle indices are (0, 1, 2), (0, 2, 3), (0, 3, 4), ... (0, n-2, n-1).*** + for (int i = 2; i < pointsCount; i++) { + resultConvexIndices.add(0); + resultConvexIndices.add(i - 1); + resultConvexIndices.add(i); + } + } + + private boolean isValidVector(Vector2d vector) { + boolean valid = true; + if (!Double.isNaN(vector.get(0)) && !Double.isNaN(vector.get(1))) { + // check if vector is zero.*** + valid = vector.x != 0.0 || vector.y != 0.0; + } else { + valid = false; + } + + return valid; + } + + public float calculateNormal2D(List resultConcaveIndices) { + // calculate the normal of the polygon.*** + int pointsCount = points.size(); + if (pointsCount < 3) { + return 0.0f; + } + + float normal = 0.0f; + List positivePoints = new ArrayList<>(); + List negativePoints = new ArrayList<>(); + + double error = 1.0e-10; + + for (int i = 0; i < pointsCount; i++) { + int idxNext = getNextIdx(i); + int idxPrev = getPrevIdx(i); + Vector2d currPoint = getPoint(i).getPoint(); + Vector2d nextPoint = getPoint(idxNext).getPoint(); + Vector2d prevPoint = getPoint(idxPrev).getPoint(); + + Vector2d v1 = new Vector2d(); + Vector2d v2 = new Vector2d(); + currPoint.sub(prevPoint, v1); + // check if v1 valid.*** + if (!isValidVector(v1)) { + // v1 is invalid.*** + continue; + } + v1.normalize(); + + nextPoint.sub(currPoint, v2); + // check if v2 valid.*** + if (!isValidVector(v2)) { + // v2 is invalid.*** + continue; + } + v2.normalize(); + + double dot = v1.dot(v2); + + // if dot == 1.0, then this point is a convex point.*** + if (dot > 1.0 - error) { + continue; + } + + + double cross = v1.x * v2.y - v1.y * v2.x; + if (cross < 0) { + negativePoints.add(i); + } else { + positivePoints.add(i); + } + + double angRad = Math.acos(dot); // because v1 and v2 are normalized.*** + normal += (float) (angRad * cross); + } + + if (normal > 0.0f) { + normal = 1.0f; + resultConcaveIndices.addAll(negativePoints); + } else if (normal < 0.0f) { + normal = -1.0f; + resultConcaveIndices.addAll(positivePoints); + } + + return normal; + } + + public int getPointsCount() { + return points.size(); + } + + public Segment2DTess getSegment2DTess(int idx, Segment2DTess resultSegment) { + // get the segment of the polygon.*** + int pointsCount = points.size(); + if (pointsCount < 3) { + return null; + } + + int i1 = idx; + int i2 = (idx + 1) % pointsCount; + if (resultSegment == null) { + resultSegment = new Segment2DTess(points.get(i1), points.get(i2)); + } else { + resultSegment.setPoint1(points.get(i1)); + resultSegment.setPoint2(points.get(i2)); + } + + return resultSegment; + } + + public boolean isSegmentIntersectingPolygon(Segment2DTess segment, double error) { + // check if the segment is intersecting the polygon.*** + // 0 = no intersection, 1 = intersection.*** + int pointsCount = points.size(); + if (pointsCount < 3) { + return false; + } + + int intersectionsCount = 0; + Point2DTess intersectionPoint = new Point2DTess(null, null); + Segment2DTess polygonSegment = new Segment2DTess(null, null); + for (int i = 0; i < pointsCount; i++) { + //********************************************************************* + // segments intersection type.*** + // 0 = no intersection, + // 1 = intersection point is inside the both segments, + // 2 = intersection point is the start point of segmentA, + // 3 = intersection point is the end point of segmentA, + // 4 = intersection point is the start point of segmentB, + // 5 = intersection point is the end point segmentB. + //********************************************************************* + + getSegment2DTess(i, polygonSegment); + if (polygonSegment.intersectionWithSegment(segment, intersectionPoint, error) == 1) { + return true; + } + } + + return false; + } + + public int getNextIdx(int currIdx) { + int pointsCount = points.size(); + return (currIdx + 1) % pointsCount; + } + + public int getPrevIdx(int currIdx) { + int pointsCount = points.size(); + return (currIdx - 1 + pointsCount) % pointsCount; + } + + public void splitPolygon(int idx1, int idx2, List resultSplittedPolygons) { + // split this polygon in polygonA & polygonB.*** + // polygonA = (idx1, idx2), polygonB = (idx2, idx1).*** + int pointsCount = points.size(); + if (pointsCount < 3) { + return; + } + + List pointsA = new ArrayList<>(); + pointsA.add(points.get(idx1)); + pointsA.add(points.get(idx2)); + List pointsB = new ArrayList<>(); + pointsB.add(points.get(idx2)); + pointsB.add(points.get(idx1)); + + // polygonA.*** + boolean finished = false; + int currIdx = idx2; + int startIdx = idx1; + int i = 0; + while (!finished && i < pointsCount) { + int nextIdx = getNextIdx(currIdx); + if (nextIdx == startIdx) { + finished = true; + } else { + pointsA.add(points.get(nextIdx)); + currIdx = nextIdx; + } + i++; + } + + // polygonB.*** + finished = false; + currIdx = idx1; + startIdx = idx2; + i = 0; + while (!finished && i < pointsCount) { + int nextIdx = getNextIdx(currIdx); + if (nextIdx == startIdx) { + finished = true; + } else { + pointsB.add(points.get(nextIdx)); + currIdx = nextIdx; + } + i++; + } + + Polygon2DTess polygonA = new Polygon2DTess(pointsA); + Polygon2DTess polygonB = new Polygon2DTess(pointsB); + + resultSplittedPolygons.add(polygonA); + resultSplittedPolygons.add(polygonB); + } +} diff --git a/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/Segment2DTess.java b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/Segment2DTess.java new file mode 100644 index 00000000..181df062 --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/converter/geometry/tessellator/Segment2DTess.java @@ -0,0 +1,97 @@ +package com.gaia3d.converter.geometry.tessellator; + +import org.joml.Vector2d; + +public class Segment2DTess { + public Point2DTess startPoint; + public Point2DTess endPoint; + + public Segment2DTess(Point2DTess startPoint, Point2DTess endPoint) { + this.startPoint = startPoint; + this.endPoint = endPoint; + } + + public void setPoint1(Point2DTess point2DTess) { + this.startPoint = point2DTess; + } + + public void setPoint2(Point2DTess point2DTess) { + this.endPoint = point2DTess; + } + + public Line2D getLine(Line2D resultLine) { + if (resultLine == null) { + resultLine = new Line2D(null, null); + } + + resultLine.setBy2Points(this.startPoint.point, this.endPoint.point); + + return resultLine; + } + + public double getLength() { + return this.startPoint.distanceTo(this.endPoint); + } + + public int intersectionWithPointByDistances(Point2DTess point, double error) { + //**************************************************** + // 0 = no intersection, + // 1 = intersection point is inside the segment, + // 2 = intersection point is the start point, + // 3 = intersection point is the end point. + + double distance1 = this.startPoint.distanceTo(point); + double distance2 = this.endPoint.distanceTo(point); + double distance = getLength(); + + if (distance1 < error) { + return 2; + } else if (distance2 < error) { + return 3; + } else if (distance1 + distance2 - distance < error) { + return 1; + } + + return 0; + } + + public int intersectionWithSegment(Segment2DTess segment, Point2DTess intersectionPoint, double error) { + //********************************************************************* + // 0 = no intersection, + // 1 = intersection point is inside the both segments, + // 2 = intersection point is the start point of this segment, + // 3 = intersection point is the end point of this segment, + // 4 = intersection point is the start point of the segment, + // 5 = intersection point is the end point of the segment. + //********************************************************************* + + Line2D line1 = new Line2D(null, null); + Line2D line2 = new Line2D(null, null); + + this.getLine(line1); + segment.getLine(line2); + + if (intersectionPoint.point == null) { + intersectionPoint.point = new Vector2d(); + } + + if (line1.intersectionWithLine(line2, intersectionPoint.point)) { + int intersectionType1 = this.intersectionWithPointByDistances(intersectionPoint, error); + int intersectionType2 = segment.intersectionWithPointByDistances(intersectionPoint, error); + + if (intersectionType1 == 1 && intersectionType2 == 1) { + return 1; + } else if (intersectionType1 == 2 && intersectionType2 == 1) { + return 2; + } else if (intersectionType1 == 3 && intersectionType2 == 1) { + return 3; + } else if (intersectionType1 == 1 && intersectionType2 == 2) { + return 4; + } else if (intersectionType1 == 1 && intersectionType2 == 3) { + return 5; + } + } + + return 0; + } +} diff --git a/tiler/src/main/java/com/gaia3d/converter/jgltf/GltfWriter.java b/tiler/src/main/java/com/gaia3d/converter/jgltf/GltfWriter.java index f396036e..c165b4a0 100644 --- a/tiler/src/main/java/com/gaia3d/converter/jgltf/GltfWriter.java +++ b/tiler/src/main/java/com/gaia3d/converter/jgltf/GltfWriter.java @@ -121,7 +121,6 @@ private void convertNode(GlTF gltf, GltfBinary binary, Node parentNode, List= 65535; + boolean isIntegerIndices = gaiaMesh.getIndicesCount() >= 65535; if (isIntegerIndices) { - log.warn("Integer indices are used. The number of indices is greater than {}/65535", gaiaMesh.getPositionsCount() / 3); + log.warn("Integer indices are used. The number of indices is greater than {}/65535", gaiaMesh.getIndicesCount()); } - //boolean isIntegerIndices = true; GltfNodeBuffer nodeBuffer = initNodeBuffer(gaiaMesh, isIntegerIndices); createBuffer(gltf, nodeBuffer); @@ -200,8 +192,8 @@ private GltfNodeBuffer convertGeometryInfo(GlTF gltf, GaiaMesh gaiaMesh, Node no } } if (normalsBuffer != null) { - for (Byte normal: normalBytes) { - normalsBuffer.put(normal); + for (Float normal: normals) { + normalsBuffer.putFloat(normal); } } if (colorsBuffer != null) { @@ -233,9 +225,8 @@ private GltfNodeBuffer convertGeometryInfo(GlTF gltf, GaiaMesh gaiaMesh, Node no int verticesAccessorId = createAccessor(gltf, positionsBufferViewId, 0, positions.length / 3, GltfConstants.GL_FLOAT, AccessorType.VEC3, false); nodeBuffer.setPositionsAccessorId(verticesAccessorId); } - if (normalsBufferViewId > -1 && normalBytes.length > 0) { - //int normalsAccessorId = createAccessor(gltf, normalsBufferViewId, 0, normals.size() / 3, GltfConstants.GL_FLOAT, AccessorType.VEC3, false); - int normalsAccessorId = createAccessor(gltf, normalsBufferViewId, 0, normals.length / 3, GltfConstants.GL_BYTE, AccessorType.VEC3, true); + if (normalsBufferViewId > -1 && normals.length > 0) { + int normalsAccessorId = createAccessor(gltf, normalsBufferViewId, 0, normals.length / 3, GltfConstants.GL_FLOAT, AccessorType.VEC3, false); nodeBuffer.setNormalsAccessorId(normalsAccessorId); } if (colorsBufferViewId > -1 && colors.length > 0) { @@ -259,6 +250,14 @@ private GltfNodeBuffer convertGeometryInfo(GlTF gltf, GaiaMesh gaiaMesh, Node no return nodeBuffer; } + private int padMultiple4(int value) { + int remainder = value % 4; + if (remainder == 0) { + return value; + } + return value + (4 - remainder); + } + private GltfNodeBuffer initNodeBuffer(GaiaMesh gaiaMesh, boolean isIntegerIndices) { GltfNodeBuffer nodeBuffer = new GltfNodeBuffer(); int SHORT_SIZE = 2; @@ -267,11 +266,18 @@ private GltfNodeBuffer initNodeBuffer(GaiaMesh gaiaMesh, boolean isIntegerIndice int indicesCapacity = gaiaMesh.getIndicesCount() * (isIntegerIndices ? INT_SIZE : SHORT_SIZE); int positionsCapacity = gaiaMesh.getPositionsCount() * FLOAT_SIZE; - int normalsCapacity = gaiaMesh.getNormalsCount() / 3 * 4; + int normalsCapacity = gaiaMesh.getPositionsCount() * FLOAT_SIZE; int colorsCapacity = gaiaMesh.getColorsCount(); int texcoordCapacity = gaiaMesh.getTexcoordsCount() * FLOAT_SIZE; int batchIdCapacity = gaiaMesh.getBatchIdsCount() * FLOAT_SIZE; + indicesCapacity = padMultiple4(indicesCapacity); + positionsCapacity = padMultiple4(positionsCapacity); + normalsCapacity = padMultiple4(normalsCapacity); + colorsCapacity = padMultiple4(colorsCapacity); + texcoordCapacity = padMultiple4(texcoordCapacity); + batchIdCapacity = padMultiple4(batchIdCapacity); + int bodyLength = 0; bodyLength += indicesCapacity; bodyLength += positionsCapacity; @@ -373,8 +379,7 @@ private void createBuffer(GlTF gltf, GltfNodeBuffer nodeBuffer) { } if (nodeBuffer.getNormalsBuffer() != null) { ByteBuffer normalsBuffer = nodeBuffer.getNormalsBuffer(); - int bufferViewId = createBufferView(gltf, bufferId, bufferLength + bufferOffset, normalsBuffer.capacity(), 4, GL20.GL_ARRAY_BUFFER); - //int bufferViewId = createBufferView(gltf, bufferId, bufferLength + bufferOffset, normalsBuffer.capacity(), 12, GL20.GL_ARRAY_BUFFER); + int bufferViewId = createBufferView(gltf, bufferId, bufferLength + bufferOffset, normalsBuffer.capacity(), 12, GL20.GL_ARRAY_BUFFER); nodeBuffer.setNormalsBufferViewId(bufferViewId); BufferView bufferView = gltf.getBufferViews().get(bufferViewId); bufferView.setName("normals"); @@ -423,7 +428,7 @@ private int createBufferView(GlTF gltf, int buffer, int offset, int length, int private Node createNode(GlTF gltf, Node parentNode, GaiaNode gaiaNode) { Node node; if (parentNode == null) { - node = gltf.getNodes().get(0); // root node + node = gltf.getNodes().get(0); } else { node = new Node(); gltf.addNodes(node); @@ -453,8 +458,6 @@ private void createMaterial(GlTF gltf, GaiaMaterial gaiaMaterial) { } MaterialPbrMetallicRoughness pbrMetallicRoughness = new MaterialPbrMetallicRoughness(); - //Vector4d diffuseColor = gaiaMaterial.getDiffuseColor(); - //pbrMetallicRoughness.setBaseColorFactor(new float[]{(float) diffuseColor.x, (float) diffuseColor.y, (float) diffuseColor.z, (float) diffuseColor.w}); if (!diffuseTextures.isEmpty()) { GaiaTexture gaiaTexture = diffuseTextures.get(0); int textureId = createTexture(gltf, gaiaTexture); diff --git a/tiler/src/main/java/com/gaia3d/converter/kml/ShapeReader.java b/tiler/src/main/java/com/gaia3d/converter/kml/ShapeReader.java index 6600ebf7..2f818943 100644 --- a/tiler/src/main/java/com/gaia3d/converter/kml/ShapeReader.java +++ b/tiler/src/main/java/com/gaia3d/converter/kml/ShapeReader.java @@ -1,14 +1,7 @@ package com.gaia3d.converter.kml; -import com.gaia3d.basic.geometry.GaiaBoundingBox; -import com.gaia3d.basic.structure.GaiaMaterial; -import com.gaia3d.basic.structure.GaiaNode; -import com.gaia3d.basic.structure.GaiaScene; import com.gaia3d.command.mago.GlobalOptions; -import com.gaia3d.converter.geometry.Extruder; -import com.gaia3d.converter.geometry.Extrusion; -import com.gaia3d.converter.geometry.GaiaBuilding; -import com.gaia3d.converter.geometry.Tessellator; +import com.gaia3d.converter.geometry.GaiaExtrusionBuilding; import com.gaia3d.util.GlobeUtils; import lombok.extern.slf4j.Slf4j; import org.geotools.data.DataStore; @@ -19,24 +12,14 @@ import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureSource; import org.geotools.feature.FeatureIterator; -import org.geotools.geometry.jts.JTSFactoryFinder; import org.geotools.util.factory.Hints; -import org.joml.Matrix4d; import org.joml.Vector3d; import org.locationtech.jts.geom.*; import org.locationtech.proj4j.CoordinateReferenceSystem; import org.locationtech.proj4j.ProjCoordinate; import org.opengis.feature.simple.SimpleFeature; import org.opengis.filter.Filter; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; + import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -82,7 +65,7 @@ public List readAll(File file) { SimpleFeatureCollection features = source.getFeatures(query); FeatureIterator iterator = features.features(); - List buildings = new ArrayList<>(); + List buildings = new ArrayList<>(); while (iterator.hasNext()) { SimpleFeature feature = iterator.next(); Geometry geom = (Geometry) feature.getDefaultGeometry(); diff --git a/tiler/src/main/java/com/gaia3d/converter/pointcloud/LasConverter.java b/tiler/src/main/java/com/gaia3d/converter/pointcloud/LasConverter.java index 98c1fbd5..c478854c 100644 --- a/tiler/src/main/java/com/gaia3d/converter/pointcloud/LasConverter.java +++ b/tiler/src/main/java/com/gaia3d/converter/pointcloud/LasConverter.java @@ -5,12 +5,15 @@ import com.gaia3d.basic.structure.GaiaVertex; import com.gaia3d.command.mago.GlobalOptions; import com.gaia3d.util.GlobeUtils; +import com.github.mreutegg.laszip4j.CloseablePointIterable; import com.github.mreutegg.laszip4j.LASHeader; import com.github.mreutegg.laszip4j.LASPoint; import com.github.mreutegg.laszip4j.LASReader; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.joml.Vector3d; +import org.locationtech.proj4j.BasicCoordinateTransform; +import org.locationtech.proj4j.CRSFactory; import org.locationtech.proj4j.CoordinateReferenceSystem; import org.locationtech.proj4j.ProjCoordinate; @@ -18,7 +21,9 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; @Slf4j @RequiredArgsConstructor @@ -57,12 +62,12 @@ private List convert(File file) { } else { hasRgbColor = false; } -// log.info("[Pre][LoadFile] Loading a pointcloud file. : {}", file.getAbsolutePath()); -// log.info(" - LAS Version : {}.{}", major, minor); -// log.info(" - LAS Point Data Record Format : {}", recordFormat); -// log.info(" - LAS Point Data Record Length : {}", recordLength); -// log.info(" - LAS Point Data Record has RGB Color : {}", hasRgbColor); - Iterable pointIterable = reader.getPoints(); + /*log.info("[Pre] Loading a pointcloud file. : {}", file.getAbsolutePath()); + log.info(" - LAS Version : {}.{}", major, minor); + log.info(" - LAS Point Data Record Format : {}", recordFormat); + log.info(" - LAS Point Data Record Length : {}", recordLength); + log.info(" - LAS Point Data Record has RGB Color : {}", hasRgbColor);*/ + //Iterable pointIterable = reader.getPoints(); GaiaBoundingBox boundingBox = pointCloud.getGaiaBoundingBox(); double xScaleFactor = header.getXScaleFactor(); @@ -72,39 +77,77 @@ private List convert(File file) { double zScaleFactor = header.getZScaleFactor(); double zOffset = header.getZOffset(); + CRSFactory factory = new CRSFactory(); CoordinateReferenceSystem crs = globalOptions.getCrs(); - pointIterable.forEach(point -> { - double x = point.getX() * xScaleFactor + xOffset; - double y = point.getY() * yScaleFactor + yOffset; - double z = point.getZ() * zScaleFactor + zOffset; - - ProjCoordinate coordinate = new ProjCoordinate(x, y, z); - ProjCoordinate transformedCoordinate = GlobeUtils.transform(crs, coordinate); - - Vector3d position = new Vector3d(transformedCoordinate.x, transformedCoordinate.y, z); - - byte[] rgb; - if (hasRgbColor) { - rgb = getColorByRGB(point); - } else { - rgb = getColorIntensity(point); + CoordinateReferenceSystem wgs84 = factory.createFromParameters("WGS84", "+proj=longlat +datum=WGS84 +no_defs"); + BasicCoordinateTransform transformer = new BasicCoordinateTransform(crs, wgs84); + ProjCoordinate result = new ProjCoordinate(); + //transformer.transform(coordinate, result); + //return result; + + CloseablePointIterable pointIterable = reader.getCloseablePoints(); + //pointIterable.close(); + + int pointSkip = globalOptions.getPointSkip(); + AtomicInteger pointIndex = new AtomicInteger(); + AtomicInteger maxColorValue = new AtomicInteger(); + //for (LASPoint point : reader.getPoints()) { + + for (LASPoint point : pointIterable) { + if (pointIndex.get() % pointSkip == 0) { + double x = point.getX() * xScaleFactor + xOffset; + double y = point.getY() * yScaleFactor + yOffset; + double z = point.getZ() * zScaleFactor + zOffset; + + ProjCoordinate coordinate = new ProjCoordinate(x, y, z); + ProjCoordinate transformedCoordinate = transformer.transform(coordinate, new ProjCoordinate()); + + Vector3d position = new Vector3d(transformedCoordinate.x, transformedCoordinate.y, z); + transformedCoordinate = null; + coordinate = null; + + byte[] rgb; + if (hasRgbColor) { + rgb = getColorByRGB(point); + //rgb = getColorByByteRGB(point); // only for test + } else { + rgb = getColorIntensity(point); + } + + GaiaVertex vertex = new GaiaVertex(); + vertex.setPosition(position); + vertex.setColor(rgb); + vertex.setBatchId(0); + vertices.add(vertex); + boundingBox.addPoint(position); } + pointIndex.getAndIncrement(); + } - GaiaVertex vertex = new GaiaVertex(); - vertex.setPosition(position); - vertex.setColor(rgb); - vertex.setBatchId(0); - vertices.add(vertex); - boundingBox.addPoint(position); - }); + pointIterable.close(); // randomize arrays Collections.shuffle(vertices); + pointCloud.setVertices(vertices); pointClouds.add(pointCloud); + System.gc(); + return pointClouds; } + private void trasnformPostions(List vertices) { + vertices.forEach((vertex) -> { + Vector3d positions = vertex.getPosition(); + ProjCoordinate coordinate = new ProjCoordinate(positions.x, positions.y, positions.z); + ProjCoordinate transformedCoordinate = GlobeUtils.transform(GlobalOptions.getInstance().getCrs(), coordinate); + positions.x = transformedCoordinate.x; + positions.y = transformedCoordinate.y; + positions.z = transformedCoordinate.z; + }); + } + + /** * Get color by RGB * @param point LASPoint @@ -122,6 +165,20 @@ private byte[] getColorByRGB(LASPoint point) { return rgb; } + /** + * Get color by RGB + * @param point LASPoint + * @return byte[3] + */ + private byte[] getColorByByteRGB(LASPoint point) { + byte[] rgb = new byte[3]; + rgb[0] = (byte) point.getRed(); + rgb[1] = (byte) point.getGreen(); + rgb[2] = (byte) point.getBlue(); + + return rgb; + } + /** * Get color by intensity (Gray scale) * @param point LASPoint diff --git a/tiler/src/main/java/com/gaia3d/process/ProcessOptions.java b/tiler/src/main/java/com/gaia3d/process/ProcessOptions.java index cac28d5f..45e14fc5 100644 --- a/tiler/src/main/java/com/gaia3d/process/ProcessOptions.java +++ b/tiler/src/main/java/com/gaia3d/process/ProcessOptions.java @@ -6,55 +6,52 @@ @AllArgsConstructor @Getter public enum ProcessOptions { - HELP("help", "h", "help", false, "print this message"), - VERSION("version", "v", "version", false, "print version"), - QUIET("quiet", "q", "quiet", false, "quiet mode"), - LOG("log", "l", "log", true, "output log file path"), - INPUT("input", "i", "input", true, "input file path"), - OUTPUT("output", "o", "output", true, "output file path"), - INPUT_TYPE("inputType", "it", "inputType", true, "input file type (kml, 3ds, obj, gltf, etc...)"), - OUTPUT_TYPE("outputType", "ot", "outputType", true, "output file type"), - CRS("crs", "c", "crs", true,"Coordinate Reference Systems, only epsg code (4326, 3857, etc...)"), - PROJ4("proj", "p", "proj", true, "proj4 parameters (ex: +proj=tmerc +la...)"), - - // 3D Options - RECURSIVE("recursive", "r", "recursive", false, "deep directory exploration"), - //SWAP_YZ("swapYZ", "yz", "swapYZ", false, "swap vertices axis YZ"), - REVERSE_TEXCOORD("reverseTexCoord", "rt", "reverseTexCoord", false, "texture y-axis coordinate reverse"), - MULTI_THREAD("multiThread", "mt", "multiThread", false, "multi thread mode"), - MULTI_THREAD_COUNT("multiThreadCount", "mc", "multiThreadCount", true, "multi thread count (Default: 8)"), - PNG_TEXTURE("pngTexture", "pt", "pngTexture", false, "png texture mode"), - Y_UP_AXIS("yUpAxis", "ya", "yAxis", false, "Assign 3D root transformed matrix Y-UP axis"), - - // 3D Tiles Options - REFINE_ADD("refineAdd", "ra", "refineAdd", false, "refine addd mode"), - MAX_COUNT("maxCount", "mx", "maxCount", true, "max count of nodes (Default: 1024)"), - MAX_LOD("maxLod", "xl", "maxLod", true, "max level of detail (Default: 3)"), + // Default Options + HELP("help", "h", "help", false, "Print Gelp"), + VERSION("version", "v", "version", false, "Print Version Info"), + QUIET("quiet", "q", "quiet", false, "Quiet mode/Silent mode"), + + // Path Options + INPUT("input", "i", "input", true, "Input directory path"), + OUTPUT("output", "o", "output", true, "Output directory file path"), + INPUT_TYPE("inputType", "it", "inputType", true, "Input files type (kml, 3ds, fbx, obj, gltf, glb, las, laz, citygml, indoorgml, shp, geojson)(Default: kml)"), + OUTPUT_TYPE("outputType", "ot", "outputType", true, "Output 3DTiles Type (b3dm, i3dm, pnts)(Default : b3dm)"), + LOG("log", "l", "log", true, "Output log file path."), + TERRAIN("terrain", "te", "terrain", true, "GeoTiff Terrain file path, 3D Object applied as clampToGround (Supports geotiff format)"), + INSTANCE_FILE("instance", "if", "instance", true, "Instance file path for I3DM (Default: {OUTPUT}/instance.dae)"), + RECURSIVE("recursive", "r", "recursive", false, "Tree directory deep navigation."), + + // Coordinate Options + CRS("crs", "c", "crs", true,"Coordinate Reference Systems, EPSG Code(4326, 3857, 32652, 5186...)"), + PROJ4("proj", "p", "proj", true, "Proj4 parameters (ex: +proj=tmerc +la...)"), + + // Execution Options + MULTI_THREAD_COUNT("multiThreadCount", "mc", "multiThreadCount", true, "Multi-Thread count (Default: 4)"), + + // 3DTiles Options + REFINE_ADD("refineAdd", "ra", "refineAdd", false, "Set 3D Tiles Refine 'ADD' mode"), + MAX_COUNT("maxCount", "mx", "maxCount", true, "Maximum number of triangles per node."), MIN_LOD("minLod", "nl", "minLod", true, "min level of detail (Default: 0)"), - MAX_POINTS("maxPoints", "mp", "maxPoints", true, "max points of pointcloud data (Default: 20000)"), - - // 2D Options - FLIP_COORDINATE("flipCoordinate", "fc", "flipCoordinate", false, "flip x,y Coordinate."), - NAME_COLUMN("nameColumn", "nc", "nameColumn", true, "name column setting. (Default: name)"), - HEIGHT_COLUMN("heightColumn", "hc", "heightColumn", true, "height column setting. (Default: height)"), - ALTITUDE_COLUMN("altitudeColumn", "ac", "altitudeColumn", true, "altitude Column setting."), - MINIMUM_HEIGHT("minimumHeight", "mh", "minimumHeight", true, "minimum height setting."), - ABSOLUTE_ALTITUDE("absoluteAltitude", "aa", "absoluteAltitude", true, "absolute altitude mode."), - - IGNORE_TEXTURES("ignoreTextures", "igtx", "ignoreTextures", false,"Ignore diffuse textures."), + MAX_LOD("maxLod", "xl", "maxLod", true, "Max Level of detail (Default: 3)"), + MAX_POINTS("maxPoints", "mp", "maxPoints", true, "Limiting the maximum number of points in point cloud data. (Default: 65536)"), + POINT_SCALE("pointScale", "ps", "pointScale", true, "Pointscloud geometryError scale setting (Default: 2)"), + POINT_SKIP("pointSkip", "pk", "pointSkip", true, "Number of pointcloud omissions (ex: 1/4)(Default: 4)"), + Y_UP_AXIS("yUpAxis", "ya", "yAxis", false, "Assign 3D root transformed matrix Y-UP axis"), ZERO_ORIGIN("zeroOrigin", "zo", "zeroOrigin", false, "[Experimental] fix 3d root transformed matrix origin to zero point."), - AUTO_UP_AXIS("autoUpAxis", "aa", "autoUpAxis", false, "[Experimental] automatically Assign 3D Matrix Axes"), - //Z_UP_AXIS("zAxis", "ya", "zAxis", false, "[Experimental] Assign 3D root transformed matrix Z-UP axis"), - //GENERATE_NORMALS("genNormals", "gn", "genNormals", false, "generate normals"), - //SCALE("scale", "sc", "scale", false, "scale factor"), - //STRICT("strict", "st", "strict", false, "strict mode"), - GEO_TIFF("geoTiff", "gt", "geoTiff", true, "[Experimental] geoTiff file path, 3D Object applied as clampToGround."), - TERRAIN("terrain", "te", "terrain", true, "[Experimental] terrain file path, 3D Object applied as clampToGround."), - - DEBUG("debug", "d", "debug", false,"debug mode"), - DEBUG_ALL_DRAWING("debugAllDrawing", "dad", "debugAllDrawing", false,"debug all drawing"), - DEBUG_GLTF("gltf", "gltf", "gltf", false, "create gltf file."), - DEBUG_GLB("glb", "glb", "glb", false, "create glb file."); + IGNORE_TEXTURES("ignoreTextures", "igtx", "ignoreTextures", false,"Ignore diffuse textures. "), + AUTO_UP_AXIS("autoUpAxis", "aa", "autoUpAxis", false, "Automatically Assign 3D Matrix Axes. If your 3D data up-axis is incorrect, try this option."), + + // Extrusion Options + FLIP_COORDINATE("flipCoordinate", "fc", "flipCoordinate", false, "Flip x, y Coordinate (Default: false)"), + NAME_COLUMN("nameColumn", "nc", "nameColumn", true, "Name column setting for extrusion model (Default: name)"), + HEIGHT_COLUMN("heightColumn", "hc", "heightColumn", true, "Height column setting for extrusion model (Default: height)"), + ALTITUDE_COLUMN("altitudeColumn", "ac", "altitudeColumn", true, "Altitude Column setting for extrusion model (Default: altitude)"), + MINIMUM_HEIGHT("minimumHeight", "mh", "minimumHeight", true, "Minimum height value for extrusion model (Default: 1.0)"), + ABSOLUTE_ALTITUDE("absoluteAltitude", "aa", "absoluteAltitude", true, "Absolute altitude value for extrusion model"), + SKIRT_HEIGHT("skirtHeight", "sh", "skirtHeight", true, "Building Skirt height setting for extrusion model (Default: 4.0)"), + + DEBUG("debug", "d", "debug", false,"More detailed log output and stops on Multi-Thread bugs."), + DEBUG_GLB("glb", "glb", "glb", false, "Create glb file with B3DM."); private final String longName; private final String shortName; diff --git a/tiler/src/main/java/com/gaia3d/process/TilingPipeline.java b/tiler/src/main/java/com/gaia3d/process/TilingPipeline.java index fedcf396..b67f43a1 100644 --- a/tiler/src/main/java/com/gaia3d/process/TilingPipeline.java +++ b/tiler/src/main/java/com/gaia3d/process/TilingPipeline.java @@ -110,10 +110,6 @@ private void startPreProcesses(FileLoader fileLoader) throws InterruptedExceptio private void calcNodeLimit(long nodeCountValue) { if (globalOptions.getNodeLimit() < 0) { - /*if (nodeCountValue > 262144) { - globalOptions.setNodeLimit(16384); - } else */ - if (nodeCountValue > 131072) { globalOptions.setNodeLimit(8192); } else if (nodeCountValue > 65536) { @@ -123,20 +119,6 @@ private void calcNodeLimit(long nodeCountValue) { } else { globalOptions.setNodeLimit(1024); } - - /* if (nodeCountValue > 262144) { - globalOptions.setNodeLimit(16384); - } else if (nodeCountValue > 131072) { - globalOptions.setNodeLimit(8192); - } else if (nodeCountValue > 65536) { - globalOptions.setNodeLimit(4096); - } else if (nodeCountValue > 32768) { - globalOptions.setNodeLimit(2048); - } else if (nodeCountValue > 16384) { - globalOptions.setNodeLimit(1024); - } else { - globalOptions.setNodeLimit(512); - }*/ } } @@ -205,11 +187,10 @@ private void deleteTemp() throws IOException { private void executeThread(ExecutorService executorService, List tasks) throws InterruptedException { try { for (Runnable task : tasks) { - executorService.submit(task); - //Future future = executorService.submit(task); - // TODO MultiThead BUG - //future.get(); - //future.isDone(); + Future future = executorService.submit(task); + if (globalOptions.isDebug()) { + future.get(); + } } } catch (Exception e) { throw new RuntimeException(e); @@ -219,6 +200,6 @@ private void executeThread(ExecutorService executorService, List tasks if (executorService.isTerminated()) { executorService.shutdownNow(); } - } while (!executorService.awaitTermination(2, TimeUnit.SECONDS)); + } while (!executorService.awaitTermination(1, TimeUnit.SECONDS)); } } diff --git a/tiler/src/main/java/com/gaia3d/process/postprocess/batch/Batched3DModel.java b/tiler/src/main/java/com/gaia3d/process/postprocess/batch/Batched3DModel.java index 70c53551..48ebddf3 100644 --- a/tiler/src/main/java/com/gaia3d/process/postprocess/batch/Batched3DModel.java +++ b/tiler/src/main/java/com/gaia3d/process/postprocess/batch/Batched3DModel.java @@ -25,7 +25,9 @@ import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Slf4j @@ -53,21 +55,24 @@ public ContentInfo run(ContentInfo contentInfo) { List tileInfos = contentInfo.getTileInfos(); int batchLength = tileInfos.size(); - List projectNames = tileInfos.stream() - .map((tileInfo) -> tileInfo.getSet().getProjectName()) - .collect(Collectors.toList()); - List nodeNames = tileInfos.stream() - .map(TileInfo::getName) - .collect(Collectors.toList()); - List geometricErrors = tileInfos.stream() - .map((tileInfo) -> tileInfo.getBoundingBox().getLongestDistance()) - .collect(Collectors.toList()); - List heights = tileInfos.stream() - .map((tileInfo) -> { - GaiaBoundingBox boundingBox = tileInfo.getBoundingBox(); - return boundingBox.getMaxZ() - boundingBox.getMinZ(); - }) - .collect(Collectors.toList()); + + List projectNames = new ArrayList<>(); + List nodeNames = new ArrayList<>(); + List geometricErrors = new ArrayList<>(); + List heights = new ArrayList<>(); + tileInfos.forEach((tileInfo) -> { + GaiaSet set = tileInfo.getSet(); + String projectName = set.getProjectName(); + // convert utf-8 to ascii + String asciiProjectName = StringUtils.convertUTF8(projectName); + projectNames.add(asciiProjectName); + nodeNames.add(tileInfo.getName()); + geometricErrors.add(tileInfo.getBoundingBox().getLongestDistance()); + GaiaBoundingBox boundingBox = tileInfo.getBoundingBox(); + heights.add(boundingBox.getMaxZ() - boundingBox.getMinZ()); + }); + + GaiaScene scene = new GaiaScene(batchedSet); /* FeatureTable */ @@ -101,11 +106,6 @@ public ContentInfo run(ContentInfo contentInfo) { } byte[] glbBytes; - if (globalOptions.isGltf()) { - String glbFileName = nodeCode + ".gltf"; - File glbOutputFile = outputRoot.resolve(glbFileName).toFile(); - this.gltfWriter.writeGltf(scene, glbOutputFile); - } if (globalOptions.isGlb()) { String glbFileName = nodeCode + ".glb"; File glbOutputFile = outputRoot.resolve(glbFileName).toFile(); diff --git a/tiler/src/main/java/com/gaia3d/process/postprocess/batch/GaiaBatcher.java b/tiler/src/main/java/com/gaia3d/process/postprocess/batch/GaiaBatcher.java index 07d14842..741c1e7d 100644 --- a/tiler/src/main/java/com/gaia3d/process/postprocess/batch/GaiaBatcher.java +++ b/tiler/src/main/java/com/gaia3d/process/postprocess/batch/GaiaBatcher.java @@ -240,7 +240,6 @@ public GaiaSet runBatching(List tileInfos, String nodeCode, LevelOfDet GaiaMaterial material = findMaterial(textureMaterials, bufferDataSet.getMaterialId()); return !material.isRepeat(); }).collect(Collectors.toList()); - setMaterialsIndexInList(clampMaterials, clampDataSets); /* 2. repeat pattern texture use materials */ List repeatMaterials = textureMaterials.stream().filter(GaiaMaterial::isRepeat).collect(Collectors.toList()); @@ -248,6 +247,8 @@ public GaiaSet runBatching(List tileInfos, String nodeCode, LevelOfDet GaiaMaterial material = findMaterial(textureMaterials, bufferDataSet.getMaterialId()); return material.isRepeat(); }).collect(Collectors.toList()); + + setMaterialsIndexInList(clampMaterials, clampDataSets); setMaterialsIndexInList(repeatMaterials, repeatDataSets); /* batching textures for clamp texture material */ diff --git a/tiler/src/main/java/com/gaia3d/process/postprocess/batch/GaiaTextureCoordinator.java b/tiler/src/main/java/com/gaia3d/process/postprocess/batch/GaiaTextureCoordinator.java index 266fbe8e..61bad530 100644 --- a/tiler/src/main/java/com/gaia3d/process/postprocess/batch/GaiaTextureCoordinator.java +++ b/tiler/src/main/java/com/gaia3d/process/postprocess/batch/GaiaTextureCoordinator.java @@ -315,7 +315,7 @@ public void batchTextures(LevelOfDetail lod) { if (existPngTextures) { extension = "png"; } - this.writeBatchedImage(extension); + //this.writeBatchedImage(extension); // end test.************************ List materialBufferDataSets = bufferDataSets.stream().filter((bufferDataSet) -> bufferDataSet.getMaterialId() == target.getMaterialId()).collect(Collectors.toList()); diff --git a/tiler/src/main/java/com/gaia3d/process/postprocess/instance/Instanced3DModel.java b/tiler/src/main/java/com/gaia3d/process/postprocess/instance/Instanced3DModel.java index 878f3775..3061ad0e 100644 --- a/tiler/src/main/java/com/gaia3d/process/postprocess/instance/Instanced3DModel.java +++ b/tiler/src/main/java/com/gaia3d/process/postprocess/instance/Instanced3DModel.java @@ -85,8 +85,8 @@ public ContentInfo run(ContentInfo contentInfo) { double headingValue = Math.toRadians(kmlInfo.getHeading()); Matrix3d rotationMatrix = new Matrix3d(); rotationMatrix.rotateY(headingValue); - //normalUp = rotationMatrix.transform(normalUp); - //normalRight = rotationMatrix.transform(normalRight); + normalUp = rotationMatrix.transform(normalUp); + normalRight = rotationMatrix.transform(normalRight); // scale double scale = kmlInfo.getScaleZ(); diff --git a/tiler/src/main/java/com/gaia3d/process/preprocess/GaiaLargeMeshDivider.java b/tiler/src/main/java/com/gaia3d/process/preprocess/GaiaLargeMeshDivider.java new file mode 100644 index 00000000..826a8e8a --- /dev/null +++ b/tiler/src/main/java/com/gaia3d/process/preprocess/GaiaLargeMeshDivider.java @@ -0,0 +1,228 @@ +package com.gaia3d.process.preprocess; + +import com.gaia3d.basic.geometry.GaiaBoundingBox; +import com.gaia3d.basic.structure.*; +import com.gaia3d.basic.types.FormatType; +import com.gaia3d.command.mago.GlobalOptions; +import com.gaia3d.converter.kml.KmlInfo; +import com.gaia3d.process.tileprocess.tile.TileInfo; +import com.gaia3d.util.GlobeUtils; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.geotools.coverage.grid.GridCoverage2D; +import org.geotools.geometry.DirectPosition2D; +import org.geotools.referencing.crs.DefaultGeographicCRS; +import org.joml.Matrix4d; +import org.joml.Vector3d; +import org.locationtech.proj4j.CoordinateReferenceSystem; +import org.locationtech.proj4j.ProjCoordinate; +import org.opengis.geometry.DirectPosition; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +@AllArgsConstructor +public class GaiaLargeMeshDivider { + public List run(List gaiaScenes) { + + /*for (GaiaScene gaiaScene : gaiaScenes) { + divideScene(gaiaScene); + gaiaScene.setTileInfos(tileInfos); + + + + } + + List nodes = gaiaScene.getNodes(); + divideNode(nodes); + + GaiaNode rootNode = gaiaScene.getNodes().get(0); + Matrix4d transform = rootNode.getTransformMatrix(); + return tileInfo;*/ + return gaiaScenes; + } + + private void divideScene(GaiaScene scene) { + List nodes = scene.getNodes(); + for (GaiaNode node : nodes) { + divideNode(node); + } + } + + private void divideNode(GaiaNode node) { + List dividedMeshes = new ArrayList<>(); + List meshes = node.getMeshes(); + meshes.forEach((mesh) -> { + int[] indices = mesh.getIndices(); + if (indices.length >= 65535) { + log.info("Divide mesh : {}", indices.length); + dividedMeshes.addAll(divideMesh(mesh)); + } else { + dividedMeshes.add(mesh); + } + }); + node.setMeshes(dividedMeshes); + List children = node.getChildren(); + for (GaiaNode child : children) { + divideNode(child); + } + } + + private List divideMesh(GaiaMesh mesh) { + List dividedMeshes = new ArrayList<>(); + + List primitives = mesh.getPrimitives(); + primitives.forEach((primitive) -> { + int[] indices = primitive.getIndices(); + if (indices.length >= 65535) { + log.info("Divide Primitive : {}", indices.length); + List dividedPrimitives = dividePrimitive(primitive); + for (GaiaPrimitive dividedPrimitive : dividedPrimitives) { + GaiaMesh dividedMesh = new GaiaMesh(); + dividedMesh.setPrimitives(List.of(dividedPrimitive)); + dividedMeshes.add(dividedMesh); + } + } else { + GaiaMesh dividedMesh = new GaiaMesh(); + dividedMesh.setPrimitives(primitives); + dividedMeshes.add(dividedMesh); + } + }); + return dividedMeshes; + } + + private List dividePrimitive(GaiaPrimitive primitive) { + List dividedPrimitives = new ArrayList<>(); + + List surfaces = primitive.getSurfaces(); + surfaces.forEach((surface) -> { + int[] indices = surface.getIndices(); + if (indices.length >= 65535) { + log.info("Divide Surface : {}", indices.length); + dividedPrimitives.addAll(divideSurface(primitive, surface)); + } else { + GaiaPrimitive dividedPrimitive = new GaiaPrimitive(); + dividedPrimitive.setMaterialIndex(primitive.getMaterialIndex()); + dividedPrimitive.getSurfaces().add(surface); + dividedPrimitives.add(dividedPrimitive); + } + }); + + return dividedPrimitives; + } + + private List divideSurface(GaiaPrimitive primitive, GaiaSurface surface) { + List dividedPrimitives = new ArrayList<>(); + List faces = surface.getFaces(); + List vertices = primitive.getVertices(); + + GaiaSurface dividedSurface = null; + //List dividedVertices = null; + + int minIndex = Integer.MAX_VALUE; + int maxIndex = 0; + for (GaiaFace face : faces) { + if (dividedSurface == null) { + dividedSurface = new GaiaSurface(); + //dividedVertices = new ArrayList<>(); + GaiaPrimitive dividedPrimitive = new GaiaPrimitive(); + dividedPrimitive.getSurfaces().add(dividedSurface); + dividedPrimitive.setMaterialIndex(primitive.getMaterialIndex()); + dividedPrimitive.setVertices(primitive.getVertices()); + + dividedPrimitives.add(dividedPrimitive); + } else if (dividedSurface.getIndices().length >= 65000) { + dividedSurface = new GaiaSurface(); + //dividedVertices = new ArrayList<>(); + + GaiaPrimitive dividedPrimitive = new GaiaPrimitive(); + dividedPrimitive.getSurfaces().add(dividedSurface); + dividedPrimitive.setMaterialIndex(primitive.getMaterialIndex()); + dividedPrimitive.setVertices(primitive.getVertices()); + + // only test + dividedPrimitives.add(dividedPrimitive); + } + + List dividedSurfaceFaces = dividedSurface.getFaces(); + dividedSurfaceFaces.add(face.clone()); + + /*int[] indices = face.getIndices(); + for (int index : indices) { + dividedVertices.add(vertices.get(index)); + }*/ + } + + for (GaiaPrimitive dividedPrimitive : dividedPrimitives) { + rearrangeVerticesWithIndices(dividedPrimitive); + } + return dividedPrimitives; + } + + /*private void rearrangeVerticesWithIndices(GaiaPrimitive primitive) { + GaiaSurface surface = primitive.getSurfaces().get(0); // only have one surface + List faces = surface.getFaces(); + List vertices = primitive.getVertices(); + + int minIndex = Integer.MAX_VALUE; + int maxIndex = 0; + for (GaiaFace face : faces) { + int[] indices = face.getIndices(); + for (int index : indices) { + if (index < minIndex) { + minIndex = index; + } + if (index > maxIndex) { + maxIndex = index; + } + } + } + System.out.println("minIndex : " + minIndex + ", maxIndex : " + maxIndex); + log.info("minIndex : {}, maxIndex : {}, offset : {}", minIndex, maxIndex, maxIndex - minIndex); + + //List subtractedVertices = vertices.subList(minIndex, maxIndex + 1); + List subtractedVertices = new ArrayList<>(); + for (int i = minIndex; i <= maxIndex; i++) { + subtractedVertices.add(vertices.get(i).clone()); + } + + for (GaiaFace face : faces) { + int[] indices = face.getIndices(); + for (int i = 0; i < indices.length; i++) { + indices[i] -= minIndex; + } + } + + primitive.setVertices(subtractedVertices); + }*/ + + private void rearrangeVerticesWithIndices(GaiaPrimitive primitive) { + GaiaSurface surface = primitive.getSurfaces().get(0); // only have one surface + List faces = surface.getFaces(); + List vertices = primitive.getVertices(); + + Map vertexHashMap = new HashMap<>(); + for (GaiaFace face : faces) { + int[] indices = face.getIndices(); + for (int index : indices) { + GaiaVertex vertex = vertices.get(index); + vertexHashMap.put(index, vertex.clone()); + } + } + List subtractedVertices = new ArrayList<>(vertexHashMap.values()); + + + for (GaiaFace face : faces) { + int[] indices = face.getIndices(); + for (int i = 0; i < indices.length; i++) { + indices[i] = subtractedVertices.indexOf(vertexHashMap.get(indices[i])); + } + } + + primitive.setVertices(subtractedVertices); + } +} diff --git a/tiler/src/main/java/com/gaia3d/process/preprocess/GaiaTexCoordCorrector.java b/tiler/src/main/java/com/gaia3d/process/preprocess/GaiaTexCoordCorrector.java index 2cbed02a..226d728d 100644 --- a/tiler/src/main/java/com/gaia3d/process/preprocess/GaiaTexCoordCorrector.java +++ b/tiler/src/main/java/com/gaia3d/process/preprocess/GaiaTexCoordCorrector.java @@ -45,12 +45,13 @@ public synchronized TileInfo run(TileInfo tileInfo) { List allMeshes = new ArrayList<>(); rootNode.extractMeshes(allMeshes); - for (GaiaMesh mesh : allMeshes) { + // TODO ToPositveQuadrant Option coming soon. + /*for (GaiaMesh mesh : allMeshes) { List allPrimitives = mesh.getPrimitives(); for (GaiaPrimitive primitive : allPrimitives) { translatePrimitiveTexCoordsToPositiveQuadrant(primitive); } - } + }*/ if (invertTexCoordsYAxis) { for (GaiaMesh mesh : allMeshes) { @@ -102,7 +103,6 @@ private void translatePrimitiveTexCoordsToPositiveQuadrant(GaiaPrimitive primiti translateSurfaceTexCoordsToPositiveQuadrant(surface, primitive.getVertices()); } } - } private void translateSurfaceTexCoordsToPositiveQuadrant(GaiaSurface surface, List vertices) { diff --git a/tiler/src/main/java/com/gaia3d/process/preprocess/GaiaTileInfoInitiator.java b/tiler/src/main/java/com/gaia3d/process/preprocess/GaiaTileInfoInitiator.java index c1f1bf65..1caa7995 100644 --- a/tiler/src/main/java/com/gaia3d/process/preprocess/GaiaTileInfoInitiator.java +++ b/tiler/src/main/java/com/gaia3d/process/preprocess/GaiaTileInfoInitiator.java @@ -6,8 +6,6 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import java.io.File; - @Slf4j @AllArgsConstructor public class GaiaTileInfoInitiator implements PreProcess { @@ -22,6 +20,7 @@ public TileInfo run(TileInfo tileInfo) { tileInfo.setBoundingBox(scene.getBoundingBox()); tileInfo.setScenePath(tileInfo.getScene().getOriginalPath()); tileInfo.setTempPath(tileInfo.getOutputPath().resolve("temp")); + tileInfo.setTriangleCount(tileInfo.getScene().calcTriangleCount()); return tileInfo; } } diff --git a/tiler/src/main/java/com/gaia3d/process/preprocess/GaiaTranslator.java b/tiler/src/main/java/com/gaia3d/process/preprocess/GaiaTranslator.java index 4e58dc9d..815b7699 100644 --- a/tiler/src/main/java/com/gaia3d/process/preprocess/GaiaTranslator.java +++ b/tiler/src/main/java/com/gaia3d/process/preprocess/GaiaTranslator.java @@ -28,7 +28,7 @@ public class GaiaTranslator implements PreProcess { @Override public TileInfo run(TileInfo tileInfo) { GlobalOptions globalOptions = GlobalOptions.getInstance(); - FormatType inputType = FormatType.fromExtension(globalOptions.getInputFormat()); + FormatType inputType = globalOptions.getInputFormat(); GaiaScene gaiaScene = tileInfo.getScene(); GaiaNode rootNode = gaiaScene.getNodes().get(0); @@ -45,8 +45,12 @@ public TileInfo run(TileInfo tileInfo) { DirectPosition2D memSave_posWorld = new DirectPosition2D(DefaultGeographicCRS.WGS84, center.x, center.y); double[] memSave_alt = new double[1]; memSave_alt[0] = 0; - coverage.evaluate((DirectPosition) memSave_posWorld, memSave_alt); - log.info("memSave_alt[0] : {}", memSave_alt[0]); + try { + coverage.evaluate((DirectPosition) memSave_posWorld, memSave_alt); + } catch (Exception e) { + log.warn("Failed to evaluate terrain height", e); + } + //log.info("memSave_alt[0] : {}", memSave_alt[0]); center.z = memSave_alt[0]; }); @@ -69,6 +73,7 @@ public TileInfo run(TileInfo tileInfo) { private Vector3d getTranslation(GaiaScene gaiaScene) { GaiaBoundingBox boundingBox = gaiaScene.getBoundingBox(); Vector3d center = boundingBox.getCenter(); + //Vector3d traslation = new Vector3d(center.x, center.y, 0.0d); Vector3d traslation = new Vector3d(center.x, center.y, 0.0d); traslation.negate(); return traslation; @@ -77,7 +82,7 @@ private Vector3d getTranslation(GaiaScene gaiaScene) { private Vector3d getPosition(FormatType formatType, GaiaScene gaiaScene) { GlobalOptions globalOptions = GlobalOptions.getInstance(); Vector3d position; - if (formatType == FormatType.CITY_GML || formatType == FormatType.SHP || formatType == FormatType.GEOJSON) { + if (formatType == FormatType.CITYGML|| formatType == FormatType.INDOORGML || formatType == FormatType.SHP || formatType == FormatType.GEOJSON) { GaiaNode rootNode = gaiaScene.getNodes().get(0); Matrix4d transform = rootNode.getTransformMatrix(); Vector3d center = new Vector3d(transform.get(3,0), transform.get(3,1), 0.0d); diff --git a/tiler/src/main/java/com/gaia3d/process/preprocess/GeometryOptimizer.java b/tiler/src/main/java/com/gaia3d/process/preprocess/GeometryOptimizer.java index 6c45654e..41219858 100644 --- a/tiler/src/main/java/com/gaia3d/process/preprocess/GeometryOptimizer.java +++ b/tiler/src/main/java/com/gaia3d/process/preprocess/GeometryOptimizer.java @@ -39,7 +39,7 @@ public void deleteFacesWithNormalInNode(GaiaNode gaiaNode, Vector3d normalRefere ArrayList meshesToRemove = new ArrayList<>(); for (GaiaMesh mesh : meshes) { boolean faceRemoved = false; - ArrayList primitives = mesh.getPrimitives(); + List primitives = mesh.getPrimitives(); ArrayList primitivesToRemove = new ArrayList<>(); for (GaiaPrimitive primitive : primitives) { List surfaces = primitive.getSurfaces(); diff --git a/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/Batched3DModelTiler.java b/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/Batched3DModelTiler.java index 09846b17..d0cdbd4e 100644 --- a/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/Batched3DModelTiler.java +++ b/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/Batched3DModelTiler.java @@ -83,8 +83,20 @@ private void createNode(Node parentNode, List tileInfos) throws IOExce BoundingVolume squareBoundingVolume = parentBoundingVolume.createSqureBoundingVolume(); boolean refineAdd = globalOptions.isRefineAdd(); - int nodeLimit = globalOptions.getNodeLimit() * 4; // for quadtree node limit - if (tileInfos.size() > nodeLimit) { + //int nodeLimit = globalOptions.getNodeLimit() * 4; // for quadtree node limit + + long triangleLimit = 65536 * 4; + long totalTriangleCount = tileInfos.stream().mapToLong(TileInfo::getTriangleCount).sum(); + log.info("[TriangleCount] Total : {}", totalTriangleCount); + + if (tileInfos.size() <= 1) { + Node childNode = createContentNode(parentNode, tileInfos, 0); + if (childNode != null) { + parentNode.getChildren().add(childNode); + createNode(childNode, tileInfos); + } + } else if (totalTriangleCount > triangleLimit) { + //if (tileInfos.size() > nodeLimit) { // logical node distribute List> childrenScenes = squareBoundingVolume.distributeScene(tileInfos); for (int index = 0; index < childrenScenes.size(); index++) { @@ -95,7 +107,7 @@ private void createNode(Node parentNode, List tileInfos) throws IOExce createNode(childNode, childTileInfos); } } - } else if (tileInfos.size() > 1) { + } else if (totalTriangleCount > 1) { // phiysical node distribute List> childrenScenes = squareBoundingVolume.distributeScene(tileInfos); for (int index = 0; index < childrenScenes.size(); index++) { @@ -113,7 +125,7 @@ private void createNode(Node parentNode, List tileInfos) throws IOExce } } } - } else if (!tileInfos.isEmpty()) { + } else if (tileInfos.size() <= 4 || !tileInfos.isEmpty()) { Node childNode = createContentNode(parentNode, tileInfos, 0); if (childNode != null) { parentNode.getChildren().add(childNode); diff --git a/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/Instanced3DModelTiler.java b/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/Instanced3DModelTiler.java index cee495ad..84d59168 100644 --- a/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/Instanced3DModelTiler.java +++ b/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/Instanced3DModelTiler.java @@ -81,8 +81,14 @@ private void createNode(Node parentNode, List tileInfos) throws IOExce BoundingVolume parentBoundingVolume = parentNode.getBoundingVolume(); BoundingVolume squareBoundingVolume = parentBoundingVolume.createSqureBoundingVolume(); - int nodeLimit = globalOptions.getNodeLimit() * 4; - if (tileInfos.size() > nodeLimit) { + //int nodeLimit = globalOptions.getNodeLimit() * 4; + + long triangleLimit = 65536 * 8; + long totalTriangleCount = tileInfos.stream().mapToLong(TileInfo::getTriangleCount).sum(); + log.info("[TriangleCount] Total : {}", totalTriangleCount); + + if (totalTriangleCount > triangleLimit) { + //if (tileInfos.size() > nodeLimit) { List> childrenScenes = squareBoundingVolume.distributeScene(tileInfos); for (int index = 0; index < childrenScenes.size(); index++) { List childTileInfos = childrenScenes.get(index); @@ -92,7 +98,8 @@ private void createNode(Node parentNode, List tileInfos) throws IOExce createNode(childNode, childTileInfos); } } - } else if (tileInfos.size() > 1) { + } else if (totalTriangleCount > 1) { + //} else if (tileInfos.size() > 1) { List> childrenScenes = squareBoundingVolume.distributeScene(tileInfos); for (int index = 0; index < childrenScenes.size(); index++) { List childTileInfos = childrenScenes.get(index); diff --git a/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/PointCloudTiler.java b/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/PointCloudTiler.java index 51033ae2..07320194 100644 --- a/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/PointCloudTiler.java +++ b/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/PointCloudTiler.java @@ -141,7 +141,9 @@ private void createNode(Node parentNode, GaiaPointCloud pointCloud, double geome int vertexLength = pointCloud.getVertices().size(); int pointLimit = globalOptions.getPointLimit(); - List divided = pointCloud.divideChunkSize(pointLimit); + int pointScale = globalOptions.getPointScale(); + + List divided = pointCloud.divideChunkSize(pointLimit * pointScale); GaiaPointCloud selfPointCloud = divided.get(0); GaiaPointCloud remainPointCloud = divided.get(1); @@ -151,6 +153,7 @@ private void createNode(Node parentNode, GaiaPointCloud pointCloud, double geome BoundingVolume boundingVolume = new BoundingVolume(childBoundingBox); double geometricErrorCalc = calcGeometricError(selfPointCloud); + double calculatedGeometricError = geometricErrorCalc / pointScale / 12; Node childNode = new Node(); childNode.setParent(parentNode); @@ -160,8 +163,8 @@ private void createNode(Node parentNode, GaiaPointCloud pointCloud, double geome childNode.setRefine(Node.RefineType.ADD); childNode.setChildren(new ArrayList<>()); childNode.setNodeCode(parentNode.getNodeCode() + pointCloud.getCode()); - childNode.setGeometricError(geometricErrorCalc/2); - childNode.setGeometricError(geometricError); + + childNode.setGeometricError(calculatedGeometricError); TileInfo selfTileInfo = TileInfo.builder() .pointCloud(selfPointCloud) @@ -171,7 +174,7 @@ private void createNode(Node parentNode, GaiaPointCloud pointCloud, double geome tileInfos.add(selfTileInfo); ContentInfo contentInfo = new ContentInfo(); - contentInfo.setName("gaiaPointcloud"); + contentInfo.setName("points-cloud"); contentInfo.setLod(LevelOfDetail.LOD0); contentInfo.setBoundingBox(childBoundingBox); contentInfo.setNodeCode(childNode.getNodeCode()); @@ -186,10 +189,10 @@ private void createNode(Node parentNode, GaiaPointCloud pointCloud, double geome log.info("[Tiling][ContentNode][{}]",childNode.getNodeCode()); if (vertexLength > 0) { // vertexLength > DEFUALT_MAX_COUNT - List distributes = remainPointCloud.distributeOct(); + List distributes = remainPointCloud.distribute(); distributes.forEach(distribute -> { if (!distribute.getVertices().isEmpty()) { - createNode(childNode, distribute, geometricError/2); + createNode(childNode, distribute, geometricError / 2); } }); } diff --git a/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/TileInfo.java b/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/TileInfo.java index d844cafb..37d2d29b 100644 --- a/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/TileInfo.java +++ b/tiler/src/main/java/com/gaia3d/process/tileprocess/tile/TileInfo.java @@ -23,6 +23,8 @@ @Builder @Slf4j public class TileInfo { + + @Builder.Default private int serial = -1; private GaiaScene scene; @@ -37,6 +39,9 @@ public class TileInfo { private Path outputPath; private Path tempPath; + @Builder.Default + private long triangleCount = 0; + @Builder.Default private boolean isI3dm = false; private void init() { @@ -58,7 +63,7 @@ private void init() { * Write the scene file to the output directory. * @param serial */ - public void minimize(int serial) { + /*public void minimize(int serial) { GlobalOptions options = GlobalOptions.getInstance(); if (this.scene != null && !this.scene.getNodes().isEmpty()) { @@ -69,7 +74,7 @@ public void minimize(int serial) { this.scene.clear(); this.scene = null; } - } + }*/ /** * Load the minimized scene file and create a GaiaSet object. diff --git a/tiler/src/test/java/com/gaia3d/command/mago/Mago3DTilerFormatTest.java b/tiler/src/test/java/com/gaia3d/command/mago/Mago3DTilerFormatTest.java new file mode 100644 index 00000000..2dc9bcc1 --- /dev/null +++ b/tiler/src/test/java/com/gaia3d/command/mago/Mago3DTilerFormatTest.java @@ -0,0 +1,171 @@ +package com.gaia3d.command.mago; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.joml.Random; +import org.joml.Vector3d; +import org.junit.jupiter.api.Test; + +import java.io.File; + +@Slf4j +class Mago3DTilerFormatTest { + + private static final String INPUT_PATH = "D:\\DT\\"; + private static final String OUTPUT_PATH = "C:\\Workspaces\\GitSources\\mago-viewer\\data\\tilesets\\DT\\"; + + @Test + void THREEDS() { + String path = "3DS"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-output", output.getAbsolutePath(), + "-inputType", "3ds", + "-crs", "5186", + }; + Mago3DTilerMain.main(args); + } + + @Test + void DAE() { + String path = "DAE"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-output", output.getAbsolutePath(), + "-inputType", "dae", + "-crs", "5186", + }; + Mago3DTilerMain.main(args); + } + + @Test + void FBX() { + String path = "FBX"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-output", output.getAbsolutePath(), + "-inputType", "fbx", + "-crs", "5186", + }; + Mago3DTilerMain.main(args); + } + + @Test + void CityGML() { + String path = "CityGML"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-output", output.getAbsolutePath(), + "-inputType", "gml", + "-crs", "5186", + }; + Mago3DTilerMain.main(args); + } + + @Test + void GLTF() { + String path = "GLTF"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-output", output.getAbsolutePath(), + "-inputType", "gltf", + "-crs", "5186", + }; + Mago3DTilerMain.main(args); + } + + @Test + void IFC() { + String path = "IFC"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-output", output.getAbsolutePath(), + "-inputType", "ifc", + "-crs", "5186", + }; + Mago3DTilerMain.main(args); + } + + @Test + void OBJ() { + String path = "OBJ"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-output", output.getAbsolutePath(), + "-inputType", "obj", + "-crs", "5186", + }; + Mago3DTilerMain.main(args); + } + + @Test + void CITYGML() { + String path = "CITYGML"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-output", output.getAbsolutePath(), + "-inputType", "citygml", + "-crs", "5186", + "-debug" + }; + Mago3DTilerMain.main(args); + } + + @Test + void LAS() { + String path = "LAS"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-output", output.getAbsolutePath(), + "-inputType", "las", + "-pointSkip", "256", + "-crs", "5186", + }; + Mago3DTilerMain.main(args); + } + + @Test + void INDOORGML() { + String path = "INDOORGML"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-output", output.getAbsolutePath(), + "-inputType", "indoorgml", + "-crs", "5186", + "-glb", + "-debug" + }; + Mago3DTilerMain.main(args); + } + +} \ No newline at end of file diff --git a/tiler/src/test/java/com/gaia3d/command/mago/Mago3DTilerUnitTest.java b/tiler/src/test/java/com/gaia3d/command/mago/Mago3DTilerUnitTest.java index 7f1c4856..f9013d95 100644 --- a/tiler/src/test/java/com/gaia3d/command/mago/Mago3DTilerUnitTest.java +++ b/tiler/src/test/java/com/gaia3d/command/mago/Mago3DTilerUnitTest.java @@ -18,11 +18,11 @@ class Mago3DTilerUnitTest { @Test void case00() { - String path = "case00-3ds-gy"; + String path = "case00-3ds-icgy"; File input = new File(INPUT_PATH, path); File output = new File(OUTPUT_PATH, path); FileUtils.deleteQuietly(output); - String args[] = { + String[] args = { "-input", input.getAbsolutePath(), "-inputType", "3ds", "-crs", "5186", @@ -34,17 +34,16 @@ void case00() { @Test void case01() { - String path = "case01-3ds-ws2"; + String path = "case01-3ds-nyjws2"; File input = new File(INPUT_PATH, path); File output = new File(OUTPUT_PATH, path); FileUtils.deleteQuietly(output); - String args[] = { + String[] args = { "-input", input.getAbsolutePath(), "-inputType", "3ds", "-crs", "5186", - "-ignoreTextures", - //"-autoUpAxis", "-output", output.getAbsolutePath(), + "-glb", }; Mago3DTilerMain.main(args); } @@ -55,10 +54,9 @@ void case02() { File input = new File(INPUT_PATH, path); File output = new File(OUTPUT_PATH, path); FileUtils.deleteQuietly(output); - String args[] = { + String[] args = { "-input", input.getAbsolutePath(), "-output", output.getAbsolutePath(), - //"-autoUpAxis", // is not supported }; Mago3DTilerMain.main(args); } @@ -69,12 +67,10 @@ void case03() { File input = new File(INPUT_PATH, path); File output = new File(OUTPUT_PATH, path); FileUtils.deleteQuietly(output); - String args[] = { + String[] args = { "-input", input.getAbsolutePath(), "-inputType", "shp", "-output", output.getAbsolutePath(), - "-autoUpAxis", - "-refineAdd", "-proj", "+proj=tmerc +lat_0=38 +lon_0=127.0028902777778 +k=1 +x_0=200000 +y_0=500000 +ellps=bessel +units=m +no_defs +towgs84=-115.80,474.99,674.11,1.16,-2.31,-1.63,6.43", }; Mago3DTilerMain.main(args); @@ -86,7 +82,7 @@ void case04() { File input = new File(INPUT_PATH, path); File output = new File(OUTPUT_PATH, path); FileUtils.deleteQuietly(output); - String args[] = { + String[] args = { "-input", input.getAbsolutePath(), "-inputType", "las", "-autoUpAxis", @@ -102,12 +98,11 @@ void case05() { File input = new File(INPUT_PATH, path); File output = new File(OUTPUT_PATH, path); FileUtils.deleteQuietly(output); - String args[] = { + String[] args = { "-input", input.getAbsolutePath(), "-output", output.getAbsolutePath(), "-outputType", "i3dm", "-autoUpAxis", - //"-debug" }; Mago3DTilerMain.main(args); } @@ -115,11 +110,11 @@ void case05() { @Test void case06() { String path = "case06-kml-auto-instance"; - sampleI3dm(path, 20 , 100); + sampleI3dm(path, 100, 100); File input = new File(INPUT_PATH, path); File output = new File(OUTPUT_PATH, path); FileUtils.deleteQuietly(output); - String args[] = { + String[] args = { "-input", input.getAbsolutePath(), "-output", output.getAbsolutePath(), "-outputType", "i3dm", @@ -128,23 +123,6 @@ void case06() { Mago3DTilerMain.main(args); } - //@Test - void case06B3dm() { - String path = "case06-kml-auto-batched"; - //String path = "case06-kml-auto-instance"; - //sampleI3dm(path, 20 , 100); - File input = new File(INPUT_PATH, path); - File output = new File(OUTPUT_PATH, path); - FileUtils.deleteQuietly(output); - String args[] = { - "-input", input.getAbsolutePath(), - "-output", output.getAbsolutePath(), - //"-autoUpAxis", - "-debug" - }; - Mago3DTilerMain.main(args); - } - //@Test void case07() { String path = "case07-shp-seoul"; @@ -152,15 +130,15 @@ void case07() { File output = new File(OUTPUT_PATH, path); File terrain = new File(input, "terrain.tif"); //FileUtils.deleteQuietly(output); - String args[] = { + String[] args = { "-input", input.getAbsolutePath(), "-output", output.getAbsolutePath(), "-terrain", terrain.getAbsolutePath(), "-inputType", "shp", "-crs", "5181", - "-maxCount", "32768", - "-autoUpAxis", - "-refineAdd", + //"-autoUpAxis", + //"-refineAdd", + "-debug" }; Mago3DTilerMain.main(args); } @@ -169,12 +147,14 @@ void case07() { void case08() { String path = "case08-geojson-ws2"; File input = new File(INPUT_PATH, path); + File terrain = new File(input, "terrain.tif"); File output = new File(OUTPUT_PATH, path); FileUtils.deleteQuietly(output); - String args[] = { + String[] args = { "-input", input.getAbsolutePath(), "-output", output.getAbsolutePath(), "-inputType", "geojson", + "-terrain", terrain.getAbsolutePath(), "-crs", "5186", "-autoUpAxis", "-refineAdd", @@ -188,7 +168,7 @@ void case09() { File input = new File(INPUT_PATH, path); File output = new File(OUTPUT_PATH, path); FileUtils.deleteQuietly(output); - String args[] = { + String[] args = { "-input", input.getAbsolutePath(), "-output", output.getAbsolutePath(), "-inputType", "kml", @@ -204,7 +184,7 @@ void case10() { File input = new File(INPUT_PATH, path); File output = new File(OUTPUT_PATH, path); FileUtils.deleteQuietly(output); - String args[] = { + String[] args = { "-input", input.getAbsolutePath(), "-output", output.getAbsolutePath(), "-inputType", "laz", @@ -213,28 +193,31 @@ void case10() { Mago3DTilerMain.main(args); } - //@Test - void case011() { + @Test + void case11() { String path = "case11-shp-jeju"; File input = new File(INPUT_PATH, path); File output = new File(OUTPUT_PATH, path); - FileUtils.deleteQuietly(output); - String args[] = { + //FileUtils.deleteQuietly(output); + String[] args = { "-input", input.getAbsolutePath(), "-output", output.getAbsolutePath(), - "-inputType", "shp", + "-inputType", "json", + "-c", "4326", + "-hc", "B_Height_m", "-refineAdd", + "-debug" }; Mago3DTilerMain.main(args); } //@Test - void case012() { + void case12() { String path = "case12-kml-seoul"; File input = new File(INPUT_PATH, path); File output = new File(OUTPUT_PATH, path); //FileUtils.deleteQuietly(output); - String args[] = { + String[] args = { "-input", input.getAbsolutePath(), "-output", output.getAbsolutePath(), "-refineAdd", @@ -243,13 +226,13 @@ void case012() { } //@Test - void case013() { + void case13() { String path = "case13-kml-seoul-forest-instance"; File input = new File(INPUT_PATH, path); File terrain = new File(input, "terrain.tif"); File output = new File(OUTPUT_PATH, path); FileUtils.deleteQuietly(output); - String args[] = { + String[] args = { "-input", input.getAbsolutePath(), "-inputType", "shp", "-output", output.getAbsolutePath(), @@ -263,13 +246,13 @@ void case013() { } //@Test - void case014() { + void case14() { String path = "case14-3ds-seoul"; File input = new File(INPUT_PATH, path); File terrain = new File(input, "terrain.tif"); File output = new File(OUTPUT_PATH, path); //FileUtils.deleteQuietly(output); - String args[] = { + String[] args = { "-input", input.getAbsolutePath(), "-inputType", "3ds", "-output", output.getAbsolutePath(), @@ -282,10 +265,297 @@ void case014() { Mago3DTilerMain.main(args); } + @Test + void case15() { + String path = "case15-3ds-nyjws1"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "3ds", + "-crs", "5186", + "-output", output.getAbsolutePath(), + "-r" + }; + Mago3DTilerMain.main(args); + } + + @Test + void case16() { + String path = "case16-3ds-hngs"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "3ds", + "-crs", "5186", + "-output", output.getAbsolutePath(), + "-r" + }; + Mago3DTilerMain.main(args); + } + + @Test + void case17() { + String path = "case17-3ds-gggy"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "3ds", + "-crs", "5186", + "-output", output.getAbsolutePath(), + "-r" + }; + Mago3DTilerMain.main(args); + } + + @Test + void case18() { + String path = "case18-3ds-gcgc"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "3ds", + "-crs", "5186", + "-output", output.getAbsolutePath(), + "-r" + }; + Mago3DTilerMain.main(args); + } + + @Test + void case19() { + String path = "case19-3ds-bcdj"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "3ds", + "-crs", "5186", + "-output", output.getAbsolutePath(), + /*"-ignoreTextures", + "-refineAdd",*/ + "-r" + }; + Mago3DTilerMain.main(args); + } + + @Test + void case20() { + String path = "case20-3ds-complicated"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "kml", + "-output", output.getAbsolutePath(), + "-r" + }; + Mago3DTilerMain.main(args); + } + + //@Test + void case21() { + String path = "case21-las-sangjiuni"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "las", + "-autoUpAxis", + "-output", output.getAbsolutePath(), + "-r", + "-crs", "5186" + //"-proj", "+proj=utm +zone=52 +datum=WGS84 +units=m +no_defs", + }; + Mago3DTilerMain.main(args); + } + + @Test + void case22() { + String path = "case22-las-sewoon"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "las", + "-autoUpAxis", + "-output", output.getAbsolutePath(), + "-r", + //"-proj", "+proj=utm +zone=52 +datum=WGS84 +units=m +no_defs", + "-crs", "5186", + }; + Mago3DTilerMain.main(args); + } + + @Test + void case23() { + String path = "case23-las-sangam"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "las", + "-autoUpAxis", + "-output", output.getAbsolutePath(), + "-r", + "-proj", "+proj=utm +zone=52 +datum=WGS84 +units=m +no_defs", + }; + Mago3DTilerMain.main(args); + } + + @Test + void case24() { + String path = "case24-las-ndtp"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "las", + "-autoUpAxis", + "-output", output.getAbsolutePath(), + "-r", + "-proj", "+proj=utm +zone=52 +datum=WGS84 +units=m +no_defs", + }; + Mago3DTilerMain.main(args); + } + + @Test + void case25() { + String path = "case25-las-sinchon"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "las", + "-autoUpAxis", + "-output", output.getAbsolutePath(), + "-r", + "-proj", "+proj=utm +zone=52 +datum=WGS84 +units=m +no_defs", + }; + Mago3DTilerMain.main(args); + } + + //@Test + void case26() { + String path = "case26-collada-sangjiuni"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "dae", + //"-autoUpAxis", + "-refineAdd", + "-output", output.getAbsolutePath(), + "-r", + "-crs", "5186", + "-glb", + //"-proj", "+proj=utm +zone=52 +datum=WGS84 +units=m +no_defs", + //+proj=tmerc +lat_0=38 +lon_0=127 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs + "-proj", "+proj=tmerc +lat_0=38 +lon_0=127 +k=1 +x_0=-82345 +y_0=-69494 +ellps=GRS80 +units=m +no_defs", + + + /* + + + + EPSG:5186 + + 282345,530506,0 + + */ + }; + Mago3DTilerMain.main(args); + } + + @Test + void case27() { + String path = "case27-las-gis"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "las", + "-autoUpAxis", + "-output", output.getAbsolutePath(), + "-r", + "-crs", "5186" + }; + Mago3DTilerMain.main(args); + } + + @Test + void case28() { + String path = "case28-las-gis"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "las", + "-autoUpAxis", + "-output", output.getAbsolutePath(), + "-r", + "-crs", "5186" + }; + Mago3DTilerMain.main(args); + } + + @Test + void case29() { + String path = "case29-indoor-gml"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "indoorgml", + //"-autoUpAxis", + "-output", output.getAbsolutePath(), + "-crs", "5186", + "-glb", + "-debug" + }; + Mago3DTilerMain.main(args); + } + + @Test + void case30() { + String path = "case30-big-points"; + File input = new File(INPUT_PATH, path); + File output = new File(OUTPUT_PATH, path); + //FileUtils.deleteQuietly(output); + String[] args = { + "-input", input.getAbsolutePath(), + "-inputType", "las", + "-autoUpAxis", + "-pointSkip", "256", + "-output", output.getAbsolutePath(), + "-r", + "-crs", "5186" + }; + Mago3DTilerMain.main(args); + } + //@Test void sampleI3dm(String filePath, int length, int fileCount) { Vector3d min = new Vector3d(128.4602 , 37.7214, 0.0); - Vector3d max = new Vector3d(128.4802, 37.7414, 0.0); + Vector3d max = new Vector3d(128.5602, 37.8214, 0.0); File output = new File(INPUT_PATH, filePath); if (output.mkdirs()) { @@ -361,15 +631,4 @@ void sampleI3dm(String filePath, int length, int fileCount) { } } } - - /*@Test - void createTemp() throws IOException { - int fileCount = 2000000; - for (int count = 0; count < fileCount; count++) { - File tempFile = new File("C:\\Workspaces\\GitSources\\mago\\mago-3d-tiler\\viewer\\mago-3d-tiler-data\\case07-shp-seoul\\temp", "building_" + count + ".mgb"); - // write temp file - tempFile.createNewFile(); - } - - }*/ } \ No newline at end of file diff --git a/tiler/src/test/java/com/gaia3d/converter/geometry/InnerRingRemoverTest.java b/tiler/src/test/java/com/gaia3d/converter/geometry/InnerRingRemoverTest.java new file mode 100644 index 00000000..8df04504 --- /dev/null +++ b/tiler/src/test/java/com/gaia3d/converter/geometry/InnerRingRemoverTest.java @@ -0,0 +1,122 @@ +package com.gaia3d.converter.geometry; + +import org.joml.Vector2d; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + + +class InnerRingRemoverTest { + + @Test + void crossSame() { + Vector2d a = new Vector2d(73.7843483535828,18.62558007045379); + Vector2d b = new Vector2d(73.7843483535828,18.62558007045379); + Vector2d c = new Vector2d(73.78440228477372, 18.6259135389622); + + InnerRingRemover innerRingRemover = new InnerRingRemover(); + double result = innerRingRemover.cross(a, b, c); + System.out.println(result); + // 2.3946318491139483E-11 + //0.000000000023946318491139483 + + } + + @Test + void crossSame2() { + Vector2d a = new Vector2d(73.7843483535828,18.62558007045379); + Vector2d b = new Vector2d(73.78440228477372, 18.6259135389622); + Vector2d c = new Vector2d(73.7843483535828,18.62558007045379); + + InnerRingRemover innerRingRemover = new InnerRingRemover(); + double result = innerRingRemover.cross(a, b, c); + System.out.println(result); + // 2.3946318491139483E-11 + //0.000000000023946318491139483 + + } + + @Test + void crossSame3() { + Vector2d a = new Vector2d(73.7843483535828,18.62558007045379); + Vector2d b = new Vector2d(73.7843483535828,18.62558007045379); + Vector2d c = new Vector2d(73.7843483535828,18.62558007045379); + + InnerRingRemover innerRingRemover = new InnerRingRemover(); + double result = innerRingRemover.cross(a, b, c); + System.out.println(result); + // 2.3946318491139483E-11 + //0.000000000023946318491139483 + + } + + @Test + void cross() { + Vector2d a = new Vector2d(73.7843483535828,18.62558007045379); + Vector2d b = new Vector2d(73.78435220006716,18.62560341010312); + Vector2d c = new Vector2d(73.78440228477372, 18.6259135389622); + + InnerRingRemover innerRingRemover = new InnerRingRemover(); + double result = innerRingRemover.cross(a, b, c); + System.out.println(result); + // 2.3946318491139483E-11 + //0.000000000023946318491139483 + + } + + @Test + void cross2() { +// 73.78440228477372 18.6259135389622 +// 73.78441732294155 18.626005734391615 +// 73.78467447041096 18.62596769054658 + + + + Vector2d a = new Vector2d(73.78291833506383, 18.62630056325255); + Vector2d b = new Vector2d(73.78291229, 18.626253743514127); + Vector2d c = new Vector2d(73.78290929128853, 18.62623056109944); + +// 73.78291833506383 18.62630056325255 +// +// 73.78291229 18.626253743514127 +// +// 73.78290929128853 18.62623056109944 +// +// 73.78290613806246 18.626206152324304 + + //-2.3534644180789321E-4 + //-0.00023534644180789321 + +// +// +// Vector2d a = new Vector2d(73.78291229, 18.6259135389622); +// Vector2d b = new Vector2d(73.78441732294155, 18.626005734391615); +// Vector2d c = new Vector2d(73.78467447041096, 18.62596769054658); + + InnerRingRemover innerRingRemover = new InnerRingRemover(); + double result = innerRingRemover.cross(a, b, c); + System.out.println(result); + + // -2.4279931091618658E-8 + // -0.000000024279931091618658 + + // 2.3946318491139483E-11 + //0.000000000023946318491139483 + + } + + @Test + void isIntersect() { + Vector2d a = new Vector2d(0, 0); + Vector2d b = new Vector2d(1, 1); + + Vector2d c = new Vector2d(0, 1); + Vector2d d = new Vector2d(1, 0); + + + InnerRingRemover innerRingRemover = new InnerRingRemover(); + //boolean result = innerRingRemover.isIntersect(a, b, c, d); + + //System.out.println(result); + } +} \ No newline at end of file diff --git a/tiler/src/test/java/com/gaia3d/converter/pointcloud/LasConverterTest.java b/tiler/src/test/java/com/gaia3d/converter/pointcloud/LasConverterTest.java new file mode 100644 index 00000000..e93471ed --- /dev/null +++ b/tiler/src/test/java/com/gaia3d/converter/pointcloud/LasConverterTest.java @@ -0,0 +1,42 @@ +package com.gaia3d.converter.pointcloud; + +import com.github.mreutegg.laszip4j.CloseablePointIterable; +import com.github.mreutegg.laszip4j.LASHeader; +import com.github.mreutegg.laszip4j.LASReader; +import org.apache.commons.io.FileUtils; +import org.joml.Vector3d; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.*; + +class LasConverterTest { + + @Test + void case00() { + String path = "D:\\Mago3DTiler-UnitTest\\input\\case21-las-sangjiuni"; + File input = new File(path); + List inputFiles = (ArrayList) FileUtils.listFiles(input, new String[]{"las"}, true); + + List pointsList = new ArrayList<>(); + inputFiles.forEach((inputFile) -> { + LASReader reader = new LASReader(inputFile); + LASHeader header = reader.getHeader(); + CloseablePointIterable pointIterable = reader.getCloseablePoints(); + + pointIterable.forEach((point) -> { + double x = point.getX(); + double y = point.getY(); + double z = point.getZ(); + Vector3d vector3d = new Vector3d(x, y, z); + pointsList.add(vector3d); + }); + System.out.println("pointIterable = " + pointIterable); + }); + } + +} \ No newline at end of file