
org.bimserver.serializers.binarygeometry.BinaryGeometryMessagingStreamingSerializer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of binaryserializers Show documentation
Show all versions of binaryserializers Show documentation
BIMserver plugins that provides binary serializers
The newest version!
package org.bimserver.serializers.binarygeometry;
/******************************************************************************
* Copyright (C) 2009-2019 BIMserver.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {@literal }.
*****************************************************************************/
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.bimserver.BimserverDatabaseException;
import org.bimserver.database.queries.om.QueryException;
import org.bimserver.emf.PackageMetaData;
import org.bimserver.geometry.Matrix;
import org.bimserver.geometry.Matrix3;
import org.bimserver.geometry.Vector;
import org.bimserver.geometry.Vector3D;
import org.bimserver.interfaces.objects.SVector3f;
import org.bimserver.models.geometry.GeometryPackage;
import org.bimserver.plugins.LittleEndianSerializerDataOutputStream;
import org.bimserver.plugins.PluginManagerInterface;
import org.bimserver.plugins.SerializerDataOutputStream;
import org.bimserver.plugins.serializers.MessagingStreamingSerializer;
import org.bimserver.plugins.serializers.ObjectProvider;
import org.bimserver.plugins.serializers.ProgressReporter;
import org.bimserver.plugins.serializers.ProjectInfo;
import org.bimserver.plugins.serializers.SerializerException;
import org.bimserver.serializers.binarygeometry.clipping.Point;
import org.bimserver.shared.AbstractHashMapVirtualObject;
import org.bimserver.shared.HashMapVirtualObject;
import org.bimserver.shared.HashMapWrappedVirtualObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.primitives.UnsignedBytes;
public class BinaryGeometryMessagingStreamingSerializer implements MessagingStreamingSerializer {
private static final Logger LOGGER = LoggerFactory.getLogger(BinaryGeometryMessagingStreamingSerializer.class);
/*
* Format history (starting at version 8):
*
* Version 8:
* - Using short instead of int for indices. SceneJS was converting the indices to Uint16 anyways, so this saves bytes and a conversion on the client-side
* Version 9:
* - Sending the materials/colors for splitted geometry as well, before sending the actual parts
* - Aligning bytes to 8s instead of 4s when sending splitted geometry
* - Incrementing splitcounter instead of decrementing (no idea why it was doing that)
* Version 10:
* - Sending the materials/colors for parts as well again
* Version 11:
* - Added ability to send one specific color for all geometry contained in a GeometryData object
* Version 12:
* - Added boolean value that indicates whether an object/geometry has transparency
* Version 13:
* - Added integer value that indicates how many times geometry is being reused
* Version 14:
* - Added ifcproduct oid to simplify client-side operations, also added type of object, also added type for GeometryData
* Version 15:
* - Also sending a multiplier to convert to mm
* Version 16:
* - Just a version bump to make sure older client will err-out
* Version 17:
* - Sending the amount of colors now for GeometryInfo and also sending 1 byte to indicate whether geometry is in a completeBuffer
* - Added writePreparedBuffer (should not have an impact if you don't send the prepareBuffers option)
* Version 18:
* - Oct-encoding of normals, mat4 globalTransformation input is now vec3 globalTranslationVector
* Version 19:
* - Added optional generateLineRenders, protocol has changed because the counts are always sent regardless of setting
* Version 20:
* - Added ifcProductPid
*/
private static final byte FORMAT_VERSION = 20;
private enum Mode {
LOAD,
START,
DATA,
PREPARED_BUFFER_INIT,
PREPARED_BUFFER_TRANSPARENT,
PREPARED_BUFFER_OPAQUE,
END
}
private enum MessageType {
INIT((byte)0),
GEOMETRY_TRIANGLES_PARTED((byte)3),
GEOMETRY_TRIANGLES((byte)1),
GEOMETRY_INFO((byte)5),
MINIMAL_GEOMETRY_INFO((byte)9),
PREPARED_BUFFER_TRANSPARENT((byte)7),
PREPARED_BUFFER_OPAQUE((byte)8),
PREPARED_BUFFER_TRANSPARENT_INIT((byte)10),
PREPARED_BUFFER_OPAQUE_INIT((byte)11),
END((byte)6);
private byte id;
private MessageType(byte id) {
this.id = id;
}
public byte getId() {
return id;
}
}
private boolean splitGeometry = true;
private boolean useSingleColors = false;
private boolean quantizeNormals = false;
private boolean octEncodeNormals = false;
private boolean quantizeVertices = false;
private boolean quantizeColors = false;
private boolean prepareBuffers = false;
private boolean normalizeUnitsToMM = false;
private boolean useSmallInts = true;
private boolean reportProgress = true;
private boolean useUuidAndRid = false;
private Map vertexQuantizationMatrices;
private GeometryMainBuffer transparentGeometryBuffer;
private GeometryMainBuffer opaqueGeometryBuffer;
private final Map oidToGeometryData = new HashMap<>();
private final Map dataToGeometryInfo = new HashMap<>();
private double[] vertexQuantizationMatrix;
private Mode mode = Mode.LOAD;
private long splitCounter = 0;
private ObjectProvider objectProvider;
private ProjectInfo projectInfo;
private SerializerDataOutputStream serializerDataOutputStream;
private HashMapVirtualObject next;
private ProgressReporter progressReporter;
private int nrObjectsWritten;
private int size;
private ByteBuffer lastTransformation;
private Set reusedDataOids;
private double[] globalTranslationVector;
private boolean generateLineRenders;
private float[] lastNormal;
@Override
public void init(ObjectProvider objectProvider, ProjectInfo projectInfo, PluginManagerInterface pluginManager, PackageMetaData packageMetaData) throws SerializerException {
this.objectProvider = objectProvider;
this.projectInfo = projectInfo;
ObjectNode queryNode = objectProvider.getQueryNode();
if (queryNode.has("tiles")) {
ObjectNode tilesNode = (ObjectNode)queryNode.get("tiles");
if (tilesNode.has("geometryDataToReuse") && !tilesNode.get("geometryDataToReuse").isNull()) {
ArrayNode reuseNodes = (ArrayNode)tilesNode.get("geometryDataToReuse");
this.reusedDataOids = new HashSet<>();
for (JsonNode jsonNode : reuseNodes) {
this.reusedDataOids.add(jsonNode.asLong());
}
}
}
if (queryNode.has("loaderSettings")) {
ObjectNode geometrySettings = (ObjectNode) queryNode.get("loaderSettings");
useUuidAndRid = geometrySettings.has("useUuidAndRid") && geometrySettings.get("useUuidAndRid").asBoolean();
useSingleColors = geometrySettings.has("useObjectColors") && geometrySettings.get("useObjectColors").asBoolean();
splitGeometry = geometrySettings.has("splitGeometry") && geometrySettings.get("splitGeometry").asBoolean();
quantizeNormals = geometrySettings.has("quantizeNormals") && geometrySettings.get("quantizeNormals").asBoolean();
octEncodeNormals = geometrySettings.has("octEncodeNormals") && geometrySettings.get("octEncodeNormals").asBoolean();
quantizeVertices = geometrySettings.has("quantizeVertices") && geometrySettings.get("quantizeVertices").asBoolean();
quantizeColors = geometrySettings.has("quantizeColors") && geometrySettings.get("quantizeColors").asBoolean();
normalizeUnitsToMM = geometrySettings.has("normalizeUnitsToMM") && geometrySettings.get("normalizeUnitsToMM").asBoolean();
reportProgress = !geometrySettings.has("reportProgress") || geometrySettings.get("reportProgress").asBoolean(); // default is true for backwards compat
useSmallInts = !geometrySettings.has("useSmallInts") || geometrySettings.get("useSmallInts").asBoolean(); // default is true for backwards compat
prepareBuffers = geometrySettings.has("prepareBuffers") && geometrySettings.get("prepareBuffers").asBoolean();
generateLineRenders = geometrySettings.has("generateLineRenders") && geometrySettings.get("generateLineRenders").asBoolean();
if (geometrySettings.has("globalTranslationVector")) {
this.globalTranslationVector = new double[3];
ArrayNode matrixNode = (ArrayNode) geometrySettings.get("globalTranslationVector");
int i=0;
for (JsonNode v : matrixNode) {
this.globalTranslationVector[i++] = v.asDouble();
}
}
if (prepareBuffers) {
transparentGeometryBuffer = new GeometryMainBuffer();
opaqueGeometryBuffer = new GeometryMainBuffer();
}
if (quantizeVertices) {
if (queryNode.has("loaderSettings")) {
ArrayNode vqmNode = (ArrayNode) geometrySettings.get("vertexQuantizationMatrix");
if (vqmNode != null) {
vertexQuantizationMatrix = new double[16];
int i=0;
for (JsonNode v : vqmNode) {
vertexQuantizationMatrix[i++] = v.doubleValue();
}
// Matrix.dump(vertexQuantizationMatrix);
}
}
ObjectNode vqmNode = (ObjectNode) geometrySettings.get("vertexQuantizationMatrices");
if (vqmNode != null) {
Iterator fieldNames = vqmNode.fieldNames();
vertexQuantizationMatrices = new HashMap<>();
while (fieldNames.hasNext()) {
String key = fieldNames.next();
long croid = Long.parseLong(key);
float[] vertexQuantizationMatrix = new float[16];
ArrayNode mNode = (ArrayNode) vqmNode.get(key);
vertexQuantizationMatrices.put(croid, vertexQuantizationMatrix);
int i=0;
for (JsonNode v : mNode) {
vertexQuantizationMatrix[i++] = v.floatValue();
}
}
}
}
}
}
@Override
public boolean writeMessage(OutputStream outputStream, ProgressReporter progressReporter) throws IOException, SerializerException {
this.progressReporter = progressReporter;
serializerDataOutputStream = null;
if (outputStream instanceof SerializerDataOutputStream) {
serializerDataOutputStream = (SerializerDataOutputStream)outputStream;
} else {
serializerDataOutputStream = new LittleEndianSerializerDataOutputStream(outputStream);
}
switch (mode) {
case LOAD: {
load();
mode = Mode.START;
// Explicitly no break here, move on to start right away
}
case START:
writeStart();
serializerDataOutputStream.align8();
if (next == null) {
mode = Mode.END;
} else {
mode = Mode.DATA;
}
break;
case DATA:
if (!writeData()) {
serializerDataOutputStream.align8();
if (prepareBuffers) {
mode = Mode.PREPARED_BUFFER_OPAQUE;
} else {
mode = Mode.END;
}
return true;
}
serializerDataOutputStream.align8();
break;
case PREPARED_BUFFER_OPAQUE:
if (!writePreparedBuffer(mode)) {
mode = Mode.PREPARED_BUFFER_TRANSPARENT;
}
break;
case PREPARED_BUFFER_TRANSPARENT:
if (!writePreparedBuffer(mode)) {
mode = Mode.END;
}
break;
case END:
writeEnd();
serializerDataOutputStream.align8();
return false;
default:
break;
}
return true;
}
private boolean writePreparedBuffer(Mode mode) throws IOException, SerializerException {
try {
GeometryMainBuffer geometryMainBuffer = getCurrentMainBuffer();
if (geometryMainBuffer == null) {
return false;
}
GeometryBuffer geometryBuffer = geometryMainBuffer.getCurrentReadBuffer();
while (geometryBuffer == null || geometryBuffer.isEmpty() || !geometryBuffer.hasNextGeometryMapping()) {
if (geometryMainBuffer.hasNextReadBuffer()) {
geometryBuffer = geometryMainBuffer.getNextReadBuffer();
} else {
return false;
}
}
if (!geometryBuffer.initSent()) {
ByteBuffer buffer = ByteBuffer.allocate(25).order(ByteOrder.LITTLE_ENDIAN);
buffer.put(mode == Mode.PREPARED_BUFFER_OPAQUE ? MessageType.PREPARED_BUFFER_OPAQUE_INIT.getId() : MessageType.PREPARED_BUFFER_TRANSPARENT_INIT.getId());
buffer.putInt(geometryBuffer.getNrObjects());
buffer.putInt(geometryBuffer.getNrIndices());
buffer.putInt(generateLineRenders ? geometryBuffer.getNrLineIndices() : 0);
buffer.putInt(geometryBuffer.getNrVertices());
buffer.putInt(geometryBuffer.getNrVertices());
buffer.putInt(geometryBuffer.getNrColors());
serializerDataOutputStream.write(buffer.array());
serializerDataOutputStream.align8();
geometryBuffer.setInitSent();
return true;
}
int vertexPosition = 0;
serializerDataOutputStream.writeByte(mode == Mode.PREPARED_BUFFER_TRANSPARENT ? 7 : 8);
GeometrySubBuffer geometryMapping = geometryBuffer.getNextGeometryMapping();
ByteBuffer buffer = ByteBuffer.allocate(geometryMapping.getPreparedByteSize()).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(geometryMapping.getNrObjects());
buffer.putInt(geometryMapping.getNrIndices());
buffer.putInt(generateLineRenders ? geometryMapping.getNrLineIndices() : 0);
buffer.putInt(geometryMapping.getNrVertices());
buffer.putInt(geometryMapping.getNrVertices());
buffer.putInt(geometryMapping.getNrColors());
int indicesStartByte = 24;
int lineIndicesStartByte = indicesStartByte + geometryMapping.getNrIndices() * 4;
int originalLineIndicesStartByte = lineIndicesStartByte;
int indicesMappingStartByte = generateLineRenders ? (lineIndicesStartByte + geometryMapping.getNrLineIndices() * 4) : lineIndicesStartByte;
int verticesStartByte = indicesMappingStartByte + geometryMapping.getNrObjects() * ((44 + (generateLineRenders ? 8 : 0)) + (useUuidAndRid ? 24 : 0)) + geometryMapping.getTotalColorPackSize();
int normalsStartByte = verticesStartByte + geometryMapping.getNrVertices() * (quantizeVertices ? 2 : 4);
int endByte = normalsStartByte + (quantizeNormals ? (octEncodeNormals ? geometryMapping.getNrVertices() / 3 * 2 : geometryMapping.getNrVertices()) : geometryMapping.getNrVertices() * 4);
int baseIndex = geometryMapping.getBaseIndex();
for (HashMapVirtualObject info : geometryMapping.keySet()) {
HashMapVirtualObject data = geometryMapping.get(info);
// short cid = (short) data.get("type");
// EClass eClass = objectProvider.getEClassForCid(cid);
DoubleBuffer transformation = ByteBuffer.wrap((byte[]) info.eGet(info.eClass().getEStructuralFeature("transformation"))).order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer();
double[] ms = new double[16];
for (int i=0; i<16; i++) {
ms[i] = transformation.get();
}
AbstractHashMapVirtualObject indicesBuffer = data.getDirectFeature(GeometryPackage.eINSTANCE.getGeometryData_Indices());
byte[] normals = null;
byte[] vertices = null;
AbstractHashMapVirtualObject normalsBuffer = data.getDirectFeature(GeometryPackage.eINSTANCE.getGeometryData_Normals());
normals = (byte[]) normalsBuffer.get("data");
AbstractHashMapVirtualObject verticesBuffer = data.getDirectFeature(GeometryPackage.eINSTANCE.getGeometryData_Vertices());
vertices = (byte[]) verticesBuffer.get("data");
IntBuffer indices = ByteBuffer.wrap((byte[]) indicesBuffer.get("data")).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
ByteBuffer vertexByteBuffer = ByteBuffer.wrap(vertices).order(ByteOrder.LITTLE_ENDIAN);
ByteBuffer normalsByteBuffer = ByteBuffer.wrap(normals).order(ByteOrder.LITTLE_ENDIAN);
AbstractHashMapVirtualObject lineIndicesBuffer = data.getDirectFeature(GeometryPackage.eINSTANCE.getGeometryData_LineIndices());
byte[] lineIndicesBytes = null;
if (lineIndicesBuffer != null) {
lineIndicesBytes = (byte[]) lineIndicesBuffer.get("data");
}
Long oid = (Long)info.get("ifcProductOid");
buffer.putLong(indicesMappingStartByte, oid);
indicesMappingStartByte += 8;
if (useUuidAndRid) {
buffer.position(indicesMappingStartByte);
byte[] bytes = (byte[])info.get("ifcProductUuid");
buffer.put(bytes);
indicesMappingStartByte += 16;
int pid = (int)info.get("ifcProductPid");
buffer.putInt(indicesMappingStartByte, pid);
indicesMappingStartByte += 4;
int rid = info.getRid();// (int)info.get("ifcProductRid");
buffer.putInt(indicesMappingStartByte, rid);
indicesMappingStartByte += 4;
}
// Start index for object
int indicesStart = (indicesStartByte - 24) / 4;
buffer.putInt(indicesMappingStartByte, indicesStart);
indicesMappingStartByte += 4;
// Start line index for object
buffer.putInt(indicesMappingStartByte, (lineIndicesStartByte - originalLineIndicesStartByte) / 4);
indicesMappingStartByte += 4;
// Nr of indices
buffer.putInt(indicesMappingStartByte, indices.capacity());
indicesMappingStartByte += 4;
// Nr of line indices
buffer.putInt(indicesMappingStartByte, lineIndicesBytes == null ? 0 :lineIndicesBytes.length / 4);
indicesMappingStartByte += 4;
// Nr of vertices
buffer.putInt(indicesMappingStartByte, vertices.length / 8);
indicesMappingStartByte += 4;
int minIndex = -1;
int maxIndex = -1;
for (int i=0; i maxIndex) {
maxIndex = modifiedIndex;
}
buffer.putInt(indicesStartByte, modifiedIndex);
indicesStartByte += 4;
}
int minLineIndex = -1;
int maxLineIndex = -1;
if (generateLineRenders) {
IntBuffer lineIndicesInt = ByteBuffer.wrap(lineIndicesBytes).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
for (int i=0; i maxLineIndex) {
maxLineIndex = modifiedIndex;
}
buffer.putInt(lineIndicesStartByte, modifiedIndex);
lineIndicesStartByte += 4;
}
}
buffer.putInt(indicesMappingStartByte, minIndex);
indicesMappingStartByte += 4;
buffer.putInt(indicesMappingStartByte, maxIndex);
indicesMappingStartByte += 4;
if (generateLineRenders) {
buffer.putInt(indicesMappingStartByte, minLineIndex);
indicesMappingStartByte += 4;
buffer.putInt(indicesMappingStartByte, maxLineIndex);
indicesMappingStartByte += 4;
}
float density = (float) info.get("density");
buffer.putFloat(indicesMappingStartByte, density);
indicesMappingStartByte += 4;
HashMapVirtualObject colorPack = (HashMapVirtualObject) data.getDirectFeature(GeometryPackage.eINSTANCE.getGeometryData_ColorPack());
byte[] colorPackData = colorPack == null ? null : (byte[]) colorPack.eGet(GeometryPackage.eINSTANCE.getColorPack_Data());
if (colorPackData == null || colorPackData.length == 0) {
buffer.putInt(indicesMappingStartByte, 0);
indicesMappingStartByte += 4;
} else {
int colorPackSize = colorPackData.length / 8;
buffer.putInt(indicesMappingStartByte, colorPackSize);
indicesMappingStartByte += 4;
buffer.position(indicesMappingStartByte);
buffer.put(colorPackData);
indicesMappingStartByte += colorPackData.length;
}
double[] in = new double[4];
double[] vertex = new double[4];
double[] result = new double[4];
in[3] = 1;
vertex[3] = 1;
int nrPos = vertexByteBuffer.capacity() / 8;
for (int i=0; i -0.0001 && normal[i] < 0.0001) {
normal[i] = 0;
}
}
}
private void reorderForLineRendering(IntBuffer indices, DoubleBuffer vertices, FloatBuffer normals) {
Mesh mesh = new Mesh(indices, vertices, normals);
Mesh newMesh = mesh.copy();
MeshChanger meshChanger = new MeshChanger(mesh, newMesh);
Map lineSegments = new HashMap<>();
for (int i=0; i= 0f ? 1 : -1;
}
// function octEncodeVec3(array, i, xfunc, yfunc) {
// var x = array[i ] / (Math.abs(array[i]) + Math.abs(array[i + 1]) + Math.abs(array[i + 2]));
// var y = array[i + 1] / (Math.abs(array[i]) + Math.abs(array[i + 1]) + Math.abs(array[i + 2]));
//
// if (array[i + 2] < 0) {
// var tempx = x;
// var tempy = y;
// tempx = (1 - Math.abs(y)) * (x >= 0 ? 1 : -1);
// tempy = (1 - Math.abs(x)) * (y >= 0 ? 1 : -1);
// x = tempx;
// y = tempy;
// }
//
// return new Int8Array([
// Math[xfunc](x * 127.5 + (x < 0 ? -1 : 0)),
// Math[yfunc](y * 127.5 + (y < 0 ? -1 : 0))
// ]);
//
// }
private void writeNormalToOct(ByteBuffer buffer, int normalsStartByte, float[] normal) {
float x = Math.abs(normal[0]) + Math.abs(normal[1]) + Math.abs(normal[2]);
float[] p = new float[] {normal[0] / x, normal[1] / x};
if (normal[2] <= 0f) {
float a = (1f - Math.abs(p[0])) * signNotZero(p[0]);
float b = (1f - Math.abs(p[1])) * signNotZero(p[1]);
p = new float[]{a, b};
}
byte a = (byte)(p[0] * 127f);
buffer.put(normalsStartByte, a);
byte b = (byte)(p[1] * 127f);
buffer.put(normalsStartByte + 1, b);
// System.out.println(normal[0] + ", " + normal[1] + ", " + normal[2]);
// if (lastNormal == null || !Arrays.equals(this.lastNormal, normal)) {
// System.out.println(normal[0] + ", " + normal[1] + ", " + normal[2]);
// System.out.println(a + ", " + b);
// if (this.lastNormal == null) {
// this.lastNormal = new float[3];
// }
// System.arraycopy(normal, 0, this.lastNormal, 0, 3);
// }
}
private void load() throws SerializerException {
// long start = System.nanoTime();
if (reportProgress) {
size = 0;
HashMapVirtualObject next = null;
try {
next = objectProvider.next();
while (next != null) {
if (next.eClass() == GeometryPackage.eINSTANCE.getGeometryInfo()) {
size++;
}
next = objectProvider.next();
}
} catch (BimserverDatabaseException e) {
throw new SerializerException(e);
}
try {
objectProvider = objectProvider.copy();
} catch (IOException e) {
e.printStackTrace();
} catch (QueryException e) {
e.printStackTrace();
}
}
try {
this.next = objectProvider.next();
} catch (BimserverDatabaseException e) {
e.printStackTrace();
}
// long end = System.nanoTime();
// System.out.println(((end - start) / 1000000) + " ms prepare time");
}
private boolean writeEnd() throws IOException {
serializerDataOutputStream.write(MessageType.END.getId());
return true;
}
private void writeStart() throws IOException {
// Identifier for clients to determine if this server is even serving binary geometry
serializerDataOutputStream.writeByte(MessageType.INIT.getId());
serializerDataOutputStream.writeUTF("BGS");
// Version of the current format being outputted, should be changed for every (released) change in protocol
serializerDataOutputStream.writeByte(FORMAT_VERSION);
serializerDataOutputStream.writeFloat(projectInfo.getMultiplierToMm());
serializerDataOutputStream.align8();
// TODO These are known to be wrong for multi-roid queries
SVector3f minBounds = projectInfo.getMinBounds();
SVector3f maxBounds = projectInfo.getMaxBounds();
if (normalizeUnitsToMM && projectInfo.getMultiplierToMm() != 1f) {
serializerDataOutputStream.writeDouble(minBounds.getX() * projectInfo.getMultiplierToMm());
serializerDataOutputStream.writeDouble(minBounds.getY() * projectInfo.getMultiplierToMm());
serializerDataOutputStream.writeDouble(minBounds.getZ() * projectInfo.getMultiplierToMm());
serializerDataOutputStream.writeDouble(maxBounds.getX() * projectInfo.getMultiplierToMm());
serializerDataOutputStream.writeDouble(maxBounds.getY() * projectInfo.getMultiplierToMm());
serializerDataOutputStream.writeDouble(maxBounds.getZ() * projectInfo.getMultiplierToMm());
} else {
serializerDataOutputStream.writeDouble(minBounds.getX());
serializerDataOutputStream.writeDouble(minBounds.getY());
serializerDataOutputStream.writeDouble(minBounds.getZ());
serializerDataOutputStream.writeDouble(maxBounds.getX());
serializerDataOutputStream.writeDouble(maxBounds.getY());
serializerDataOutputStream.writeDouble(maxBounds.getZ());
}
}
private boolean writeData() throws IOException, SerializerException {
if (next == null) {
return false;
}
boolean wroteSomething = false;
while (!wroteSomething && next != null) {
if (GeometryPackage.eINSTANCE.getGeometryInfo() == next.eClass()) {
writeGeometryInfo(next);
wroteSomething = true;
} else if (GeometryPackage.eINSTANCE.getGeometryData() == next.eClass()) {
wroteSomething = writeGeometryData(next, next.getOid());
}
try {
next = objectProvider.next();
} catch (BimserverDatabaseException e) {
LOGGER.error("", e);
}
}
return next != null;
}
private void writeGeometryInfo(HashMapVirtualObject info) throws IOException, SerializerException {
boolean hasTransparancy = (boolean)info.eGet(GeometryPackage.eINSTANCE.getGeometryInfo_HasTransparency());
long geometryDataId = (long)info.eGet(info.eClass().getEStructuralFeature("data"));
boolean inPreparedBuffer = false;
long oid = (long) info.eGet(GeometryPackage.eINSTANCE.getGeometryInfo_IfcProductOid());
if (prepareBuffers && (reusedDataOids == null || !reusedDataOids.contains(geometryDataId))) {
inPreparedBuffer = true;
if (oidToGeometryData.containsKey(geometryDataId)) {
GeometryMainBuffer geometryMainBuffer = getCurrentMainBuffer(hasTransparancy);
GeometryBuffer currentBuffer = geometryMainBuffer.getCurrentWriteBuffer();
GeometrySubBuffer geometryMapping = currentBuffer.getCurrentGeometryMapping(true);
HashMapVirtualObject gd = oidToGeometryData.get(geometryDataId);
geometryMapping.put(info, gd);
updateSize(gd, currentBuffer);
} else {
dataToGeometryInfo.put(geometryDataId, info);
}
writeMinimalGeometryInfo(info);
return;
}
byte[] transformation = (byte[]) info.eGet(info.eClass().getEStructuralFeature("transformation"));
long dataOid = geometryDataId;
serializerDataOutputStream.writeByte(MessageType.GEOMETRY_INFO.getId());
serializerDataOutputStream.writeByte(inPreparedBuffer ? (byte)1 : (byte)0);
serializerDataOutputStream.writeLong(oid);
if (useUuidAndRid) {
serializerDataOutputStream.write((byte[])info.get("ifcProductUuid"));
serializerDataOutputStream.writeInt((int)info.get("ifcProductPid"));
serializerDataOutputStream.writeInt(info.getRid());
}
String type = objectProvider.getEClassForOid(oid).getName();
serializerDataOutputStream.writeUTF(type);
int nrColors = (int)info.eGet(GeometryPackage.eINSTANCE.getGeometryInfo_NrColors());
if (nrColors == 0) {
int nrVertices = (int)info.eGet(GeometryPackage.eINSTANCE.getGeometryInfo_NrVertices());
nrColors = nrVertices / 3 * 4;
}
serializerDataOutputStream.writeInt(nrColors);
serializerDataOutputStream.align8();
serializerDataOutputStream.ensureExtraCapacity(24);
serializerDataOutputStream.writeLongUnchecked(info.getRoid());
serializerDataOutputStream.writeLongUnchecked(info.getOid());
serializerDataOutputStream.writeLongUnchecked(hasTransparancy ? 1 : 0);
writeBounds(info);
lastTransformation = ByteBuffer.wrap(transformation);
lastTransformation.order(ByteOrder.LITTLE_ENDIAN);
ByteBuffer newTransformation = lastTransformation;
// Apply the globalTranslation (usually used to move the model to around 0, 0, 0)
if (globalTranslationVector != null) {
DoubleBuffer asDoubleBuffer = lastTransformation.asDoubleBuffer();
double[] ms = new double[16];
for (int i=0; i<16; i++) {
ms[i] = asDoubleBuffer.get();
}
double[] tmp = new double[16];
double[] translationMatrix = new double[16];
Matrix.setIdentityM(translationMatrix, 0);
Matrix.translateM(translationMatrix, 0, translationMatrix, 0, globalTranslationVector[0] / projectInfo.getMultiplierToMm(), globalTranslationVector[1] / projectInfo.getMultiplierToMm(), globalTranslationVector[2] / projectInfo.getMultiplierToMm());
Matrix.multiplyMM(tmp, 0, translationMatrix, 0, ms, 0);
ms = tmp;
newTransformation = ByteBuffer.wrap(new byte[16 * 8]).order(ByteOrder.LITTLE_ENDIAN);
for (double d : ms) {
newTransformation.putDouble(d);
}
}
serializerDataOutputStream.ensureExtraCapacity(((byte[])transformation).length + 8);
serializerDataOutputStream.write(newTransformation.array());
serializerDataOutputStream.writeLong(dataOid);
nrObjectsWritten++;
if (reportProgress) {
if (progressReporter != null) {
progressReporter.update(nrObjectsWritten, size);
}
}
}
private void writeBounds(HashMapVirtualObject info) throws IOException {
HashMapWrappedVirtualObject bounds = (HashMapWrappedVirtualObject) info.eGet(GeometryPackage.eINSTANCE.getGeometryInfo_Bounds());
HashMapWrappedVirtualObject minBounds = (HashMapWrappedVirtualObject) bounds.eGet(GeometryPackage.eINSTANCE.getBounds_Min());
HashMapWrappedVirtualObject maxBounds = (HashMapWrappedVirtualObject) bounds.eGet(GeometryPackage.eINSTANCE.getBounds_Max());
Double minX = (Double) minBounds.eGet("x");
Double minY = (Double) minBounds.eGet("y");
Double minZ = (Double) minBounds.eGet("z");
Double maxX = (Double) maxBounds.eGet("x");
Double maxY = (Double) maxBounds.eGet("y");
Double maxZ = (Double) maxBounds.eGet("z");
if (normalizeUnitsToMM && projectInfo.getMultiplierToMm() != 1f) {
minX = minX * projectInfo.getMultiplierToMm();
minY = minY * projectInfo.getMultiplierToMm();
minZ = minZ * projectInfo.getMultiplierToMm();
maxX = maxX * projectInfo.getMultiplierToMm();
maxY = maxY * projectInfo.getMultiplierToMm();
maxZ = maxZ * projectInfo.getMultiplierToMm();
}
serializerDataOutputStream.ensureExtraCapacity(8 * 6);
serializerDataOutputStream.writeDoubleUnchecked(minX);
serializerDataOutputStream.writeDoubleUnchecked(minY);
serializerDataOutputStream.writeDoubleUnchecked(minZ);
serializerDataOutputStream.writeDoubleUnchecked(maxX);
serializerDataOutputStream.writeDoubleUnchecked(maxY);
serializerDataOutputStream.writeDoubleUnchecked(maxZ);
}
private void writeMinimalGeometryInfo(HashMapVirtualObject info) throws IOException {
boolean hasTransparancy = (boolean)info.eGet(GeometryPackage.eINSTANCE.getGeometryInfo_HasTransparency());
long geometryDataId = (long)info.eGet(GeometryPackage.eINSTANCE.getGeometryInfo_Data());
long oid = (long) info.eGet(GeometryPackage.eINSTANCE.getGeometryInfo_IfcProductOid());
serializerDataOutputStream.writeByte(MessageType.MINIMAL_GEOMETRY_INFO.getId());
serializerDataOutputStream.writeLong(oid);
if (useUuidAndRid) {
serializerDataOutputStream.write((byte[])info.get("ifcProductUuid"));
serializerDataOutputStream.writeInt((int)info.get("ifcProductPid"));
serializerDataOutputStream.writeInt(info.getRid());
}
String type = objectProvider.getEClassForOid(oid).getName();
serializerDataOutputStream.writeUTF(type);
int nrColors = (int)info.eGet(GeometryPackage.eINSTANCE.getGeometryInfo_NrColors());
if (nrColors == 0) {
int nrVertices = (int)info.eGet(GeometryPackage.eINSTANCE.getGeometryInfo_NrVertices());
nrColors = nrVertices / 3 * 4;
}
serializerDataOutputStream.writeInt(nrColors);
serializerDataOutputStream.ensureExtraCapacity(32);
serializerDataOutputStream.writeLongUnchecked(info.getRoid());
serializerDataOutputStream.writeLongUnchecked(info.getOid());
serializerDataOutputStream.writeLongUnchecked(hasTransparancy ? 1 : 0);
serializerDataOutputStream.align8();
writeBounds(info);
serializerDataOutputStream.writeLong(geometryDataId);
nrObjectsWritten++;
if (reportProgress) {
if (progressReporter != null) {
progressReporter.update(nrObjectsWritten, size);
}
}
}
private boolean writeGeometryData(HashMapVirtualObject data, long oid) throws IOException, SerializerException {
// This geometry info is pointing to a not-yet-sent geometry data, so we send that first
// This way the client can be sure that geometry data is always available when geometry info is received, simplifying bookkeeping
EStructuralFeature hasTransparencyFeature = data.eClass().getEStructuralFeature("hasTransparency");
boolean hasTransparancy = (boolean)data.eGet(hasTransparencyFeature);
if (prepareBuffers && (reusedDataOids == null || !reusedDataOids.contains(oid))) {
oidToGeometryData.put(oid, data);
if (dataToGeometryInfo.containsKey(oid)) {
GeometryMainBuffer geometryMainBuffer = getCurrentMainBuffer(hasTransparancy);
GeometryBuffer currentBuffer = geometryMainBuffer.getCurrentWriteBuffer();
GeometrySubBuffer geometryMapping = currentBuffer.getCurrentGeometryMapping(true);
geometryMapping.put(dataToGeometryInfo.get(oid), data);
updateSize(data, currentBuffer);
}
return false;
}
EStructuralFeature indicesFeature = data.eClass().getEStructuralFeature("indices");
EStructuralFeature lineIndicesFeature = data.eClass().getEStructuralFeature("lineIndices");
EStructuralFeature verticesFeature = data.eClass().getEStructuralFeature("vertices");
EStructuralFeature verticesQuantizedFeature = data.eClass().getEStructuralFeature("verticesQuantized");
EStructuralFeature normalsFeature = data.eClass().getEStructuralFeature("normals");
EStructuralFeature normalsQuantizedFeature = data.eClass().getEStructuralFeature("normalsQuantized");
EStructuralFeature colorsFeature = data.eClass().getEStructuralFeature("colorsQuantized");
EStructuralFeature colorFeature = data.eClass().getEStructuralFeature("color");
EStructuralFeature mostUsedColorFeature = data.eClass().getEStructuralFeature("mostUsedColor");
AbstractHashMapVirtualObject indicesBuffer = data.getDirectFeature(indicesFeature);
byte[] normals = null;
byte[] normalsQuantized = null;
byte[] vertices = null;
byte[] verticesQuantized = null;
AbstractHashMapVirtualObject quantizedNormalsBuffer = data.getDirectFeature(normalsQuantizedFeature);
if (quantizeNormals && quantizedNormalsBuffer != null) {
normalsQuantized = (byte[]) quantizedNormalsBuffer.get("data");
} else {
AbstractHashMapVirtualObject normalsBuffer = data.getDirectFeature(normalsFeature);
normals = (byte[]) normalsBuffer.get("data");
}
AbstractHashMapVirtualObject quantizedVerticesBuffer = data.getDirectFeature(verticesQuantizedFeature);
if (quantizeVertices && quantizedVerticesBuffer != null) {
verticesQuantized = (byte[]) quantizedVerticesBuffer.get("data");
} else {
AbstractHashMapVirtualObject verticesBuffer = data.getDirectFeature(verticesFeature);
vertices = (byte[]) verticesBuffer.get("data");
}
AbstractHashMapVirtualObject colorsBuffer = data.getDirectFeature(colorsFeature);
HashMapWrappedVirtualObject color = (HashMapWrappedVirtualObject)data.eGet(colorFeature);
HashMapWrappedVirtualObject mostUsedColor = (HashMapWrappedVirtualObject)data.eGet(mostUsedColorFeature);
byte[] indices = (byte[]) indicesBuffer.get("data");
byte[] colors = null;
if (colorsBuffer != null) {
colors = (byte[]) colorsBuffer.get("data");
}
int totalNrIndices = indices.length / 4;
int maxIndexValues = 16389;
if (splitGeometry && totalNrIndices > maxIndexValues) {
serializerDataOutputStream.writeByte(MessageType.GEOMETRY_TRIANGLES_PARTED.getId());
serializerDataOutputStream.writeInt((int) data.get("reused"));
short cid = (short) data.get("type");
String type = objectProvider.getEClassForCid(cid).getName();
serializerDataOutputStream.writeUTF(type);
serializerDataOutputStream.align8();
serializerDataOutputStream.writeLong(hasTransparancy ? 1 : 0);
serializerDataOutputStream.writeLong(data.getOid());
// Split geometry, this algorithm - for now - just throws away all the reuse of vertices that might be there
// Also, although usually the vertices buffers are too large, this algorithm is based on the indices, so we
// probably are not cramming as much data as we can in each "part", but that's not really a problem I think
int nrParts = (totalNrIndices + maxIndexValues - 1) / maxIndexValues;
serializerDataOutputStream.writeInt(nrParts);
ByteBuffer indicesByteBuffer = ByteBuffer.wrap(indices);
indicesByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
IntBuffer indicesIntBuffer = indicesByteBuffer.asIntBuffer();
ByteBuffer vertexBuffer = ByteBuffer.wrap(vertices);
vertexBuffer.order(ByteOrder.LITTLE_ENDIAN);
DoubleBuffer verticesDoubleBuffer = vertexBuffer.asDoubleBuffer();
ByteBuffer normalsBuffer = ByteBuffer.wrap(normals);
normalsBuffer.order(ByteOrder.LITTLE_ENDIAN);
FloatBuffer normalsFloatBuffer = normalsBuffer.asFloatBuffer();
for (int part=0; part bounds[3] || a.getY() > bounds[4] || a.getZ() > bounds[5]) {
return false;
}
return true;
}
@Override
public void close() {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy