package org.sunflow.core; import org.sunflow.SunflowAPI; import org.sunflow.math.BoundingBox; import org.sunflow.math.Matrix4; import org.sunflow.math.Point3; import org.sunflow.math.Vector3; import org.sunflow.system.UI; import org.sunflow.system.UI.Module; /** * This represents an instance of a {@link Geometry} into the scene. This class * maps object space to world space and maintains a list of shaders and * modifiers attached to the surface. */ public class Instance implements RenderObject { private Matrix4 o2w; private Matrix4 w2o; private BoundingBox bounds; private Geometry geometry; private Shader[] shaders; private Modifier[] modifiers; public boolean update(ParameterList pl, SunflowAPI api) { String geometryName = pl.getString("geometry", null); if (geometry == null || geometryName != null) { if (geometryName == null) { UI.printError(Module.GEOM, "geometry parameter missing - unable to create instance"); return false; } geometry = api.lookupGeometry(geometryName); if (geometry == null) { UI.printError(Module.GEOM, "Geometry \"%s\" was not declared yet - instance is invalid", geometryName); return false; } } String[] shaderNames = pl.getStringArray("shaders", null); if (shaderNames != null) { // new shader names have been provided shaders = new Shader[shaderNames.length]; for (int i = 0; i < shaders.length; i++) { shaders[i] = api.lookupShader(shaderNames[i]); if (shaders[i] == null) UI.printWarning(Module.GEOM, "Shader \"%s\" was not declared yet - ignoring", shaderNames[i]); } } else { // re-use existing shader array } String[] modifierNames = pl.getStringArray("modifiers", null); if (modifierNames != null) { // new modifier names have been provided modifiers = new Modifier[modifierNames.length]; for (int i = 0; i < modifiers.length; i++) { modifiers[i] = api.lookupModifier(modifierNames[i]); if (modifiers[i] == null) UI.printWarning(Module.GEOM, "Modifier \"%s\" was not declared yet - ignoring", modifierNames[i]); } } Matrix4 transform = pl.getMatrix("transform", o2w); if (transform != o2w) { o2w = transform; if (o2w != null) { w2o = o2w.inverse(); if (w2o == null) { UI.printError(Module.GEOM, "Unable to compute transform inverse - determinant is: %g", o2w.determinant()); return false; } } else o2w = w2o = null; } return true; } /** * Recompute world space bounding box of this instance. */ public void updateBounds() { bounds = geometry.getWorldBounds(o2w); } /** * Checks to see if this instance is relative to the specified geometry. * * @param g geometry to check against * @return <code>true</code> if the instanced geometry is equals to g, * <code>false</code> otherwise */ public boolean hasGeometry(Geometry g) { return geometry == g; } /** * Remove the specified shader from the instance's list if it is being used. * * @param s shader to remove */ public void removeShader(Shader s) { if (shaders != null) { for (int i = 0; i < shaders.length; i++) if (shaders[i] == s) shaders[i] = null; } } /** * Remove the specified modifier from the instance's list if it is being * used. * * @param m modifier to remove */ public void removeModifier(Modifier m) { if (modifiers != null) { for (int i = 0; i < modifiers.length; i++) if (modifiers[i] == m) modifiers[i] = null; } } /** * Get the world space bounding box for this instance. * * @return bounding box in world space */ public BoundingBox getBounds() { return bounds; } int getNumPrimitives() { return geometry.getNumPrimitives(); } void intersect(Ray r, IntersectionState state) { Ray localRay = r.transform(w2o); state.current = this; geometry.intersect(localRay, state); // FIXME: transfer max distance to current ray r.setMax(localRay.getMax()); } /** * Prepare the shading state for shader invocation. This also runs the * currently attached surface modifier. * * @param state shading state to be prepared */ public void prepareShadingState(ShadingState state) { geometry.prepareShadingState(state); if (state.getNormal() != null && state.getGeoNormal() != null) state.correctShadingNormal(); // run modifier if it was provided if (state.getModifier() != null) state.getModifier().modify(state); } /** * Get a shader for the instance's list. * * @param i index into the shader list * @return requested shader, or <code>null</code> if the input is invalid */ public Shader getShader(int i) { if (shaders == null || i < 0 || i >= shaders.length) return null; return shaders[i]; } /** * Get a modifier for the instance's list. * * @param i index into the modifier list * @return requested modifier, or <code>null</code> if the input is * invalid */ public Modifier getModifier(int i) { if (modifiers == null || i < 0 || i >= modifiers.length) return null; return modifiers[i]; } /** * Transform the given point from object space to world space. A new * {@link Point3} object is returned. * * @param p object space position to transform * @return transformed position */ public Point3 transformObjectToWorld(Point3 p) { return o2w == null ? new Point3(p) : o2w.transformP(p); } /** * Transform the given point from world space to object space. A new * {@link Point3} object is returned. * * @param p world space position to transform * @return transformed position */ public Point3 transformWorldToObject(Point3 p) { return o2w == null ? new Point3(p) : w2o.transformP(p); } /** * Transform the given normal from object space to world space. A new * {@link Vector3} object is returned. * * @param n object space normal to transform * @return transformed normal */ public Vector3 transformNormalObjectToWorld(Vector3 n) { return o2w == null ? new Vector3(n) : w2o.transformTransposeV(n); } /** * Transform the given normal from world space to object space. A new * {@link Vector3} object is returned. * * @param n world space normal to transform * @return transformed normal */ public Vector3 transformNormalWorldToObject(Vector3 n) { return o2w == null ? new Vector3(n) : o2w.transformTransposeV(n); } /** * Transform the given vector from object space to world space. A new * {@link Vector3} object is returned. * * @param v object space vector to transform * @return transformed vector */ public Vector3 transformVectorObjectToWorld(Vector3 v) { return o2w == null ? new Vector3(v) : o2w.transformV(v); } /** * Transform the given vector from world space to object space. A new * {@link Vector3} object is returned. * * @param v world space vector to transform * @return transformed vector */ public Vector3 transformVectorWorldToObject(Vector3 v) { return o2w == null ? new Vector3(v) : w2o.transformV(v); } PrimitiveList getBakingPrimitives() { return geometry.getBakingPrimitives(); } Geometry getGeometry() { return geometry; } }