Modified: trunk/base/maps/test.ent
===================================================================
--- trunk/base/maps/test.ent	2007-01-14 23:58:12 UTC (rev 763)
+++ trunk/base/maps/test.ent	2007-01-16 15:19:42 UTC (rev 764)
@@ -28,13 +28,13 @@
 	"classname" "model"
 	"model" "models/MD5/enemies/zombie/zombie.md5mesh"
 }
+//{
+//	"origin" "0 -48 52"
+//	"classname" "gravitysphere"
+//	"gravitystrength" "5000"
+//	"scale" "500"
+//}
 {
-	"origin" "0 -48 52"
-	"classname" "gravitysphere"
-	"gravitystrength" "5000"
-	"scale" "500"
-}
-{
 	"origin" "0 0 0"
 	"classname" "model"
 	"model" "maps/test/terrain.md5mesh"

Modified: trunk/collision.c
===================================================================
--- trunk/collision.c	2007-01-14 23:58:12 UTC (rev 763)
+++ trunk/collision.c	2007-01-16 15:19:42 UTC (rev 764)
@@ -199,7 +199,7 @@
 // generate edges from the planes
 
 // FIXME: only works with orthogonal matrices
-void Collision_Brush_Transform(Collision_Brush *newbrush, Collision_Brush *oldbrush, matrix4x4_t *matrix)
+void Collision_Brush_Transform(Collision_Brush *newbrush, const Collision_Brush *oldbrush, const matrix4x4_t *matrix)
 {
 	NUint32 i;
 	newbrush->numpoints = oldbrush->numpoints;
@@ -215,9 +215,10 @@
 }
 
 // FIXME: only works with orthogonal matrices
-void Collision_Trace_Begin(Collision_Trace *trace, matrix4x4_t *modelmatrix, matrix4x4_t *modelinversematrix, Collision_Brush *brush_start, Collision_Brush *brush_end, Nvec nudge)
+void Collision_Trace_Begin(Collision_Trace *trace, const matrix4x4_t *modelmatrix, const matrix4x4_t *modelinversematrix, const Collision_Brush *brush_start, const Collision_Brush *brush_end, Nvec nudge)
 {
 	NUint32 i;
+	trace->startsolid = false;
 	trace->modelmatrix = *modelmatrix;
 	trace->fraction = 1;
 	trace->nudgefraction = 1;
@@ -400,7 +401,7 @@
 	// return if it started in solid
 	if (enterfrac < 0)
 	{
-		//trace->startsolid = true;
+		trace->startsolid = true;
 		return;
 	}
 	// we don't need to check enterfrac >= leavefrac because it was checked

Modified: trunk/collision.h
===================================================================
--- trunk/collision.h	2007-01-14 23:58:12 UTC (rev 763)
+++ trunk/collision.h	2007-01-16 15:19:42 UTC (rev 764)
@@ -60,7 +60,7 @@
 void Collision_Brush_UpdateTriangleMeshBrushes(Collision_Brush *brushes, NUint32 numtriangles, const NUint32 *element3i, const float *vertex3f);
 void Collision_Brush_UpdateCullingData(Collision_Brush *brush);
 
-void Collision_Brush_Transform(Collision_Brush *newbrush, Collision_Brush *oldbrush, matrix4x4_t *matrix);
+void Collision_Brush_Transform(Collision_Brush *newbrush, const Collision_Brush *oldbrush, const matrix4x4_t *matrix);
 
 #define COLLISION_TRACE_MAXPOINTS 64
 #define COLLISION_TRACE_MAXEDGES 64
@@ -85,6 +85,8 @@
 	Nvec radius;
 	// nudge off from impacted surface
 	Nvec nudge;
+	// returns whether the start of the trace was in a solid object
+	Nbool startsolid;
 	// the brush that is conceptually moving through the world, in modelspace
 	// (does not change during a trace)
 	// INTERNAL USE ONLY
@@ -98,7 +100,7 @@
 Collision_Trace;
 
 // transforms must be normalized!
-void Collision_Trace_Begin(Collision_Trace *trace, matrix4x4_t *modelmatrix, matrix4x4_t *modelinversematrix, Collision_Brush *brush_start, Collision_Brush *brush_end, Nvec nudge);
+void Collision_Trace_Begin(Collision_Trace *trace, const matrix4x4_t *modelmatrix, const matrix4x4_t *modelinversematrix, const Collision_Brush *brush_start, const Collision_Brush *brush_end, Nvec nudge);
 
 // collision check against one or more brushes
 void Collision_Trace_CheckBrush(Collision_Trace *trace, const Collision_Brush *other_start, const Collision_Brush *other_end);

Modified: trunk/game/g_entity.c
===================================================================
--- trunk/game/g_entity.c	2007-01-14 23:58:12 UTC (rev 763)
+++ trunk/game/g_entity.c	2007-01-16 15:19:42 UTC (rev 764)
@@ -6,7 +6,7 @@
 #include "model.h"
 #include "modelanim.h"
 
-G_Entity *G_Entity_New(G_Entity *parent, const G_Entity_Class *eclass, const G_Entity_Position *position)
+G_Entity *G_Entity_New(G_Entity *parent, const G_Entity_Class *eclass, const G_Position *position)
 {
 	G_Entity *entity;
 	Nvec tbest;
@@ -406,77 +406,78 @@
 	// TODO: link into rooms for rendering
 }
 
-void G_Entity_Move(G_Entity *self, NUint32 scope, Nvec stepheight, Nvec bounce)
+void G_Entity_Move(G_Entity *self, NUint32 scope, Nvec stepheight, Nvec bounce, Nvec gravityscale)
 {
 	NUint32 bump;
+	Nvec3 up;
 	Nvec t;
-	matrix4x4_t newmatrix;
+	G_Position newposition;
 	G_Trace trace;
 
-	if (VectorLength2(self->position.velocity) < (1.0 / 1024.0))
-	{
-		// no movement
-		return;
-	}
+	VectorNegate(self->position.gravityacceleration, up);
+	VectorNormalize(up);
 
 	self->groundentity = NULL;
 	VectorClear(self->groundnormal);
 
-	if (!scope)
-	{
-		// noclip
-		self->position.m.m[0][3] = self->position.m.m[0][3] + G.frametime * self->position.velocity[0];
-		self->position.m.m[1][3] = self->position.m.m[1][3] + G.frametime * self->position.velocity[1];
-		self->position.m.m[2][3] = self->position.m.m[2][3] + G.frametime * self->position.velocity[2];
-		G_Entity_Relink(self);
-		return;
-	}
+	// apply the half of the gravity acceleration to velocity, this gives us a
+	// position that exactly matches Implicit Euler integration, the other
+	// half of the acceleration will be added to velocity later
+	if (gravityscale)
+		G_Position_Gravity(&self->position, 0.5 * gravityscale * G.frametime);
 
 	for (bump = 0, t = 1;bump < 16 && t >= (1.0 / 128.0);bump++)
 	{
-		newmatrix = self->position.m;
-		newmatrix.m[0][3] = self->position.m.m[0][3] + t * G.frametime * self->position.velocity[0];
-		newmatrix.m[1][3] = self->position.m.m[1][3] + t * G.frametime * self->position.velocity[1];
-		newmatrix.m[2][3] = self->position.m.m[2][3] + t * G.frametime * self->position.velocity[2];
+		newposition = self->position;
+		G_World_Trace_Move(&trace, &self->collisionbrush, &newposition, t * G.frametime, self, scope);
 
-		G_World_Trace_Brush(&trace, &self->collisionbrush, &self->position.m, &newmatrix, self, scope);
 		if (trace.impact_fraction == 1)
 		{
-			self->position.m = trace.impact_matrix;
+			self->position = newposition;
 			break;
 		}
 
 		// step up stairs
-		if (trace.impact_normal[2] < 0.7)
+		if (false && DotProduct(trace.impact_normal, up) < 0.7)
 		{
-			// we hit non-walkable ground (probably a step)
+			// we hit something that can not be walked on, may be a stair step
 			G_Trace trace2;
-			matrix4x4_t matrix2;
-			matrix4x4_t newmatrix2;
+			G_Position newposition2;
 			// trace again, but higher
-			matrix2 = self->position.m;
-			matrix2.m[2][3] += stepheight;
-			newmatrix2 = newmatrix;
-			newmatrix2.m[2][3] += stepheight;
-			G_World_Trace_Brush(&trace2, &self->collisionbrush, &matrix2, &newmatrix2, self, scope);
-			// TODO: check startsolid
-			// trace down from the higher trace result
-			matrix2 = trace2.impact_matrix;
-			newmatrix2 = matrix2;
-			newmatrix2.m[2][3] -= stepheight;
-			G_World_Trace_Brush(&trace2, &self->collisionbrush, &matrix2, &newmatrix2, self, scope);
-			// if this landed on good ground, use it
-			if (trace2.impact_fraction < 1 && trace2.impact_normal[2] >= 0.7)
-				trace = trace2;
+			newposition2 = self->position;
+			newposition2.m.m[0][3] += stepheight * up[0];
+			newposition2.m.m[1][3] += stepheight * up[1];
+			newposition2.m.m[2][3] += stepheight * up[2];
+			G_World_Trace_Move(&trace2, &self->collisionbrush, &newposition2, t * G.frametime, self, scope);
+			// check startsolid to make sure we don't start in the ceiling
+			if (!trace2.startsolid)
+			{
+				// trace down from the higher trace result
+				matrix4x4_t matrix2;
+				matrix4x4_t newmatrix2;
+				matrix2 = trace2.impact_matrix;
+				newmatrix2 = matrix2;
+				newmatrix2.m[0][3] -= stepheight * up[0];
+				newmatrix2.m[1][3] -= stepheight * up[1];
+				newmatrix2.m[2][3] -= stepheight * up[2];
+				G_World_Trace_Brush(&trace2, &self->collisionbrush, &matrix2, &newmatrix2, self, scope);
+				// if this landed on good ground, use it
+				if (trace2.impact_fraction < 1 && DotProduct(trace2.impact_normal, up) >= 0.7)
+				{
+					newposition.m = trace2.impact_matrix;
+					trace.impact_entity = trace2.impact_entity;
+					VectorCopy(trace2.impact_normal, trace.impact_normal);
+				}
+			}
 		}
 
-		if (trace.impact_normal[2] >= 0.7)
+		if (DotProduct(trace.impact_normal, up) >= 0.7)
 		{
 			self->groundentity = trace.impact_entity;
 			VectorCopy(trace.impact_normal, self->groundnormal);
 		}
 
-		self->position.m = trace.impact_matrix;
+		self->position = newposition;
 		VectorReflect(self->position.velocity, bounce, trace.impact_normal, self->position.velocity);
 		t -= t * trace.impact_fraction;
 	}
@@ -490,13 +491,23 @@
 		matrix4x4_t newmatrix2;
 		matrix2 = self->position.m;
 		newmatrix2 = matrix2;
-		newmatrix2.m[2][3] -= stepheight;
+		newmatrix2.m[0][3] -= stepheight * up[0];
+		newmatrix2.m[1][3] -= stepheight * up[1];
+		newmatrix2.m[2][3] -= stepheight * up[2];
 		G_World_Trace_Brush(&trace2, &self->collisionbrush, &matrix2, &newmatrix2, self, scope);
 		if (trace2.impact_fraction < 1)
 			self->position.m = trace2.impact_matrix;
 	}
 	*/
 
+	// apply the other half of the gravity acceleration to velocity
+	if (gravityscale)
+		G_Position_Gravity(&self->position, 0.5 * gravityscale * G.frametime);
+
+	// reset the gravityacceleration vector to the current global gravity,
+	// this may be modified by gravity influence entities before the next move
+	VectorCopy(G.globalgravity, self->position.gravityacceleration);
+
 	G_Entity_Relink(self);
 }
 
@@ -504,27 +515,26 @@
 {
 	NUint32 j;
 	Nvec f;
-	Nvec3 gravityimpulse, countergravityimpulse;
 	for (j = 0; j < 3; j++)
 		if (IS_NAN(self->position.velocity[j]))
 			self->position.velocity[j] = 0;
-	VectorScale(self->position.gravityacceleration, G.frametime * gravityscale, gravityimpulse);
-	if (self->groundentity)
+	if (self->groundentity && !jumpspeed)
 	{
 		Nvec3 groundwishdir;
-		if (!jumpspeed)
-		{
-			// apply stopspeed
-			VectorReflect(self->position.velocity, 0, self->groundnormal, groundwishdir);
-			VectorNormalize(groundwishdir);
-			f = DotProduct(self->position.velocity, groundwishdir);
-			f = Min(f, groundstopspeed * G.frametime);
-			if (f > 0)
-				VectorMA(self->position.velocity, -f, groundwishdir, self->position.velocity);
-			// apply friction
-			f = Bound(0, G.frametime * groundfriction, 1);
-			VectorLerp(self->position.velocity, f, self->groundentity->position.velocity, self->position.velocity);
-		}
+#if 0
+		// apply stopspeed
+		VectorReflect(self->position.velocity, 0, self->groundnormal, groundwishdir);
+		VectorNormalize(groundwishdir);
+		f = DotProduct(self->position.velocity, groundwishdir);
+		f = Min(f, groundstopspeed * G.frametime);
+		if (f > 0)
+			VectorMA(self->position.velocity, -f, groundwishdir, self->position.velocity);
+		// apply a damping force (not really friction)
+		f = Bound(0, G.frametime * groundfriction, 1);
+		VectorLerp(self->position.velocity, f, self->groundentity->position.velocity, self->position.velocity);
+
+		// subtract gravity from wishvel because the player doesn't want to slide down slopes
+		VectorSubtract(wishvel, self->position.gravityacceleration, wishvel);
 		if (wishvel)
 		{
 			Nvec wishspeed;
@@ -541,15 +551,24 @@
 				VectorMA(self->position.velocity, f, groundwishdir, self->position.velocity);
 			}
 		}
+#endif
+
+		// subtract gravity from wishvel because the player doesn't want to slide down slopes
+		VectorSubtract(wishvel, self->position.gravityacceleration, groundwishdir);
+		VectorReflect(groundwishdir, 0, self->groundnormal, groundwishdir);
+		VectorAdd(self->position.velocity, groundwishdir, self->position.velocity);
+
+		// blend between current velocity and the new wishvel, but do not alter velocity along the ground normal, only perpendicular to it
+		f = 1;//Bound(0, G.frametime * groundaccel, 1);
+		VectorSubtract(wishvel, self->position.velocity, groundwishdir);
+		VectorReflect(groundwishdir, 0, self->groundnormal, groundwishdir);
+		VectorMA(self->position.velocity, f, groundwishdir, self->position.velocity);
+
 		// add jump impulse along ground normal
 		if (jumpspeed)
 			VectorMA(self->position.velocity, jumpspeed, self->groundnormal, self->position.velocity);
-		// apply gravity acceleration
-		VectorAdd(self->position.velocity, gravityimpulse, self->position.velocity);
-		// add another impulse to counteract the sliding that results from gravity
-		VectorReflect(gravityimpulse, 0, self->groundnormal, countergravityimpulse);
-		VectorSubtract(self->position.velocity, countergravityimpulse, self->position.velocity);
 	}
+#if 0
 	else
 	{
 		// apply stopspeed
@@ -561,7 +580,7 @@
 		}
 		else
 			VectorClear(self->position.velocity);
-		// apply friction
+		// apply a damping force (not really friction)
 		f = Bound(0, 1 - G.frametime * airfriction, 1);
 		VectorScale(self->position.velocity, f, self->position.velocity);
 		if (wishvel)
@@ -570,17 +589,13 @@
 			f = Bound(-1, G.frametime * airaccel, 1);
 			VectorMA(self->position.velocity, f, wishvel, self->position.velocity);
 		}
-		// apply gravity acceleration
-		VectorAdd(self->position.velocity, gravityimpulse, self->position.velocity);
 	}
+#endif
 	if (thrustvel)
 		VectorMA(self->position.velocity, G.frametime, thrustvel, self->position.velocity);
 	for (j = 0; j < 3; j++)
 		if (IS_NAN(self->position.velocity[j]))
 			self->position.velocity[j] = 0;
-	// reset the gravityacceleration vector to the current global gravity,
-	// this may be modified by gravity influence entities before the next move
-	VectorCopy(G.globalgravity, self->position.gravityacceleration);
 }
 
 static Collision_Brush boxbrush;

Modified: trunk/game/g_entity.h
===================================================================
--- trunk/game/g_entity.h	2007-01-14 23:58:12 UTC (rev 763)
+++ trunk/game/g_entity.h	2007-01-16 15:19:42 UTC (rev 764)
@@ -6,7 +6,7 @@
 
 // G_Entity member functions:
 // creation/destruction of entities
-G_Entity *G_Entity_New(G_Entity *parent, const G_Entity_Class *eclass, const G_Entity_Position *position);
+G_Entity *G_Entity_New(G_Entity *parent, const G_Entity_Class *eclass, const G_Position *position);
 void G_Entity_Spawn(G_Entity *entity);
 void G_Entity_Remove(G_Entity *entity);
 void G_Entity_RemoveChildren(G_Entity *entity);
@@ -21,7 +21,7 @@
 void G_Entity_SetModel(G_Entity *entity, const char *modelname);
 // entity physics
 void G_Entity_Relink(G_Entity *entity);
-void G_Entity_Move(G_Entity *self, NUint32 scope, Nvec stepheight, Nvec bounce);
+void G_Entity_Move(G_Entity *self, NUint32 scope, Nvec stepheight, Nvec bounce, Nvec gravityscale);
 void G_Entity_PlayerPhysics(G_Entity *self, Nvec3 wishvel, Nvec groundfriction, Nvec groundaccel, Nvec stopspeed, Nvec airfriction, Nvec airaccel, Nvec airstopspeed, Nvec jumpspeed, Nvec gravityscale, Nvec3 thrustvel);
 void G_Entity_SetBrush_Box(G_Entity *entity, const Nvec3 boxmins, const Nvec3 boxmaxs);
 void G_Entity_SetBrush_Point(G_Entity *entity, const Nvec3 point);

Modified: trunk/game/g_entityclass.c
===================================================================
--- trunk/game/g_entityclass.c	2007-01-14 23:58:12 UTC (rev 763)
+++ trunk/game/g_entityclass.c	2007-01-16 15:19:42 UTC (rev 764)
@@ -179,7 +179,7 @@
 			G_Entity_PlayerPhysics(self, rotatedmovement, self->eclass->friction, self->eclass->acceleration, self->eclass->stopspeed, self->eclass->airfriction, self->eclass->airacceleration, self->eclass->airstopspeed, (buttonbits & G_BUTTON_MOVEUP) ? self->eclass->jumpvelocity : 0, 1, NULL);
 	}
 
-	G_Entity_Move(self, user->noclipmode ? 0 : G_SCOPE_BLOCK_PLAYER, self->eclass->stepheight, 0);
+	G_Entity_Move(self, user->noclipmode ? 0 : G_SCOPE_BLOCK_PLAYER, self->eclass->stepheight, 0, self->groundentity || user->flymode || user->noclipmode ? 0 : 1);
 
 	if (self->legsactiontype != G_LEGSACTIONTYPE_DEAD)
 	{
@@ -574,7 +574,7 @@
 
 static void G_Object_Weapon_LaunchProjectile(G_Entity *self)
 {
-	G_Entity_Position position;
+	G_Position position;
 	G_Entity *projectile;
 	const G_Entity_Class *projectileclass;
 	Nvec3 speedvec;
@@ -664,7 +664,7 @@
 	// TODO: use a better physics function (one that handles spin)
 	G_Entity_PlayerPhysics(self, NULL, self->eclass->friction, self->eclass->acceleration, self->eclass->stopspeed, self->eclass->airfriction, self->eclass->airacceleration, self->eclass->airstopspeed, 0, 1, NULL);
 
-	G_Entity_Move(self, G_SCOPE_BLOCK_PROJECTILE, 0, 0);
+	G_Entity_Move(self, G_SCOPE_BLOCK_PROJECTILE, 0, 0, 1);
 
 	self->lifetime -= G.frametime;
 	if (self->lifetime <= 0)
@@ -808,7 +808,7 @@
 	VectorClear(rotatedmovement);
 	G_Entity_PlayerPhysics(self, rotatedmovement, 4, 5, 0.01, 0, 0, 1, NULL);
 
-	G_Entity_Move(self, G_SCOPE_BLOCK_VEHICLE, 1, 0);
+	G_Entity_Move(self, G_SCOPE_BLOCK_VEHICLE, 1, 0, 1);
 
 	G_Entity_SendNetworkUpdate(self);
 }
@@ -976,7 +976,7 @@
 		G_Object_SpawnPad_Frame,
 		G_Object_SpawnPad_ReadPacket,
 		G_Object_SpawnPad_WritePacket,
-		G_SCOPE_RENDER_OPAQUE | G_SCOPE_RENDER_CASTSHADOW | G_SCOPE_RENDER_RECEIVELIGHT | G_SCOPE_BLOCK_EVERYTHING | G_SCOPE_NETWORK_RENDERABLE,
+		G_SCOPE_RENDER_OPAQUE | G_SCOPE_RENDER_CASTSHADOW | G_SCOPE_RENDER_RECEIVELIGHT | G_SCOPE_NETWORK_RENDERABLE,
 		"Provides a place for newbies to stand\nTODO: explain fields\n"
 	},
 	{

Modified: trunk/game/g_main.h
===================================================================
--- trunk/game/g_main.h	2007-01-14 23:58:12 UTC (rev 763)
+++ trunk/game/g_main.h	2007-01-16 15:19:42 UTC (rev 764)
@@ -2,7 +2,7 @@
 #ifndef G_MAIN_H
 #define G_MAIN_H
 
-typedef struct G_Entity_Position
+typedef struct G_Position
 {
 	matrix4x4_t m;
 	matrix4x4_t inversem;
@@ -12,7 +12,7 @@
 	// (updated every frame)
 	Nvec3 gravityacceleration;
 }
-G_Entity_Position;
+G_Position;
 
 #include "game/g_network.h"
 #include "collision.h"
@@ -205,7 +205,7 @@
 	NUint32 *client_networkresource_remapindex;
 	// reference values for initializing structs
 	matrix4x4_t identitymatrix;
-	G_Entity_Position identityposition;
+	G_Position identityposition;
 }
 G_Static;
 
@@ -559,7 +559,7 @@
 	NUint32 numtransforms;
 	matrix4x4_t *transforms;
 	// entity transform (skeletal is relative to this) and velocity
-	G_Entity_Position position;
+	G_Position position;
 	// builtin collision brush buffers (to avoid alloc/free)
 	Collision_Brush collisionbrush;
 	Collision_Point collisionbrushpoints[16];
@@ -622,7 +622,7 @@
 
 typedef struct G_Trace
 {
-	G_Entity *moveentity;
+	const G_Entity *moveentity;
 	matrix4x4_t matrix1;
 	matrix4x4_t matrix2;
 
@@ -631,6 +631,8 @@
 	G_Entity *impact_entity;
 	Nvec3 impact_normal;
 	matrix4x4_t impact_matrix;
+
+	Nbool startsolid;
 }
 G_Trace;
 

Modified: trunk/game/g_packetbuffer.c
===================================================================
--- trunk/game/g_packetbuffer.c	2007-01-14 23:58:12 UTC (rev 763)
+++ trunk/game/g_packetbuffer.c	2007-01-16 15:19:42 UTC (rev 764)
@@ -194,7 +194,7 @@
 
 // functions for more specialized data types
 
-void G_PacketBuffer_WritePosition(G_PacketBuffer *buf, G_Entity_Position *p, Nbool sendorigin, Nbool sendrotation, Nbool sendscale, Nbool sendvelocity, Nbool sendspin)
+void G_PacketBuffer_WritePosition(G_PacketBuffer *buf, G_Position *p, Nbool sendorigin, Nbool sendrotation, Nbool sendscale, Nbool sendvelocity, Nbool sendspin)
 {
 	// location
 	if (sendorigin)
@@ -235,7 +235,7 @@
 	}
 }
 
-void G_PacketBuffer_ReadPosition(G_PacketBuffer *buf, G_Entity_Position *p, Nbool sendorigin, Nbool sendrotation, Nbool sendscale, Nbool sendvelocity, Nbool sendspin)
+void G_PacketBuffer_ReadPosition(G_PacketBuffer *buf, G_Position *p, Nbool sendorigin, Nbool sendrotation, Nbool sendscale, Nbool sendvelocity, Nbool sendspin)
 {
 	*p = GS.identityposition;
 	if (sendorigin)

Modified: trunk/game/g_packetbuffer.h
===================================================================
--- trunk/game/g_packetbuffer.h	2007-01-14 23:58:12 UTC (rev 763)
+++ trunk/game/g_packetbuffer.h	2007-01-16 15:19:42 UTC (rev 764)
@@ -51,10 +51,10 @@
 Nsize G_PacketBuffer_ReadBytes(G_PacketBuffer *buf, NUint8 *buffer, Nsize buffersize);
 
 // functions for more specialized data types:
-struct G_Entity_Position;
+struct G_Position;
 struct G_Entity;
-void G_PacketBuffer_WritePosition(G_PacketBuffer *buf, struct G_Entity_Position *p, Nbool sendorigin, Nbool sendrotation, Nbool sendscale, Nbool sendvelocity, Nbool sendspin);
-void G_PacketBuffer_ReadPosition(G_PacketBuffer *buf, struct G_Entity_Position *p, Nbool sendorigin, Nbool sendrotation, Nbool sendscale, Nbool sendvelocity, Nbool sendspin);
+void G_PacketBuffer_WritePosition(G_PacketBuffer *buf, struct G_Position *p, Nbool sendorigin, Nbool sendrotation, Nbool sendscale, Nbool sendvelocity, Nbool sendspin);
+void G_PacketBuffer_ReadPosition(G_PacketBuffer *buf, struct G_Position *p, Nbool sendorigin, Nbool sendrotation, Nbool sendscale, Nbool sendvelocity, Nbool sendspin);
 void G_PacketBuffer_WriteColorVector16(G_PacketBuffer *buf, Nvec3 color);
 void G_PacketBuffer_ReadColorVector16(G_PacketBuffer *buf, Nvec3 color);
 void G_PacketBuffer_WriteClamped8(G_PacketBuffer *buf, Nvec f, Nvec scale);

Modified: trunk/game/g_render.c
===================================================================
--- trunk/game/g_render.c	2007-01-14 23:58:12 UTC (rev 763)
+++ trunk/game/g_render.c	2007-01-16 15:19:42 UTC (rev 764)
@@ -378,7 +378,7 @@
 	// (the black background color is exactly what we want, so only draw depth)
 	G_DrawSurfaceList(G_SCOPE_RENDER_OPAQUE, G_SCOPE_RENDER_OPAQUE, G.drawview_numvisiblesurfaces, G.drawview_visiblesurfaces);
 
-	// draw sky surfaces as fullbright with depth writing and no fogging
+	// draw sky surfaces as fullbright with no fogging
 	R_SetDrawMode(R_DRAWMODE_SKY);
 	G_DrawSurfaceList(G_SCOPE_RENDER_SKY, G_SCOPE_RENDER_SKY, G.drawview_numvisiblesurfaces, G.drawview_visiblesurfaces);
 

Modified: trunk/game/g_world.c
===================================================================
--- trunk/game/g_world.c	2007-01-14 23:58:12 UTC (rev 763)
+++ trunk/game/g_world.c	2007-01-16 15:19:42 UTC (rev 764)
@@ -231,7 +231,7 @@
 	char *key[2];
 	Nvec3 origin, angles;
 	Nvec scale;
-	G_Entity_Position position;
+	G_Position position;
 
 	Util_ParseC_SetUpThread(&thread, filedata, filesize);
 	StringKeys_Init(&keys, GS.memzone, 65536);
@@ -507,7 +507,7 @@
 {
 	G_Entity *player;
 	G_Entity *spot;
-	G_Entity_Position position;
+	G_Position position;
 	// TODO: better spawn point selection
 	spot = G_Entity_Find_CodeName(NULL, "spawninfantry");
 	if (spot)
@@ -524,7 +524,7 @@
 	G_Entity_Spawn(player);
 }
 
-NUint32 G_World_FindEntities_BoxAndSphere(G_Entity **entitylist, NUint32 maxentities, Nvec3 mins, Nvec3 maxs, Nvec3 center, Nvec radius, NUint32 scopeflagsmask)
+NUint32 G_World_FindEntities_BoxAndSphere(G_Entity **entitylist, NUint32 maxentities, const Nvec3 mins, const Nvec3 maxs, const Nvec3 center, Nvec radius, NUint32 scopeflagsmask)
 {
 	NUint32 i;
 	NUint32 numentities = 0;
@@ -544,7 +544,7 @@
 	return numentities;
 }
 
-NUint32 G_World_FindEntities_Sphere(G_Entity **entitylist, NUint32 maxentities, Nvec3 center, Nvec radius, NUint32 scopeflagsmask)
+NUint32 G_World_FindEntities_Sphere(G_Entity **entitylist, NUint32 maxentities, const Nvec3 center, Nvec radius, NUint32 scopeflagsmask)
 {
 	Nvec3 mins, maxs;
 	VectorSet(mins, center[0] - radius, center[1] - radius, center[2] - radius);
@@ -552,7 +552,7 @@
 	return G_World_FindEntities_BoxAndSphere(entitylist, maxentities, mins, maxs, center, radius, scopeflagsmask);
 }
 
-NUint32 G_World_FindEntities_Box(G_Entity **entitylist, NUint32 maxentities, Nvec3 mins, Nvec3 maxs, NUint32 scopeflagsmask)
+NUint32 G_World_FindEntities_Box(G_Entity **entitylist, NUint32 maxentities, const Nvec3 mins, const Nvec3 maxs, NUint32 scopeflagsmask)
 {
 	Nvec3 temp;
 	Nvec3 center;
@@ -563,7 +563,7 @@
 	return G_World_FindEntities_BoxAndSphere(entitylist, maxentities, mins, maxs, center, radius, scopeflagsmask);
 }
 
-void G_World_Trace_Brush(G_Trace *trace, Collision_Brush *brush, matrix4x4_t *startmatrix, matrix4x4_t *endmatrix, G_Entity *ignoreentity, NUint32 scopeflagsmask)
+void G_World_Trace_Brush(G_Trace *trace, const Collision_Brush *brush, const matrix4x4_t *startmatrix, const matrix4x4_t *endmatrix, const G_Entity *ignoreentity, NUint32 scopeflagsmask)
 {
 	NUint32 i;
 	G_Entity *e;
@@ -653,6 +653,8 @@
 			}
 		}
 		Collision_Trace_End(&collisiontrace);
+		if (collisiontrace.startsolid)
+			trace->startsolid = true;
 		if (trace->impact_realfraction > collisiontrace.fraction)
 		{
 			trace->impact_realfraction = collisiontrace.fraction;
@@ -673,3 +675,53 @@
 #endif
 }
 
+void G_World_Trace_Move(G_Trace *trace, const Collision_Brush *brush, G_Position *position, Nvec timestep, const G_Entity *ignoreentity, NUint32 scopeflagsmask)
+{
+	NUint32 bumps;
+	Nvec t;
+	G_Position newposition;
+	G_Trace mytrace;
+	// rotation causes problems with the linear morphing traces we use, so if
+	// there is an impact we repeatedly retry the move to get the most correct
+	// rotation result at the time of impact
+	// (is this really a good approach?)
+	for (bumps = 0, t = timestep;bumps < 16;bumps++)
+	{
+		newposition = *position;
+		G_Position_Move(&newposition, t);
+		G_World_Trace_Brush(&mytrace, brush, &position->m, &newposition.m, ignoreentity, scopeflagsmask);
+		// see if we found a safe position
+		if (mytrace.impact_fraction == 1)
+		{
+			if (bumps == 0)
+				*trace = mytrace;
+			break;
+		}
+		// found a closer impact, update results
+		*trace = mytrace;
+		t = timestep * trace->impact_fraction;
+	}
+	// return the best position we found
+	*position = newposition;
+}
+
+void G_Position_Gravity(G_Position *position, Nvec timestep)
+{
+	position->velocity[0] += position->gravityacceleration[0] * timestep;
+	position->velocity[1] += position->gravityacceleration[1] * timestep;
+	position->velocity[2] += position->gravityacceleration[2] * timestep;
+}
+
+void G_Position_Move(G_Position *position, Nvec timestep)
+{
+	matrix4x4_t rotatematrix;
+	matrix4x4_t oldmatrix;
+	// rotation
+	Matrix4x4_CreateRotate(&rotatematrix, position->spin[3] * timestep, position->spin[0], position->spin[1], position->spin[2]);
+	oldmatrix = position->m;
+	Matrix4x4_Concat(&position->m, &rotatematrix, &oldmatrix);
+	// position
+	position->m.m[0][3] += position->velocity[0] * timestep;
+	position->m.m[1][3] += position->velocity[1] * timestep;
+	position->m.m[2][3] += position->velocity[2] * timestep;
+}

Modified: trunk/game/g_world.h
===================================================================
--- trunk/game/g_world.h	2007-01-14 23:58:12 UTC (rev 763)
+++ trunk/game/g_world.h	2007-01-16 15:19:42 UTC (rev 764)
@@ -4,6 +4,9 @@
 
 #include "game/g_main.h"
 
+void G_Position_Gravity(G_Position *position, Nvec timestep);
+void G_Position_Move(G_Position *position, Nvec timestep);
+
 // functions pertaining to the whole world, most of these involve networking
 // init/shutdown
 void G_World_Init(void);
@@ -35,11 +38,12 @@
 void G_World_NetworkUpdate(void);
 void G_World_Physics(Ndouble frametime);
 void G_World_SpawnUser(GS_User *user);
-void G_World_Trace_Brush(G_Trace *trace, Collision_Brush *brush, matrix4x4_t *startmatrix, matrix4x4_t *endmatrix, G_Entity *ignoreentity, NUint32 scopeflagsmask);
+void G_World_Trace_Brush(G_Trace *trace, const Collision_Brush *brush, const matrix4x4_t *startmatrix, const matrix4x4_t *endmatrix, const G_Entity *ignoreentity, NUint32 scopeflagsmask);
+void G_World_Trace_Move(G_Trace *trace, const Collision_Brush *brush, G_Position *position, Nvec timestep, const G_Entity *ignoreentity, NUint32 scopeflagsmask);
 // area queries
-NUint32 G_World_FindEntities_BoxAndSphere(G_Entity **entitylist, NUint32 maxentities, Nvec3 mins, Nvec3 maxs, Nvec3 center, Nvec radius, NUint32 scopeflagsmask);
-NUint32 G_World_FindEntities_Sphere(G_Entity **entitylist, NUint32 maxentities, Nvec3 center, Nvec radius, NUint32 scopeflagsmask);
-NUint32 G_World_FindEntities_Box(G_Entity **entitylist, NUint32 maxentities, Nvec3 mins, Nvec3 maxs, NUint32 scopeflagsmask);
+NUint32 G_World_FindEntities_BoxAndSphere(G_Entity **entitylist, NUint32 maxentities, const Nvec3 mins, const Nvec3 maxs, const Nvec3 center, Nvec radius, NUint32 scopeflagsmask);
+NUint32 G_World_FindEntities_Sphere(G_Entity **entitylist, NUint32 maxentities, const Nvec3 center, Nvec radius, NUint32 scopeflagsmask);
+NUint32 G_World_FindEntities_Box(G_Entity **entitylist, NUint32 maxentities, const Nvec3 mins, const Nvec3 maxs, NUint32 scopeflagsmask);
 
 #endif
 

Modified: trunk/r_main.c
===================================================================
--- trunk/r_main.c	2007-01-14 23:58:12 UTC (rev 763)
+++ trunk/r_main.c	2007-01-16 15:19:42 UTC (rev 764)
@@ -1458,8 +1458,8 @@
 		case R_DRAWMODE_SKY:
 			qglColorMask(1, 1, 1, 1);
 			qglDepthFunc(GL_LEQUAL);
-			qglDepthMask(GL_TRUE);
-			R_SetBlendFunc(GL_ONE, GL_ONE);
+			qglDepthMask(GL_FALSE);
+			R_SetBlendFunc(GL_ONE, GL_ZERO);
 
 			qglActiveTextureARB(GL_TEXTURE0_ARB);
 			qglEnable(GL_TEXTURE_2D);
@@ -1491,6 +1491,7 @@
 			qglDepthFunc(GL_LEQUAL);
 			qglDepthMask(GL_FALSE);
 			R_CheckError();
+			// FIXME: should use GL_ONE, GL_ZERO if the surface is opaque (faster than blend and hides zfighting artifacts at long range)
 			R_SetBlendFunc(GL_ONE, GL_ONE);
 
 			// fog darkening
@@ -1693,8 +1694,8 @@
 		case R_DRAWMODE_SKY:
 			qglColorMask(1, 1, 1, 1);
 			qglDepthFunc(GL_LEQUAL);
-			qglDepthMask(GL_TRUE);
-			R_SetBlendFunc(GL_ONE, GL_ONE);
+			qglDepthMask(GL_FALSE);
+			R_SetBlendFunc(GL_ONE, GL_ZERO);
 
 			qglActiveTextureARB(GL_TEXTURE0_ARB);
 			qglEnable(GL_TEXTURE_2D);
@@ -1726,6 +1727,7 @@
 			qglDepthFunc(GL_LEQUAL);
 			qglDepthMask(GL_FALSE);
 			R_CheckError();
+			// FIXME: should use GL_ONE, GL_ZERO if the surface is opaque (faster than blend and hides zfighting artifacts at long range)
 			R_SetBlendFunc(GL_ONE, GL_ONE);
 
 			// fog darkening
@@ -1899,7 +1901,7 @@
 	R_CheckError();
 }
 
-void R_DrawMesh(NUint32 materialresource, NUint32 meshindex, matrix4x4_t *transforms)
+void R_DrawMesh(NUint32 materialresource, NUint32 meshindex, const matrix4x4_t *transforms)
 {
 	NUint32 i, perm;
 	Model *model;

Modified: trunk/r_main.h
===================================================================
--- trunk/r_main.h	2007-01-14 23:58:12 UTC (rev 763)
+++ trunk/r_main.h	2007-01-16 15:19:42 UTC (rev 764)
@@ -268,7 +268,7 @@
 void R_SetFogTexture(NUint32 fogmaskresourceindex, NUint32 fogblendresourceindex, float fogrange);
 void R_ClearStencil(void);
 void R_SetDrawMode(R_DrawMode drawmode);
-void R_DrawMesh(NUint32 materialresource, NUint32 meshindex, matrix4x4_t *transforms);
+void R_DrawMesh(NUint32 materialresource, NUint32 meshindex, const matrix4x4_t *transforms);
 // R_DrawBox ONLY works with R_DRAWMODE_LINES.
 void R_DrawBox(Nvec3 boxmins, Nvec3 boxmaxs);
 

