
org.bimserver.serializers.binarygeometry.BinaryGeometryMessagingStreamingSerializer2 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.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.bimserver.BimserverDatabaseException;
import org.bimserver.emf.PackageMetaData;
import org.bimserver.interfaces.objects.SVector3f;
import org.bimserver.models.geometry.GeometryPackage;
import org.bimserver.plugins.PluginManagerInterface;
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.shared.HashMapVirtualObject;
import org.bimserver.shared.HashMapWrappedVirtualObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.io.LittleEndianDataOutputStream;
public class BinaryGeometryMessagingStreamingSerializer2 implements MessagingStreamingSerializer {
private static final Logger LOGGER = LoggerFactory.getLogger(BinaryGeometryMessagingStreamingSerializer2.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
*
*
*/
private static final byte FORMAT_VERSION = 8;
private enum Mode {
LOAD,
START,
DATA,
END
}
private enum MessageType {
INIT((byte)0),
GEOMETRY_TRIANGLES((byte)1),
GEOMETRY_TRIANGLES_PARTED((byte)3),
GEOMETRY_INFO((byte)5),
END((byte)6);
private byte id;
private MessageType(byte id) {
this.id = id;
}
public byte getId() {
return id;
}
}
private Mode mode = Mode.LOAD;
private long splitCounter = -1;
private ObjectProvider objectProvider;
private ProjectInfo projectInfo;
private LittleEndianDataOutputStream dataOutputStream;
@Override
public void init(ObjectProvider objectProvider, ProjectInfo projectInfo, PluginManagerInterface pluginManager, PackageMetaData packageMetaData) throws SerializerException {
this.objectProvider = objectProvider;
this.projectInfo = projectInfo;
}
@Override
public boolean writeMessage(OutputStream outputStream, ProgressReporter progressReporter) throws IOException, SerializerException {
dataOutputStream = new LittleEndianDataOutputStream(outputStream);
switch (mode) {
case LOAD: {
load();
mode = Mode.START;
}
case START:
writeStart();
mode = Mode.DATA;
break;
case DATA:
if (!writeData()) {
mode = Mode.END;
return true;
}
break;
case END:
writeEnd();
return false;
default:
break;
}
return true;
}
private final List infoList = new ArrayList<>();
private final Map dataList = new HashMap<>();
private Iterator iterator;
private void load() throws SerializerException {
HashMapVirtualObject next = null;
try {
next = objectProvider.next();
} catch (BimserverDatabaseException e) {
throw new SerializerException(e);
}
while (next != null) {
if (next.eClass() == GeometryPackage.eINSTANCE.getGeometryInfo()) {
infoList.add(next);
} else if (next.eClass() == GeometryPackage.eINSTANCE.getGeometryData()) {
dataList.put(next.getOid(), next);
}
try {
next = objectProvider.next();
} catch (BimserverDatabaseException e) {
throw new SerializerException(e);
}
}
Collections.sort(infoList, new Comparator(){
@Override
public int compare(HashMapVirtualObject o1, HashMapVirtualObject o2) {
return getBoundingBoxVolume(o2) - getBoundingBoxVolume(o1);
}});
iterator = infoList.iterator();
}
private int getBoundingBoxVolume(HashMapVirtualObject o1) {
HashMapWrappedVirtualObject minBounds = (HashMapWrappedVirtualObject) o1.eGet(o1.eClass().getEStructuralFeature("minBounds"));
HashMapWrappedVirtualObject maxBounds = (HashMapWrappedVirtualObject) o1.eGet(o1.eClass().getEStructuralFeature("maxBounds"));
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");
int volume = (int) ((maxX - minX) * (maxY - minY) * (maxZ - minZ));
return volume;
}
private boolean writeEnd() throws IOException {
dataOutputStream.write(MessageType.END.getId());
return true;
}
private void writeStart() throws IOException {
// 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);
int skip = 4 - (7 % 4);
if(skip != 0 && skip != 4) {
dataOutputStream.write(new byte[skip]);
}
SVector3f minBounds = projectInfo.getMinBounds();
dataOutputStream.writeDouble(minBounds.getX());
dataOutputStream.writeDouble(minBounds.getY());
dataOutputStream.writeDouble(minBounds.getZ());
SVector3f maxBounds = projectInfo.getMaxBounds();
dataOutputStream.writeDouble(maxBounds.getX());
dataOutputStream.writeDouble(maxBounds.getY());
dataOutputStream.writeDouble(maxBounds.getZ());
}
HashMapVirtualObject last = null;
private boolean writeData() throws IOException, SerializerException {
HashMapVirtualObject info = null;
if (last != null) {
info = last;
last = null;
} else {
info = iterator.next();
}
Object transformation = info.eGet(info.eClass().getEStructuralFeature("transformation"));
Object dataOid = info.eGet(info.eClass().getEStructuralFeature("data"));
HashMapVirtualObject data = dataList.get(dataOid);
if (data != null) {
last = info;
EStructuralFeature indicesFeature = data.eClass().getEStructuralFeature("indices");
EStructuralFeature verticesFeature = data.eClass().getEStructuralFeature("vertices");
EStructuralFeature normalsFeature = data.eClass().getEStructuralFeature("normals");
EStructuralFeature materialsFeature = data.eClass().getEStructuralFeature("materials");
byte[] indices = (byte[])data.eGet(indicesFeature);
byte[] vertices = (byte[])data.eGet(verticesFeature);
byte[] normals = (byte[])data.eGet(normalsFeature);
byte[] materials = (byte[])data.eGet(materialsFeature);
int totalNrIndices = indices.length / 4;
int maxIndexValues = 16389;
if (totalNrIndices > maxIndexValues) {
dataOutputStream.write(MessageType.GEOMETRY_TRIANGLES_PARTED.getId());
dataOutputStream.write(new byte[3]);
dataOutputStream.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;
dataOutputStream.writeInt(nrParts);
ByteBuffer indicesBuffer = ByteBuffer.wrap(indices);
indicesBuffer.order(ByteOrder.LITTLE_ENDIAN);
IntBuffer indicesIntBuffer = indicesBuffer.asIntBuffer();
ByteBuffer vertexBuffer = ByteBuffer.wrap(vertices);
vertexBuffer.order(ByteOrder.LITTLE_ENDIAN);
FloatBuffer verticesFloatBuffer = vertexBuffer.asFloatBuffer();
ByteBuffer normalsBuffer = ByteBuffer.wrap(normals);
normalsBuffer.order(ByteOrder.LITTLE_ENDIAN);
FloatBuffer normalsFloatBuffer = normalsBuffer.asFloatBuffer();
for (int part=0; part
© 2015 - 2025 Weber Informatics LLC | Privacy Policy