/*
 *			GPAC - MPEG-4 Systems C Development Kit
 *
 *			Copyright (c) Jean Le Feuvre 2000-2005
 *					All rights reserved
 *
 *  This file is part of GPAC / 3D rendering plugin
 *
 *  GPAC 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, or (at your option)
 *  any later version.
 *   
 *  GPAC 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 GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */

#ifndef RENDER3D_H
#define RENDER3D_H

#include <gpac/m4_x3d_nodes.h>

#include "camera.h"
#include "mesh.h"

typedef struct
{
	/*world->local and local->world transform at hit point*/
	M4Matrix world_to_local, local_to_world;
	/*hit point in local coord & world coord*/
	SFVec3f local_point, world_point;
	/*stored world ray - this is needed because of layer3D which defines a complete new world coord system*/
	M4Ray world_ray;
	/*normal at hit point, local coord system*/
	SFVec3f hit_normal;
	/*tex coords at hit point*/
	SFVec2f hit_texcoords;
	/*appearance at hit point - used for composite texture*/
	SFNode *appear;
} RayHitInfo;

typedef struct _render3d
{
	/*remember main renderer*/
	SceneRenderer *compositor;
	/*all outlines cached*/
	Chain *strike_bank;
	/*main 3D surface*/
	struct _visual_surface *surface;
	Bool main_surface_setup;

	/*options*/
	/*emulate power-of-2 for video texturing by using a po2 texture and texture scaling. If any textureTransform
	is applied to this texture, black stripes will appear on the borders.
	If not set video is written through glDrawPixels with bitmap (slow scaling), or converted to
	po2 texture*/
	Bool emul_pow2;
	/*use openGL for outline rather than vectorial ones*/
	Bool raster_outlines;
	Bool no_backcull;
	/*disable RECT extensions (except for Bitmap...)*/
	Bool disable_rect_ext;
	/*disable RECT extensions (except for Bitmap...)*/
	Bool bitmap_use_pixels;

	/*top level effect for zoom/pan*/
	struct _render3d_effect *top_effect;

	/*current output info: screen size and top-left point of video surface, and current scaled scene size*/
	u32 out_width, out_height, out_x, out_y, cur_width, cur_height;
	/*needed for bitmap scaling when using DrawPixels rather than texture*/
	Fixed scale_x, scale_y;

	Bool poly_aa;
	u32 wiremode;
	u32 collide_mode;
	Bool gravity_on;
	/*unit box (1.0 size) and unit sphere (1.0 radius)*/
	M4Mesh *unit_bbox;

	/*type of main display - this is used for user navigation*/
	Bool root_is_3D;

	/*sensitive node storage*/
	Bool is_grabbed;
	Chain *sensors, *prev_sensors;
	RayHitInfo hit_info;
	/*distance from ray origin used to discards further hits - FIXME: may not properly work with transparent layer3D*/
	Fixed sq_dist;
	/*last cursor type user display*/
	u32 last_cursor;
	void *hs_grab;

	/*active layer3D for layer navigation - may be NULL*/
	SFNode *active_layer;

	Fixed grab_x, grab_y;
	u32 nav_is_grabbed;
} Render3D;

/*user interaction event*/
typedef struct
{
	u32 event_type;
	Fixed x, y;
} UserEvent3D;

/*sensor node handler - this is not defined as a stack because Anchor is both a grouping node and a 
sensor node, and we DO need the groupingnode stack...*/
typedef struct _sensor3D_handler
{
	/*sensor enabled or not ?*/
	Bool (*IsEnabled)(SFNode *node);
	/*user input on sensor:
	is_over: pointing device is over a shape the sensor is attached to
	evt_type: mouse event type
	hit_info: current hit info
	*/
	void (*OnUserEvent)(struct _sensor3D_handler *sh, Bool is_over, u32 eventType, RayHitInfo *hit_info);
	/*set the node pointer here*/
	SFNode *owner;
} SensorHandler;

SensorHandler *r3d_get_sensor_handler(SFNode *n);

#define MAX_USER_CLIP_PLANES		4

/*rendering modes*/
enum
{
	/*when set objects are drawn as soon as traversed, at each frame - THIS MODE IS AND WILL ALWAYS BE 0
	only called on nodes appearing on screen*/
	TRAVERSE_RENDER = 0,
	/*main prerender routine - needed for true alpha compositing - depth sorting and culling is done there*/
	TRAVERSE_SORT,
	/*set global lights on. Since the model_matrix is not pushed to the target surface in this 
	pass, global lights shall not forget to do it (cf lighting.c)*/
	TRAVERSE_LIGHTING,
	/*picking mode*/
	TRAVERSE_PICK,
	/*collide mode*/
	TRAVERSE_COLLIDE,
	/*recomputes bounds - result shall be stored in effect bbox field*/
	TRAVERSE_GET_BOUNDS,
	/*set to signal bindable render - only called on bindable stack top if present.
	for background (drawing request), viewports/viewpoints fog and navigation (setup)
	all other nodes ARE NEVER SNED THIS EVENT
	*/
	TRAVERSE_RENDER_BINDABLE,
};

enum
{
	/*the subtree indicates no cull shall be performed*/
	TF_DONT_CULL = (1<<2),
};

/*the traversing context: set_up at top-level and passed through SFNode_Render*/
typedef struct _render3d_effect
{
	AUDIO_EFFECT_CLASS

	u32 traversing_mode;

	/*the one and only surface currently traversed*/
	struct _visual_surface *surface;
	/*the current camera*/
	M4Camera *camera;
	/*is graph traversed in pixel metrics*/
	Bool is_pixel_metrics;
	/*half of min (width, height) of current graph traversed. If graph has no size info
	the size is inherited from parent graph*/
	Fixed min_hsize;

	/*current object (model) transformation from top-level, view is NOT included*/
	M4Matrix model_matrix;
	/*set if any scaling at current depth in subtree - needed for GL normalization :( */
	Bool has_scale;
	/*current color transformation from top-level*/
	M4ColorMatrix color_mat;
	/*set if cmat is used*/
	Bool has_cmat;

	/*current appearance when traversing geometry nodes*/
	SFNode *appear;
	/*parent group for composition: can be Form, Layout or Layer2D*/
	struct _parent_group *parent;

	/*current bindable stacks*/
	Chain *backgrounds;	/*holds 2D and 3D backgrounds*/
	Chain *viewpoints; /*holds viewpoints (3D) and viewports (2D)*/
	Chain *fogs; /*holds fogs info*/
	Chain *navigations; /*holds navigation info*/

	/*bounds for TRAVERSE_GET_BOUNDS*/
	M4BBox bbox;
	/*cull flag (used to skip culling of children when parent bbox is completely inside/outside frustum)*/
	u32 cull_flag;

	/*text splitting*/
	u32 text_split_mode;
	/*1-based idx of text element drawn*/
	u32 split_text_idx;

	/*all sensors for the current level - only valid in TRAVERSE_SORT*/
	Chain *sensors;

	/*ray used for TRAVERSE_PICK*/
	M4Ray ray;

	/*toggle local lights on/off - field is ONLY valid in TRAVERSE_RENDER mode, and local lights
	are always set off in reverse order as when set on*/
	Bool local_light_on;

	/*current directional ligths contexts - only valid in TRAVERSE_SORT*/
	Chain *local_lights;

	/*we have 2 clippers, one for regular clipping (layout, form if clipping) which is maintained in world coord system
	and one for layer2D which is maintained in parent coord system (cf layer rendering). The layer clipper
	is used only when cascading layers - layer3D doesn't use clippers*/
	Bool has_clip, has_layer_clip;
	/*active clipper in world coord system */
	M4Rect clipper, layer_clipper;

	/*set to the first traversed layers when picking*/
	SFNode *collect_layer;


	/*this flag is set when setting the material & texture to disable backface culling. Not used by non-solid meshes*/
	Bool mesh_is_transparent;

	/*clip planes in world coords*/
	M4Plane clip_planes[MAX_USER_CLIP_PLANES];
	u32 num_clip_planes;
} RenderEffect3D;

RenderEffect3D *effect3d_new();
void effect3d_reset(RenderEffect3D *eff);
void effect3d_delete(RenderEffect3D *eff);

/*get current surface size in scene metrics (eg, pixel or meter) and returns 1 if pixelMetrics, 0 otherwise*/
Bool R3D_GetSurfaceSizeInfo(RenderEffect3D *eff, Fixed *surf_width, Fixed *surf_height);

void R3D_LoadExtensions(Render3D *sr);
void R3D_SensorDeleted(LPSCENERENDER rend, SensorHandler *hdl);
void R3D_SetGrabbed(LPSCENERENDER rend, Bool bOn);

void R3D_LinePropsRemoved(Render3D *sr, SFNode *n);
Bool R3D_NodeChanged(VisualRenderer *vr, SFNode *byObj);
void R3D_NodeInit(VisualRenderer *vr, SFNode *node);


/*user vp modifs*/
Bool R3D_HandleUserEvent(Render3D *sr, M4UserEvent *event);
void R3D_ResetCamera(Render3D *sr);

TextureHandler *R3D_GetTextureHandler(SFNode *n);



M4Err tx_allocate(TextureHandler *txh);
void tx_delete(TextureHandler *txh);
M4Err R3D_SetTextureData(TextureHandler *hdl);
void R3D_TextureHWReset(TextureHandler *hdl);
Bool tx_enable(TextureHandler *txh, SFNode *tx_transform);
void tx_disable(TextureHandler *txh);
char *tx_get_data(TextureHandler *txh, u32 *pix_format);
Bool tx_needs_reload(TextureHandler *hdl);
Bool tx_check_bitmap_load(TextureHandler *hdl);
void tx_copy_to_texture(TextureHandler *txh);
Bool tx_setup_format(TextureHandler *txh);
Bool tx_set_image(TextureHandler *txh, Bool generate_mipmaps);
/*gets texture transform matrix - returns 1 if not identity
@tx_transform: texture transform node from appearance*/
Bool tx_get_transform(TextureHandler *txh, SFNode *tx_transform, M4Matrix *mx);

/*set blending mode*/
enum
{
	TX_DECAL = 0,
	TX_MODULATE,
	TX_REPLACE,
	TX_BLEND,
};
void tx_set_blend_mode(TextureHandler *txh, u32 mode);

/*returns 1 if intersection of ray and plane z=0, storinf intersection point. returns 0 otherwise*/
Bool R3D_Get2DPlaneIntersection(M4Ray *ray, SFVec3f *res);
/*returns 1 if pick ray is in clipper, 0 otherwise*/
Bool R3D_PickInClipper(RenderEffect3D *eff, M4Rect *clip);
/*update the effect clipper with the given clipper in local coords system. The return value
is the clipper in the local coord system. The effect clipper is ALWAYS retranslated to world coord
@for_layer: indcates the update concerns layer clippers, not regular ones*/
M4Rect R3D_UpdateClipper(RenderEffect3D *eff, M4Rect this_clip, Bool *need_restore, M4Rect *original, Bool for_layer);


/*returns 1 if node is a light source. @local_only: only returns 1 if local source (which only lights its parent sub-graph)*/
Bool r3d_is_light(SFNode *n, Bool local_only);



typedef struct 
{
	/*the directional light*/
	SFNode *dlight;
	/*light matrix*/
	M4Matrix light_matrix;
} DLightContext;

typedef struct
{
	/*the one and only node to draw. Can be
		1 - a Shape (to keep track of texturing and appearance) most of the time
		2 - a LayerND which is rendered as a single element, without depth sorting. The layer is then 
		responsible of applying clipping & co
	*/
	SFNode *node_to_draw;
	/*model matrix at this node*/
	M4Matrix model_matrix;
	Bool has_scale;
	/*current color transformation*/
	M4ColorMatrix color_mat;
	/*set if cmat is used*/
	Bool has_cmat;
	/*1-based idx of text element drawn*/
	u32 split_text_idx;
	/*needed for bitmap*/
	Bool is_pixel_metrics;
	/*cull flag - needed for AABB tree culling in case object is 100% inside frustum*/
	u32 cull_flag;

	/*directional lights at this node*/
	Chain *directional_lights;
	/*z-depth for sorting*/
	Fixed zmax;

	/*clipper in world coords*/
	M4Rect clipper;
	Bool has_clipper;

	/*clip planes in world coords*/
	M4Plane clip_planes[MAX_USER_CLIP_PLANES];
	u32 num_clip_planes;
} Draw3DContext;


#endif

