├── 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 |
--------------------------------------------------------------------------------