├── Gltf ├── .classpath ├── .gitignore ├── .project ├── .settings │ ├── org.eclipse.core.resources.prefs │ ├── org.eclipse.jdt.core.prefs │ └── org.eclipse.m2e.core.prefs ├── license.txt ├── plugin │ ├── icon.png │ ├── plugin.xml │ └── version.properties ├── pom.xml ├── shaders │ ├── fragmentcolor.shader │ ├── fragmentmaterial.shader │ ├── vertexcolor.shader │ └── vertexmaterial.shader └── src │ └── org │ └── bimserver │ └── gltf │ ├── BinaryGltfBaseSerializer.java │ ├── BinaryGltfSerializer.java │ ├── BinaryGltfSerializer2.java │ ├── BinaryGltfSerializerPlugin.java │ └── BinaryGltfSerializerPlugin2.java └── README.md /Gltf/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Gltf/.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /target/ 3 | -------------------------------------------------------------------------------- /Gltf/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | Gltf 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.m2e.core.maven2Nature 21 | org.eclipse.jdt.core.javanature 22 | 23 | 24 | -------------------------------------------------------------------------------- /Gltf/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding/=UTF-8 3 | encoding/plugin=UTF-8 4 | encoding/shaders=UTF-8 5 | encoding/src=UTF-8 6 | -------------------------------------------------------------------------------- /Gltf/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.8 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled 11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 12 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 13 | org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore 14 | org.eclipse.jdt.core.compiler.release=disabled 15 | org.eclipse.jdt.core.compiler.source=1.8 16 | -------------------------------------------------------------------------------- /Gltf/.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /Gltf/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2009-2019 BIMserver.org 2 | 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU Affero General Public License as 5 | published by the Free Software Foundation, either version 3 of the 6 | License, or (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU Affero General Public License for more details. 12 | 13 | You should have received a copy of the GNU Affero General Public License 14 | along with this program. If not, see {@literal}. -------------------------------------------------------------------------------- /Gltf/plugin/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensourceBIM/GltfSerializers/32cd52cfd564100c30be39f3877eb8ff3a18a941/Gltf/plugin/icon.png -------------------------------------------------------------------------------- /Gltf/plugin/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | org.bimserver.plugins.serializers.SerializerPlugin 5 | org.bimserver.gltf.BinaryGltfSerializerPlugin 6 | Serializer to binary glTF 7 | Binary glTF Serializer 8 | 9 | 10 | org.bimserver.plugins.serializers.SerializerPlugin 11 | org.bimserver.gltf.BinaryGltfSerializerPlugin2 12 | Serializer to binary glTF 2 13 | Binary glTF Serializer 2 14 | 15 | -------------------------------------------------------------------------------- /Gltf/plugin/version.properties: -------------------------------------------------------------------------------- 1 | build.date=${timestamp} -------------------------------------------------------------------------------- /Gltf/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.opensourcebim 5 | gltf 6 | 0.0.65-SNAPSHOT 7 | Gltf 8 | BIMserver plugin that provides a binary glTf serializer 9 | https://github.com/opensourceBIM/GltfSerializers 10 | 11 | OpenSource BIM 12 | opensourcebim.org 13 | 14 | 15 | 16 | GNU Affero General Public License 17 | http://www.gnu.org/licenses/agpl-3.0.en.html 18 | repo 19 | 20 | 21 | 22 | 23 | Ruben de Laat 24 | ruben@logic-labs.nl 25 | 26 | 27 | 28 | scm:git:https://github.com/opensourceBIM/GltfSerializers.git 29 | scm:git:https://github.com/opensourceBIM/GltfSerializers.git 30 | https://github.com/opensourceBIM/GltfSerializers.git 31 | HEAD 32 | 33 | 34 | GitHub 35 | https://github.com/opensourceBIM/GltfSerializers/issues 36 | 37 | 38 | 39 | ossrh 40 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 41 | 42 | 43 | ossrh 44 | https://oss.sonatype.org/content/repositories/snapshots 45 | 46 | 47 | 48 | yyyy-MM-dd'T'HH:mm:ssZ 49 | UTF-8 50 | ${maven.build.timestamp} 51 | 52 | 53 | 54 | org.opensourcebim 55 | pluginbase 56 | 1.5.188-SNAPSHOT 57 | 58 | 59 | 60 | 61 | central 62 | https://repo1.maven.org/maven2 63 | 64 | 65 | 66 | src 67 | 68 | 69 | plugin 70 | true 71 | plugin 72 | 73 | 74 | shaders 75 | shaders 76 | 77 | 78 | 79 | 80 | org.codehaus.mojo 81 | build-helper-maven-plugin 82 | 3.6.0 83 | 84 | 85 | attach-plugin 86 | package 87 | 88 | attach-artifact 89 | 90 | 91 | 92 | 93 | plugin/plugin.xml 94 | xml 95 | plugin 96 | 97 | 98 | plugin/icon.png 99 | png 100 | icon 101 | 102 | 103 | ${project.build.outputDirectory}/plugin/version.properties 104 | properties 105 | version 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | maven-compiler-plugin 114 | 3.10.1 115 | 116 | 8 117 | 118 | 119 | 120 | org.sonatype.plugins 121 | nexus-staging-maven-plugin 122 | 1.6.3 123 | true 124 | 125 | ossrh 126 | https://oss.sonatype.org/ 127 | false 128 | 60 129 | 130 | 131 | 132 | maven-release-plugin 133 | 2.5.3 134 | 135 | false 136 | release 137 | deploy 138 | 139 | 140 | 141 | 142 | 143 | 144 | release 145 | 146 | 147 | 148 | maven-gpg-plugin 149 | 1.5 150 | 151 | 152 | sign-artifacts 153 | verify 154 | 155 | sign 156 | 157 | 158 | 159 | --pinentry-mode 160 | loopback 161 | 162 | ${gpg.keyname} 163 | 164 | 165 | 166 | 167 | 168 | maven-javadoc-plugin 169 | 3.11.2 170 | 171 | 172 | attach-javadocs 173 | 174 | jar 175 | 176 | 177 | none 178 | 179 | 180 | 181 | 182 | 183 | maven-source-plugin 184 | 2.4 185 | 186 | 187 | attach-javadocs 188 | 189 | jar 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /Gltf/shaders/fragmentcolor.shader: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | varying vec3 v_normal; 4 | varying vec4 v_color; 5 | uniform vec4 u_diffuse; 6 | uniform vec4 u_specular; 7 | uniform float u_shininess; 8 | 9 | void main(void) { 10 | vec3 normal = normalize(v_normal); 11 | gl_FragColor = v_color; 12 | } -------------------------------------------------------------------------------- /Gltf/shaders/fragmentmaterial.shader: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | varying vec3 v_normal; 4 | uniform vec4 u_diffuse; 5 | uniform vec4 u_specular; 6 | uniform float u_shininess; 7 | 8 | void main(void) { 9 | vec3 normal = normalize(v_normal); 10 | vec4 color = vec4(0., 0., 0., 1.); 11 | vec4 diffuse = vec4(0., 0., 0., 1.); 12 | vec4 specular; 13 | diffuse = u_diffuse; 14 | specular = u_specular; 15 | diffuse.xyz *= max(dot(normal,vec3(0.,0.,1.)), 0.); 16 | color.xyz += diffuse.xyz; 17 | color = vec4(color.rgb * diffuse.a, diffuse.a); 18 | gl_FragColor = color; 19 | } -------------------------------------------------------------------------------- /Gltf/shaders/vertexcolor.shader: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | attribute vec3 a_position; 4 | attribute vec3 a_normal; 5 | attribute vec4 a_color; 6 | varying vec3 v_normal; 7 | varying vec4 v_color; 8 | uniform mat3 u_normalMatrix; 9 | uniform mat4 u_modelViewMatrix; 10 | uniform mat4 u_projectionMatrix; 11 | 12 | void main(void) { 13 | vec4 pos = u_modelViewMatrix * vec4(a_position,1.0); 14 | v_normal = u_normalMatrix * a_normal; 15 | v_color = a_color; 16 | gl_Position = u_projectionMatrix * pos; 17 | } -------------------------------------------------------------------------------- /Gltf/shaders/vertexmaterial.shader: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | attribute vec3 a_position; 4 | attribute vec3 a_normal; 5 | varying vec3 v_normal; 6 | uniform mat3 u_normalMatrix; 7 | uniform mat4 u_modelViewMatrix; 8 | uniform mat4 u_projectionMatrix; 9 | 10 | void main(void) { 11 | vec4 pos = u_modelViewMatrix * vec4(a_position,1.0); 12 | v_normal = u_normalMatrix * a_normal; 13 | gl_Position = u_projectionMatrix * pos; 14 | } -------------------------------------------------------------------------------- /Gltf/src/org/bimserver/gltf/BinaryGltfBaseSerializer.java: -------------------------------------------------------------------------------- 1 | package org.bimserver.gltf; 2 | 3 | /****************************************************************************** 4 | * Copyright (C) 2009-2019 BIMserver.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see {@literal}. 18 | *****************************************************************************/ 19 | 20 | import org.bimserver.models.geometry.GeometryData; 21 | import org.bimserver.models.geometry.GeometryInfo; 22 | import org.bimserver.models.ifc2x3tc1.IfcAnnotation; 23 | import org.bimserver.models.ifc2x3tc1.IfcProduct; 24 | import org.bimserver.plugins.serializers.EmfSerializer; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | 28 | public abstract class BinaryGltfBaseSerializer extends EmfSerializer { 29 | private static final Logger LOGGER = LoggerFactory.getLogger(BinaryGltfSerializer2.class); 30 | 31 | protected boolean checkGeometry(IfcProduct ifcProduct, boolean print) { 32 | String name = ifcProduct.eClass().getName(); 33 | if (name.equals("IfcOpeningElement") || name.equals("IfcBuildingStorey") || name.equals("IfcBuilding")) { 34 | return false; 35 | } 36 | GeometryInfo geometryInfo = ifcProduct.getGeometry(); 37 | if (geometryInfo == null) { 38 | if (ifcProduct instanceof IfcAnnotation) { 39 | return false; 40 | } 41 | if (print) { 42 | LOGGER.info("No GeometryInfo for " + name); 43 | } 44 | return false; 45 | } 46 | GeometryData geometryData = geometryInfo.getData(); 47 | if (geometryData == null) { 48 | if (print) { 49 | LOGGER.info("No GeometryData for " + name); 50 | } 51 | return false; 52 | } 53 | if (geometryData.getVertices() == null) { 54 | if (print) { 55 | LOGGER.info("No Vertices for " + name); 56 | } 57 | return false; 58 | } 59 | return true; 60 | } 61 | } -------------------------------------------------------------------------------- /Gltf/src/org/bimserver/gltf/BinaryGltfSerializer.java: -------------------------------------------------------------------------------- 1 | package org.bimserver.gltf; 2 | 3 | /****************************************************************************** 4 | * Copyright (C) 2009-2019 BIMserver.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see {@literal}. 18 | *****************************************************************************/ 19 | 20 | import java.io.IOException; 21 | import java.io.OutputStream; 22 | import java.nio.ByteBuffer; 23 | import java.nio.ByteOrder; 24 | import java.nio.DoubleBuffer; 25 | import java.nio.FloatBuffer; 26 | import java.nio.IntBuffer; 27 | import java.util.HashSet; 28 | import java.util.Set; 29 | 30 | import org.bimserver.geometry.IfcColors; 31 | import org.bimserver.geometry.Matrix; 32 | import org.bimserver.models.geometry.GeometryData; 33 | import org.bimserver.models.geometry.GeometryInfo; 34 | import org.bimserver.models.ifc2x3tc1.IfcProduct; 35 | import org.bimserver.plugins.serializers.ProgressReporter; 36 | import org.bimserver.plugins.serializers.SerializerException; 37 | 38 | import com.fasterxml.jackson.databind.JsonNode; 39 | import com.fasterxml.jackson.databind.ObjectMapper; 40 | import com.fasterxml.jackson.databind.node.ArrayNode; 41 | import com.fasterxml.jackson.databind.node.ObjectNode; 42 | import com.google.common.base.Charsets; 43 | import com.google.common.io.LittleEndianDataOutputStream; 44 | 45 | /** 46 | * @author Ruben de Laat 47 | * 48 | * Annoying things about glTF so far: - Total and scene length have to 49 | * be computed in advance, so no streaming is possible 50 | * 51 | */ 52 | public class BinaryGltfSerializer extends BinaryGltfBaseSerializer { 53 | 54 | private static final String VERTEX_COLOR_MATERIAL = "VertexColorMaterial"; 55 | private static final int FLOAT_VEC_4 = 35666; 56 | // private static final int SHORT = 5122; 57 | private static final int ARRAY_BUFFER = 34962; 58 | private static final int ELEMENT_ARRAY_BUFFER = 34963; 59 | private static final String MAGIC = "glTF"; 60 | private static final int UNSIGNED_SHORT = 5123; 61 | private static final int TRIANGLES = 4; 62 | private static final int FLOAT = 5126; 63 | private int JSON_SCENE_FORMAT = 0; 64 | private int FORMAT_VERSION_1 = 1; 65 | 66 | private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); 67 | private ObjectNode buffers; 68 | private ByteBuffer body; 69 | private ObjectNode meshes; 70 | private ObjectNode accessors; 71 | private int accessorCounter = 0; 72 | private int bufferViewCounter; 73 | private ObjectNode buffersViews; 74 | private ArrayNode defaultSceneNodes; 75 | private ObjectNode scenesNode; 76 | private ObjectNode gltfNode; 77 | private ObjectNode nodes; 78 | 79 | private byte[] vertexColorFragmentShaderBytes; 80 | private byte[] vertexColorVertexShaderBytes; 81 | private byte[] materialColorFragmentShaderBytes; 82 | private byte[] materialColorVertexShaderBytes; 83 | 84 | float[] min = {Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE}; 85 | float[] max = {-Float.MAX_VALUE, -Float.MAX_VALUE, -Float.MAX_VALUE}; 86 | private ArrayNode modelTranslation; 87 | private ObjectNode materials; 88 | private ObjectNode shaders; 89 | 90 | private final Set createdMaterials = new HashSet<>(); 91 | private ArrayNode translationChildrenNode; 92 | 93 | public BinaryGltfSerializer(byte[] vertexColorFragmentShaderBytes, byte[] vertexColorVertexShaderBytes, byte[] materialColorFragmentShaderBytes, byte[] materialColorVertexShaderBytes) { 94 | this.vertexColorFragmentShaderBytes = vertexColorFragmentShaderBytes; 95 | this.vertexColorVertexShaderBytes = vertexColorVertexShaderBytes; 96 | this.materialColorFragmentShaderBytes = materialColorFragmentShaderBytes; 97 | this.materialColorVertexShaderBytes = materialColorVertexShaderBytes; 98 | } 99 | 100 | @Override 101 | protected boolean write(OutputStream outputStream, ProgressReporter progressReporter) throws SerializerException { 102 | gltfNode = OBJECT_MAPPER.createObjectNode(); 103 | 104 | buffers = OBJECT_MAPPER.createObjectNode(); 105 | meshes = OBJECT_MAPPER.createObjectNode(); 106 | buffersViews = OBJECT_MAPPER.createObjectNode(); 107 | scenesNode = OBJECT_MAPPER.createObjectNode(); 108 | accessors = OBJECT_MAPPER.createObjectNode(); 109 | nodes = OBJECT_MAPPER.createObjectNode(); 110 | materials = OBJECT_MAPPER.createObjectNode(); 111 | shaders = OBJECT_MAPPER.createObjectNode(); 112 | 113 | gltfNode.set("meshes", meshes); 114 | gltfNode.set("bufferViews", buffersViews); 115 | gltfNode.set("scenes", scenesNode); 116 | gltfNode.set("accessors", accessors); 117 | gltfNode.set("nodes", nodes); 118 | gltfNode.set("buffers", buffers); 119 | gltfNode.set("materials", materials); 120 | gltfNode.set("shaders", shaders); 121 | 122 | createVertexColorMaterial(); 123 | 124 | try { 125 | LittleEndianDataOutputStream dataOutputStream = new LittleEndianDataOutputStream(outputStream); 126 | 127 | generateSceneAndBody(); 128 | 129 | // StringWriter stringWriter = new StringWriter(); 130 | // OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValue(stringWriter, gltfNode); 131 | // System.out.println(stringWriter); 132 | 133 | byte[] sceneBytes = gltfNode.toString().getBytes(Charsets.UTF_8); 134 | writeHeader(dataOutputStream, 20, sceneBytes.length, body.capacity()); 135 | writeScene(dataOutputStream, sceneBytes); 136 | writeBody(dataOutputStream, body.array()); 137 | } catch (Exception e) { 138 | throw new SerializerException(e); 139 | } 140 | return false; 141 | } 142 | 143 | private void createModelNode() { 144 | ObjectNode translationNode = OBJECT_MAPPER.createObjectNode(); 145 | modelTranslation = OBJECT_MAPPER.createArrayNode(); 146 | 147 | translationChildrenNode = OBJECT_MAPPER.createArrayNode(); 148 | translationNode.set("children", translationChildrenNode); 149 | translationNode.set("translation", modelTranslation); 150 | ObjectNode rotationNode = OBJECT_MAPPER.createObjectNode(); 151 | 152 | ArrayNode rotation = OBJECT_MAPPER.createArrayNode(); 153 | ArrayNode rotationChildrenNode = OBJECT_MAPPER.createArrayNode(); 154 | rotationChildrenNode.add("translationNode"); 155 | rotationNode.set("children", rotationChildrenNode); 156 | rotationNode.set("rotation", rotation); 157 | 158 | rotation.add(1f); 159 | rotation.add(0f); 160 | rotation.add(0f); 161 | rotation.add(-1f); 162 | 163 | nodes.set("rotationNode", rotationNode); 164 | nodes.set("translationNode", translationNode); 165 | defaultSceneNodes.add("rotationNode"); 166 | } 167 | 168 | private void generateSceneAndBody() throws SerializerException { 169 | int totalBodyByteLength = 0; 170 | int totalIndicesByteLength = 0; 171 | int totalVerticesByteLength = 0; 172 | int totalNormalsByteLength = 0; 173 | int totalColorsByteLength = 0; 174 | 175 | int maxIndexValues = 16389; 176 | 177 | for (IfcProduct ifcProduct : model.getAllWithSubTypes(IfcProduct.class)) { 178 | if (checkGeometry(ifcProduct, true)) { 179 | GeometryInfo geometryInfo = ifcProduct.getGeometry(); 180 | GeometryData data = geometryInfo.getData(); 181 | int nrIndicesBytes = data.getIndices().getData().length; 182 | 183 | totalIndicesByteLength += nrIndicesBytes / 2; 184 | if (nrIndicesBytes > 4 * maxIndexValues) { 185 | int nrIndices = nrIndicesBytes / 4; 186 | totalVerticesByteLength += nrIndices * 3 * 4; 187 | totalNormalsByteLength += nrIndices * 3 * 4; 188 | if (data.getColorsQuantized() != null) { 189 | totalColorsByteLength += nrIndices * 4 * 4; 190 | } 191 | } else { 192 | totalVerticesByteLength += data.getVertices().getData().length; 193 | totalNormalsByteLength += data.getNormals().getData().length; 194 | if (data.getColorsQuantized() != null) { 195 | totalColorsByteLength += data.getColorsQuantized().getData().length; 196 | } 197 | } 198 | } 199 | } 200 | totalBodyByteLength = totalIndicesByteLength + totalVerticesByteLength + totalNormalsByteLength + totalColorsByteLength; 201 | 202 | body = ByteBuffer.allocate(totalBodyByteLength + materialColorFragmentShaderBytes.length + materialColorVertexShaderBytes.length + vertexColorFragmentShaderBytes.length + vertexColorVertexShaderBytes.length); 203 | body.order(ByteOrder.LITTLE_ENDIAN); 204 | 205 | ByteBuffer newIndicesBuffer = ByteBuffer.allocate(totalIndicesByteLength); 206 | newIndicesBuffer.order(ByteOrder.LITTLE_ENDIAN); 207 | 208 | ByteBuffer newVerticesBuffer = ByteBuffer.allocate(totalVerticesByteLength); 209 | newVerticesBuffer.order(ByteOrder.LITTLE_ENDIAN); 210 | 211 | ByteBuffer newNormalsBuffer = ByteBuffer.allocate(totalNormalsByteLength); 212 | newNormalsBuffer.order(ByteOrder.LITTLE_ENDIAN); 213 | 214 | ByteBuffer newColorsBuffer = ByteBuffer.allocate(totalColorsByteLength); 215 | newColorsBuffer.order(ByteOrder.LITTLE_ENDIAN); 216 | 217 | String indicesBufferView = createBufferView(totalIndicesByteLength, 0, ELEMENT_ARRAY_BUFFER); 218 | String verticesBufferView = createBufferView(totalVerticesByteLength, totalIndicesByteLength, ARRAY_BUFFER); 219 | String normalsBufferView = createBufferView(totalNormalsByteLength, totalIndicesByteLength + totalVerticesByteLength, ARRAY_BUFFER); 220 | String colorsBufferView = null; 221 | 222 | scenesNode.set("defaultScene", createDefaultScene()); 223 | createModelNode(); 224 | 225 | for (IfcProduct ifcProduct : model.getAllWithSubTypes(IfcProduct.class)) { 226 | if (checkGeometry(ifcProduct, false)) { 227 | GeometryInfo geometryInfo = ifcProduct.getGeometry(); 228 | ByteBuffer matrixByteBuffer = ByteBuffer.wrap(ifcProduct.getGeometry().getTransformation()); 229 | matrixByteBuffer.order(ByteOrder.LITTLE_ENDIAN); 230 | DoubleBuffer doubleBuffer = matrixByteBuffer.asDoubleBuffer(); 231 | float[] matrix = new float[16]; 232 | for (int i=0; i maxIndexValues) { 277 | int totalNrIndices = indicesIntBuffer.capacity(); 278 | int nrParts = (totalNrIndices + maxIndexValues - 1) / maxIndexValues; 279 | 280 | ArrayNode primitivesNode = OBJECT_MAPPER.createArrayNode(); 281 | 282 | for (int part=0; part Short.MAX_VALUE) { 382 | throw new SerializerException("Index too large to store as short " + index); 383 | } 384 | newIndicesBuffer.putShort((short)(index)); 385 | } 386 | 387 | newVerticesBuffer.put(data.getVertices().getData()); 388 | newNormalsBuffer.put(data.getNormals().getData()); 389 | if (data.getColorsQuantized() != null) { 390 | newColorsBuffer.put(data.getColorsQuantized().getData()); 391 | } 392 | 393 | int totalNrIndices = indicesIntBuffer.capacity(); 394 | 395 | ArrayNode primitivesNode = OBJECT_MAPPER.createArrayNode(); 396 | 397 | ObjectNode primitiveNode = OBJECT_MAPPER.createObjectNode(); 398 | 399 | String indicesAccessor = addIndicesAccessor(ifcProduct, indicesBufferView, startPositionIndices, totalNrIndices); 400 | String verticesAccessor = addVerticesAccessor(ifcProduct, verticesBufferView, startPositionVertices, data.getNrVertices()); 401 | String normalsAccessor = addNormalsAccessor(ifcProduct, normalsBufferView, startPositionNormals, data.getNrNormals()); 402 | String colorAccessor = null; 403 | if (data.getColor() != null) { 404 | if (colorsBufferView == null) { 405 | colorsBufferView = createBufferView(totalColorsByteLength, totalIndicesByteLength + totalVerticesByteLength + totalNormalsByteLength, ARRAY_BUFFER); 406 | } 407 | colorAccessor = addColorsAccessor(ifcProduct, colorsBufferView, startPositionColors, data.getNrVertices()); 408 | } 409 | primitivesNode.add(primitiveNode); 410 | 411 | primitiveNode.put("indices", indicesAccessor); 412 | primitiveNode.put("mode", TRIANGLES); 413 | ObjectNode attributesNode = OBJECT_MAPPER.createObjectNode(); 414 | primitiveNode.set("attributes", attributesNode); 415 | attributesNode.put("NORMAL", normalsAccessor); 416 | attributesNode.put("POSITION", verticesAccessor); 417 | if (colorAccessor != null) { 418 | attributesNode.put("COLOR", colorAccessor); 419 | primitiveNode.put("material", VERTEX_COLOR_MATERIAL); 420 | } else { 421 | primitiveNode.put("material", createOrGetMaterial(ifcProduct.eClass().getName(), IfcColors.getDefaultColor(ifcProduct.eClass().getName()))); 422 | } 423 | 424 | String meshName = addMesh(ifcProduct, primitivesNode); 425 | String nodeName = addNode(meshName, ifcProduct); 426 | translationChildrenNode.add(nodeName); 427 | } 428 | } 429 | } 430 | 431 | if (newIndicesBuffer.position() != newIndicesBuffer.capacity()) { 432 | throw new SerializerException("Not all space used"); 433 | } 434 | if (newVerticesBuffer.position() != newVerticesBuffer.capacity()) { 435 | throw new SerializerException("Not all space used"); 436 | } 437 | if (newNormalsBuffer.position() != newNormalsBuffer.capacity()) { 438 | throw new SerializerException("Not all space used"); 439 | } 440 | if (newColorsBuffer.position() != newColorsBuffer.capacity()) { 441 | throw new SerializerException("Not all space used"); 442 | } 443 | 444 | newIndicesBuffer.position(0); 445 | newVerticesBuffer.position(0); 446 | newNormalsBuffer.position(0); 447 | newColorsBuffer.position(0); 448 | 449 | body.put(newIndicesBuffer); 450 | body.put(newVerticesBuffer); 451 | body.put(newNormalsBuffer); 452 | body.put(newColorsBuffer); 453 | 454 | String vertexColorFragmentShaderBufferViewName = createBufferView(vertexColorFragmentShaderBytes.length, body.position()); 455 | body.put(vertexColorFragmentShaderBytes); 456 | 457 | String vertexColorVertexShaderBufferViewName = createBufferView(vertexColorVertexShaderBytes.length, body.position()); 458 | body.put(vertexColorVertexShaderBytes); 459 | 460 | String materialColorFragmentShaderBufferViewName = createBufferView(materialColorFragmentShaderBytes.length, body.position()); 461 | body.put(materialColorFragmentShaderBytes); 462 | 463 | String materialColorVertexShaderBufferViewName = createBufferView(materialColorVertexShaderBytes.length, body.position()); 464 | body.put(materialColorVertexShaderBytes); 465 | 466 | gltfNode.set("animations", createAnimations()); 467 | gltfNode.set("asset", createAsset()); 468 | gltfNode.set("programs", createPrograms()); 469 | gltfNode.put("scene", "defaultScene"); 470 | gltfNode.set("skins", createSkins()); 471 | gltfNode.set("techniques", createTechniques()); 472 | 473 | createVertexColorShaders(vertexColorFragmentShaderBufferViewName, vertexColorVertexShaderBufferViewName); 474 | createMaterialColorShaders(materialColorFragmentShaderBufferViewName, materialColorVertexShaderBufferViewName); 475 | 476 | addBuffer("binary_glTF", "arraybuffer", body.capacity()); 477 | 478 | ArrayNode extensions = OBJECT_MAPPER.createArrayNode(); 479 | extensions.add("KHR_binary_glTF"); 480 | gltfNode.set("extensionsUsed", extensions); 481 | } 482 | 483 | private float[] getOffsets() { 484 | float[] changes = new float[3]; 485 | for (int i=0; i<3; i++) { 486 | changes[i] = (max[i] - min[i]) / 2.0f + min[i]; 487 | } 488 | return changes; 489 | } 490 | 491 | private void updateExtends(GeometryInfo geometryInfo, float[] matrix) { 492 | ByteBuffer verticesByteBufferBuffer = ByteBuffer.wrap(geometryInfo.getData().getVertices().getData()); 493 | verticesByteBufferBuffer.order(ByteOrder.LITTLE_ENDIAN); 494 | FloatBuffer floatBuffer = verticesByteBufferBuffer.asFloatBuffer(); 495 | for (int i=0; i max[j]) { 502 | max[j] = value; 503 | } 504 | if (value < min[j]) { 505 | min[j] = value; 506 | } 507 | } 508 | } 509 | } 510 | 511 | private String addNode(String meshName, IfcProduct ifcProduct) { 512 | String nodeName = "node_" + ifcProduct.getOid(); 513 | ObjectNode nodeNode = OBJECT_MAPPER.createObjectNode(); 514 | 515 | ArrayNode matrixArray = OBJECT_MAPPER.createArrayNode(); 516 | ByteBuffer matrixByteBuffer = ByteBuffer.wrap(ifcProduct.getGeometry().getTransformation()); 517 | matrixByteBuffer.order(ByteOrder.LITTLE_ENDIAN); 518 | DoubleBuffer doubleBuffer = matrixByteBuffer.asDoubleBuffer(); 519 | for (int i = 0; i < 16; i++) { 520 | matrixArray.add(doubleBuffer.get(i)); 521 | } 522 | 523 | ArrayNode meshes = OBJECT_MAPPER.createArrayNode(); 524 | meshes.add(meshName); 525 | 526 | nodeNode.set("meshes", meshes); 527 | nodeNode.set("matrix", matrixArray); 528 | nodes.set(nodeName, nodeNode); 529 | 530 | return nodeName; 531 | } 532 | 533 | private ObjectNode createDefaultScene() { 534 | ObjectNode sceneNode = OBJECT_MAPPER.createObjectNode(); 535 | 536 | defaultSceneNodes = OBJECT_MAPPER.createArrayNode(); 537 | 538 | sceneNode.set("nodes", defaultSceneNodes); 539 | 540 | return sceneNode; 541 | } 542 | 543 | private String createBufferView(int byteLength, int byteOffset) { 544 | return createBufferView(byteLength, byteOffset, -1); 545 | } 546 | 547 | private String createBufferView(int byteLength, int byteOffset, int target) { 548 | String name = "bufferView_" + (bufferViewCounter++); 549 | ObjectNode bufferViewNode = OBJECT_MAPPER.createObjectNode(); 550 | 551 | bufferViewNode.put("buffer", "binary_glTF"); 552 | bufferViewNode.put("byteLength", byteLength); 553 | bufferViewNode.put("byteOffset", byteOffset); 554 | if (target != -1) { 555 | bufferViewNode.put("target", target); 556 | } 557 | 558 | buffersViews.set(name, bufferViewNode); 559 | 560 | return name; 561 | } 562 | 563 | private String addNormalsAccessor(IfcProduct ifcProduct, String bufferViewName, int byteOffset, int count) throws SerializerException { 564 | if (count <= 0) { 565 | throw new SerializerException("Count <= 0"); 566 | } 567 | String accessorName = "accessor_normal_" + (accessorCounter++); 568 | 569 | ObjectNode accessor = OBJECT_MAPPER.createObjectNode(); 570 | accessor.put("bufferView", bufferViewName); 571 | accessor.put("byteOffset", byteOffset); 572 | accessor.put("byteStride", 12); 573 | accessor.put("componentType", FLOAT); 574 | accessor.put("count", count); 575 | accessor.put("type", "VEC3"); 576 | 577 | ArrayNode min = OBJECT_MAPPER.createArrayNode(); 578 | min.add(-1d); 579 | min.add(-1d); 580 | min.add(-1d); 581 | ArrayNode max = OBJECT_MAPPER.createArrayNode(); 582 | max.add(1); 583 | max.add(1); 584 | max.add(1); 585 | 586 | accessor.set("min", min); 587 | accessor.set("max", max); 588 | 589 | accessors.set(accessorName, accessor); 590 | 591 | return accessorName; 592 | } 593 | 594 | private String addColorsAccessor(IfcProduct ifcProduct, String bufferViewName, int byteOffset, int count) { 595 | String accessorName = "accessor_color_" + (accessorCounter++); 596 | 597 | ObjectNode accessor = OBJECT_MAPPER.createObjectNode(); 598 | accessor.put("bufferView", bufferViewName); 599 | accessor.put("byteOffset", byteOffset); 600 | accessor.put("byteStride", 16); 601 | accessor.put("componentType", FLOAT); 602 | accessor.put("count", count); 603 | accessor.put("type", "VEC4"); 604 | 605 | // ArrayNode min = OBJECT_MAPPER.createArrayNode(); 606 | // min.add(-1d); 607 | // min.add(-1d); 608 | // min.add(-1d); 609 | // ArrayNode max = OBJECT_MAPPER.createArrayNode(); 610 | // max.add(1); 611 | // max.add(1); 612 | // max.add(1); 613 | // 614 | // accessor.set("min", min); 615 | // accessor.set("max", max); 616 | 617 | accessors.set(accessorName, accessor); 618 | 619 | return accessorName; 620 | } 621 | 622 | private String addVerticesAccessor(IfcProduct ifcProduct, String bufferViewName, int startPosition, int count) throws SerializerException { 623 | if (count <= 0) { 624 | throw new SerializerException("Count <= 0"); 625 | } 626 | String accessorName = "accessor_vertex_" + (accessorCounter++); 627 | 628 | GeometryData data = ifcProduct.getGeometry().getData(); 629 | ByteBuffer verticesBuffer = ByteBuffer.wrap(data.getVertices().getData()); 630 | 631 | ObjectNode accessor = OBJECT_MAPPER.createObjectNode(); 632 | accessor.put("bufferView", bufferViewName); 633 | accessor.put("byteOffset", startPosition); 634 | accessor.put("byteStride", 12); 635 | accessor.put("componentType", FLOAT); 636 | accessor.put("count", count); 637 | accessor.put("type", "VEC3"); 638 | 639 | verticesBuffer.order(ByteOrder.LITTLE_ENDIAN); 640 | double[] min = { Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE }; 641 | double[] max = { -Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE }; 642 | for (int i = 0; i < verticesBuffer.capacity(); i += 3) { 643 | for (int j = 0; j < 3; j++) { 644 | double val = verticesBuffer.get(i + j); 645 | if (val > max[j]) { 646 | max[j] = val; 647 | } 648 | if (val < min[j]) { 649 | min[j] = val; 650 | } 651 | } 652 | } 653 | 654 | ArrayNode minNode = OBJECT_MAPPER.createArrayNode(); 655 | minNode.add(min[0]); 656 | minNode.add(min[1]); 657 | minNode.add(min[2]); 658 | ArrayNode maxNode = OBJECT_MAPPER.createArrayNode(); 659 | maxNode.add(max[0]); 660 | maxNode.add(max[1]); 661 | maxNode.add(max[2]); 662 | 663 | accessor.set("min", minNode); 664 | accessor.set("max", maxNode); 665 | 666 | accessors.set(accessorName, accessor); 667 | 668 | return accessorName; 669 | } 670 | 671 | private String addIndicesAccessor(IfcProduct ifcProduct, String bufferViewName, int offsetBytes, int count) throws SerializerException { 672 | if (count <= 0) { 673 | throw new SerializerException(count + " <= 0"); 674 | } 675 | String accessorName = "accessor_index_" + (accessorCounter++); 676 | 677 | ObjectNode accessor = OBJECT_MAPPER.createObjectNode(); 678 | accessor.put("bufferView", bufferViewName); 679 | accessor.put("byteOffset", offsetBytes); 680 | accessor.put("byteStride", 0); 681 | accessor.put("componentType", UNSIGNED_SHORT); 682 | accessor.put("count", count); 683 | accessor.put("type", "SCALAR"); 684 | 685 | accessors.set(accessorName, accessor); 686 | 687 | return accessorName; 688 | } 689 | 690 | private String addMesh(IfcProduct ifcProduct, ArrayNode primitivesNode) { 691 | ObjectNode meshNode = OBJECT_MAPPER.createObjectNode(); 692 | String meshName = "mesh_" + ifcProduct.getOid(); 693 | meshNode.put("name", meshName); 694 | meshNode.set("primitives", primitivesNode); 695 | meshes.set(meshName, meshNode); 696 | return meshName; 697 | } 698 | 699 | private void addBuffer(String name, String type, int byteLength) { 700 | ObjectNode bufferNode = OBJECT_MAPPER.createObjectNode(); 701 | 702 | bufferNode.put("byteLength", byteLength); 703 | bufferNode.put("type", type); 704 | bufferNode.put("uri", "data:,"); 705 | 706 | buffers.set(name, bufferNode); 707 | } 708 | 709 | private JsonNode createAnimations() { 710 | // TODO 711 | return OBJECT_MAPPER.createObjectNode(); 712 | } 713 | 714 | private JsonNode createTechniques() { 715 | ObjectNode techniques = OBJECT_MAPPER.createObjectNode(); 716 | 717 | techniques.set("vertexColorTechnique", createVertexColorTechnique()); 718 | techniques.set("materialColorTechnique", createMaterialColorTechnique()); 719 | 720 | return techniques; 721 | } 722 | 723 | private ObjectNode createMaterialColorTechnique() { 724 | ObjectNode technique = OBJECT_MAPPER.createObjectNode(); 725 | 726 | ObjectNode attributes = OBJECT_MAPPER.createObjectNode(); 727 | ObjectNode parameters = OBJECT_MAPPER.createObjectNode(); 728 | ObjectNode states = OBJECT_MAPPER.createObjectNode(); 729 | ObjectNode uniforms = OBJECT_MAPPER.createObjectNode(); 730 | 731 | attributes.put("a_normal", "normal"); 732 | attributes.put("a_position", "position"); 733 | 734 | ObjectNode diffuse = OBJECT_MAPPER.createObjectNode(); 735 | diffuse.put("type", 35666); 736 | parameters.set("diffuse", diffuse); 737 | 738 | ObjectNode modelViewMatrix = OBJECT_MAPPER.createObjectNode(); 739 | modelViewMatrix.put("semantic", "MODELVIEW"); 740 | modelViewMatrix.put("type", 35676); 741 | parameters.set("modelViewMatrix", modelViewMatrix); 742 | 743 | ObjectNode normal = OBJECT_MAPPER.createObjectNode(); 744 | normal.put("semantic", "NORMAL"); 745 | normal.put("type", 35665); 746 | parameters.set("normal", normal); 747 | 748 | ObjectNode normalMatrix = OBJECT_MAPPER.createObjectNode(); 749 | normalMatrix.put("semantic", "MODELVIEWINVERSETRANSPOSE"); 750 | normalMatrix.put("type", 35675); 751 | parameters.set("normalMatrix", normalMatrix); 752 | 753 | ObjectNode position = OBJECT_MAPPER.createObjectNode(); 754 | position.put("semantic", "POSITION"); 755 | position.put("type", 35665); 756 | parameters.set("position", position); 757 | 758 | ObjectNode projectionMatrix = OBJECT_MAPPER.createObjectNode(); 759 | projectionMatrix.put("semantic", "PROJECTION"); 760 | projectionMatrix.put("type", 35676); 761 | parameters.set("projectionMatrix", projectionMatrix); 762 | 763 | ObjectNode shininess = OBJECT_MAPPER.createObjectNode(); 764 | shininess.put("type", 5126); 765 | parameters.set("shininess", shininess); 766 | 767 | ObjectNode specular = OBJECT_MAPPER.createObjectNode(); 768 | specular.put("type", 35666); 769 | parameters.set("specular", specular); 770 | 771 | technique.put("program", "materialColorProgram"); 772 | 773 | ArrayNode statesEnable = OBJECT_MAPPER.createArrayNode(); 774 | statesEnable.add(2929); 775 | statesEnable.add(2884); 776 | states.set("enable", statesEnable); 777 | 778 | uniforms.put("u_diffuse", "diffuse"); 779 | uniforms.put("u_modelViewMatrix", "modelViewMatrix"); 780 | uniforms.put("u_normalMatrix", "normalMatrix"); 781 | uniforms.put("u_projectionMatrix", "projectionMatrix"); 782 | uniforms.put("u_shininess", "shininess"); 783 | uniforms.put("u_specular", "specular"); 784 | 785 | technique.set("attributes", attributes); 786 | technique.set("parameters", parameters); 787 | technique.set("states", states); 788 | technique.set("uniforms", uniforms); 789 | 790 | return technique; 791 | } 792 | 793 | private ObjectNode createVertexColorTechnique() { 794 | ObjectNode technique = OBJECT_MAPPER.createObjectNode(); 795 | 796 | ObjectNode attributes = OBJECT_MAPPER.createObjectNode(); 797 | ObjectNode parameters = OBJECT_MAPPER.createObjectNode(); 798 | ObjectNode states = OBJECT_MAPPER.createObjectNode(); 799 | ObjectNode uniforms = OBJECT_MAPPER.createObjectNode(); 800 | 801 | attributes.put("a_normal", "normal"); 802 | attributes.put("a_position", "position"); 803 | attributes.put("a_color", "color"); 804 | 805 | ObjectNode modelViewMatrix = OBJECT_MAPPER.createObjectNode(); 806 | modelViewMatrix.put("semantic", "MODELVIEW"); 807 | modelViewMatrix.put("type", 35676); 808 | parameters.set("modelViewMatrix", modelViewMatrix); 809 | 810 | ObjectNode normal = OBJECT_MAPPER.createObjectNode(); 811 | normal.put("semantic", "NORMAL"); 812 | normal.put("type", 35665); 813 | parameters.set("normal", normal); 814 | 815 | ObjectNode normalMatrix = OBJECT_MAPPER.createObjectNode(); 816 | normalMatrix.put("semantic", "MODELVIEWINVERSETRANSPOSE"); 817 | normalMatrix.put("type", 35675); 818 | parameters.set("normalMatrix", normalMatrix); 819 | 820 | ObjectNode position = OBJECT_MAPPER.createObjectNode(); 821 | position.put("semantic", "POSITION"); 822 | position.put("type", 35665); 823 | parameters.set("position", position); 824 | 825 | ObjectNode color = OBJECT_MAPPER.createObjectNode(); 826 | color.put("semantic", "COLOR"); 827 | color.put("type", FLOAT_VEC_4); 828 | parameters.set("color", color); 829 | 830 | ObjectNode projectionMatrix = OBJECT_MAPPER.createObjectNode(); 831 | projectionMatrix.put("semantic", "PROJECTION"); 832 | projectionMatrix.put("type", 35676); 833 | parameters.set("projectionMatrix", projectionMatrix); 834 | 835 | technique.put("program", "vertexColorProgram"); 836 | 837 | ArrayNode statesEnable = OBJECT_MAPPER.createArrayNode(); 838 | statesEnable.add(2929); 839 | statesEnable.add(2884); 840 | states.set("enable", statesEnable); 841 | 842 | uniforms.put("u_modelViewMatrix", "modelViewMatrix"); 843 | uniforms.put("u_normalMatrix", "normalMatrix"); 844 | uniforms.put("u_projectionMatrix", "projectionMatrix"); 845 | 846 | technique.set("attributes", attributes); 847 | technique.set("parameters", parameters); 848 | technique.set("states", states); 849 | technique.set("uniforms", uniforms); 850 | 851 | return technique; 852 | } 853 | 854 | private JsonNode createSkins() { 855 | // TODO 856 | return OBJECT_MAPPER.createObjectNode(); 857 | } 858 | 859 | private void createVertexColorShaders(String fragmentShaderBufferViewName, String vertexShaderBufferViewName) { 860 | ObjectNode fragmentShaderExtensions = OBJECT_MAPPER.createObjectNode(); 861 | ObjectNode fragmentShaderBinary = OBJECT_MAPPER.createObjectNode(); 862 | fragmentShaderExtensions.set("KHR_binary_glTF", fragmentShaderBinary); 863 | fragmentShaderBinary.put("bufferView", fragmentShaderBufferViewName); 864 | 865 | ObjectNode fragmentShader = OBJECT_MAPPER.createObjectNode(); 866 | fragmentShader.put("type", 35632); 867 | fragmentShader.put("uri", "data:,"); 868 | fragmentShader.set("extensions", fragmentShaderExtensions); 869 | 870 | ObjectNode vertexShaderExtensions = OBJECT_MAPPER.createObjectNode(); 871 | ObjectNode vertexShaderBinary = OBJECT_MAPPER.createObjectNode(); 872 | vertexShaderExtensions.set("KHR_binary_glTF", vertexShaderBinary); 873 | vertexShaderBinary.put("bufferView", vertexShaderBufferViewName); 874 | 875 | ObjectNode vertexShader = OBJECT_MAPPER.createObjectNode(); 876 | vertexShader.put("type", 35633); 877 | vertexShader.put("uri", "data:,"); 878 | vertexShader.set("extensions", vertexShaderExtensions); 879 | 880 | shaders.set("vertexColorFragmentShader", fragmentShader); 881 | shaders.set("vertexColorVertexShader", vertexShader); 882 | } 883 | 884 | private void createMaterialColorShaders(String fragmentShaderBufferViewName, String vertexShaderBufferViewName) { 885 | ObjectNode fragmentShaderExtensions = OBJECT_MAPPER.createObjectNode(); 886 | ObjectNode fragmentShaderBinary = OBJECT_MAPPER.createObjectNode(); 887 | fragmentShaderExtensions.set("KHR_binary_glTF", fragmentShaderBinary); 888 | fragmentShaderBinary.put("bufferView", fragmentShaderBufferViewName); 889 | 890 | ObjectNode fragmentShader = OBJECT_MAPPER.createObjectNode(); 891 | fragmentShader.put("type", 35632); 892 | fragmentShader.put("uri", "data:,"); 893 | fragmentShader.set("extensions", fragmentShaderExtensions); 894 | 895 | ObjectNode vertexShaderExtensions = OBJECT_MAPPER.createObjectNode(); 896 | ObjectNode vertexShaderBinary = OBJECT_MAPPER.createObjectNode(); 897 | vertexShaderExtensions.set("KHR_binary_glTF", vertexShaderBinary); 898 | vertexShaderBinary.put("bufferView", vertexShaderBufferViewName); 899 | 900 | ObjectNode vertexShader = OBJECT_MAPPER.createObjectNode(); 901 | vertexShader.put("type", 35633); 902 | vertexShader.put("uri", "data:,"); 903 | vertexShader.set("extensions", vertexShaderExtensions); 904 | 905 | shaders.set("materialColorFragmentShader", fragmentShader); 906 | shaders.set("materialColorVertexShader", vertexShader); 907 | } 908 | 909 | private ObjectNode createPrograms() { 910 | ObjectNode programs = OBJECT_MAPPER.createObjectNode(); 911 | 912 | programs.set("vertexColorProgram", createVertexColorsPrograms()); 913 | programs.set("materialColorProgram", createMaterialColorsPrograms()); 914 | 915 | return programs; 916 | } 917 | 918 | private JsonNode createVertexColorsPrograms() { 919 | ObjectNode program = OBJECT_MAPPER.createObjectNode(); 920 | ArrayNode attributes = OBJECT_MAPPER.createArrayNode(); 921 | 922 | program.set("attributes", attributes); 923 | attributes.add("a_normal"); 924 | attributes.add("a_position"); 925 | attributes.add("a_color"); 926 | 927 | program.put("fragmentShader", "vertexColorFragmentShader"); 928 | program.put("vertexShader", "vertexColorVertexShader"); 929 | 930 | return program; 931 | } 932 | 933 | private JsonNode createMaterialColorsPrograms() { 934 | ObjectNode program = OBJECT_MAPPER.createObjectNode(); 935 | ArrayNode attributes = OBJECT_MAPPER.createArrayNode(); 936 | 937 | program.set("attributes", attributes); 938 | attributes.add("a_normal"); 939 | attributes.add("a_position"); 940 | 941 | program.put("fragmentShader", "materialColorFragmentShader"); 942 | program.put("vertexShader", "materialColorVertexShader"); 943 | 944 | return program; 945 | } 946 | 947 | private String createOrGetMaterial(String name, float[] colors) { 948 | if (createdMaterials.contains(name)) { 949 | return name; 950 | } 951 | ObjectNode material = OBJECT_MAPPER.createObjectNode(); 952 | 953 | material.put("name", name + "Material"); 954 | material.put("technique", "materialColorTechnique"); 955 | 956 | ObjectNode values = OBJECT_MAPPER.createObjectNode(); 957 | 958 | ArrayNode diffuse = OBJECT_MAPPER.createArrayNode(); 959 | for (int i=0; i<4; i++) { 960 | diffuse.add(colors[i]); 961 | } 962 | // diffuse.add(0.8000000119209291); 963 | // diffuse.add(0); 964 | // diffuse.add(0); 965 | // diffuse.add(1); 966 | 967 | ArrayNode specular = OBJECT_MAPPER.createArrayNode(); 968 | specular.add(0.20000000298023218); 969 | specular.add(0.20000000298023218); 970 | specular.add(0.20000000298023218); 971 | 972 | values.set("diffuse", diffuse); 973 | values.set("specular", specular); 974 | values.put("shininess", 256); 975 | 976 | material.set("values", values); 977 | 978 | materials.set(name, material); 979 | 980 | createdMaterials.add(name); 981 | 982 | return name; 983 | } 984 | 985 | private void createVertexColorMaterial() { 986 | ObjectNode defaultMaterial = OBJECT_MAPPER.createObjectNode(); 987 | 988 | defaultMaterial.put("name", VERTEX_COLOR_MATERIAL); 989 | defaultMaterial.put("technique", "vertexColorTechnique"); 990 | 991 | ObjectNode values = OBJECT_MAPPER.createObjectNode(); 992 | 993 | defaultMaterial.set("values", values); 994 | 995 | materials.set(VERTEX_COLOR_MATERIAL, defaultMaterial); 996 | } 997 | 998 | private JsonNode createAsset() { 999 | // TODO 1000 | return OBJECT_MAPPER.createObjectNode(); 1001 | } 1002 | 1003 | private void writeHeader(LittleEndianDataOutputStream dataOutputStream, int headerLength, int sceneLength, int bodyLength) throws IOException { 1004 | dataOutputStream.write(MAGIC.getBytes(Charsets.US_ASCII)); 1005 | dataOutputStream.writeInt(FORMAT_VERSION_1); 1006 | dataOutputStream.writeInt(headerLength + sceneLength + bodyLength); 1007 | dataOutputStream.writeInt(sceneLength); 1008 | dataOutputStream.writeInt(JSON_SCENE_FORMAT); 1009 | } 1010 | 1011 | private void writeBody(LittleEndianDataOutputStream dataOutputStream, byte[] body) throws IOException { 1012 | dataOutputStream.write(body); 1013 | } 1014 | 1015 | private void writeScene(LittleEndianDataOutputStream dataOutputStream, byte[] scene) throws IOException { 1016 | dataOutputStream.write(scene); 1017 | } 1018 | } 1019 | -------------------------------------------------------------------------------- /Gltf/src/org/bimserver/gltf/BinaryGltfSerializer2.java: -------------------------------------------------------------------------------- 1 | package org.bimserver.gltf; 2 | 3 | /****************************************************************************** 4 | * Copyright (C) 2009-2019 BIMserver.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see {@literal}. 18 | *****************************************************************************/ 19 | 20 | import java.io.IOException; 21 | import java.io.OutputStream; 22 | import java.nio.ByteBuffer; 23 | import java.nio.ByteOrder; 24 | import java.nio.DoubleBuffer; 25 | import java.nio.IntBuffer; 26 | import java.util.HashMap; 27 | import java.util.Map; 28 | 29 | import org.bimserver.geometry.IfcColors; 30 | import org.bimserver.geometry.Matrix; 31 | import org.bimserver.models.geometry.GeometryData; 32 | import org.bimserver.models.geometry.GeometryInfo; 33 | import org.bimserver.models.geometry.Vector4f; 34 | import org.bimserver.models.ifc2x3tc1.IfcProduct; 35 | import org.bimserver.plugins.serializers.ProgressReporter; 36 | import org.bimserver.plugins.serializers.SerializerException; 37 | import org.slf4j.Logger; 38 | import org.slf4j.LoggerFactory; 39 | 40 | import com.fasterxml.jackson.databind.JsonNode; 41 | import com.fasterxml.jackson.databind.ObjectMapper; 42 | import com.fasterxml.jackson.databind.node.ArrayNode; 43 | import com.fasterxml.jackson.databind.node.ObjectNode; 44 | import com.google.common.base.Charsets; 45 | import com.google.common.io.LittleEndianDataOutputStream; 46 | import com.google.common.primitives.UnsignedBytes; 47 | 48 | /** 49 | * @author Ruben de Laat 50 | * 51 | * Annoying things about glTF so far: - Total and scene length have to 52 | * be computed in advance, so no streaming is possible 53 | * 54 | */ 55 | public class BinaryGltfSerializer2 extends BinaryGltfBaseSerializer { 56 | 57 | private static final Logger LOGGER = LoggerFactory.getLogger(BinaryGltfSerializer2.class); 58 | private static final int ARRAY_BUFFER = 34962; 59 | private static final int ELEMENT_ARRAY_BUFFER = 34963; 60 | private static final int MAGIC = 0x46546C67; 61 | private static final int UNSIGNED_INT = 5125; 62 | private static final int TRIANGLES = 4; 63 | private static final int FLOAT = 5126; 64 | private static final int UNSIGNED_BYTE = 5121; 65 | private static final int JSON_CHUNK = 0x4E4F534A; 66 | private static final int BINARY_CHUNK = 0x004E4942; 67 | private static final int FORMAT_VERSION = 2; 68 | private static final String GLTF_VERSION = "2.0"; 69 | 70 | private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); 71 | private ArrayNode buffers; 72 | private ByteBuffer body; 73 | private ArrayNode meshes; 74 | private ArrayNode accessors; 75 | private ArrayNode buffersViews; 76 | private ArrayNode defaultSceneNodes; 77 | private ArrayNode scenesNode; 78 | private ObjectNode gltfNode; 79 | private ArrayNode nodes; 80 | 81 | double[] min = {Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE}; 82 | double[] max = {-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE}; 83 | private ArrayNode modelTranslation; 84 | private ArrayNode materials; 85 | 86 | private final Map createdMaterials = new HashMap<>(); 87 | private ArrayNode translationChildrenNode; 88 | private int vertexColorIndex; 89 | 90 | public BinaryGltfSerializer2() { 91 | } 92 | 93 | @Override 94 | protected boolean write(OutputStream outputStream, ProgressReporter progressReporter) throws SerializerException { 95 | LOGGER.info("Starting serialization"); 96 | gltfNode = OBJECT_MAPPER.createObjectNode(); 97 | 98 | buffers = OBJECT_MAPPER.createArrayNode(); 99 | meshes = OBJECT_MAPPER.createArrayNode(); 100 | buffersViews = OBJECT_MAPPER.createArrayNode(); 101 | scenesNode = OBJECT_MAPPER.createArrayNode(); 102 | accessors = OBJECT_MAPPER.createArrayNode(); 103 | nodes = OBJECT_MAPPER.createArrayNode(); 104 | materials = OBJECT_MAPPER.createArrayNode(); 105 | 106 | gltfNode.set("meshes", meshes); 107 | gltfNode.set("bufferViews", buffersViews); 108 | gltfNode.set("scenes", scenesNode); 109 | gltfNode.set("accessors", accessors); 110 | gltfNode.set("nodes", nodes); 111 | gltfNode.set("buffers", buffers); 112 | gltfNode.set("materials", materials); 113 | 114 | createVertexColorMaterial(); 115 | 116 | try { 117 | LittleEndianDataOutputStream dataOutputStream = new LittleEndianDataOutputStream(outputStream); 118 | 119 | generateSceneAndBody(); 120 | 121 | // StringWriter stringWriter = new StringWriter(); 122 | // OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValue(stringWriter, gltfNode); 123 | // System.out.println(stringWriter); 124 | 125 | byte[] sceneBytes = gltfNode.toString().getBytes(Charsets.UTF_8); 126 | int sceneLength = sceneBytes.length + (sceneBytes.length % 4 == 0 ? 0 : 4 - sceneBytes.length % 4); 127 | int bodyLength = body.capacity() + (body.capacity() % 4 == 0 ? 0 : 4 - body.capacity() % 4); 128 | writeHeader(dataOutputStream, 12, 8 + sceneLength, 8 + bodyLength); 129 | writeScene(dataOutputStream, sceneBytes); 130 | writeBody(dataOutputStream, body.array()); 131 | dataOutputStream.flush(); 132 | } catch (Exception e) { 133 | throw new SerializerException(e); 134 | } 135 | return false; 136 | } 137 | 138 | private void createModelNode() { 139 | ObjectNode translationNode = OBJECT_MAPPER.createObjectNode(); 140 | modelTranslation = OBJECT_MAPPER.createArrayNode(); 141 | 142 | translationChildrenNode = OBJECT_MAPPER.createArrayNode(); 143 | translationNode.set("children", translationChildrenNode); 144 | translationNode.set("translation", modelTranslation); 145 | ObjectNode rotationNode = OBJECT_MAPPER.createObjectNode(); 146 | 147 | ArrayNode rotation = OBJECT_MAPPER.createArrayNode(); 148 | ArrayNode rotationChildrenNode = OBJECT_MAPPER.createArrayNode(); 149 | 150 | nodes.add(translationNode); 151 | 152 | rotationChildrenNode.add(nodes.size() - 1); 153 | rotationNode.set("children", rotationChildrenNode); 154 | rotationNode.set("rotation", rotation); 155 | 156 | float[] quat = normalizeQuaternion(new float[]{1, 0 , 0, -1f}); 157 | 158 | rotation.add(quat[0]); 159 | rotation.add(quat[1]); 160 | rotation.add(quat[2]); 161 | rotation.add(quat[3]); 162 | 163 | nodes.add(rotationNode); 164 | defaultSceneNodes.add(nodes.size() - 1); 165 | } 166 | 167 | public float len2(float[] input) { 168 | return input[0] * input[0] + input[1] * input[1] + input[2] * input[2] + input[3] * input[3]; 169 | } 170 | 171 | private float[] normalizeQuaternion(float[] input) { 172 | float len = len2(input); 173 | if (len != 0.f && len != 1f) { 174 | len = (float)Math.sqrt(len); 175 | input[0] /= len; 176 | input[1] /= len; 177 | input[2] /= len; 178 | input[3] /= len; 179 | } 180 | return input; 181 | } 182 | 183 | private void generateSceneAndBody() throws SerializerException { 184 | int totalBodyByteLength = 0; 185 | int totalIndicesByteLength = 0; 186 | int totalVerticesByteLength = 0; 187 | int totalNormalsByteLength = 0; 188 | int totalColorsByteLength = 0; 189 | 190 | for (IfcProduct ifcProduct : model.getAllWithSubTypes(IfcProduct.class)) { 191 | if (checkGeometry(ifcProduct, false)) { 192 | GeometryInfo geometryInfo = ifcProduct.getGeometry(); 193 | GeometryData data = geometryInfo.getData(); 194 | totalIndicesByteLength += data.getNrIndices() * 4; 195 | totalVerticesByteLength += data.getNrVertices() * 4; 196 | totalNormalsByteLength += data.getNrNormals() * 4; 197 | if (data.getColorsQuantized() != null) { 198 | totalColorsByteLength += data.getColorsQuantized().getData().length; 199 | } else { 200 | totalColorsByteLength += (data.getNrVertices() / 3) * 4; 201 | } 202 | } 203 | } 204 | if (totalIndicesByteLength == 0) { 205 | throw new SerializerException("No geometry"); 206 | } 207 | totalBodyByteLength = totalIndicesByteLength + totalVerticesByteLength + totalNormalsByteLength + totalColorsByteLength; 208 | 209 | body = ByteBuffer.allocate(totalBodyByteLength); 210 | body.order(ByteOrder.LITTLE_ENDIAN); 211 | 212 | ByteBuffer newIndicesBuffer = ByteBuffer.allocate(totalIndicesByteLength); 213 | newIndicesBuffer.order(ByteOrder.LITTLE_ENDIAN); 214 | 215 | ByteBuffer newVerticesBuffer = ByteBuffer.allocate(totalVerticesByteLength); 216 | newVerticesBuffer.order(ByteOrder.LITTLE_ENDIAN); 217 | 218 | ByteBuffer newNormalsBuffer = ByteBuffer.allocate(totalNormalsByteLength); 219 | newNormalsBuffer.order(ByteOrder.LITTLE_ENDIAN); 220 | 221 | ByteBuffer newColorsBuffer = ByteBuffer.allocate(totalColorsByteLength); 222 | newColorsBuffer.order(ByteOrder.LITTLE_ENDIAN); 223 | 224 | int indicesBufferView = createBufferView(totalIndicesByteLength, 0, ELEMENT_ARRAY_BUFFER, -1); 225 | int verticesBufferView = createBufferView(totalVerticesByteLength, totalIndicesByteLength, ARRAY_BUFFER, 12); 226 | int normalsBufferView = createBufferView(totalNormalsByteLength, totalIndicesByteLength + totalVerticesByteLength, ARRAY_BUFFER, 12); 227 | int colorsBufferView = -1; 228 | 229 | scenesNode.add(createDefaultScene()); 230 | gltfNode.put("scene", 0); 231 | createModelNode(); 232 | 233 | for (IfcProduct ifcProduct : model.getAllWithSubTypes(IfcProduct.class)) { 234 | if (checkGeometry(ifcProduct, false)) { 235 | GeometryInfo geometryInfo = ifcProduct.getGeometry(); 236 | ByteBuffer matrixByteBuffer = ByteBuffer.wrap(ifcProduct.getGeometry().getTransformation()); 237 | matrixByteBuffer.order(ByteOrder.LITTLE_ENDIAN); 238 | DoubleBuffer doubleBuffer = matrixByteBuffer.asDoubleBuffer(); 239 | double[] matrix = new double[16]; 240 | for (int i=0; i maxVal) { 280 | maxVal = index; 281 | } 282 | } 283 | 284 | int[] min = new int[]{0}; 285 | int[] max = new int[]{maxVal}; 286 | 287 | for (int i=0; i max[j]) { 401 | max[j] = value; 402 | } 403 | if (value < min[j]) { 404 | min[j] = value; 405 | } 406 | } 407 | } 408 | } 409 | 410 | private int addNode(int meshId, IfcProduct ifcProduct) { 411 | ObjectNode nodeNode = OBJECT_MAPPER.createObjectNode(); 412 | 413 | ArrayNode matrixArray = OBJECT_MAPPER.createArrayNode(); 414 | ByteBuffer matrixByteBuffer = ByteBuffer.wrap(ifcProduct.getGeometry().getTransformation()); 415 | matrixByteBuffer.order(ByteOrder.LITTLE_ENDIAN); 416 | DoubleBuffer doubleBuffer = matrixByteBuffer.asDoubleBuffer(); 417 | double[] buffer = new double[16]; 418 | for (int i = 0; i < 16; i++) { 419 | double d= doubleBuffer.get(i); 420 | matrixArray.add(d); 421 | buffer[i] = d; 422 | } 423 | String GUID = ifcProduct.getGlobalId(); 424 | ObjectNode extrasNode = OBJECT_MAPPER.createObjectNode(); 425 | nodeNode.set("extras", extrasNode); 426 | extrasNode.put("ifcID", GUID); 427 | nodeNode.put("mesh", meshId); 428 | if (!Matrix.isIdentity(buffer)) { 429 | if (!Matrix.invertM(new double[16], 0, buffer, 0)) { 430 | LOGGER.info("Could not invert matrix, omitting"); 431 | // Cannot be inverted, we know cesium at least doesn't like that 432 | } else { 433 | nodeNode.set("matrix", matrixArray); 434 | } 435 | } 436 | nodes.add(nodeNode); 437 | 438 | return nodes.size() - 1; 439 | } 440 | 441 | private ObjectNode createDefaultScene() { 442 | ObjectNode sceneNode = OBJECT_MAPPER.createObjectNode(); 443 | 444 | defaultSceneNodes = OBJECT_MAPPER.createArrayNode(); 445 | 446 | sceneNode.set("nodes", defaultSceneNodes); 447 | 448 | return sceneNode; 449 | } 450 | 451 | private int createBufferView(int byteLength, int byteOffset, int target, int byteStride) { 452 | ObjectNode bufferViewNode = OBJECT_MAPPER.createObjectNode(); 453 | 454 | bufferViewNode.put("buffer", 0); 455 | bufferViewNode.put("byteLength", byteLength); 456 | bufferViewNode.put("byteOffset", byteOffset); 457 | if (byteStride != -1) { 458 | bufferViewNode.put("byteStride", byteStride); 459 | } 460 | if (target != -1) { 461 | bufferViewNode.put("target", target); 462 | } 463 | 464 | buffersViews.add(bufferViewNode); 465 | 466 | return buffersViews.size() - 1; 467 | } 468 | 469 | private int addNormalsAccessor(IfcProduct ifcProduct, int bufferViewIndex, int byteOffset, int count) throws SerializerException { 470 | if (count <= 0) { 471 | throw new SerializerException("Count <= 0"); 472 | } 473 | 474 | ObjectNode accessor = OBJECT_MAPPER.createObjectNode(); 475 | accessor.put("bufferView", bufferViewIndex); 476 | accessor.put("byteOffset", byteOffset); 477 | // accessor.put("byteStride", 12); 478 | accessor.put("componentType", FLOAT); 479 | accessor.put("count", count); 480 | accessor.put("type", "VEC3"); 481 | 482 | ArrayNode min = OBJECT_MAPPER.createArrayNode(); 483 | min.add(-1d); 484 | min.add(-1d); 485 | min.add(-1d); 486 | ArrayNode max = OBJECT_MAPPER.createArrayNode(); 487 | max.add(1); 488 | max.add(1); 489 | max.add(1); 490 | 491 | // accessor.set("min", min); 492 | // accessor.set("max", max); 493 | 494 | accessors.add(accessor); 495 | 496 | return accessors.size() - 1; 497 | } 498 | 499 | private int addColorsAccessor(IfcProduct ifcProduct, int bufferViewIndex, int byteOffset, int count) { 500 | ObjectNode accessor = OBJECT_MAPPER.createObjectNode(); 501 | accessor.put("bufferView", bufferViewIndex); 502 | accessor.put("byteOffset", byteOffset); 503 | // accessor.put("byteStride", 16); 504 | accessor.put("componentType", UNSIGNED_BYTE); 505 | accessor.put("normalized", true); 506 | accessor.put("count", count); 507 | accessor.put("type", "VEC4"); 508 | 509 | // ArrayNode min = OBJECT_MAPPER.createArrayNode(); 510 | // min.add(-1d); 511 | // min.add(-1d); 512 | // min.add(-1d); 513 | // ArrayNode max = OBJECT_MAPPER.createArrayNode(); 514 | // max.add(1); 515 | // max.add(1); 516 | // max.add(1); 517 | // 518 | // accessor.set("min", min); 519 | // accessor.set("max", max); 520 | 521 | accessors.add(accessor); 522 | 523 | return accessors.size() - 1; 524 | } 525 | 526 | private int addVerticesAccessor(IfcProduct ifcProduct, int bufferViewIndex, int startPosition, int count) throws SerializerException { 527 | if (count <= 0) { 528 | throw new SerializerException("Count <= 0"); 529 | } 530 | GeometryData data = ifcProduct.getGeometry().getData(); 531 | ByteBuffer verticesBuffer = ByteBuffer.wrap(data.getVertices().getData()); 532 | 533 | ObjectNode accessor = OBJECT_MAPPER.createObjectNode(); 534 | accessor.put("bufferView", bufferViewIndex); 535 | accessor.put("byteOffset", startPosition); 536 | // accessor.put("byteStride", 12); 537 | accessor.put("componentType", FLOAT); 538 | accessor.put("count", count); 539 | accessor.put("type", "VEC3"); 540 | 541 | verticesBuffer.order(ByteOrder.LITTLE_ENDIAN); 542 | double[] min = { Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE }; 543 | double[] max = { -Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE }; 544 | for (int i = 0; i < verticesBuffer.capacity(); i += 24) { 545 | for (int j = 0; j < 3; j++) { 546 | double val = verticesBuffer.getDouble(i + (j * 8)); 547 | if (val > max[j]) { 548 | max[j] = val; 549 | } 550 | if (val < min[j]) { 551 | min[j] = val; 552 | } 553 | } 554 | } 555 | 556 | ArrayNode minNode = OBJECT_MAPPER.createArrayNode(); 557 | minNode.add(min[0]); 558 | minNode.add(min[1]); 559 | minNode.add(min[2]); 560 | ArrayNode maxNode = OBJECT_MAPPER.createArrayNode(); 561 | maxNode.add(max[0]); 562 | maxNode.add(max[1]); 563 | maxNode.add(max[2]); 564 | 565 | accessor.set("min", minNode); 566 | accessor.set("max", maxNode); 567 | 568 | accessors.add(accessor); 569 | 570 | return accessors.size() - 1; 571 | } 572 | 573 | private int addIndicesAccessor(IfcProduct ifcProduct, int bufferViewIndex, int offsetBytes, int count, int[] min, int[] max) throws SerializerException { 574 | if (count <= 0) { 575 | throw new SerializerException(count + " <= 0"); 576 | } 577 | 578 | ObjectNode accessor = OBJECT_MAPPER.createObjectNode(); 579 | accessor.put("bufferView", bufferViewIndex); 580 | accessor.put("byteOffset", offsetBytes); 581 | // accessor.put("byteStride", 0); 582 | accessor.put("componentType", UNSIGNED_INT); 583 | accessor.put("count", count); 584 | accessor.put("type", "SCALAR"); 585 | 586 | // ArrayNode minArray = OBJECT_MAPPER.createArrayNode(); 587 | // ArrayNode maxArray = OBJECT_MAPPER.createArrayNode(); 588 | // 589 | // for (int i=0; i 0) { 688 | dataOutputStream.write(pad(rest, (char)0)); 689 | } 690 | } 691 | 692 | private byte[] pad(int length, char c) { 693 | byte[] result = new byte[length]; 694 | for (int i=0; i 0) { 706 | dataOutputStream.write(pad(rest, ' ')); 707 | } 708 | } 709 | } -------------------------------------------------------------------------------- /Gltf/src/org/bimserver/gltf/BinaryGltfSerializerPlugin.java: -------------------------------------------------------------------------------- 1 | package org.bimserver.gltf; 2 | 3 | /****************************************************************************** 4 | * Copyright (C) 2009-2019 BIMserver.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see {@literal}. 18 | *****************************************************************************/ 19 | 20 | import java.io.IOException; 21 | import java.nio.file.Files; 22 | import java.nio.file.Path; 23 | import java.util.Collections; 24 | import java.util.HashSet; 25 | import java.util.Set; 26 | 27 | import org.bimserver.emf.Schema; 28 | import org.bimserver.plugins.PluginConfiguration; 29 | import org.bimserver.plugins.PluginContext; 30 | import org.bimserver.plugins.SchemaName; 31 | import org.bimserver.plugins.serializers.AbstractSerializerPlugin; 32 | import org.bimserver.plugins.serializers.Serializer; 33 | import org.bimserver.shared.exceptions.PluginException; 34 | 35 | public class BinaryGltfSerializerPlugin extends AbstractSerializerPlugin { 36 | 37 | private byte[] vertexColorFragmentShaderBytes; 38 | private byte[] vertexColorVertexShaderBytes; 39 | private byte[] materialColorFragmentShaderBytes; 40 | private byte[] materialColorVertexShaderBytes; 41 | 42 | @Override 43 | public void init(PluginContext pluginContext, PluginConfiguration systemSettings) throws PluginException { 44 | Path vertexColorFragmentShaderPath = pluginContext.getRootPath().resolve("shaders/fragmentcolor.shader"); 45 | Path vertexColorVertexShaderPath = pluginContext.getRootPath().resolve("shaders/vertexcolor.shader"); 46 | Path materialColorFragmentShaderPath = pluginContext.getRootPath().resolve("shaders/fragmentmaterial.shader"); 47 | Path materialColorVertexShaderPath = pluginContext.getRootPath().resolve("shaders/vertexmaterial.shader"); 48 | 49 | try { 50 | vertexColorFragmentShaderBytes = Files.readAllBytes(vertexColorFragmentShaderPath); 51 | vertexColorVertexShaderBytes = Files.readAllBytes(vertexColorVertexShaderPath); 52 | materialColorFragmentShaderBytes = Files.readAllBytes(materialColorFragmentShaderPath); 53 | materialColorVertexShaderBytes = Files.readAllBytes(materialColorVertexShaderPath); 54 | } catch (IOException e) { 55 | throw new PluginException(e); 56 | } 57 | } 58 | 59 | @Override 60 | public Serializer createSerializer(PluginConfiguration plugin) { 61 | return new BinaryGltfSerializer(vertexColorFragmentShaderBytes, vertexColorVertexShaderBytes, materialColorFragmentShaderBytes, materialColorVertexShaderBytes); 62 | } 63 | 64 | @Override 65 | public Set getSupportedSchemas() { 66 | return Collections.singleton(Schema.IFC2X3TC1); 67 | } 68 | 69 | @Override 70 | public Set getRequiredGeometryFields() { 71 | Set set = new HashSet<>(); 72 | set.add("indices"); 73 | set.add("vertices"); 74 | set.add("normals"); 75 | set.add("colorsQuantized"); 76 | return set; 77 | } 78 | 79 | @Override 80 | public String getDefaultExtension() { 81 | return "glb"; 82 | } 83 | 84 | @Override 85 | public String getDefaultContentType() { 86 | return "model/gltf+binary"; 87 | } 88 | 89 | @Override 90 | public String getOutputFormat(Schema schema) { 91 | return SchemaName.GLTF_BIN_1_0.name(); 92 | } 93 | } -------------------------------------------------------------------------------- /Gltf/src/org/bimserver/gltf/BinaryGltfSerializerPlugin2.java: -------------------------------------------------------------------------------- 1 | package org.bimserver.gltf; 2 | 3 | /****************************************************************************** 4 | * Copyright (C) 2009-2019 BIMserver.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see {@literal}. 18 | *****************************************************************************/ 19 | 20 | import java.util.Collections; 21 | import java.util.HashSet; 22 | import java.util.Set; 23 | 24 | import org.bimserver.emf.Schema; 25 | import org.bimserver.plugins.PluginConfiguration; 26 | import org.bimserver.plugins.PluginContext; 27 | import org.bimserver.plugins.SchemaName; 28 | import org.bimserver.plugins.serializers.AbstractSerializerPlugin; 29 | import org.bimserver.plugins.serializers.Serializer; 30 | import org.bimserver.shared.exceptions.PluginException; 31 | 32 | public class BinaryGltfSerializerPlugin2 extends AbstractSerializerPlugin { 33 | 34 | @Override 35 | public void init(PluginContext pluginContext, PluginConfiguration systemSettings) throws PluginException { 36 | } 37 | 38 | @Override 39 | public Serializer createSerializer(PluginConfiguration plugin) { 40 | return new BinaryGltfSerializer2(); 41 | } 42 | 43 | @Override 44 | public Set getSupportedSchemas() { 45 | return Collections.singleton(Schema.IFC2X3TC1); 46 | } 47 | 48 | @Override 49 | public Set getRequiredGeometryFields() { 50 | Set set = new HashSet<>(); 51 | set.add("indices"); 52 | set.add("vertices"); 53 | set.add("normals"); 54 | set.add("colorsQuantized"); 55 | return set; 56 | } 57 | 58 | @Override 59 | public String getDefaultExtension() { 60 | return "glb"; 61 | } 62 | 63 | @Override 64 | public String getDefaultContentType() { 65 | return "model/gltf-binary"; 66 | } 67 | 68 | @Override 69 | public String getOutputFormat(Schema schema) { 70 | return SchemaName.GLTF_BIN_2_0.name(); 71 | } 72 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GltfSerializers 2 | 3 | This is mainly just a demo, but it might evolve into a useful glTF serializer with a bit more effort. 4 | 5 | ## 6 | 7 | > Note: This plugin only works with the (yet unreleased) version 1.5 of BIMserver 8 | 9 | ## Implemented: 10 | - https://github.com/KhronosGroup/glTF/blob/master/extensions/Khronos/KHR_binary_glTF/README.md 11 | - Using vertex-colors where available, otherwise it defaults to a set of predefined type-colors 12 | 13 | > Update glTF 2.0 does not use an extension anymore but natively supports embedding all data in one binary file. This is now also implementen as the glTF 2.0 serializer in this plugin. 14 | 15 | ## Usage 16 | - Install this plugin 17 | - Download .glb files of your models with the "Binary glTF Serializer" 18 | 19 | ## Using with CesiumJS 20 | - You can use a little bit of boilerplate code from the CesiumLoader project 21 | - https://github.com/opensourceBIM/CesiumLoader 22 | 23 | ## TODO 24 | 25 | - Convert to meters (the default length unit in glTF and Cesium) server-side 26 | - Transparency does not seem to work (for window for example) 27 | - Better default colors for objects with no vertex-colors 28 | - Implement non-binary version as well 29 | - Use object instancing 30 | - Actually reuse vertices (requires a BIMserver change as well, vertices are now stored on a per-object basis). At the moment all object's vertices array are just concatenated into one big buffer (with separate views for each object). 31 | - Do something with the normals in the shaders (maybe add some lights to the scene as well, not sure how that works in Cesium) 32 | --------------------------------------------------------------------------------