/*-
 * Copyright (c) 1999 Qube Software, Ltd.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of the source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Any redistribution solely in binary form must conspicuously
 *    reproduce the following disclaimer in documentation provided with the
 *    binary redistribution.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'', WITHOUT ANY WARRANTIES, EXPRESS
 * OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  LICENSOR SHALL
 * NOT BE LIABLE FOR ANY LOSS OR DAMAGES RESULTING FROM THE USE OF THIS
 * SOFTWARE, EITHER ALONE OR IN COMBINATION WITH ANY OTHER SOFTWARE.
 *
 *	$Id: patch.h,v 1.14 2000/02/14 14:43:19 peterj Exp $
 */

#ifndef _PATCH_H_
#define _PATCH_H_

#include "qsys.h"
#include "maths.h"
#include "draw.h"

#include "linkage.h"

#ifndef NULL
#define NULL 0
#endif

#define PATCH_BEGIN	namespace Patch {
#define PATCH_END	};

PATCH_BEGIN

struct World;
struct Instance;
struct Model;
struct Patch;

/**
 * A set of control points and colors to define a bicubic patch.
 */
struct BicubicData
{
    Maths::Vec3f			points[16];
    //Maths::Vec3f			normals[16];
    Maths::Vec3s			colors[16];
    Maths::Vec2f			uv[16];
    int					shaderIndex;
};

/**
 * A World is a container for Intances and Models.
 *
 * @see Instance
 * @see Model
 */
struct World
{
    /**
     * Create a new empty world.
     */
    static EXPORT_PATCH World*		create(Draw::Factory*);

    /**
     * Destroy the world and any instances and models which it contains.
     */
    virtual				~World() = 0;

    /**
     * Add an instance to the world.  After adding, the instance will be
     * owned by the world and cannot be deleted without removing it from
     * the world first.  Generate an exception if the instance is
     * already contained by any world.
     *
     * @see World::remove(const Instance* instance);
     */
    virtual void			add(const Instance* instance) = 0;

    /**
     * Remove an instance from the world.  After removing, the instance
     * may be deleted explicitly with delete.  Generate an exception if
     * the instance is not contained by the world.
     */
    virtual void			remove(const Instance* instance) = 0;

    /**
     * Add a model to the world.  After adding, the model will be
     * owned by the world and cannot be deleted without removing it from
     * the world first.  Generate an exception is the model is already
     * contained by any world.
     *
     * @see World::remove(const Model* model);
     */
    virtual void			add(const Model* model) = 0;

    /**
     * Remove a model from the world.  After removing, the model
     * may be deleted explicitly with delete.  Generate an exception if
     * the model is not contained by the world.
     */
    virtual void			remove(const Model* model) = 0;

    /**
     * Render all the instances in the world on the given buffer.
     *
     * XXX this is the wrong place for the shader.  A later release will
     * probably move the shader to Patch or possibly Instance.
     */
    virtual void			render(Draw::Buffer* buffer,
					       Draw::Shader** shader,
					       Draw::LightManager* lights) = 0;
};

/**
 * An Instance combines a Model with a position and orientation (defined
 * by a transform matrix).  The same model can be multiply instanced with
 * different positions to allow re-use of objects. 
 *
 * @see Model
 */
struct Instance
{
    /**
     * Create a new instance of the given model.  The new instance will
     * have an identity transform matrix.
     *
     * XXX can the model be 0?
     */
    static EXPORT_PATCH Instance*		create(Model* model);

    /**
     * Destroy the instance (but not the referenced model).  This will
     * generate an exception if the instance is currently contained in a
     * world.  To delete an instance which is in a world, you must remove
     * it first. 
     */
    virtual				~Instance() = 0;

    /**
     * Return the model which is being instanced.
     */
    virtual Model*			model() const = 0;

    /**
     * Change the instanced model.
     *
     * XXX can the model be 0?
     */
    virtual void			model(Model* model) = 0;

    /**
     * Return the current transform matrix for the instance.
     */
    virtual const Maths::Matrix4f&	matrix() const = 0;

    /**
     * Change the transform matrix.
     */
    virtual void			matrix(const Maths::Matrix4f& matrix) = 0;
};

/**
 * A Model is a container of Patches.  It also has a set of parameters
 * to control the rendering process as the patches are subdivided into
 * triangles.
 *
 * @see Patch
 */
struct Model
{
    /**
     * Create a new model which contains no patches.
     */
    static EXPORT_PATCH Model*		create(Draw::Factory* factory);

    /**
     * Destroy the model and all the patches which it contains. Generate
     * an exception if the model is currently contained in a world.  To
     * delete a model which is in a world, you must remove it first.
     * Generate an exception if there is any instance which uses this
     * model. 
     */
    virtual				~Model() = 0;

    /**
     * Add a single patch to the model.  After the patch has been added
     * to the model it will be deleted automatically if the model is
     * deleted and it must not be deleted without removing it first.
     * Generate an exception if the the patch is contained by any model.
     */
    virtual void			add(const Patch* patch) = 0;

    /**
     * Create a patch with the given data and add it to the model.
     *
     * XXX default values or min/max split level etc.
     */
    virtual void			add(const BicubicData& data) = 0;

    /**
     * Create a number of patches with the given data and add them to
     * the model.
     */
    virtual void			add(int count,
					    const BicubicData data[]) = 0;

    /**
     * Remove a patch from the model.  After removing the patch, it may
     * be explicitly deleted.  Generate an exception if the patch is not
     * contained by this model.
     */
    virtual void			remove(const Patch* patch) = 0;
    
    /**
     * Get or set the minimum split level for the patches in this
     * object.
     */
    virtual int				minSplitLevel() const = 0;
    virtual void			minSplitLevel(int) = 0;

    /**
     * Get or set the maximum split level for the patches in this
     * object.
     */
    virtual int				maxSplitLevel() const = 0;
    virtual void			maxSplitLevel(int) = 0;

    /**
     * Get or set the area threshhold of the patch subdivision. The
     * patch will carry on subdividing until its area is less than this
     * value or the maximum split level has been reached.
     */
    virtual int				minPatchSize() const = 0;
    virtual void			minPatchSize(int) = 0;

    /**
     * Get or set the refresh rate for the patches. This is a value
     * that will count down and the patch will be updated.
     *
     * XXX reword this - what does it mean to count up and down and what
     * is being updated?
     */
    virtual int				refreshRate() const = 0;
    virtual void			refreshRate(unsigned int) = 0;

    /**
     * Change the material properties of the patch (used for lighting).
     */
    virtual void			material(Draw::Material& material) = 0;

    /**
     * Return the current material properties of the patch.
     */
    virtual const Draw::Material&	material() const = 0;
};

/**
 * A Patch has a set of control points which describe a curved surface.
 * The only supported type of surface is the bicubic patch.  A future
 * release may also support quadratic patches.
 *
 * @see Model
 */
struct Patch
{
    /**
     * Create a new Patch from the given set of control points.
     */
    static EXPORT_PATCH Patch*		create(const BicubicData& data);

    /**
     * Destroy the patch.  Generate an exception if the patch is
     * currently contained in a model.  To delete a patch which is in a
     * model, you must remove it first. 
     */
    virtual				~Patch() = 0;

    /**
     * Change the control points for a patch.
     */
    virtual void			modify(const BicubicData&) = 0;
};

PATCH_END

#endif // _PATCH_H_

// Local Variables:
// mode: c++
// End:
