Skip to content

External models #125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions build-adds/example_config.properties
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ forceUnbufferedPNGRendering = false
# If this is lower than the width or height of the requested png, performance suffers.
# Increase it if your graphics hardware is capable of handling larger sizes.
canvasLimit = 1024

# download external obj models for OSM objects with model:url
useExternalModels = true
5 changes: 3 additions & 2 deletions build-adds/osm2world.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ if [[ $1 == --vm-params=* ]]
fi

# choose path for the native JOGL libs depending on system and java version
script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

MACHINE_TYPE=`uname -m`
KERNEL_NAME=`uname -s`
Expand All @@ -27,6 +28,6 @@ fi

# run OSM2World

export LD_LIBRARY_PATH=$lpsolvepath
export LD_LIBRARY_PATH=$script_dir/$lpsolvepath

java -Djava.library.path=$lpsolvepath $vmparams -jar OSM2World.jar --config texture_config.properties $@
java -Djava.library.path=$lpsolvepath $vmparams -jar $script_dir/OSM2World.jar --config texture_config.properties $@
2 changes: 2 additions & 0 deletions src/org/osm2world/core/ConversionFacade.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.osm2world.core.world.modules.BridgeModule;
import org.osm2world.core.world.modules.BuildingModule;
import org.osm2world.core.world.modules.CliffModule;
import org.osm2world.core.world.modules.ExternalModelModule;
import org.osm2world.core.world.modules.GolfModule;
import org.osm2world.core.world.modules.InvisibleModule;
import org.osm2world.core.world.modules.ParkingModule;
Expand Down Expand Up @@ -130,6 +131,7 @@ public <R extends Renderable> Collection<R> getRenderables(
private static final List<WorldModule> createDefaultModuleList() {

return Arrays.asList((WorldModule)
new ExternalModelModule(),
new RoadModule(),
new RailwayModule(),
new BuildingModule(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ public class OSMToMapDataConverter {
private final Configuration config;

private static final Tag MULTIPOLYON_TAG = new Tag("type", "multipolygon");


public OSMToMapDataConverter(MapProjection mapProjection, Configuration config) {
this.mapProjection = mapProjection;
Expand All @@ -73,7 +72,7 @@ public MapData createMapData(OSMData osmData) throws IOException {

createMapElements(osmData, mapNodes, mapWaySegs, mapAreas);

MapData mapData = new MapData(mapNodes, mapWaySegs, mapAreas,
MapData mapData = new MapData(mapProjection, mapNodes, mapWaySegs, mapAreas,
calculateFileBoundary(osmData.getBounds()));

calculateIntersectionsInMapData(mapData);
Expand Down
10 changes: 8 additions & 2 deletions src/org/osm2world/core/map_data/data/MapData.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Collection;
import java.util.List;

import org.osm2world.core.map_data.creation.MapProjection;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.osm.data.OSMData;
Expand All @@ -23,13 +24,14 @@ public class MapData {
final List<MapNode> mapNodes;
final List<MapWaySegment> mapWaySegments;
final List<MapArea> mapAreas;
final MapProjection mapProjection;

AxisAlignedBoundingBoxXZ fileBoundary;
AxisAlignedBoundingBoxXZ dataBoundary;

public MapData(List<MapNode> mapNodes, List<MapWaySegment> mapWaySegments,
public MapData(MapProjection mapProjection, List<MapNode> mapNodes, List<MapWaySegment> mapWaySegments,
List<MapArea> mapAreas, AxisAlignedBoundingBoxXZ fileBoundary) {

this.mapProjection = mapProjection;
this.mapNodes = mapNodes;
this.mapWaySegments = mapWaySegments;
this.mapAreas = mapAreas;
Expand Down Expand Up @@ -145,5 +147,9 @@ public Iterable<WorldObject> getWorldObjects() {
public <T> Iterable<T> getWorldObjects(Class<T> type) {
return Iterables.filter(getWorldObjects(), type);
}

public MapProjection getMapProjection() {
return this.mapProjection;
}

}
13 changes: 13 additions & 0 deletions src/org/osm2world/core/target/Target.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.osm2world.core.math.VectorXYZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.target.common.material.Material;
import org.osm2world.core.target.common.model.Model;
import org.osm2world.core.world.data.WorldObject;

/**
Expand Down Expand Up @@ -110,6 +111,18 @@ void drawColumn(Material material, Integer corners,
VectorXYZ base, double height, double radiusBottom,
double radiusTop, boolean drawBottom, boolean drawTop);

/**
* draws an instance of a 3D model
*
* @param model the model to be drawn
* @param direction rotation of the model in the XZ plane, as an angle in radians
* @param height height of the model; null for default (unspecified) height
* @param width width of the model; null for default (unspecified) width
* @param length length of the model; null for default (unspecified) length
*/
public void drawModel(Model model, VectorXYZ position,
double direction, Double height, Double width, Double length);

/**
* gives the target the chance to perform finish/cleanup operations
* after all objects have been drawn.
Expand Down
9 changes: 9 additions & 0 deletions src/org/osm2world/core/target/common/AbstractTarget.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.osm2world.core.target.Renderable;
import org.osm2world.core.target.Target;
import org.osm2world.core.target.common.material.Material;
import org.osm2world.core.target.common.model.Model;
import org.osm2world.core.world.data.WorldObject;

/**
Expand Down Expand Up @@ -196,6 +197,14 @@ public void drawConvexPolygon(Material material, List<VectorXYZ> vs,
drawTriangleFan(material, vs, texCoordLists);
}

@Override
public void drawModel(Model model, VectorXYZ position,
double direction, Double height, Double width, Double length) {

model.render(this, position, direction, height, width, length);

}

@Override
public void finish() {}

Expand Down
12 changes: 12 additions & 0 deletions src/org/osm2world/core/target/common/material/Material.java
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,18 @@ public Material brighter() {
getTransparency(), getShadow(), getAmbientOcclusion(), getTextureDataList());
}

public Material withAmbientFactor(float newAmbientFactor) {
return new ImmutableMaterial(interpolation, getColor(),
newAmbientFactor, getDiffuseFactor(), getSpecularFactor(), getShininess(),
getTransparency(), getShadow(), getAmbientOcclusion(), getTextureDataList());
}

public Material withDefuseFactor(float newDefuseFactor) {
return new ImmutableMaterial(interpolation, getColor(),
getAmbientFactor(), newDefuseFactor, getSpecularFactor(), getShininess(),
getTransparency(), getShadow(), getAmbientOcclusion(), getTextureDataList());
}

public Material darker() {
return new ImmutableMaterial(interpolation, getColor().darker(),
getAmbientFactor(), getDiffuseFactor(), getSpecularFactor(), getShininess(),
Expand Down
23 changes: 23 additions & 0 deletions src/org/osm2world/core/target/common/model/Model.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.osm2world.core.target.common.model;

import org.osm2world.core.math.VectorXYZ;
import org.osm2world.core.target.Target;

/**
* a single 3D model, typically loaded from a file or other resource
*/
public interface Model {

/**
* draws an instance of the model to any {@link Target}
*
* @param target target for the model; != null
* @param direction rotation of the model in the XZ plane, as an angle in radians
* @param height height of the model; null for default (unspecified) height
* @param width width of the model; null for default (unspecified) width
* @param length length of the model; null for default (unspecified) length
*/
public void render(Target<?> target, VectorXYZ position,
double direction, Double height, Double width, Double length);

}
60 changes: 60 additions & 0 deletions src/org/osm2world/core/target/common/model/obj/ExternalModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.osm2world.core.target.common.model.obj;

import java.util.ArrayList;
import java.util.List;

import org.osm2world.core.math.VectorXYZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.target.Target;
import org.osm2world.core.target.common.model.Model;
import org.osm2world.core.target.common.model.obj.parser.ModelLinksProxy;
import org.osm2world.core.target.common.model.obj.parser.ObjModel;
import org.osm2world.core.target.common.model.obj.parser.ObjModel.ObjFace;


public class ExternalModel implements Model {

private ObjModel model;

private VectorXZ originT = new VectorXZ(0.0, 0.0);
private double scale = 1.0;
private boolean zAxisUp = false;


public ExternalModel(String link, ModelLinksProxy proxy) {
this.model = new ObjModel(link, proxy);

originT = this.model.getBBOX().center().xz().invert();
}

@Override
public void render(Target<?> target, VectorXYZ position,
double direction, Double height, Double width,
Double length) {

VectorXYZ translate = position.add(originT);

for(ObjFace f : this.model.listFaces()) {
List<VectorXYZ> vs = new ArrayList<>(f.vs.size());

for (VectorXYZ src : f.vs) {
src = src.mult(scale);

if (zAxisUp) {
src = src.rotateX(Math.toRadians(-90.0));
}

src = src.rotateY(direction);
src = src.add(translate);

vs.add(src);
}

if (f.material != null) {
f.material = f.material.withAmbientFactor(0.9f);
target.drawTriangleFan(f.material, vs, f.texCoordLists);
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package org.osm2world.core.target.common.model.obj.parser;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;

public class ModelLinksProxy {

private String localCachePath;

public ModelLinksProxy(String localCachePath) {
this.localCachePath = localCachePath;
}

public static String resolveLink(URL base, String link) throws MalformedURLException {
if (new File(link).isAbsolute()) {
URL root = new URL(base.getProtocol() + "://" + base.getAuthority());
return new URL(root, link).toString();
}
else {
return new URL(base, link).toString();
}
}

public File getFile(String link) {
try {
if(isURL(link)) {
File file = getPathForObjUrl(link);
return saveFile(file, new URL(link));
}
return null;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}

private File saveFile(File file, URL link)
throws IOException, FileNotFoundException, MalformedURLException, InterruptedException {

if (!file.exists()) {
file.getParentFile().mkdirs();
if(file.createNewFile()) {
FileOutputStream fileOutputStream = new FileOutputStream(file);
try {
java.nio.channels.FileLock lock = fileOutputStream.getChannel().lock();
try {
saveFile(fileOutputStream, link);
} finally {
lock.release();
}
} finally {
fileOutputStream.close();
}
}
}

// File exists, but might be locked by other thred/app for writing data
// wait untill it will be released
int timeout = 30 * 1000;
while(!file.canWrite()) {
if (timeout > 0) {
Thread.sleep(100);
timeout -= 100;
}
else {
break;
}
}

return file;
}

private void saveFile(FileOutputStream fileOutputStream, URL url) throws IOException {
System.err.println("Download " + url);
ReadableByteChannel rbc = Channels.newChannel(url.openStream());
fileOutputStream.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
}

private File getPathForObjUrl(String link) {
try {
URL url = new URL(link);
String path = url.getPath();

return new File(localCachePath, path);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}

public File getLinkedFile(String local, String base) {
try {
String link = resolveLink(new URL(base), local);
return getFile(link);
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}

private static boolean isURL(String link) {
return link.startsWith("http://") || link.startsWith("https://");
}

}
Loading