org.bimserver.serializers.binarygeometry.BinaryGeometryMessagingSerializer Maven / Gradle / Ivy
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.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.bimserver.emf.IdEObject;
import org.bimserver.emf.IfcModelInterface;
import org.bimserver.emf.PackageMetaData;
import org.bimserver.models.geometry.GeometryData;
import org.bimserver.models.geometry.GeometryInfo;
import org.bimserver.plugins.PluginManagerInterface;
import org.bimserver.plugins.serializers.MessagingSerializer;
import org.bimserver.plugins.serializers.ProgressReporter;
import org.bimserver.plugins.serializers.ProjectInfo;
import org.bimserver.plugins.serializers.SerializerException;
import org.eclipse.emf.ecore.EClass;
import com.google.common.base.Charsets;
import com.google.common.io.LittleEndianDataOutputStream;
public class BinaryGeometryMessagingSerializer implements MessagingSerializer {
private static final byte FORMAT_VERSION = 6;
private IfcModelInterface model;
private enum Mode {
START,
DATA,
END
}
private enum MessageType {
INIT((byte)0),
GEOMETRY_TRIANGLES((byte)1),
GEOMETRY_INSTANCE((byte)2),
GEOMETRY_TRIANGLES_PARTED((byte)3),
GEOMETRY_INSTANCE_PARTED((byte)4);
private byte id;
private MessageType(byte id) {
this.id = id;
}
public byte getId() {
return id;
}
}
private Mode mode = Mode.START;
private Map concreteGeometrySent;
private Iterator iterator;
private PackageMetaData packageMetaData;
private long splitCounter = -1;
@Override
public void init(IfcModelInterface model, ProjectInfo projectInfo, PluginManagerInterface pluginManager, PackageMetaData packageMetaData, boolean normalizeOids) throws SerializerException {
this.model = model;
this.packageMetaData = packageMetaData;
}
@Override
public boolean writeMessage(OutputStream outputStream, ProgressReporter progressReporter) throws IOException {
switch (mode) {
case START:
if (!writeStart(outputStream)) {
mode = Mode.END;
return false;
}
mode = Mode.DATA;
break;
case DATA:
if (!writeData(outputStream)) {
mode = Mode.END;
return false;
}
break;
case END:
return false;
default:
break;
}
return true;
}
private boolean writeStart(OutputStream outputStream) throws IOException {
LittleEndianDataOutputStream dataOutputStream = new LittleEndianDataOutputStream(outputStream);
// Identifier for clients to determine if this server is even serving binary geometry
dataOutputStream.writeByte(MessageType.INIT.getId());
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 = model.getPackageMetaData().getEClass("IfcProduct");
List products = model.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++;
}
}
int skip = 4 - (7 % 4);
if(skip != 0 && skip != 4) {
dataOutputStream.write(new byte[skip]);
}
modelBounds.writeTo(dataOutputStream);
dataOutputStream.writeInt(nrObjects);
concreteGeometrySent = new HashMap();
EClass productEClass = packageMetaData.getEClass("IfcProduct");
iterator = model.getAllWithSubTypes(productEClass).iterator();
return nrObjects > 0;
}
@SuppressWarnings("unchecked")
private boolean writeData(OutputStream outputStream) throws IOException {
IdEObject ifcProduct = iterator.next();
LittleEndianDataOutputStream dataOutputStream = new LittleEndianDataOutputStream(outputStream);
GeometryInfo geometryInfo = (GeometryInfo) ifcProduct.eGet(ifcProduct.eClass().getEStructuralFeature("geometry"));
if (geometryInfo != null && geometryInfo.getTransformation() != null) {
GeometryData geometryData = geometryInfo.getData();
int totalNrIndices = geometryData.getIndices().length / 4;
int maxIndexValues = 16389;
Object reuse = concreteGeometrySent.get(geometryData.getOid());
MessageType messageType = null;
if (reuse == null) {
if (totalNrIndices > maxIndexValues) {
messageType = MessageType.GEOMETRY_TRIANGLES_PARTED;
} else {
messageType = MessageType.GEOMETRY_TRIANGLES;
}
} else {
if (reuse instanceof List) {
messageType = MessageType.GEOMETRY_INSTANCE_PARTED;
} else {
messageType = MessageType.GEOMETRY_INSTANCE;
}
}
dataOutputStream.writeByte(messageType.getId());
dataOutputStream.writeUTF(ifcProduct.eClass().getName());
Long roid = model.getPidRoidMap().get(ifcProduct.getPid());
dataOutputStream.writeLong(roid);
dataOutputStream.writeLong(ifcProduct.getOid());
// BEWARE, ByteOrder is always LITTLE_ENDIAN, because that's what GPU's seem to prefer, Java's ByteBuffer default is BIG_ENDIAN though!
int skip = 4 - ((3 + ifcProduct.eClass().getName().getBytes(Charsets.UTF_8).length) % 4);
if(skip != 0 && skip != 4) {
dataOutputStream.write(new byte[skip]);
}
dataOutputStream.write(geometryInfo.getTransformation());
if (reuse != null && reuse instanceof Long) {
// Reused geometry, only send the id of the reused geometry data
dataOutputStream.writeLong(geometryData.getOid());
} else if (reuse != null && reuse instanceof List) {
List list = (List)reuse;
dataOutputStream.writeInt(list.size());
for (long coreId : list) {
dataOutputStream.writeLong(coreId);
}
} else {
if (totalNrIndices > maxIndexValues) {
// 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;
dataOutputStream.writeInt(nrParts);
Bounds objectBounds = new Bounds(geometryInfo.getMinBounds(), geometryInfo.getMaxBounds());
objectBounds.writeTo(dataOutputStream);
ByteBuffer indicesBuffer = ByteBuffer.wrap(geometryData.getIndices());
indicesBuffer.order(ByteOrder.LITTLE_ENDIAN);
IntBuffer indicesIntBuffer = indicesBuffer.asIntBuffer();
ByteBuffer vertexBuffer = ByteBuffer.wrap(geometryData.getVertices());
vertexBuffer.order(ByteOrder.LITTLE_ENDIAN);
FloatBuffer verticesFloatBuffer = vertexBuffer.asFloatBuffer();
ByteBuffer normalsBuffer = ByteBuffer.wrap(geometryData.getNormals());
normalsBuffer.order(ByteOrder.LITTLE_ENDIAN);
FloatBuffer normalsFloatBuffer = normalsBuffer.asFloatBuffer();
for (int part=0; part arrayList = new ArrayList();
arrayList.add(geometryData.getOid());
concreteGeometrySent.put(geometryData.getOid(), arrayList);
}
}
}
return iterator.hasNext();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy