/*
=================
R_SetupEntityLightingGrid

=================
*/
static void R_SetupEntityLightingGrid( trRefEntity_t *ent ) {
	vec3_t			lightOrigin;
	int				pos[3];
	int				i, j;
	float			frac[3];
	int				gridStep[3];
	vec3_t			direction;
	float			totalFactor;
	unsigned short	*startGridPos;


	if (r_fullbright->integer)
	{
		ent->ambientLight[0] = ent->ambientLight[1] = ent->ambientLight[2] = 255.0;
		ent->directedLight[0] = ent->directedLight[1] = ent->directedLight[2] = 255.0;
		VectorCopy( tr.sunDirection, ent->lightDir );
		return;
	}

	if (r_newDLights->integer)
	{
		vec3_t v, invfrac;
		float fraction[8];

		if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) 
		{
			// seperate lightOrigins are needed so an object that is
			// sinking into the ground can still be lit, and so
			// multi-part models can be lit identically
			VectorCopy( ent->e.lightingOrigin, lightOrigin );
		}
		else 
		{
			VectorCopy( ent->e.origin, lightOrigin );
		}

		VectorSubtract( lightOrigin, tr.world->lightGridOrigin, lightOrigin );
		VectorScaleVector( lightOrigin, tr.world->lightGridInverseSize, v );

		pos[0] = (int)floorf(v[0]);
		pos[1] = (int)floorf(v[1]);
		pos[2] = (int)floorf(v[2]);

		frac[0] = v[0] - (float)pos[0];
		frac[1] = v[1] - (float)pos[1];
		frac[2] = v[2] - (float)pos[2];

		invfrac[0] = 1.0f - frac[0];
		invfrac[1] = 1.0f - frac[1];
		invfrac[2] = 1.0f - frac[2];

		fraction[0] = invfrac[0] * invfrac[1] * invfrac[2];
		fraction[1] = frac[0] * invfrac[1] * invfrac[2];
		fraction[2] = invfrac[0] * frac[1] * invfrac[2];
		fraction[3] = frac[0] * frac[1] * invfrac[2];
		fraction[4] = invfrac[0] * invfrac[1] * frac[2];
		fraction[5] = frac[0] * invfrac[1] * frac[2];
		fraction[6] = invfrac[0] * frac[1] * frac[2];
		fraction[7] = frac[0] * frac[1] * frac[2];

		pos[0] = Com_Clamp(0, tr.world->lightGridBounds[0] - 1, pos[0]);
		pos[1] = Com_Clamp(0, tr.world->lightGridBounds[1] - 1, pos[1]);
		pos[2] = Com_Clamp(0, tr.world->lightGridBounds[2] - 1, pos[2]);

		VectorClear( ent->ambientLight );
		VectorClear( ent->directedLight );
		VectorClear( direction );

		// trilerp the light value
		/*
		startGridPos = tr.world->lightGridArray + (pos[0] * tr.world->lightGridStep[0]) + (pos[1] * tr.world->lightGridStep[1]) + (pos[2] * tr.world->lightGridStep[2]);
		*/
		startGridPos = tr.world->lightGridArray + (int)((pos[0] * tr.world->lightGridStep[0])) + (int)((pos[1] * tr.world->lightGridStep[1])) + (int)((pos[2] * tr.world->lightGridStep[2]));

		totalFactor = 0;
		for ( i = 0 ; i < 8 ; i++ ) 
		{
			float			factor;
			mgrid_t			*data;
			unsigned short	*gridPos;
			int				lat, lng;
			vec3_t			normal;

			gridPos = startGridPos + tr.world->lightGridOffsets[i];

			if (gridPos >= tr.world->lightGridArray + tr.world->numGridArrayElements)
			{
				//we've gone off the array somehow
				continue;
			}

			data = tr.world->lightGridData + *gridPos;
			if ( data->styles[0] == LS_LSNONE ) 
			{
				continue;	// ignore samples in walls
			}

			factor = fraction[i];
			totalFactor += factor;

			for(j = 0; j < MAXLIGHTMAPS; j++)
			{
				if (data->styles[j] != LS_LSNONE)
				{
					const byte	style = data->styles[j];

					ent->ambientLight[0] += factor * data->ambientLight[j][0] * styleColors[style][0] / 255.0f;
					ent->ambientLight[1] += factor * data->ambientLight[j][1] * styleColors[style][1] / 255.0f;
					ent->ambientLight[2] += factor * data->ambientLight[j][2] * styleColors[style][2] / 255.0f;

					ent->directedLight[0] += factor * data->directLight[j][0] * styleColors[style][0] / 255.0f;
					ent->directedLight[1] += factor * data->directLight[j][1] * styleColors[style][1] / 255.0f;
					ent->directedLight[2] += factor * data->directLight[j][2] * styleColors[style][2] / 255.0f;
				}
				else
				{
					break;
				}
			}

			lat = data->latLong[1] << 2;
			lng = data->latLong[0] << 2;

			// decode X as cos( lat ) * sin( long )
			// decode Y as sin( lat ) * sin( long )
			// decode Z as cos( long )

			normal[0] = tr.sinTable[(lat + (FUNCTABLE_SIZE / 4)) & FUNCTABLE_MASK] * tr.sinTable[lng];
			normal[1] = tr.sinTable[lat] * tr.sinTable[lng];
			normal[2] = tr.sinTable[(lng + (FUNCTABLE_SIZE / 4)) & FUNCTABLE_MASK];

			VectorMA( direction, factor, normal, direction );
		}

		if ( totalFactor > 0 && totalFactor < 0.99 ) 
		{
			totalFactor = 1.0 / totalFactor;
			VectorScale( ent->ambientLight, totalFactor, ent->ambientLight );
			VectorScale( ent->directedLight, totalFactor, ent->directedLight );
		}

		VectorScale( ent->ambientLight, r_ambientScale->value, ent->ambientLight );
		VectorScale( ent->directedLight, r_directedScale->value, ent->directedLight );
		VectorNormalize2( direction, ent->lightDir );
	}
	else
	{
		if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) {
			// seperate lightOrigins are needed so an object that is
			// sinking into the ground can still be lit, and so
			// multi-part models can be lit identically
			VectorCopy( ent->e.lightingOrigin, lightOrigin );
		} else {
			VectorCopy( ent->e.origin, lightOrigin );
		}

		VectorSubtract( lightOrigin, tr.world->lightGridOrigin, lightOrigin );
		for ( i = 0 ; i < 3 ; i++ ) {
			float	v;

			v = lightOrigin[i]*tr.world->lightGridInverseSize[i];
			pos[i] = floor( v );
			frac[i] = v - pos[i];
			if ( pos[i] < 0 ) {
				pos[i] = 0;
			} else if ( pos[i] >= tr.world->lightGridBounds[i] - 1 ) {
				pos[i] = tr.world->lightGridBounds[i] - 1;
			}
		}

		VectorClear( ent->ambientLight );
		VectorClear( ent->directedLight );
		VectorClear( direction );

		// trilerp the light value
		gridStep[0] = 1;
		gridStep[1] = tr.world->lightGridBounds[0];
		gridStep[2] = tr.world->lightGridBounds[0] * tr.world->lightGridBounds[1];
		startGridPos = tr.world->lightGridArray + (pos[0] * gridStep[0] + pos[1] * gridStep[1] + pos[2] * gridStep[2]);

		totalFactor = 0;
		for ( i = 0 ; i < 8 ; i++ ) {
			float			factor;
			mgrid_t			*data;
			unsigned short	*gridPos;
			int				lat, lng;
			vec3_t			normal;

			factor = 1.0;
			gridPos = startGridPos;
			for ( j = 0 ; j < 3 ; j++ ) {
				if ( i & (1<<j) ) {
					factor *= frac[j];
					gridPos += gridStep[j];
				} else {
					factor *= (1.0 - frac[j]);
				}
			}

			if (gridPos >= tr.world->lightGridArray + tr.world->numGridArrayElements)
			{//we've gone off the array somehow
				continue;
			}
			data = tr.world->lightGridData + *gridPos;
			if ( data->styles[0] == LS_LSNONE ) 
			{
				continue;	// ignore samples in walls
			}

			totalFactor += factor;

			for(j=0;j<MAXLIGHTMAPS;j++)
			{
				if (data->styles[j] != LS_LSNONE)
				{
					const byte	style= data->styles[j];

					ent->ambientLight[0] += factor * data->ambientLight[j][0] * styleColors[style][0] / 255.0f;
					ent->ambientLight[1] += factor * data->ambientLight[j][1] * styleColors[style][1] / 255.0f;
					ent->ambientLight[2] += factor * data->ambientLight[j][2] * styleColors[style][2] / 255.0f;

					ent->directedLight[0] += factor * data->directLight[j][0] * styleColors[style][0] / 255.0f;
					ent->directedLight[1] += factor * data->directLight[j][1] * styleColors[style][1] / 255.0f;
					ent->directedLight[2] += factor * data->directLight[j][2] * styleColors[style][2] / 255.0f;
				}
				else
				{
					break;
				}
			}

			lat = data->latLong[1];
			lng = data->latLong[0];
			lat *= (FUNCTABLE_SIZE/256);
			lng *= (FUNCTABLE_SIZE/256);

			// decode X as cos( lat ) * sin( long )
			// decode Y as sin( lat ) * sin( long )
			// decode Z as cos( long )

			normal[0] = tr.sinTable[(lat + (FUNCTABLE_SIZE / 4)) & FUNCTABLE_MASK] * tr.sinTable[lng];
			normal[1] = tr.sinTable[lat] * tr.sinTable[lng];
			normal[2] = tr.sinTable[(lng + (FUNCTABLE_SIZE / 4)) & FUNCTABLE_MASK];

			VectorMA( direction, factor, normal, direction );
		}

		if ( totalFactor > 0 && totalFactor < 0.99 ) 
		{
			totalFactor = 1.0 / totalFactor;
			VectorScale( ent->ambientLight, totalFactor, ent->ambientLight );
			VectorScale( ent->directedLight, totalFactor, ent->directedLight );
		}

		VectorScale( ent->ambientLight, r_ambientScale->value, ent->ambientLight );
		VectorScale( ent->directedLight, r_directedScale->value, ent->directedLight );

		VectorNormalize2( direction, ent->lightDir );
	}
}
void SP_misc_model_static( void ) {
	char* model;
	vec_t angle;
	vec3_t angles;
	vec_t scale;
	vec3_t vScale;
	vec3_t org;
	vec_t zoffset;
	int i;
	int modelIndex;
	cg_staticmodel_t *staticmodel;

	if( cgs.numMiscStaticModels >= MAX_STATIC_MODELS ) {
		CG_Error( "MAX_STATIC_MODELS(%i) hit", MAX_STATIC_MODELS );
	}

	CG_SpawnString( "model", "", &model );

	if( !model || !model[0] ) {
		CG_Error( "misc_model_static with no model." );
	}

	CG_SpawnVector( "origin", "0 0 0", org );
	CG_SpawnFloat( "zoffset", "0", &zoffset );

	if( !CG_SpawnVector( "angles", "0 0 0", angles ) ) {
		if( CG_SpawnFloat( "angle", "0", &angle ) ) {
			angles[YAW] = angle;
		}
	}

	if( !CG_SpawnVector( "modelscale_vec", "1 1 1", vScale ) ) {
		if( CG_SpawnFloat( "modelscale", "1", &scale ) ) {
			VectorSet( vScale, scale, scale, scale );
		}
	}

	modelIndex = trap_R_RegisterModel( model );
	if( modelIndex == 0 ) {
		CG_Error( "misc_model_static failed to load model '%s'", model );
		return;
	}

	staticmodel		= &cgs.miscStaticModels[cgs.numMiscStaticModels++];
	staticmodel->model	= modelIndex;
	AnglesToAxis( angles, staticmodel->axes );
	for( i = 0; i < 3; i++ ) {
		VectorScale( staticmodel->axes[i], vScale[i], staticmodel->axes[i] );
	}

	VectorCopy( org, staticmodel->org );
	staticmodel->zoffset = zoffset;

	if( staticmodel->model ) {
		vec3_t mins, maxs;

		trap_R_ModelBounds( staticmodel->model, mins, maxs );

		VectorScaleVector( mins, vScale, mins );
		VectorScaleVector( maxs, vScale, maxs );

		staticmodel->radius = RadiusFromBounds( mins, maxs );
	}
	else {
		staticmodel->radius = 0;
	}
}