
org.bimserver.serializers.binarygeometry.BinaryGeometrySerializer 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
package org.bimserver.serializers.binarygeometry;
/******************************************************************************
* Copyright (C) 2009-2016 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.util.HashSet;
import java.util.List;
import java.util.Set;
import org.bimserver.emf.IdEObject;
import org.bimserver.models.geometry.GeometryData;
import org.bimserver.models.geometry.GeometryInfo;
import org.bimserver.plugins.serializers.AbstractGeometrySerializer;
import org.bimserver.plugins.serializers.ProgressReporter;
import org.bimserver.plugins.serializers.SerializerException;
import org.eclipse.emf.ecore.EClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Charsets;
import com.google.common.io.LittleEndianDataOutputStream;
@Deprecated
public class BinaryGeometrySerializer extends AbstractGeometrySerializer {
private static final Logger LOGGER = LoggerFactory.getLogger(BinaryGeometrySerializer.class);
private static final byte FORMAT_VERSION = 6;
private static final byte GEOMETRY_TYPE_TRIANGLES = 0;
private static final byte GEOMETRY_TYPE_INSTANCE = 1;
@Override
protected boolean write(OutputStream outputStream, ProgressReporter progressReporter) throws SerializerException {
if (getMode() == Mode.BODY) {
try {
calculateGeometryExtents();
writeGeometries(outputStream);
} catch (Exception e) {
LOGGER.error("", e);
}
setMode(Mode.FINISHED);
return true;
} else if (getMode() == Mode.FINISHED) {
return false;
}
return false;
}
private void writeGeometries(OutputStream outputStream) throws IOException {
long start = System.nanoTime();
LittleEndianDataOutputStream dataOutputStream = new LittleEndianDataOutputStream(outputStream);
// Identifier for clients to determine if this server is even serving binary geometry
dataOutputStream.writeUTF("BGS");
// Version of the current format being outputted, should be changed for every (released) change in protocol
dataOutputStream.writeByte(FORMAT_VERSION);
Bounds modelBounds = new Bounds();
int nrObjects = 0;
// All access to EClass is being done generically to support multiple IFC schema's with 1 serializer
EClass productClass = getModel().getPackageMetaData().getEClass("IfcProduct");
List products = getModel().getAllWithSubTypes(productClass);
// First iteration, to determine number of objects with geometry and calculate model bounds
for (IdEObject ifcProduct : products) {
GeometryInfo geometryInfo = (GeometryInfo) ifcProduct.eGet(ifcProduct.eClass().getEStructuralFeature("geometry"));
if (geometryInfo != null && geometryInfo.getTransformation() != null) {
Bounds objectBounds = new Bounds(new Double3(geometryInfo.getMinBounds().getX(), geometryInfo.getMinBounds().getY(), geometryInfo.getMinBounds()
.getZ()), new Double3(geometryInfo.getMaxBounds().getX(), geometryInfo.getMaxBounds().getY(), geometryInfo.getMaxBounds().getZ()));
modelBounds.integrate(objectBounds);
nrObjects++;
}
}
modelBounds.writeTo(dataOutputStream);
dataOutputStream.writeInt(nrObjects);
int bytesSaved = 0;
int bytesTotal = 0;
// Keeping track of geometry already sent, this can be used for instancing of reused geometry
Set concreteGeometrySent = new HashSet<>();
// Flushing here so the client can show progressbar etc...
dataOutputStream.flush();
int bytes = 6;
int counter = 0;
// Second iteration actually writing the geometry
for (IdEObject ifcProduct : products) {
GeometryInfo geometryInfo = (GeometryInfo) ifcProduct.eGet(ifcProduct.eClass().getEStructuralFeature("geometry"));
if (geometryInfo != null && geometryInfo.getTransformation() != null) {
String type = ifcProduct.eClass().getName();
dataOutputStream.writeUTF(type);
dataOutputStream.writeLong(ifcProduct.getOid());
GeometryData geometryData = geometryInfo.getData();
byte[] vertices = geometryData.getVertices();
// BEWARE, ByteOrder is always LITTLE_ENDIAN, because that's what GPU's seem to prefer, Java's ByteBuffer default is BIG_ENDIAN though!
bytesTotal += vertices.length;
byte geometryType = concreteGeometrySent.contains(geometryData.getOid()) ? GEOMETRY_TYPE_INSTANCE : GEOMETRY_TYPE_TRIANGLES;
dataOutputStream.write(geometryType);
bytes += (type.getBytes(Charsets.UTF_8).length + 3);
// This is an ugly hack to align the bytes, but for 2 different kinds of output (this first one is the websocket implementation)
int skip = 4 - (bytes % 4); // TODO fix
if(skip != 0 && skip != 4) {
dataOutputStream.write(new byte[skip]);
}
bytes = 0;
dataOutputStream.write(geometryInfo.getTransformation());
if (concreteGeometrySent.contains(geometryData.getOid())) {
// Reused geometry, only send the id of the reused geometry data
dataOutputStream.writeLong(geometryData.getOid());
bytesSaved += vertices.length;
} else {
ByteBuffer vertexByteBuffer = ByteBuffer.wrap(vertices);
dataOutputStream.writeLong(geometryData.getOid());
Bounds objectBounds = new Bounds(geometryInfo.getMinBounds(), geometryInfo.getMaxBounds());
objectBounds.writeTo(dataOutputStream);
ByteBuffer indicesBuffer = ByteBuffer.wrap(geometryData.getIndices());
dataOutputStream.writeInt(indicesBuffer.capacity() / 4);
dataOutputStream.write(indicesBuffer.array());
dataOutputStream.writeInt(vertexByteBuffer.capacity() / 4);
dataOutputStream.write(vertexByteBuffer.array());
ByteBuffer normalsBuffer = ByteBuffer.wrap(geometryData.getNormals());
dataOutputStream.writeInt(normalsBuffer.capacity() / 4);
dataOutputStream.write(normalsBuffer.array());
// Only when materials are used we send them
if (geometryData.getMaterials() != null) {
ByteBuffer materialsByteBuffer = ByteBuffer.wrap(geometryData.getMaterials());
dataOutputStream.writeInt(materialsByteBuffer.capacity() / 4);
dataOutputStream.write(materialsByteBuffer.array());
} else {
// No materials used
dataOutputStream.writeInt(0);
}
concreteGeometrySent.add(geometryData.getOid());
}
counter++;
if (counter % 12 == 0) {
// Flushing in batches, this is to limit the amount of WebSocket messages
dataOutputStream.flush();
}
}
}
dataOutputStream.flush();
if (bytesTotal != 0 && bytesSaved != 0) {
LOGGER.info((100 * bytesSaved / bytesTotal) + "% saved");
}
long end = System.nanoTime();
LOGGER.debug(((end - start) / 1000000) + " ms");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy