package org.sunflow; import java.util.ArrayList; import java.util.Locale; import org.sunflow.core.Camera; import org.sunflow.core.Geometry; import org.sunflow.core.Instance; import org.sunflow.core.LightSource; import org.sunflow.core.Modifier; import org.sunflow.core.Options; import org.sunflow.core.ParameterList; import org.sunflow.core.PrimitiveList; import org.sunflow.core.RenderObject; import org.sunflow.core.Scene; import org.sunflow.core.Shader; import org.sunflow.core.Tesselatable; import org.sunflow.system.UI; import org.sunflow.system.UI.Module; import org.sunflow.util.FastHashMap; final class RenderObjectMap { private FastHashMap renderObjects; private boolean rebuildInstanceList; private boolean rebuildLightList; private enum RenderObjectType { UNKNOWN, SHADER, MODIFIER, GEOMETRY, INSTANCE, LIGHT, CAMERA, OPTIONS } RenderObjectMap() { renderObjects = new FastHashMap<>(); rebuildInstanceList = rebuildLightList = false; } final boolean has(String name) { return renderObjects.containsKey(name); } final void remove(String name) { RenderObjectHandle obj = renderObjects.get(name); if (obj == null) { UI.printWarning(Module.API, "Unable to remove \"%s\" - object was not defined yet"); return; } UI.printDetailed(Module.API, "Removing object \"%s\"", name); renderObjects.remove(name); // scan through all objects to make sure we don't have any // references to the old object still around switch (obj.type) { case SHADER: Shader s = obj.getShader(); for (FastHashMap.Entry e : renderObjects) { Instance i = e.getValue().getInstance(); if (i != null) { UI.printWarning(Module.API, "Removing shader \"%s\" from instance \"%s\"", name, e.getKey()); i.removeShader(s); } } break; case MODIFIER: Modifier m = obj.getModifier(); for (FastHashMap.Entry e : renderObjects) { Instance i = e.getValue().getInstance(); if (i != null) { UI.printWarning(Module.API, "Removing modifier \"%s\" from instance \"%s\"", name, e.getKey()); i.removeModifier(m); } } break; case GEOMETRY: { Geometry g = obj.getGeometry(); for (FastHashMap.Entry e : renderObjects) { Instance i = e.getValue().getInstance(); if (i != null && i.hasGeometry(g)) { UI.printWarning(Module.API, "Removing instance \"%s\" because it referenced geometry \"%s\"", e.getKey(), name); remove(e.getKey()); } } break; } case INSTANCE: rebuildInstanceList = true; break; case LIGHT: rebuildLightList = true; break; default: // no dependencies break; } } final boolean update(String name, ParameterList pl, SunflowAPI api) { RenderObjectHandle obj = renderObjects.get(name); boolean success; if (obj == null) { UI.printError(Module.API, "Unable to update \"%s\" - object was not defined yet", name); success = false; } else { UI.printDetailed(Module.API, "Updating %s object \"%s\"", obj.typeName(), name); success = obj.update(pl, api); if (!success) { UI.printError(Module.API, "Unable to update \"%s\" - removing", name); remove(name); } else { switch (obj.type) { case GEOMETRY: case INSTANCE: rebuildInstanceList = true; break; case LIGHT: rebuildLightList = true; break; default: break; } } } return success; } final void updateScene(Scene scene) { if (rebuildInstanceList) { UI.printInfo(Module.API, "Building scene instance list for rendering ..."); int numInfinite = 0, numInstance = 0; for (FastHashMap.Entry e : renderObjects) { Instance i = e.getValue().getInstance(); if (i != null) { i.updateBounds(); if (i.getBounds() == null) { numInfinite++; } else if (!i.getBounds().isEmpty()) { numInstance++; } else { UI.printWarning(Module.API, "Ignoring empty instance: \"%s\"", e.getKey()); } } } Instance[] infinite = new Instance[numInfinite]; Instance[] instance = new Instance[numInstance]; numInfinite = numInstance = 0; for (FastHashMap.Entry e : renderObjects) { Instance i = e.getValue().getInstance(); if (i != null) { if (i.getBounds() == null) { infinite[numInfinite] = i; numInfinite++; } else if (!i.getBounds().isEmpty()) { instance[numInstance] = i; numInstance++; } } } scene.setInstanceLists(instance, infinite); rebuildInstanceList = false; } if (rebuildLightList) { UI.printInfo(Module.API, "Building scene light list for rendering ..."); ArrayList lightList = new ArrayList<>(); for (FastHashMap.Entry e : renderObjects) { LightSource light = e.getValue().getLight(); if (light != null) { lightList.add(light); } } scene.setLightList(lightList.toArray(new LightSource[lightList.size()])); rebuildLightList = false; } } final void put(String name, Shader shader) { renderObjects.put(name, new RenderObjectHandle(shader)); } final void put(String name, Modifier modifier) { renderObjects.put(name, new RenderObjectHandle(modifier)); } final void put(String name, PrimitiveList primitives) { renderObjects.put(name, new RenderObjectHandle(primitives)); } final void put(String name, Tesselatable tesselatable) { renderObjects.put(name, new RenderObjectHandle(tesselatable)); } final void put(String name, Instance instance) { renderObjects.put(name, new RenderObjectHandle(instance)); } final void put(String name, LightSource light) { renderObjects.put(name, new RenderObjectHandle(light)); } final void put(String name, Camera camera) { renderObjects.put(name, new RenderObjectHandle(camera)); } final void put(String name, Options options) { renderObjects.put(name, new RenderObjectHandle(options)); } final Geometry lookupGeometry(String name) { if (name == null) { return null; } RenderObjectHandle handle = renderObjects.get(name); return (handle == null) ? null : handle.getGeometry(); } final Instance lookupInstance(String name) { if (name == null) { return null; } RenderObjectHandle handle = renderObjects.get(name); return (handle == null) ? null : handle.getInstance(); } final Camera lookupCamera(String name) { if (name == null) { return null; } RenderObjectHandle handle = renderObjects.get(name); return (handle == null) ? null : handle.getCamera(); } final Options lookupOptions(String name) { if (name == null) { return null; } RenderObjectHandle handle = renderObjects.get(name); return (handle == null) ? null : handle.getOptions(); } final Shader lookupShader(String name) { if (name == null) { return null; } RenderObjectHandle handle = renderObjects.get(name); return (handle == null) ? null : handle.getShader(); } final Modifier lookupModifier(String name) { if (name == null) { return null; } RenderObjectHandle handle = renderObjects.get(name); return (handle == null) ? null : handle.getModifier(); } final LightSource lookupLight(String name) { if (name == null) { return null; } RenderObjectHandle handle = renderObjects.get(name); return (handle == null) ? null : handle.getLight(); } private static final class RenderObjectHandle { private final RenderObject obj; private final RenderObjectType type; private RenderObjectHandle(Shader shader) { obj = shader; type = RenderObjectType.SHADER; } private RenderObjectHandle(Modifier modifier) { obj = modifier; type = RenderObjectType.MODIFIER; } private RenderObjectHandle(Tesselatable tesselatable) { obj = new Geometry(tesselatable); type = RenderObjectType.GEOMETRY; } private RenderObjectHandle(PrimitiveList prims) { obj = new Geometry(prims); type = RenderObjectType.GEOMETRY; } private RenderObjectHandle(Instance instance) { obj = instance; type = RenderObjectType.INSTANCE; } private RenderObjectHandle(LightSource light) { obj = light; type = RenderObjectType.LIGHT; } private RenderObjectHandle(Camera camera) { obj = camera; type = RenderObjectType.CAMERA; } private RenderObjectHandle(Options options) { obj = options; type = RenderObjectType.OPTIONS; } private boolean update(ParameterList pl, SunflowAPI api) { return obj.update(pl, api); } private String typeName() { return type.name().toLowerCase(Locale.ENGLISH); } private Shader getShader() { return (type == RenderObjectType.SHADER) ? (Shader) obj : null; } private Modifier getModifier() { return (type == RenderObjectType.MODIFIER) ? (Modifier) obj : null; } private Geometry getGeometry() { return (type == RenderObjectType.GEOMETRY) ? (Geometry) obj : null; } private Instance getInstance() { return (type == RenderObjectType.INSTANCE) ? (Instance) obj : null; } private LightSource getLight() { return (type == RenderObjectType.LIGHT) ? (LightSource) obj : null; } private Camera getCamera() { return (type == RenderObjectType.CAMERA) ? (Camera) obj : null; } private Options getOptions() { return (type == RenderObjectType.OPTIONS) ? (Options) obj : null; } } }