From 6f0ea951b5c3602bbe0a450a7e004bfd0d9bce73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=9Ean=20G=C3=BCne=C5=9F?= Date: Thu, 28 Jan 2016 13:38:24 +0200 Subject: [PATCH 1/4] added extra object types --- .../serializers/json/ThreeJsSerializer.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java b/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java index 97896a5..26d04a9 100644 --- a/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java +++ b/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java @@ -17,6 +17,10 @@ import org.bimserver.models.ifc2x3tc1.IfcSlab; import org.bimserver.models.ifc2x3tc1.IfcWall; import org.bimserver.models.ifc2x3tc1.IfcWindow; +import org.bimserver.models.ifc2x3tc1.IfcDistributionControlElement; +import org.bimserver.models.ifc2x3tc1.IfcFurnishingElement; +import org.bimserver.models.ifc2x3tc1.IfcBuildingElementProxy; +import org.bimserver.models.ifc2x3tc1.IfcFlowTerminal; import org.bimserver.plugins.PluginManager; import org.bimserver.plugins.renderengine.RenderEnginePlugin; import org.bimserver.plugins.serializers.EmfSerializer; @@ -117,9 +121,15 @@ private Map collectGeometryData() { Map geometryData = new HashMap(); Class[] eClasses = new Class[] { IfcWall.class, IfcWindow.class, IfcDoor.class, IfcSlab.class, IfcColumn.class, + IfcDistributionControlElement.class, IfcFurnishingElement.class, + IfcBuildingElementProxy.class, IfcFlowTerminal.class, org.bimserver.models.ifc4.IfcWall.class,org.bimserver.models.ifc4.IfcWindow.class, org.bimserver.models.ifc4.IfcDoor.class, org.bimserver.models.ifc4.IfcSlab.class, - org.bimserver.models.ifc4.IfcColumn.class + org.bimserver.models.ifc4.IfcColumn.class, + org.bimserver.models.ifc4.IfcDistributionControlElement.class, + org.bimserver.models.ifc4.IfcFurnishingElement.class, + org.bimserver.models.ifc4.IfcBuildingElementProxy.class, + org.bimserver.models.ifc4.IfcFlowTerminal.class }; for (Class eClass : eClasses) { for (IdEObject object : model.getAllWithSubTypes(eClass)) { From 8f48a408684c9a4988ae5a1c5bc40a81c30e51e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=9Ean=20G=C3=BCne=C5=9F?= Date: Thu, 28 Jan 2016 13:59:24 +0200 Subject: [PATCH 2/4] Added IFC object types as node names in the threejs scene --- .../serializers/json/ThreeJsSerializer.java | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java b/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java index 26d04a9..eaf94dd 100644 --- a/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java +++ b/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java @@ -51,7 +51,9 @@ protected boolean write(OutputStream outputStream, ProgressReporter progressRepo out.println("{"); out.println(" \"metadata\" : { \"formatVersion\" : 4.3, \"type\" : \"object\", \"generator\" : \"BIMserver three.js serializer\" }, "); out.println(" \"geometries\" : ["); - Map geometryData = collectGeometryData(); + Map geometryData = new HashMap(); + Map objectTypes = new HashMap(); + collectObjectData(geometryData, objectTypes); writeGeometries(geometryData); out.println(" ],"); out.println(" \"object\" : {"); @@ -59,7 +61,7 @@ protected boolean write(OutputStream outputStream, ProgressReporter progressRepo out.println(" \"type\" : \"Scene\","); out.println(" \"matrix\" : [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],"); out.println(" \"children\" : ["); - writeObjects(geometryData); + writeObjects(geometryData, objectTypes); out.println(" ]"); out.println(" }"); out.println("}"); @@ -117,8 +119,10 @@ private void writeGeometry(GeometryData geometryData) { } @SuppressWarnings("unchecked") - private Map collectGeometryData() { - Map geometryData = new HashMap(); + private boolean collectObjectData( + Map geometryData, + Map objectTypes_ + ) { Class[] eClasses = new Class[] { IfcWall.class, IfcWindow.class, IfcDoor.class, IfcSlab.class, IfcColumn.class, IfcDistributionControlElement.class, IfcFurnishingElement.class, @@ -138,9 +142,11 @@ private Map collectGeometryData() { if (geometryInfo != null) { geometryData.put(ifcRoot.getGlobalId(), geometryInfo); } + String objectType = eClass.getSimpleName(); + objectTypes_.put(ifcRoot.getGlobalId(), objectType); } } - return geometryData; + return true; } @@ -159,21 +165,26 @@ private void writeGeometries(Map geometryInfos) { out.println(); } - private void writeObjects(Map geometryInfos) { + private void writeObjects( + Map geometryInfos, + Map objectTypes_ + ) { boolean first = true; for (Map.Entry geometryEntry: geometryInfos.entrySet()) { String guid = geometryEntry.getKey(); GeometryInfo geometryInfo = geometryEntry.getValue(); + String objectType = objectTypes_.get(geometryEntry.getKey()); out.println(first ? " {" : " , {"); - writeObject(guid, geometryInfo); + writeObject(guid, geometryInfo, objectType); out.print(" }"); first = false; } out.println(); } - private void writeObject(String guid, GeometryInfo geometryInfo) { + private void writeObject(String guid, GeometryInfo geometryInfo, String type_) { out.println(" \"uuid\" : \"" + guid + "\", "); + out.println(" \"name\" : \"" + type_ + "\", "); out.println(" \"type\" : \"Mesh\", "); out.println(" \"geometry\" : \"" + geometryInfo.getData().getOid() + "\", "); out.print( " \"matrix\" : ["); From 9ebe4e5bdf0a92e6d0a69b811939371ded62f5c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=9Ean=20G=C3=BCne=C5=9F?= Date: Fri, 27 May 2016 14:52:56 +0300 Subject: [PATCH 3/4] Implemented colors, and some formatting modifications --- .../serializers/json/ThreeJsSerializer.java | 237 ++++++++++++++---- 1 file changed, 194 insertions(+), 43 deletions(-) diff --git a/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java b/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java index eaf94dd..c620d16 100644 --- a/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java +++ b/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java @@ -21,6 +21,7 @@ import org.bimserver.models.ifc2x3tc1.IfcFurnishingElement; import org.bimserver.models.ifc2x3tc1.IfcBuildingElementProxy; import org.bimserver.models.ifc2x3tc1.IfcFlowTerminal; +import org.bimserver.models.ifc2x3tc1.IfcSpace; import org.bimserver.plugins.PluginManager; import org.bimserver.plugins.renderengine.RenderEnginePlugin; import org.bimserver.plugins.serializers.EmfSerializer; @@ -48,21 +49,37 @@ public void reset() { protected boolean write(OutputStream outputStream, ProgressReporter progressReporter) { if (getMode() == Mode.BODY) { out = new PrintWriter(outputStream); - out.println("{"); - out.println(" \"metadata\" : { \"formatVersion\" : 4.3, \"type\" : \"object\", \"generator\" : \"BIMserver three.js serializer\" }, "); - out.println(" \"geometries\" : ["); Map geometryData = new HashMap(); Map objectTypes = new HashMap(); + // A map that holds the material indexes for each object. + // Each object has a MultiMaterial assigned to it. And the different + // faces of that object have different materials. These indexes are + // the material indexes in that MultiMaterial, corresponding to that + // face. The key to get those material indexes is a long which is + // holding the color information for that face in form 0xRRGGBBAA. + // + // This map is filled when the materials are written, and later is + // used when writing the geometries. + // + // The first key is the oid of the geometryData. + Map> objectMaterialIndexes = + new HashMap>(); collectObjectData(geometryData, objectTypes); - writeGeometries(geometryData); + out.println("{"); + out.println(" \"metadata\" : { \"formatVersion\" : 4.3, \"type\" : \"object\", \"generator\" : \"BIMserver three.js serializer\" }, "); + out.println(" \"materials\" : ["); + writeMaterials(geometryData, objectMaterialIndexes); + out.println(" ],"); + out.println(" \"geometries\" : ["); + writeGeometries(geometryData, objectMaterialIndexes); out.println(" ],"); out.println(" \"object\" : {"); - out.println(" \"uuid\" : \"root\","); - out.println(" \"type\" : \"Scene\","); - out.println(" \"matrix\" : [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],"); - out.println(" \"children\" : ["); + out.println(" \"uuid\" : \"root\","); + out.println(" \"type\" : \"Scene\","); + out.println(" \"matrix\" : [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],"); + out.println(" \"children\" : ["); writeObjects(geometryData, objectTypes); - out.println(" ]"); + out.println(" ]"); out.println(" }"); out.println("}"); out.flush(); @@ -73,12 +90,120 @@ protected boolean write(OutputStream outputStream, ProgressReporter progressRepo return false; } } + + private void writeMaterials(Map geometryInfos, + Map> objectMaterialIndexes) { + boolean first = true; + Set writtenMultiMaterials = new HashSet(); + for(GeometryInfo geometryInfo : geometryInfos.values()) { + Long id = geometryInfo.getData().getOid(); + if(!writtenMultiMaterials.contains(id)) { + out.print(first ? "" : ",\n"); + first = false; + out.println(" {"); + objectMaterialIndexes.put( + id, + new HashMap() + ); + writeMultiMaterial( + geometryInfo.getData(), + objectMaterialIndexes.get(id) + ); + out.print(" }"); + writtenMultiMaterials.add(id); + } + } + out.println(); + } + + private void writeMultiMaterial(GeometryData geometryData, + Map materialIndexes) { + // Create a MultiMaterial for this geometry that includes all of the + // materials, and add it to the global list of materials + out.println(" \"uuid\" : \"" + geometryData.getOid() + "M\","); + out.println(" \"type\" : \"MultiMaterial\", "); + out.println(" \"materials\": ["); + + // TODO + // For now just loop through the face indexes in order to find out what + // kind of materials we have, and add them to the indexes map. + List indexes = getIntegerList(geometryData.getIndices()); + if(null != indexes && 0 < indexes.size()) { + // Add the colors as keys + for(int i = 0; i < indexes.size(); i += 3) { + long color = this.getFaceColor(geometryData, i); + materialIndexes.put(color, 0); + } + // Now loop through the keys and create the materials, and write + // them. + // The counter indicates the index of the material inside the + // materials array of the MultiMaterial + int counter = 0; + for( Map.Entry material_entry : + materialIndexes.entrySet() ) { + material_entry.setValue(counter); + // Extract the color values + long color = material_entry.getKey(); + int rgb_color = (int)(color >> 8); + int alpha_color = (int)(color & 0x000000ffL); + float opacity = (float)alpha_color / 255.0f; + // Now write them + out.print(" { "); + out.print("\"type\" : \"MeshPhongMaterial\", "); + if(opacity < 1.0) { + out.print("\"transparent\" : true, "); + out.print("\"opacity\" : \"" + opacity + "\", "); + } + out.print("\"color\" : " + rgb_color + ""); + out.println(++counter >= materialIndexes.size() ? " }" : " },"); + } + } + + // End writing materials array + out.println(" ]"); + } + + // Returns the average color for that face in form of 0xRRGGBBAA. + private long getFaceColor(GeometryData geometryData, int firstVertexIndex) { + long colorCode = 0x00000000L; + byte[] b = geometryData.getMaterials(); + // If there is a problem getting the materials for this geometry, + // just return white color as default. + if(null == b) { + return 0xFFFFFFFFL; + } + List colors = getFloatList(b); + b = geometryData.getIndices(); + if(null == b) { + return 0xFFFFFFFFL; + } + List indexes = getIntegerList(b); + if( null != colors && indexes != null && + 0 < colors.size() && 0 < indexes.size()) { + for(int c = 0; c < 4; c++) { + // Now using these indexes, get the average color of the + // face. The reason for getting the average is in case the + // vertices have different color values, although this is + // unlikely. + float color_value = ( + colors.get( indexes.get(firstVertexIndex) * 4 + c ) + + colors.get( indexes.get(firstVertexIndex+1) * 4 + c ) + + colors.get( indexes.get(firstVertexIndex+2) * 4 + c ) + ) / 3.0f; + // Convert this color value to 0-255 integer, and add it to the + // long int. + colorCode += (int)(color_value*255.0f) * Math.pow(16,(3-c)*2); + } + } + return colorCode; + } - private void writeGeometry(GeometryData geometryData) { - out.println(" \"uuid\" : \"" + geometryData.getOid() + "\", "); - out.println(" \"type\" : \"Geometry\", "); - out.println(" \"data\" : {"); - out.print(" \"vertices\": [ "); + private void writeGeometry(GeometryData geometryData, + Map materialIndexes) { + out.println(" \"uuid\" : \"" + geometryData.getOid() + "\", "); + out.println(" \"type\" : \"Geometry\", "); + out.println(" \"data\" : {"); + out.print( " \"vertices\" : [ "); List vertices = getFloatList(geometryData.getVertices()); if (vertices != null && vertices.size() > 0) { @@ -89,8 +214,8 @@ private void writeGeometry(GeometryData geometryData) { } } - out.println("], "); - out.print(" \"normals\": ["); + out.println(" ],"); + out.print(" \"normals\" : [ "); List normals = getFloatList(geometryData.getNormals()); if (normals != null && normals.size() > 0) { @@ -101,21 +226,32 @@ private void writeGeometry(GeometryData geometryData) { } } - out.println("],"); - out.println(" \"uvs\": [ ],"); - out.print(" \"faces\": [ "); + out.println(" ],"); + out.print( " \"faces\" : [ "); List indices = getIntegerList(geometryData.getIndices()); + if (indices != null && indices.size() > 0) { for (int i = 0; i < indices.size(); i += 3) { out.print(i == 0 ? "" : ","); - out.print(" 32, "); + // 2 = faces have materials assigned to them. Refer to three.js + // JSON format documentation. + out.print(" 2, "); + // Write a face using the indexes of 3 vertices out.print((indices.get(i)) + "," + (indices.get(i + 1)) + "," + (indices.get(i + 2)) + ","); - out.print((indices.get(i)) + "," + (indices.get(i + 1)) + "," + (indices.get(i + 2))); + // Now write the material index for this object. + long color_code = this.getFaceColor(geometryData, i); + try { + out.print( materialIndexes.get(color_code) ); + } catch (IndexOutOfBoundsException e) { + LOGGER.error("Material information may be corrupted"); + out.print( 0 ); + } } } - out.println("]}"); + out.println(" ]"); + out.println(" }"); } @SuppressWarnings("unchecked") @@ -126,14 +262,15 @@ private boolean collectObjectData( Class[] eClasses = new Class[] { IfcWall.class, IfcWindow.class, IfcDoor.class, IfcSlab.class, IfcColumn.class, IfcDistributionControlElement.class, IfcFurnishingElement.class, - IfcBuildingElementProxy.class, IfcFlowTerminal.class, + IfcBuildingElementProxy.class, IfcFlowTerminal.class, IfcSpace.class, org.bimserver.models.ifc4.IfcWall.class,org.bimserver.models.ifc4.IfcWindow.class, org.bimserver.models.ifc4.IfcDoor.class, org.bimserver.models.ifc4.IfcSlab.class, org.bimserver.models.ifc4.IfcColumn.class, org.bimserver.models.ifc4.IfcDistributionControlElement.class, org.bimserver.models.ifc4.IfcFurnishingElement.class, org.bimserver.models.ifc4.IfcBuildingElementProxy.class, - org.bimserver.models.ifc4.IfcFlowTerminal.class + org.bimserver.models.ifc4.IfcFlowTerminal.class, + org.bimserver.models.ifc4.IfcSpace.class }; for (Class eClass : eClasses) { for (IdEObject object : model.getAllWithSubTypes(eClass)) { @@ -142,6 +279,7 @@ private boolean collectObjectData( if (geometryInfo != null) { geometryData.put(ifcRoot.getGlobalId(), geometryInfo); } + //String objectType = eClass.getSimpleName() + ":" + ifcRoot.getName(); String objectType = eClass.getSimpleName(); objectTypes_.put(ifcRoot.getGlobalId(), objectType); } @@ -150,44 +288,57 @@ private boolean collectObjectData( } - private void writeGeometries(Map geometryInfos) { + private void writeGeometries(Map geometryInfos, + Map> objectMaterialIndexes) { boolean first = true; Set writtenGeometries = new HashSet(); - for(GeometryInfo geometryInfo: geometryInfos.values()){ - if(!writtenGeometries.contains(geometryInfo.getData().getOid())){ - out.println(first ? " {" : " ,{"); + for(GeometryInfo geometryInfo : geometryInfos.values()) { + Long objectId = geometryInfo.getData().getOid(); + if(!writtenGeometries.contains(objectId)) { + out.print(first ? "" : ",\n"); + out.println(" {"); first = false; - writeGeometry(geometryInfo.getData()); - out.print(" }"); - writtenGeometries.add(geometryInfo.getData().getOid()); - } - } + writeGeometry( + geometryInfo.getData(), + objectMaterialIndexes.get(objectId) + ); + out.print(" }"); + writtenGeometries.add(objectId); + } + } out.println(); } private void writeObjects( Map geometryInfos, - Map objectTypes_ + Map objectTypes ) { boolean first = true; for (Map.Entry geometryEntry: geometryInfos.entrySet()) { String guid = geometryEntry.getKey(); GeometryInfo geometryInfo = geometryEntry.getValue(); - String objectType = objectTypes_.get(geometryEntry.getKey()); - out.println(first ? " {" : " , {"); + String objectType = objectTypes.get(geometryEntry.getKey()); + out.print(first ? "" : ",\n"); + out.println(" {"); writeObject(guid, geometryInfo, objectType); - out.print(" }"); + out.print(" }"); first = false; } out.println(); } - private void writeObject(String guid, GeometryInfo geometryInfo, String type_) { - out.println(" \"uuid\" : \"" + guid + "\", "); - out.println(" \"name\" : \"" + type_ + "\", "); - out.println(" \"type\" : \"Mesh\", "); - out.println(" \"geometry\" : \"" + geometryInfo.getData().getOid() + "\", "); - out.print( " \"matrix\" : ["); + private void writeObject( + String guid, + GeometryInfo geometryInfo, + String type + ) { + long oid = geometryInfo.getData().getOid(); + out.println(" \"uuid\" : \"" + guid + "\", "); + out.println(" \"name\" : \"" + type + "\", "); + out.println(" \"type\" : \"Mesh\", "); + out.println(" \"material\" : \"" + oid + "M\", "); + out.println(" \"geometry\" : \"" + oid + "\", "); + out.print( " \"matrix\" : ["); boolean first = true; for(float i: getFloatList(geometryInfo.getTransformation())){ out.print(first ? "" : ","); From 0c10de06d426a10e6524e0903e848461b0bd68f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=9Ean=20G=C3=BCne=C5=9F?= Date: Fri, 27 May 2016 15:07:27 +0300 Subject: [PATCH 4/4] Added url encoded object names in the threejs object's name field --- .../serializers/json/ThreeJsSerializer.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java b/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java index c620d16..4241cef 100644 --- a/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java +++ b/ServerPlugin/src/org/bimserver/serializers/json/ThreeJsSerializer.java @@ -2,6 +2,8 @@ import java.io.OutputStream; import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.*; @@ -280,8 +282,23 @@ private boolean collectObjectData( geometryData.put(ifcRoot.getGlobalId(), geometryInfo); } //String objectType = eClass.getSimpleName() + ":" + ifcRoot.getName(); + String objectName = ""; + try { + objectName = URLEncoder.encode( + ifcRoot.getName(), + java.nio.charset.StandardCharsets.UTF_8.toString() + ); + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } String objectType = eClass.getSimpleName(); - objectTypes_.put(ifcRoot.getGlobalId(), objectType); + // TODO + // rename the types with something that explains better, since + // now it holds Type:name + objectTypes_.put( + ifcRoot.getGlobalId(), + objectType + ":" + objectName); } } return true;