org.jrman.parser.Parser.java Source code

Java tutorial

Introduction

Here is the source code for org.jrman.parser.Parser.java

Source

/*
 * Parser.java
 * Copyright (C) 2003, 2004, 2006 Gerardo Horvilleur Martinez
 * 
 * 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 2 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, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package org.jrman.parser;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.StreamTokenizer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import javax.vecmath.AxisAngle4f;
import javax.vecmath.Color3f;
import javax.vecmath.Matrix4f;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.ParseException;
import org.jrman.attributes.Attributes;
import org.jrman.attributes.Basis;
import org.jrman.attributes.DetailRange;
import org.jrman.attributes.GeometricApproximation;
import org.jrman.attributes.MutableAttributes;
import org.jrman.attributes.Orientation;
import org.jrman.attributes.ShadingInterpolation;
import org.jrman.attributes.Space;
import org.jrman.attributes.TextureCoordinates;
import org.jrman.attributes.TrimCurveSense;
import org.jrman.geom.AffineTransform;
import org.jrman.geom.Bounds2f;
import org.jrman.geom.Bounds3f;
import org.jrman.geom.PerspectiveTransform;
import org.jrman.geom.Transform;
import org.jrman.main.JRMan;
import org.jrman.maps.MipMap;
import org.jrman.options.CameraProjection;
import org.jrman.options.Display;
import org.jrman.options.Exposure;
import org.jrman.options.Filter;
import org.jrman.options.Hider;
import org.jrman.options.Quantizer;
import org.jrman.parameters.Declaration;
import org.jrman.parameters.ParameterList;
import org.jrman.parameters.UniformArrayInteger;
import org.jrman.parameters.UniformScalarFloat;
import org.jrman.parameters.UniformScalarInteger;
import org.jrman.parameters.UniformScalarString;
import org.jrman.parameters.VaryingArrayFloat;
import org.jrman.parameters.VaryingScalarFloat;
import org.jrman.parameters.VaryingScalarTuple3f;
import org.jrman.parser.keywords.AbstractKeywordParser;
import org.jrman.parser.keywords.KeywordParser;
import org.jrman.primitive.BicubicPatch;
import org.jrman.primitive.BilinearPatch;
import org.jrman.primitive.Cone;
import org.jrman.primitive.CubicCurve;
import org.jrman.primitive.Cylinder;
import org.jrman.primitive.DelayedReadArchive;
import org.jrman.primitive.Disk;
import org.jrman.primitive.Hyperboloid;
import org.jrman.primitive.LinearCurve;
import org.jrman.primitive.NurbsRI;
import org.jrman.primitive.ObjectInstance;
import org.jrman.primitive.Paraboloid;
import org.jrman.primitive.Point;
import org.jrman.primitive.PointsPolygons;
import org.jrman.primitive.Primitive;
import org.jrman.primitive.Sphere;
import org.jrman.primitive.Torus;
import org.jrman.render.Renderer;
import org.jrman.shaders.DisplacementShader;
import org.jrman.shaders.Imager;
import org.jrman.shaders.LightShader;
import org.jrman.shaders.SurfaceShader;
import org.jrman.shaders.VolumeShader;
import org.jrman.util.Constants;

public class Parser {

    private final static String KEYWORD_PREFIX = "org.jrman.parser.keywords.Keyword";

    private final static Map<String, String> fullFileNames = new HashMap<String, String>();

    private String currentDirectory;

    private boolean inAreaLightSource;

    private String beginName;

    private World world;

    private Frame frame;

    private Renderer renderer;

    private Map<String, KeywordParser> keywordParsers = new HashMap<String, KeywordParser>();

    private MutableAttributes currentAttributes;

    private Attributes currentImmutableAttributes;

    private Stack<Attributes> attributeStack = new Stack<Attributes>();

    private Stack<Transform> transformStack = new Stack<Transform>();

    private Stack<World> worldStack = new Stack<World>();

    private Stack<Frame> frameStack = new Stack<Frame>();

    private Stack<State> stateStack = new Stack<State>();

    private State state = State.OUTSIDE;

    private String currentOperation; // Just now to avoid compiler warnings...

    private Map<Integer, ObjectInstanceList> objectInstanceLists = new HashMap<Integer, ObjectInstanceList>();

    private ObjectInstanceList currentObjectInstanceList;

    private boolean inObject;

    private CommandLine cmdLine;

    private int firstFrame = -1;

    private int lastFrame = -1;

    private int sphereCount;

    private int torusCount;

    private int cylinderCount;

    private int coneCount;

    private int diskCount;

    private int paraboloidCount;

    private int hyperboloidCount;

    private int bilinearPatchCount;

    private int bicubicPatchCount;

    private int polygonCount;

    private int pointCount;

    private int nurbsCount;

    private int curveCount;

    public static class State {

        public final static State OUTSIDE = new State("OUTSIDE");

        public final static State BEGIN_END = new State("BEGIN_END");

        public final static State FRAME = new State("FRAME");

        public final static State WORLD = new State("WORLD");

        public final static State ATTRIBUTE = new State("ATTRIBUTE");

        public final static State TRANSFORM = new State("TRANSFORM");

        public final static State SOLID = new State("SOLID");

        public final static State MOTION = new State("MOTION");

        public final static State OBJECT = new State("OBJECT");

        private String name;

        public State(String name) {
            this.name = name;
        }

        public String toString() {
            return name;
        }
    }

    public void setCurrentDirectory(String currentDirectory) {
        this.currentDirectory = currentDirectory;
    }

    public String getCurrentDirectory() {
        return currentDirectory;
    }

    public void parse(String filename) throws Exception {
        if (currentDirectory != null) {
            String fullFileName = (String) fullFileNames.get(filename);
            if (fullFileName == null) {
                fullFileName = currentDirectory + File.separator + filename;
                fullFileNames.put(filename, fullFileName);
            }
            filename = fullFileName;
        }
        FileReader fr = new FileReader(filename);
        Tokenizer st = new Tokenizer(new BufferedReader(fr));
        // st.commentChar('#');
        int tk;
        while ((tk = st.nextToken()) != StreamTokenizer.TT_EOF) {
            try {
                if (tk != StreamTokenizer.TT_WORD)
                    throw new Exception("Expected keyword at line " + st.lineno());
                String keyword = st.sval;
                KeywordParser kp = getKeyWordParser(keyword);
                if (!kp.getValidStates().contains(state))
                    throw new IllegalStateException(
                            "Keyword" + kp + " is not valid in state " + state + ", at line " + st.lineno());
                kp.parse(st);
            } catch (Exception pe) {
                System.err.println("Error: " + pe);
                pe.printStackTrace();
            }
        }
        fr.close();
    }

    public Parser(CommandLine cmdLine) throws ParseException {
        if (cmdLine == null)
            throw new NullPointerException("Command line cannot be null");
        this.cmdLine = cmdLine;
        // parse frame subset numbers
        String frameValue = "";
        try {
            if (cmdLine.hasOption(JRMan.OPTION_FIRSTFRAME)) {
                frameValue = cmdLine.getOptionValue(JRMan.OPTION_FIRSTFRAME);
                firstFrame = Integer.parseInt(frameValue);
            }
            if (cmdLine.hasOption(JRMan.OPTION_LASTFRAME)) {
                frameValue = cmdLine.getOptionValue(JRMan.OPTION_LASTFRAME);
                lastFrame = Integer.parseInt(frameValue);
            }
        } catch (NumberFormatException nfe) {
            throw new ParseException(frameValue + " is not a valid frame number");
        }
        world = new World();
        frame = new Frame();
        frame.setEndOfFrameStatistics(cmdLine.hasOption(JRMan.OPTION_STATISTICS));
        frame.setShowProgressEnabled(cmdLine.hasOption(JRMan.OPTION_PROGRESS));
        frame.setFramebufferAlways(cmdLine.hasOption(JRMan.OPTION_FRAMEBUFFER));
        currentAttributes = new MutableAttributes();
    }

    private KeywordParser getKeyWordParser(String keyword) throws Exception {
        KeywordParser kp = (KeywordParser) keywordParsers.get(keyword);
        if (kp == null)
            kp = (KeywordParser) keywordParsers.get(keyword.toLowerCase());
        if (kp == null) {
            char c = keyword.charAt(0);
            if (Character.isLowerCase(c))
                keyword = Character.toUpperCase(c) + keyword.substring(1);
            kp = (KeywordParser) Class.forName(KEYWORD_PREFIX + keyword).newInstance();
            kp.setParser(this);
            keywordParsers.put(keyword, kp);
            keywordParsers.put(keyword.toLowerCase(), kp);
        }
        return kp;
    }

    private void pushState(State state) {
        stateStack.push(this.state);
        this.state = state;
    }

    private void popState() {
        state = (State) stateStack.pop();
    }

    private void pushAttributes() {
        attributeStack.push(new MutableAttributes(currentAttributes));
    }

    public void pushAttributes(Attributes attributes) {
        attributeStack.push(new MutableAttributes(currentAttributes));
        currentAttributes = new MutableAttributes(attributes);
        currentAttributes.setModified(true);
    }

    public void popAttributes() {
        currentAttributes = (MutableAttributes) attributeStack.pop();
        currentAttributes.setModified(true);
    }

    public Attributes getAttributes() {
        if (currentImmutableAttributes == null || currentAttributes.isModified()) {
            currentImmutableAttributes = new Attributes(currentAttributes);
            currentAttributes.setModified(false);
        }
        return currentImmutableAttributes;
    }

    private void newObjectInstanceList(int sequenceNumber) {
        currentObjectInstanceList = new ObjectInstanceList();
        objectInstanceLists.put(new Integer(sequenceNumber), currentObjectInstanceList);
    }

    private ObjectInstanceList getObjectInstanceList(int sequenceNumber) {
        ObjectInstanceList result = (ObjectInstanceList) objectInstanceLists.get(new Integer(sequenceNumber));
        if (result == null)
            throw new IllegalArgumentException("Unknown instance: " + sequenceNumber);
        return result;
    }

    public void attributeBegin() {
        pushAttributes();
        pushState(State.ATTRIBUTE);
    }

    public void attributeEnd() {
        popAttributes();
        popState();
        inAreaLightSource = false;
    }

    public void transformBegin() {
        transformStack.push(currentAttributes.getTransform());
        pushState(State.TRANSFORM);
    }

    public void transformEnd() {
        currentAttributes.setTransform((Transform) transformStack.pop());
        popState();
    }

    public void saveCurrentTransformAs(String name) {
        Global.setTransform(name, currentAttributes.getTransform());
    }

    public void setCurrentTransformTo(String name) {
        currentAttributes.setTransform(Global.getTransform(name));
    }

    public void setIdentity() {
        currentAttributes.setTransform(AffineTransform.IDENTITY);
    }

    public void rotate(float angle, float dx, float dy, float dz) {
        AxisAngle4f axisAngle = new AxisAngle4f(dx, dy, dz, (float) Math.toRadians(angle));
        Matrix4f rotation = new Matrix4f();
        rotation.setIdentity();
        rotation.set(axisAngle);
        concatTransform(rotation);
    }

    public void scale(float sx, float sy, float sz) {
        Matrix4f scale = new Matrix4f(sx, 0f, 0f, 0f, 0f, sy, 0f, 0f, 0f, 0f, sz, 0f, 0f, 0f, 0f, 1f);
        concatTransform(scale);
    }

    public void translate(float dx, float dy, float dz) {
        Matrix4f translation = new Matrix4f();
        translation.setIdentity();
        translation.setTranslation(new Vector3f(dx, dy, dz));
        concatTransform(translation);
    }

    public void perspective(float fov) {
        PerspectiveTransform perspective = PerspectiveTransform.createWithFov(fov, 1f, Constants.INFINITY);
        Transform current = currentAttributes.getTransform();
        currentAttributes.setTransform(perspective.preConcat(current));
    }

    public void setDetailRange(float minVisible, float lowerTransition, float upperTransition, float maxVisible) {
        DetailRange detailRange = new DetailRange(minVisible, lowerTransition, upperTransition, maxVisible);
        currentAttributes.setDetailRange(detailRange);
    }

    public void setDetail(Bounds3f detail) {
        currentAttributes.setDetail(detail);
    }

    public void setBound(Bounds3f bounds) {
        currentAttributes.setBound(bounds);
    }

    public void setTransform(Matrix4f matrix) {
        currentAttributes.setTransform(AffineTransform.IDENTITY.concat(matrix));
    }

    public void concatTransform(Matrix4f trans) {
        Transform current = currentAttributes.getTransform();
        current = current.concat(trans);
        currentAttributes.setTransform(current);
    }

    public void setColor(Color3f color) {
        currentAttributes.setColor(color);
    }

    public void setOpacity(Color3f opacity) {
        currentAttributes.setOpacity(opacity);
    }

    public void setShadingInterpolation(String type) {
        currentAttributes.setShadingInterpolation(ShadingInterpolation.getNamed(type));
    }

    public void setMatte(boolean b) {
        currentAttributes.setMatte(b);
    }

    public void setGeometricApproximation(String type, float value) {
        GeometricApproximation geometricApproximation = new GeometricApproximation(type, value);
        currentAttributes.setGeometricApproximation(geometricApproximation);
    }

    public void reverseOrientation() {
        Orientation orientation = currentAttributes.getOrientation();
        currentAttributes.setOrientation(orientation.getReverse());
    }

    public void frameBegin(int n) {
        pushState(State.FRAME);
        frameStack.push(new Frame(frame));
        worldStack.push(new World(world));
        pushAttributes();
        frame.setFrameNumber(n);
    }

    public void begin(String renderer) {
        this.beginName = renderer;
        pushState(State.BEGIN_END);
    }

    public void end() {
        popState();
    }

    public void frameEnd() {
        popAttributes();
        world = (World) worldStack.pop();
        frame = (Frame) frameStack.pop();
        popState();
    }

    public void motionBegin(float[] times) {
        pushState(State.MOTION);
        frame.setMotionTimes(times);
    }

    public void motionEnd() {
        popState();
    }

    public void objectBegin(int sequenceNumber) {
        pushAttributes();
        pushState(State.OBJECT);
        setIdentity();
        newObjectInstanceList(sequenceNumber);
        inObject = true;
    }

    public void objectEnd() {
        currentObjectInstanceList = null;
        inObject = false;
        popState();
        popAttributes();
    }

    public void solidBegin(String operation) {
        pushAttributes();
        pushState(State.SOLID);
        currentOperation = operation;
    }

    public void solidEnd() {
        popState();
        popAttributes();
    }

    public void worldBegin() {
        pushState(State.WORLD);
        frameStack.push(new Frame(frame));
        worldStack.push(new World(world));
        frame.defineScreen();
        Global.setTransform("raster", frame.getRasterTransform());
        // from "screen" to "raster"
        Global.setTransform("NDC", frame.getNDCTransform()); // from "raster"
        // to "NDC"
        saveCurrentTransformAs("camera"); // from "world" to "camera"
        setIdentity();
        currentAttributes.setSpace(Space.WORLD);
        pushAttributes();
        renderer = Renderer.createRenderer(frame, world, this);
        // Must be set after defining gridsize...
        SurfaceShader.setBetterHighlights(cmdLine.hasOption(JRMan.OPTION_QUALITY));
    }

    public void worldEnd() {
        AbstractKeywordParser.reset();
        boolean doRender = true;
        if (firstFrame > -1 && frame.getFrameNumber() < firstFrame)
            doRender = false;
        if (lastFrame > -1 && frame.getFrameNumber() > lastFrame)
            doRender = false;
        if (doRender)
            renderer.render();
        renderer = null;
        world = (World) worldStack.pop();
        frame = (Frame) frameStack.pop();
        popState();
        popAttributes();
        currentAttributes.setSpace(Space.CAMERA);
    }

    public void setBasis(Basis uBasis, int uStep, Basis vBasis, int vStep) {
        currentAttributes.setBasis(uBasis, uStep, vBasis, vStep);
    }

    public void setSides(int sides) {
        currentAttributes.setSides(sides);
    }

    public void setTextureCoordinates(float s1, float t1, float s2, float t2, float s3, float t3, float s4,
            float t4) {
        currentAttributes.setTextureCoordinates(new TextureCoordinates(s1, t1, s2, t2, s3, t3, s4, t4));
    }

    public Declaration getDeclaration(String name) {
        return Global.getDeclaration(name);
    }

    public void setDeclaration(String name, String decl) {
        Global.setDeclaration(name, decl);
    }

    public void setFormat(int xResolution, int yResolution, float pixelAspectRatio) {
        if (xResolution > 0)
            frame.setHorizontalResolution(xResolution);
        if (yResolution > 0)
            frame.setVerticalResolution(yResolution);
        if (pixelAspectRatio > 0)
            frame.setPixelAspectRatio(pixelAspectRatio);
    }

    public void setFrameAspectRatio(float frameAspectRatio) {
        frame.setFrameAspectRatio(frameAspectRatio);
    }

    public void setScreenWindow(float left, float right, float bottom, float top) {
        frame.setScreenWindow(new Bounds2f(left, right, bottom, top));
    }

    public void setCropWindow(float xmin, float xmax, float ymin, float ymax) {
        frame.setCropWindow(new Bounds2f(xmin, xmax, ymin, ymax));
    }

    public void setProjection(String name, ParameterList parameters) {
        CameraProjection cameraProjection = CameraProjection.getNamed(name);
        frame.setCameraProjection(cameraProjection);
        if (cameraProjection == CameraProjection.PERSPECTIVE) {
            UniformScalarFloat parameter = (UniformScalarFloat) parameters.getParameter("fov");
            if (parameter != null)
                frame.setFieldOfView(parameter.getValue());
            currentAttributes.setTransform(PerspectiveTransform.createWithFov(frame.getFieldOfView(),
                    frame.getNearClipping(), frame.getFarClipping()));
        } else if (cameraProjection == CameraProjection.ORTHOGRAPHIC) {
            currentAttributes.setTransform(
                    AffineTransform.createOrthographic(frame.getNearClipping(), frame.getFarClipping()));
        }
        saveCurrentTransformAs("screen"); // from "camera" to "screen"
        setIdentity();
    }

    public void setClipping(float near, float far) {
        frame.setNearClipping(near);
        frame.setFarClipping(far);
    }

    public void setDepthOfField(float fstop, float focalLength, float focalDistance) {
        frame.setFStop(fstop);
        frame.setFocalLength(focalLength);
        frame.setFocalDistance(focalDistance);
    }

    public void setDepthOfField(float fstop) {
        frame.setFStop(fstop);
    }

    public void setShutter(float min, float max) {
        frame.setShutterOpen(min);
        frame.setShutterClose(max);
    }

    public void setPixelVariance(float variation) {
        frame.setPixelVariance(variation);
    }

    public void setPixelSamples(float xSamples, float ySamples) {
        frame.setHorizontalSamplingRate(xSamples);
        frame.setVerticalSamplingRate(ySamples);
    }

    public void setPixelFilter(String type, float xWidth, float yWidth) {
        frame.setFilter(new Filter(Filter.Type.getNamed(type), xWidth, yWidth));
    }

    public void setExposure(float gain, float gamma) {
        frame.setExposure(new Exposure(gain, gamma));
    }

    public void setQuantize(String type, int one, int min, int max, float ditherAmplitude) {
        if (type.equals("rgba")) {
            frame.setColorQuantizer(new Quantizer(one, min, max, ditherAmplitude));
        } else if (type.equals("z")) {
            frame.setDepthQuantizer(new Quantizer(one, min, max, ditherAmplitude));
        } else
            throw new IllegalArgumentException("no such quantizer: " + type);
    }

    public void setDisplay(String name, String type, String mode, ParameterList parameters) {
        frame.setDisplay(new Display(name, type, mode));
        UniformArrayInteger parameter = (UniformArrayInteger) parameters.getParameter("origin");
        if (parameter != null) {
            frame.setOriginX(parameter.getValue(0));
            frame.setOriginY(parameter.getValue(1));
        }
    }

    public void setHider(String type, ParameterList parameters) {
        frame.setHider(Hider.getNamed(type));
        frame.setHiderParameters(parameters);
    }

    public void setRelativeDetail(float relativeDetail) {
        frame.setRelativeDetail(relativeDetail);
    }

    public void setOption(String name, ParameterList parameters) {
        if (name.equals("limits")) {
            UniformScalarInteger param1 = (UniformScalarInteger) parameters.getParameter("gridsize");
            if (param1 != null)
                frame.setGridSize(param1.getValue());
            UniformArrayInteger param2 = (UniformArrayInteger) parameters.getParameter("bucketsize");
            if (param2 != null) {
                frame.setBucketSizeX(param2.getValue(0));
                frame.setBucketSizeY(param2.getValue(1));
            }
        } else if (name.equals("statistics")) {
            UniformScalarInteger param = (UniformScalarInteger) parameters.getParameter("endofframe");
            if (param != null)
                frame.setEndOfFrameStatistics(param.getValue() != 0);
        } else if (name.equals("quality")) {
            UniformScalarInteger param = (UniformScalarInteger) parameters.getParameter("highlights");
            if (param != null)
                SurfaceShader.setBetterHighlights(param.getValue() != 0);
        }
    }

    public void setAttribute(String name, ParameterList parameters) {
        if (name.equals("displacementbound")) {
            UniformScalarFloat param1 = (UniformScalarFloat) parameters.getParameter("sphere");
            if (param1 != null)
                currentAttributes.setDisplacementBound(param1.getValue());
            UniformScalarString param2 = (UniformScalarString) parameters.getParameter("coordinatesystem");
            if (param2 != null)
                currentAttributes.setDisplacementBoundCoordinateSystem(param2.getValue());
        } else if (name.equals("identifier")) {
            UniformScalarString param = (UniformScalarString) parameters.getParameter("name");
            if (param != null)
                currentAttributes.setObjectIdentifer(param.getValue());
        } else if (name.equals("trimcurve")) {
            UniformScalarString param = (UniformScalarString) parameters.getParameter("sense");
            if (param != null)
                currentAttributes.setTrimCurveSense(TrimCurveSense.getNamed(param.getValue()));
        } else if (name.equals("render")) {
            UniformScalarInteger param = (UniformScalarInteger) parameters.getParameter("truedisplacement");
            if (param != null)
                currentAttributes.setTrueDisplacement(param.getValue() == 1);
        }
    }

    public void setSurface(String name, ParameterList parameters) {
        currentAttributes.setSurface(SurfaceShader.createShader(name, parameters, getAttributes()));
    }

    public void setDisplacement(String name, ParameterList parameters) {
        currentAttributes.setDisplacement(DisplacementShader.createShader(name, parameters, getAttributes()));
    }

    public void setAtmosphere(String name, ParameterList parameters) {
        currentAttributes.setAtmosphere(VolumeShader.createShader(name, parameters, getAttributes()));
    }

    public void setExterior(String name, ParameterList parameters) {
        currentAttributes.setExterior(VolumeShader.createShader(name, parameters, getAttributes()));
    }

    public void setImager(String name, ParameterList parameters) {
        frame.setImager(Imager.createImager(name, parameters));
    }

    public void setInterior(String name, ParameterList parameters) {
        currentAttributes.setInterior(VolumeShader.createShader(name, parameters, getAttributes()));
    }

    public void createLightSource(String name, int sequenceNumber, ParameterList parameters) {
        LightShader light = LightShader.createShader(name, parameters, getAttributes());
        world.addLight(sequenceNumber, light);
        turnOnLight(light);
    }

    private void turnOnLight(LightShader light) {
        Set<LightShader> lightSources = new HashSet<LightShader>(currentAttributes.getLightSources());
        lightSources.add(light);
        currentAttributes.setLightSources(lightSources);
    }

    private void turnOffLight(LightShader light) {
        Set<LightShader> lightSources = new HashSet<LightShader>(currentAttributes.getLightSources());
        lightSources.remove(light);
        currentAttributes.setLightSources(lightSources);
    }

    public void illuminate(int sequenceNumber, int onOff) {
        LightShader light = world.getLight(sequenceNumber);
        if (onOff != 0)
            turnOnLight(light);
        else
            turnOffLight(light);
    }

    public void createAreaLightSource(String name, int sequenceNumber, ParameterList parameters) {
        createLightSource(name, sequenceNumber, parameters);
        inAreaLightSource = true;
    }

    public void setShadingRate(float size) {
        currentAttributes.setShadingRate(size);
    }

    public void objectInstance(int n) {
        renderer.addPrimitive(new ObjectInstance(getObjectInstanceList(n), getAttributes()));
    }

    public void addSphere(final float radius, float zMin, float zMax, float tMax, final ParameterList parameters) {
        if (inAreaLightSource)
            return;
        final float phiMin = (float) ((zMin <= -radius) ? -Math.PI / 2 : Math.asin(zMin / radius));
        final float phiMax = (float) ((zMax >= radius) ? Math.PI / 2 : Math.asin(zMax / radius));
        final float thetaMin = 0f;
        final float thetaMax = (float) Math.toRadians(tMax);
        if (!inObject) {
            renderer.addPrimitive(
                    new Sphere(radius, phiMin, phiMax, thetaMin, thetaMax, parameters, getAttributes()));
            sphereCount++;
        } else {
            final Transform transform = currentAttributes.getTransform();
            currentObjectInstanceList.addPrimitiveCreator(new ObjectInstanceList.PrimitiveCreator() {
                public Primitive create(Attributes attributes) {
                    sphereCount++;
                    return new Sphere(radius, phiMin, phiMax, thetaMin, thetaMax, parameters,
                            createAttributes(transform, attributes));
                }
            });
        }
    }

    public void addTorus(final float majorRadius, final float minorRadius, float pMin, float pMax, float tMax,
            final ParameterList parameters) {
        if (inAreaLightSource)
            return;
        final float phiMin = (float) Math.toRadians(pMin);
        final float phiMax = (float) Math.toRadians(pMax);
        final float thetaMax = (float) Math.toRadians(tMax);
        if (!inObject) {
            renderer.addPrimitive(
                    new Torus(majorRadius, minorRadius, phiMin, phiMax, 0f, thetaMax, parameters, getAttributes()));
            torusCount++;
        } else {
            final Transform transform = currentAttributes.getTransform();
            currentObjectInstanceList.addPrimitiveCreator(new ObjectInstanceList.PrimitiveCreator() {
                public Primitive create(Attributes attributes) {
                    torusCount++;
                    return new Torus(majorRadius, minorRadius, phiMin, phiMax, 0f, thetaMax, parameters,
                            createAttributes(transform, attributes));
                }
            });
        }
    }

    public void addCylinder(final float radius, final float zmin, final float zmax, float tMax,
            final ParameterList parameters) {
        if (inAreaLightSource)
            return;
        final float thetaMax = (float) Math.toRadians(tMax);
        if (!inObject) {
            renderer.addPrimitive(new Cylinder(radius, zmin, zmax, 0f, thetaMax, parameters, getAttributes()));
            cylinderCount++;
        } else {
            final Transform transform = currentAttributes.getTransform();
            currentObjectInstanceList.addPrimitiveCreator(new ObjectInstanceList.PrimitiveCreator() {
                public Primitive create(Attributes attributes) {
                    cylinderCount++;
                    return new Cylinder(radius, zmin, zmax, 0f, thetaMax, parameters,
                            createAttributes(transform, attributes));
                }
            });
        }
    }

    public void addCone(final float height, final float radius, float tMax, final ParameterList parameters) {
        if (inAreaLightSource)
            return;
        final float thetaMax = (float) Math.toRadians(tMax);
        if (!inObject) {
            renderer.addPrimitive(new Cone(radius, 0f, height, 0f, thetaMax, height, parameters, getAttributes()));
            coneCount++;
        } else {
            final Transform transform = currentAttributes.getTransform();
            currentObjectInstanceList.addPrimitiveCreator(new ObjectInstanceList.PrimitiveCreator() {
                public Primitive create(Attributes attributes) {
                    coneCount++;
                    return new Cone(radius, 0f, height, 0f, thetaMax, height, parameters,
                            createAttributes(transform, attributes));
                }
            });
        }
    }

    public void addDisk(final float height, final float radius, float tMax, final ParameterList parameters) {
        if (inAreaLightSource)
            return;
        final float thetaMax = (float) Math.toRadians(tMax);
        if (!inObject) {
            renderer.addPrimitive(new Disk(height, 0f, thetaMax, 0f, radius, parameters, getAttributes()));
            diskCount++;
        } else {
            final Transform transform = currentAttributes.getTransform();
            currentObjectInstanceList.addPrimitiveCreator(new ObjectInstanceList.PrimitiveCreator() {
                public Primitive create(Attributes attributes) {
                    coneCount++;
                    return new Disk(height, 0f, thetaMax, 0f, radius, parameters,
                            createAttributes(transform, attributes));
                }
            });
        }
    }

    public void addParaboloid(final float radius, final float zmin, final float zmax, float tMax,
            final ParameterList parameters) {
        if (inAreaLightSource)
            return;

        final float thetaMin = 0f;
        final float thetaMax = (float) Math.toRadians(tMax);
        if (!inObject) {
            renderer.addPrimitive(new Paraboloid(radius, zmin, zmax, thetaMin, thetaMax, zmin, zmax, thetaMax,
                    parameters, getAttributes()));
            paraboloidCount++;
        } else {
            final Transform transform = currentAttributes.getTransform();
            currentObjectInstanceList.addPrimitiveCreator(new ObjectInstanceList.PrimitiveCreator() {
                public Primitive create(Attributes attributes) {
                    paraboloidCount++;
                    return new Paraboloid(radius, zmin, zmax, thetaMin, thetaMax, zmin, zmax, thetaMax, parameters,
                            createAttributes(transform, attributes));
                }
            });
        }
    }

    public void addHyperboloid(final float x1, final float y1, final float z1, final float x2, final float y2,
            final float z2, final float tMax, final ParameterList parameters) {
        if (inAreaLightSource)
            return;

        final float thetaMin = 0f;
        final float thetaMax = (float) Math.toRadians(tMax);
        if (!inObject) {
            renderer.addPrimitive(
                    new Hyperboloid(x1, y1, z1, x2, y2, z2, thetaMin, thetaMax, parameters, getAttributes()));
            hyperboloidCount++;
        } else {
            final Transform transform = currentAttributes.getTransform();
            currentObjectInstanceList.addPrimitiveCreator(new ObjectInstanceList.PrimitiveCreator() {
                public Primitive create(Attributes attributes) {
                    hyperboloidCount++;
                    return new Hyperboloid(x1, y1, z1, x2, y2, z2, thetaMin, thetaMax, parameters,
                            createAttributes(transform, attributes));
                }
            });
        }

    }

    public void addBilinearPatch(final ParameterList parameters) {
        if (inAreaLightSource)
            return;
        if (!inObject) {
            renderer.addPrimitive(new BilinearPatch(parameters, getAttributes()));
            bilinearPatchCount++;
        } else {
            final Transform transform = currentAttributes.getTransform();
            currentObjectInstanceList.addPrimitiveCreator(new ObjectInstanceList.PrimitiveCreator() {
                public Primitive create(Attributes attributes) {
                    bilinearPatchCount++;
                    return new BilinearPatch(parameters, createAttributes(transform, attributes));
                }
            });
        }
    }

    public void addBicubicPatch(final ParameterList parameters) {
        if (inAreaLightSource)
            return;
        if (!inObject) {
            BicubicPatch patch = new BicubicPatch(parameters, getAttributes());
            renderer.addPrimitive(patch);
            bicubicPatchCount++;
        } else {
            final Transform transform = currentAttributes.getTransform();
            currentObjectInstanceList.addPrimitiveCreator(new ObjectInstanceList.PrimitiveCreator() {
                public Primitive create(Attributes attributes) {
                    bicubicPatchCount++;
                    BicubicPatch patch = new BicubicPatch(parameters, createAttributes(transform, attributes));
                    return patch;
                }
            });
        }
    }

    private int evalIndex(int iu, int nu, int iv, int nv) {
        return (iv % nv) * nu + (iu % nu);
    }

    private void setPatchMeshDefaultParameters(int nu, int nv, ParameterList parameters) {
        Primitive.setDefaultParameters(parameters, getAttributes());
        VaryingScalarFloat p = (VaryingScalarFloat) parameters.getParameter("u");
        p.expandTo(nu, nv);
        p = (VaryingScalarFloat) parameters.getParameter("v");
        p.expandTo(nu, nv);
        p = (VaryingScalarFloat) parameters.getParameter("s");
        p.expandTo(nu, nv);
        p = (VaryingScalarFloat) parameters.getParameter("t");
        p.expandTo(nu, nv);
    }

    public void addBilinearPatchMesh(int nu, String uWrap, int nv, String vWrap, ParameterList parameters) {
        int nU;
        if (uWrap.equals("periodic"))
            nU = nu;
        else if (uWrap.equals("nonperiodic"))
            nU = nu - 1;
        else
            throw new IllegalArgumentException("Unknown u wrap type: " + uWrap);
        int nV;
        if (vWrap.equals("periodic"))
            nV = nv;
        else if (vWrap.equals("nonperiodic"))
            nV = nv - 1;
        else
            throw new IllegalArgumentException("Unknown v wrap type: " + vWrap);
        setPatchMeshDefaultParameters(nu, nv, parameters);
        int[] uniformIndex = new int[1];
        uniformIndex[0] = 0;
        int[] indexes = new int[4];
        for (int j = 0; j < nV; j++)
            for (int i = 0; i < nU; i++) {
                indexes[0] = evalIndex(i, nu, j, nv);
                indexes[1] = evalIndex(i + 1, nu, j, nv);
                indexes[2] = evalIndex(i, nu, j + 1, nv);
                indexes[3] = evalIndex(i + 1, nu, j + 1, nv);
                addBilinearPatch(parameters.selectValues(uniformIndex, indexes, indexes));
                uniformIndex[0]++;
            }
    }

    public void addBicubicPatchMesh(int nu, String uWrap, int nv, String vWrap, ParameterList parameters) {
        int uStep = currentAttributes.getUStep();
        int nU;
        if (uWrap.equals("periodic"))
            nU = nu / uStep;
        else if (uWrap.equals("nonperiodic"))
            nU = (nu - 4) / uStep + 1;
        else
            throw new IllegalArgumentException("Unknown u wrap type: " + uWrap);
        int vStep = currentAttributes.getVStep();
        int nV;
        if (vWrap.equals("periodic"))
            nV = nv / vStep;
        else if (vWrap.equals("nonperiodic"))
            nV = (nv - 4) / vStep + 1;
        else
            throw new IllegalArgumentException("Unknown v wrap type: " + vWrap);
        int blnu;
        if (uWrap.equals("periodic"))
            blnu = nU;
        else
            blnu = nU + 1;
        int blnv;
        if (vWrap.equals("periodic"))
            blnv = nV;
        else
            blnv = nV + 1;
        setPatchMeshDefaultParameters(blnu, blnv, parameters);
        int[] uniformIndex = new int[1];
        uniformIndex[0] = 0;
        int[] varyingIndexes = new int[4];
        int[] vertexIndexes = new int[16];
        int j = 0;
        for (int jj = 0; jj < nV; jj++) {
            int i = 0;
            for (int ii = 0; ii < nU; ii++) {
                varyingIndexes[0] = evalIndex(ii, blnu, jj, blnv);
                varyingIndexes[1] = evalIndex(ii + 1, blnu, jj, blnv);
                varyingIndexes[2] = evalIndex(ii, blnu, jj + 1, blnv);
                varyingIndexes[3] = evalIndex(ii + 1, blnu, jj + 1, blnv);
                vertexIndexes[0] = evalIndex(i, nu, j, nv);
                vertexIndexes[1] = evalIndex(i + 1, nu, j, nv);
                vertexIndexes[2] = evalIndex(i + 2, nu, j, nv);
                vertexIndexes[3] = evalIndex(i + 3, nu, j, nv);
                vertexIndexes[4] = evalIndex(i, nu, j + 1, nv);
                vertexIndexes[5] = evalIndex(i + 1, nu, j + 1, nv);
                vertexIndexes[6] = evalIndex(i + 2, nu, j + 1, nv);
                vertexIndexes[7] = evalIndex(i + 3, nu, j + 1, nv);
                vertexIndexes[8] = evalIndex(i, nu, j + 2, nv);
                vertexIndexes[9] = evalIndex(i + 1, nu, j + 2, nv);
                vertexIndexes[10] = evalIndex(i + 2, nu, j + 2, nv);
                vertexIndexes[11] = evalIndex(i + 3, nu, j + 2, nv);
                vertexIndexes[12] = evalIndex(i, nu, j + 3, nv);
                vertexIndexes[13] = evalIndex(i + 1, nu, j + 3, nv);
                vertexIndexes[14] = evalIndex(i + 2, nu, j + 3, nv);
                vertexIndexes[15] = evalIndex(i + 3, nu, j + 3, nv);
                addBicubicPatch(parameters.selectValues(uniformIndex, varyingIndexes, vertexIndexes));
                uniformIndex[0]++;
                i += uStep;
            }
            j += vStep;
        }
    }

    private void addLinearCurve(final boolean periodic, final ParameterList parameters) {
        if (inAreaLightSource)
            return;
        if (!inObject) {
            LinearCurve curve = new LinearCurve(periodic, parameters, getAttributes());
            renderer.addPrimitive(curve);
            curveCount++;
        } else {
            final Transform transform = currentAttributes.getTransform();
            currentObjectInstanceList.addPrimitiveCreator(new ObjectInstanceList.PrimitiveCreator() {
                public Primitive create(Attributes attributes) {
                    curveCount++;
                    return new LinearCurve(periodic, parameters, createAttributes(transform, attributes));
                }
            });
        }
    }

    public void addLinearCurves(int[] nVertices, String wrap, ParameterList parameters) {
        boolean periodic = wrap.equals("periodic");
        int[] uniformIndex = new int[1];
        int ptr = 0;
        for (int i = 0; i < nVertices.length; i++) {
            uniformIndex[0] = i;
            int[] indexes = new int[nVertices[i]];
            for (int j = 0; j < indexes.length; j++)
                indexes[j] = ptr++;
            addLinearCurve(periodic, parameters.selectValues(uniformIndex, indexes, indexes));
        }
    }

    private void addCubicCurve(final boolean periodic, final ParameterList parameters) {
        if (inAreaLightSource)
            return;
        if (!inObject) {
            CubicCurve curve = new CubicCurve(periodic, parameters, getAttributes());
            renderer.addPrimitive(curve);
            curveCount++;
        } else {
            final Transform transform = currentAttributes.getTransform();
            currentObjectInstanceList.addPrimitiveCreator(new ObjectInstanceList.PrimitiveCreator() {
                public Primitive create(Attributes attributes) {
                    curveCount++;
                    return new CubicCurve(periodic, parameters, createAttributes(transform, attributes));
                }
            });
        }
    }

    public void addCubicCurves(int[] nVertices, String wrap, ParameterList parameters) {
        boolean periodic = wrap.equals("periodic");
        int vStep = currentAttributes.getVStep();
        int[] uniformIndex = new int[1];
        int varyingPtr = 0;
        int vertexPtr = 0;
        for (int i = 0; i < nVertices.length; i++) {
            uniformIndex[0] = i;
            int nv;
            if (periodic)
                nv = nVertices[i] / vStep;
            else
                nv = (nVertices[i] - 4) / vStep + 2;
            int[] varyingIndexes = new int[nv];
            for (int j = 0; j < nv; j++)
                varyingIndexes[j] = varyingPtr++;
            int[] vertexIndexes = new int[nVertices[i]];
            for (int j = 0; j < vertexIndexes.length; j++)
                vertexIndexes[j] = vertexPtr++;
            addCubicCurve(periodic, parameters.selectValues(uniformIndex, varyingIndexes, vertexIndexes));
        }
    }

    public void addNuPatch(final int nu, final int uorder, final float[] uknot, final float umin, final float umax,
            final int nv, final int vorder, final float[] vknot, final float vmin, final float vmax,
            final ParameterList parameters) {
        if (inAreaLightSource)
            return;
        if (!inObject) {
            renderer.addPrimitive(new NurbsRI(nu, uorder, uknot, umin, umax, nv, vorder, vknot, vmin, vmax, true,
                    parameters, getAttributes()));
            nurbsCount++;
        } else {
            final Transform transform = currentAttributes.getTransform();
            currentObjectInstanceList.addPrimitiveCreator(new ObjectInstanceList.PrimitiveCreator() {
                public Primitive create(Attributes attributes) {
                    nurbsCount++;
                    return new NurbsRI(nu, uorder, uknot, umin, umax, nv, vorder, vknot, vmin, vmax, true,
                            parameters, createAttributes(transform, attributes));
                }
            });
        }
    }

    public void addPointsPolygons(final int[] nVertices, final int[] vertices, final ParameterList parameters) {
        if (inAreaLightSource)
            return;
        setPolygonST(parameters);
        if (!inObject) {
            renderer.addPrimitive(new PointsPolygons(nVertices, vertices, parameters, getAttributes()));
            polygonCount += nVertices.length;
        } else {
            final Transform transform = currentAttributes.getTransform();
            currentObjectInstanceList.addPrimitiveCreator(new ObjectInstanceList.PrimitiveCreator() {
                public Primitive create(Attributes attributes) {
                    polygonCount += nVertices.length;
                    return new PointsPolygons(nVertices, vertices, parameters,
                            createAttributes(transform, attributes));
                }
            });
        }
    }

    private void setPolygonST(ParameterList parameters) {
        VaryingScalarFloat sParam = (VaryingScalarFloat) parameters.getParameter("s");
        VaryingScalarFloat tParam = (VaryingScalarFloat) parameters.getParameter("t");
        VaryingArrayFloat stParam = (VaryingArrayFloat) parameters.getParameter("st");
        VaryingScalarTuple3f pParam = (VaryingScalarTuple3f) parameters.getParameter("P");
        if (sParam == null && stParam == null)
            parameters.addParameter(pParam.extract(Global.getDeclaration("s"), 0));
        if (tParam == null && stParam == null)
            parameters.addParameter(pParam.extract(Global.getDeclaration("t"), 1));
    }

    public void addPolygon(final ParameterList parameters) {
        if (inAreaLightSource)
            return;
        setPolygonST(parameters);
        VaryingScalarTuple3f pParam = (VaryingScalarTuple3f) parameters.getParameter("P");
        int[] uniformIndex = new int[1];
        uniformIndex[0] = 0;
        int[] indexes = new int[] { 0, 0, 0, 0 };
        for (int i = 1; i < pParam.getCount() - 1; i++) {
            indexes[1] = i;
            indexes[3] = i + 1;
            addBilinearPatch(parameters.selectValues(uniformIndex, indexes, indexes));
            polygonCount++;
            bilinearPatchCount--; // TODO: fix this
        }
    }

    public void addPoints(final ParameterList parameters) {
        if (inAreaLightSource)
            return;
        VaryingScalarTuple3f pParam = (VaryingScalarTuple3f) parameters.getParameter("P");
        parameters.removeParameter("P");
        VaryingScalarFloat widthParam = (VaryingScalarFloat) parameters.getParameter("width");
        parameters.removeParameter("width");
        UniformScalarFloat constantWidthParam = (UniformScalarFloat) parameters.getParameter("constantwidth");
        parameters.removeParameter("constantwidth");
        // TODO: same space optimization as for PointsPolygon
        int[] uniformIndex = new int[1];
        uniformIndex[0] = 0;
        int[] indexes = new int[1];
        for (int i = 0; i < pParam.getCount(); i++) {
            final Point3f p = new Point3f();
            pParam.getValue(i, p);
            float tw;
            if (widthParam != null)
                tw = widthParam.getValue(i);
            else if (constantWidthParam != null)
                tw = constantWidthParam.getValue();
            else
                tw = 1f;
            final float w = tw;
            indexes[0] = i;
            final ParameterList param = parameters.selectValues(uniformIndex, indexes, indexes);
            if (!inObject) {
                renderer.addPrimitive(new Point(w, p.x, p.y, p.z, param, getAttributes()));
                pointCount++;
            } else {
                final Transform transform = currentAttributes.getTransform();
                currentObjectInstanceList.addPrimitiveCreator(new ObjectInstanceList.PrimitiveCreator() {
                    public Primitive create(Attributes attributes) {
                        pointCount++;
                        return new Point(w, p.x, p.y, p.z, param, createAttributes(transform, attributes));
                    }
                });
            }
        }
    }

    public void addDelayedReadArchive(String filename, float xmin, float xmax, float ymin, float ymax, float zmin,
            float zmax) {
        renderer.addPrimitive(
                new DelayedReadArchive(this, filename, xmin, xmax, ymin, ymax, zmin, zmax, getAttributes()));
    }

    public void makeTexture(String picturename, String texturename, String swrap, String twrap, String filter,
            int swidth, int twidth) {
        try {
            MipMap.makeMipMap(picturename, texturename, MipMap.Mode.getNamed(swrap), MipMap.Mode.getNamed(twrap),
                    filter, swidth, twidth);
        } catch (IOException e) {
            throw new IllegalArgumentException("Can't create texture: " + picturename);
        }
    }

    public void resetCounts() {
        sphereCount = 0;
        torusCount = 0;
        cylinderCount = 0;
        coneCount = 0;
        diskCount = 0;
        paraboloidCount = 0;
        hyperboloidCount = 0;
        bilinearPatchCount = 0;
        bicubicPatchCount = 0;
        polygonCount = 0;
        pointCount = 0;
        nurbsCount = 0;
        curveCount = 0;
    }

    public int getBicubicPatchCount() {
        return bicubicPatchCount;
    }

    public int getBilinearPatchCount() {
        return bilinearPatchCount;
    }

    public int getConeCount() {
        return coneCount;
    }

    public int getCylinderCount() {
        return cylinderCount;
    }

    public int getDiskCount() {
        return diskCount;
    }

    public int getHyperboloidCount() {
        return hyperboloidCount;
    }

    public int getParaboloidCount() {
        return paraboloidCount;
    }

    public int getPointCount() {
        return pointCount;
    }

    public int getPolygonCount() {
        return polygonCount;
    }

    public int getSphereCount() {
        return sphereCount;
    }

    public int getTorusCount() {
        return torusCount;
    }

    public int getNurbsCount() {
        return nurbsCount;
    }

    public int getCurveCount() {
        return curveCount;
    }

}