Пример #1
0
/*
 * @brief The origin is typically calculated using client sided prediction, provided
 * the client is not viewing a demo, playing in 3rd person mode, or chasing
 * another player.
 */
static void Cl_UpdateOrigin(const player_state_t *from, const player_state_t *to) {

	if (Cl_UsePrediction()) {

		// use client sided prediction
		for (int32_t i = 0; i < 3; i++) {
			r_view.origin[i] = cl.predicted_state.origin[i] + cl.predicted_state.view_offset[i];
			r_view.origin[i] -= (1.0 - cl.lerp) * cl.predicted_state.error[i];
		}

		const uint32_t delta = cl.time - cl.predicted_state.step_time;
		const uint32_t interval = cl.predicted_state.step_interval;

		if (delta < interval) { // interpolate stair traversal
			const vec_t lerp = (interval - delta) / (vec_t) interval;
			r_view.origin[2] -= cl.predicted_state.step * lerp;
		}

	} else { // just use interpolated values from frame
		vec3_t origin;
		vec3_t from_offset, to_offset, offset;

		VectorLerp(from->pm_state.origin, to->pm_state.origin, cl.lerp, origin);

		UnpackVector(from->pm_state.view_offset, from_offset);
		UnpackVector(to->pm_state.view_offset, to_offset);

		VectorLerp(from_offset, to_offset, cl.lerp, offset);

		VectorAdd(origin, offset, r_view.origin);
	}

	// update the contents mask for e.g. under-water effects
	r_view.contents = Cl_PointContents(r_view.origin);
}
Пример #2
0
/*
 * @brief Run recent movement commands through the player movement code
 * locally, storing the resulting origin and angles so that they may be
 * interpolated to by Cl_UpdateView.
 */
void Cg_PredictMovement(const GList *cmds) {
	pm_move_t pm;

	// copy current state to into the move
	memset(&pm, 0, sizeof(pm));
	pm.s = cgi.client->frame.ps.pm_state;

	pm.ground_entity = cgi.client->predicted_state.ground_entity;

	pm.PointContents = cgi.PointContents;
	pm.Trace = Cg_PredictMovement_Trace;

	pm.Debug = Cg_PredictMovement_Debug;

	const GList *e = cmds;

	// run frames
	while (e) {
		const cl_cmd_t *cmd = (cl_cmd_t *) e->data;

		if (cmd->cmd.msec) {

			pm.cmd = cmd->cmd;
			Pm_Move(&pm);

			// for each movement, check for stair interaction and interpolate
			if (pm.s.flags & PMF_ON_STAIRS) {
				cgi.client->predicted_state.step_time = cmd->time;
				cgi.client->predicted_state.step_interval = 120 * (fabs(pm.step) / 16.0);
				cgi.client->predicted_state.step = pm.step;
			}

			// save for debug checking
			const uint32_t frame = (intptr_t) (cmd - cgi.client->cmds);
			VectorCopy(pm.s.origin, cgi.client->predicted_state.origins[frame]);
		}

		e = e->next;
	}

	// copy results out for rendering
	UnpackVector(pm.s.origin, cgi.client->predicted_state.origin);

	UnpackVector(pm.s.view_offset, cgi.client->predicted_state.view_offset);
	UnpackAngles(pm.cmd.angles, cgi.client->predicted_state.view_angles);

	cgi.client->predicted_state.ground_entity = pm.ground_entity;
}
Пример #3
0
/*
 * @brief Updates the r_view_t for the renderer. Origin, angles, etc are calculated.
 * Scene population is then delegated to the client game.
 */
void Cl_UpdateView(void) {

	if (!cl.frame.valid && !r_view.update)
		return; // not a valid frame, and no forced update

	// find the previous frame to interpolate from
	cl_frame_t *prev = &cl.frames[(cl.frame.frame_num - 1) & PACKET_MASK];

	if (prev->frame_num != cl.frame.frame_num - 1 || !prev->valid)
		prev = &cl.frame; // previous frame was dropped or invalid

	Cl_UpdateLerp(prev);

	const player_state_t *ps = &cl.frame.ps;
	const player_state_t *ops = &prev->ps;

	if (ps != ops) { // see if we've teleported
		vec3_t org, old_org, delta;

		UnpackVector(ps->pm_state.origin, org);
		UnpackVector(ops->pm_state.origin, old_org);

		VectorSubtract(org, old_org, delta);

		if (VectorLength(delta) > 256.0) {
			ops = ps; // don't interpolate
		}
	}

	Cl_ClearView();

	Cl_UpdateOrigin(ps, ops);

	Cl_UpdateAngles(ps, ops);

	Cl_UpdateViewSize();

	cls.cgame->UpdateView(&cl.frame);

	// set time
	r_view.time = cl.time;

	// set area bits to mark visible leafs
	r_view.area_bits = cl.frame.area_bits;

	// create the thread which populates the view
	r_view.thread = Thread_Create((ThreadRunFunc) cls.cgame->PopulateView, &cl.frame);
}
Пример #4
0
/*
 * @brief
 */
static void Cl_DrawCounters(void) {
	static vec3_t velocity;
	static char bps[8], pps[8], fps[8], spd[8];
	static int32_t last_draw_time;
	r_pixel_t cw, ch;

	if (!cl_draw_counters->value)
		return;

	R_BindFont("small", &cw, &ch);

	const r_pixel_t x = r_context.width - 7 * cw;
	r_pixel_t y = r_context.height - 4 * ch;

	cl.frame_counter++;

	if (cls.real_time - last_draw_time >= 200) {

		UnpackVector(cl.frame.ps.pm_state.velocity, velocity);
		velocity[2] = 0.0;

		g_snprintf(spd, sizeof(spd), "%4.0fspd", VectorLength(velocity));
		g_snprintf(fps, sizeof(fps), "%4ufps", cl.frame_counter * 5);
		g_snprintf(pps, sizeof(pps), "%4upps", cl.packet_counter * 5);
		g_snprintf(bps, sizeof(bps), "%4ubps", cl.byte_counter * 5);

		last_draw_time = quetoo.time;

		cl.frame_counter = 0;
		cl.packet_counter = 0;
		cl.byte_counter = 0;
	}

	R_DrawString(x, y, spd, CON_COLOR_DEFAULT);
	y += ch;

	R_DrawString(x, y, fps, CON_COLOR_DEFAULT);
	y += ch;

	R_DrawString(x, y, pps, CON_COLOR_DEFAULT);
	y += ch;

	R_DrawString(x, y, bps, CON_COLOR_DEFAULT);

	R_BindFont(NULL, NULL, NULL);
}
void FMD3Model::LoadGeometry()
{
	FMemLump lumpdata = Wads.ReadLump(mLumpNum);
	const char *buffer = (const char *)lumpdata.GetMem();
	md3_header_t * hdr = (md3_header_t *)buffer;
	md3_surface_t * surf = (md3_surface_t*)(buffer + LittleLong(hdr->Ofs_Surfaces));

	for(int i=0;i<numSurfaces;i++)
	{
		MD3Surface * s = &surfaces[i];
		md3_surface_t * ss = surf;

		surf = (md3_surface_t *)(((char*)surf) + LittleLong(surf->Ofs_End));

		// copy triangle indices
		md3_triangle_t * tris = (md3_triangle_t*)(((char*)ss)+LittleLong(ss->Ofs_Triangles));
		s->tris = new MD3Triangle[s->numTriangles];

		for(int i=0;i<s->numTriangles;i++) for (int j=0;j<3;j++)
		{
			s->tris[i].VertIndex[j]=LittleLong(tris[i].vt_index[j]);
		}

		// Load texture coordinates
		md3_texcoord_t * tc = (md3_texcoord_t*)(((char*)ss)+LittleLong(ss->Ofs_Texcoord));
		s->texcoords = new MD3TexCoord[s->numVertices];

		for(int i=0;i<s->numVertices;i++)
		{
			s->texcoords[i].s = tc[i].s;
			s->texcoords[i].t = tc[i].t;
		}

		// Load vertices and texture coordinates
		md3_vertex_t * vt = (md3_vertex_t*)(((char*)ss)+LittleLong(ss->Ofs_XYZNormal));
		s->vertices = new MD3Vertex[s->numVertices * numFrames];

		for(int i=0;i<s->numVertices * numFrames;i++)
		{
			s->vertices[i].x = LittleShort(vt[i].x)/64.f;
			s->vertices[i].y = LittleShort(vt[i].y)/64.f;
			s->vertices[i].z = LittleShort(vt[i].z)/64.f;
			UnpackVector( LittleShort(vt[i].n), s->vertices[i].nx, s->vertices[i].ny, s->vertices[i].nz);
		}
	}
}
Пример #6
0
/*
 * @brief Determines the initial position and directional vectors of a projectile.
 */
void G_InitProjectile(g_entity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t org) {
	vec3_t view, pos;

	// resolve the projectile destination
	UnpackVector(ent->client->ps.pm_state.view_offset, view);
	VectorAdd(ent->s.origin, view, view);

	VectorMA(view, MAX_WORLD_DIST, ent->client->locals.forward, pos);
	const cm_trace_t tr = gi.Trace(view, pos, NULL, NULL, ent, MASK_CLIP_PROJECTILE);

	VectorCopy(tr.end, pos);

	// resolve the projectile origin
	vec3_t ent_forward, ent_up;
	AngleVectors(ent->s.angles, ent_forward, NULL, ent_up);

	VectorMA(view, 12.0, ent_forward, org);

	if ((ent->client->ps.pm_state.flags & PMF_DUCKED)) {
		VectorMA(org, -6.0, ent_up, org);
	} else {
		VectorMA(org, -12.0, ent_up, org);
	}

	// if the projected origin is invalid, use the entity's origin
	if (gi.Trace(org, org, NULL, NULL, ent, MASK_CLIP_PROJECTILE).start_solid) {
		VectorCopy(ent->s.origin, org);
		gi.Print("Doh\n");
	}

	// return the projectile's directional vectors
	VectorSubtract(pos, org, forward);
	VectorNormalize(forward);

	if (right || up) {
		VectorAngles(forward, view);
		AngleVectors(view, NULL, right, up);
	}
}
Пример #7
0
/**
 * @brief Checks for client side prediction errors. These will occur under normal gameplay
 * conditions if the client is pushed by another entity on the server (projectile, platform, etc.).
 */
void Cl_CheckPredictionError(void) {

	if (!cls.cgame->UsePrediction()) {
		return;
	}

	cl_predicted_state_t *pr = &cl.predicted_state;

	if (cl.delta_frame) {

		// calculate the last cl_cmd_t we sent that the server has processed
		const uint32_t cmd = cls.net_chan.incoming_acknowledged & CMD_MASK;

		// subtract what the server returned with what we had predicted it to be
		VectorSubtract(cl.frame.ps.pm_state.origin, pr->origins[cmd], pr->error);

		// if the error is too large, it was likely a teleport or respawn, so ignore it
		const vec_t len = VectorLength(pr->error);
		if (len > 64.0) {
			Com_Debug(DEBUG_CLIENT, "Clear %s\n", vtos(pr->error));
			VectorClear(pr->error);
		} else if (len > 0.1) {
			Com_Debug(DEBUG_CLIENT, "Error %s\n", vtos(pr->error));
		}

	} else {
		Com_Debug(DEBUG_CLIENT, "No delta\n");

		VectorCopy(cl.frame.ps.pm_state.origin, pr->view.origin);

		UnpackVector(cl.frame.ps.pm_state.view_offset, pr->view.offset);
		UnpackAngles(cl.frame.ps.pm_state.view_angles, pr->view.angles);

		VectorClear(pr->error);
	}
}
Пример #8
0
bool FMD3Model::Load(const char * path, int, const char * buffer, int length)
{
	#pragma pack(4)
	struct md3_header_t
	{
		DWORD Magic;
		DWORD Version;
		char Name[MAX_QPATH];
		DWORD Flags;
		DWORD Num_Frames;
		DWORD Num_Tags;
		DWORD Num_Surfaces;
		DWORD Num_Skins;
		DWORD Ofs_Frames;
		DWORD Ofs_Tags;
		DWORD Ofs_Surfaces;
		DWORD Ofs_Eof;
	};

	struct md3_surface_t
	{
		DWORD Magic;
		char Name[MAX_QPATH];
		DWORD Flags;
		DWORD Num_Frames;
		DWORD Num_Shaders;
		DWORD Num_Verts;
		DWORD Num_Triangles;
		DWORD Ofs_Triangles;
		DWORD Ofs_Shaders;
		DWORD Ofs_Texcoord;
		DWORD Ofs_XYZNormal;
		DWORD Ofs_End;
	};

	struct md3_triangle_t
	{
		DWORD vt_index[3];
	};

	struct md3_shader_t
	{
		char Name[MAX_QPATH];
		DWORD index;
	};

	struct md3_texcoord_t
	{
		float s,t;
	};

	struct md3_vertex_t
	{
		short x,y,z,n;
	};

	struct md3_frame_t
	{
		float min_Bounds[3];
		float max_Bounds[3];
		float localorigin[3];
		float radius;
		char Name[16];
	};
	#pragma pack()

	md3_header_t * hdr=(md3_header_t *)buffer;

	numFrames = LittleLong(hdr->Num_Frames);
	numTags = LittleLong(hdr->Num_Tags);
	numSurfaces = LittleLong(hdr->Num_Surfaces);

	md3_frame_t * frm = (md3_frame_t*)(buffer + LittleLong(hdr->Ofs_Frames));

	frames = new MD3Frame[numFrames];
	for(int i=0;i<numFrames;i++)
	{
		strncpy(frames[i].Name, frm[i].Name, 16);
		for(int j=0;j<3;j++) frames[i].origin[j] = frm[i].localorigin[j];
	}

	md3_surface_t * surf = (md3_surface_t*)(buffer + LittleLong(hdr->Ofs_Surfaces));

	surfaces = new MD3Surface[numSurfaces];
	for(int i=0;i<numSurfaces;i++)
	{
		MD3Surface * s = &surfaces[i];
		md3_surface_t * ss = surf;

		surf = (md3_surface_t *)(((char*)surf) + LittleLong(surf->Ofs_End));

		s->numSkins = LittleLong(ss->Num_Shaders);
		s->numTriangles = LittleLong(ss->Num_Triangles);
		s->numVertices = LittleLong(ss->Num_Verts);

		// copy triangle indices
		md3_triangle_t * tris = (md3_triangle_t*)(((char*)ss)+LittleLong(ss->Ofs_Triangles));
		s->tris = new MD3Triangle[s->numTriangles];

		for(int i=0;i<s->numTriangles;i++) for (int j=0;j<3;j++)
		{
			s->tris[i].VertIndex[j]=LittleLong(tris[i].vt_index[j]);
		}

		// copy shaders (skins)
		md3_shader_t * shader = (md3_shader_t*)(((char*)ss)+LittleLong(ss->Ofs_Shaders));
		s->skins = new FTexture *[s->numSkins];

		for(int i=0;i<s->numSkins;i++)
		{
			// [BB] According to the MD3 spec, Name is supposed to include the full path.
			s->skins[i] = LoadSkin("", shader[i].Name);
			// [BB] Fall back and check if Name is relative.
			if ( s->skins[i] == NULL )
				s->skins[i] = LoadSkin(path, shader[i].Name);
		}

		// Load texture coordinates
		md3_texcoord_t * tc = (md3_texcoord_t*)(((char*)ss)+LittleLong(ss->Ofs_Texcoord));
		s->texcoords = new MD3TexCoord[s->numVertices];

		for(int i=0;i<s->numVertices;i++)
		{
			s->texcoords[i].s = tc[i].s;
			s->texcoords[i].t = tc[i].t;
		}

		// Load vertices and texture coordinates
		md3_vertex_t * vt = (md3_vertex_t*)(((char*)ss)+LittleLong(ss->Ofs_XYZNormal));
		s->vertices = new MD3Vertex[s->numVertices * numFrames];

		for(int i=0;i<s->numVertices * numFrames;i++)
		{
			s->vertices[i].x = LittleShort(vt[i].x)/64.f;
			s->vertices[i].y = LittleShort(vt[i].y)/64.f;
			s->vertices[i].z = LittleShort(vt[i].z)/64.f * rModelAspectMod;
			UnpackVector( LittleShort(vt[i].n), s->vertices[i].nx, s->vertices[i].ny, s->vertices[i].nz);
		}
	}
	return true;
}
Пример #9
0
/**
 * @brief Decides which entities are going to be visible to the client, and
 * copies off the player state and area_bits.
 */
void Sv_BuildClientFrame(sv_client_t *client) {
	vec3_t org, off;

	g_entity_t *cent = client->entity;
	if (!cent->client) {
		return;    // not in game yet
	}

	// this is the frame we are creating
	sv_frame_t *frame = &client->frames[sv.frame_num & PACKET_MASK];
	frame->sent_time = quetoo.ticks; // timestamp for ping calculation

	// grab the current player_state_t
	frame->ps = cent->client->ps;

	// find the client's PVS
	const pm_state_t *pm = &cent->client->ps.pm_state;
	UnpackVector(pm->view_offset, off);
	VectorAdd(pm->origin, off, org);

	const int32_t leaf = Cm_PointLeafnum(org, 0);
	const int32_t area = Cm_LeafArea(leaf);

	// calculate the visible areas
	frame->area_bytes = Cm_WriteAreaBits(area, frame->area_bits);

	// resolve the visibility data
	byte pvs[MAX_BSP_LEAFS >> 3], phs[MAX_BSP_LEAFS >> 3];
	Sv_ClientVisibility(org, pvs, phs);

	// build up the list of relevant entities
	frame->num_entities = 0;
	frame->entity_state = svs.next_entity_state;

	for (uint16_t e = 1; e < svs.game->num_entities; e++) {
		g_entity_t *ent = ENTITY_FOR_NUM(e);

		// ignore entities that are local to the server
		if (ent->sv_flags & SVF_NO_CLIENT) {
			continue;
		}

		// ignore entities without visible presence unless they have an effect
		if (!ent->s.event && !ent->s.effects && !ent->s.trail && !ent->s.model1 && !ent->s.sound) {
			continue;
		}

		// ignore entities not in PVS / PHS
		if (ent != cent) {
			const sv_entity_t *sent = &sv.entities[e];

			// by first checking area
			if (!Cm_AreasConnected(area, sent->areas[0])) {
				if (!sent->areas[1] || !Cm_AreasConnected(area, sent->areas[1])) {
					continue;
				}
			}

			const byte *vis = ent->s.sound || ent->s.event ? phs : pvs;

			if (sent->num_clusters == -1) { // use top_node
				if (!Cm_HeadnodeVisible(sent->top_node, vis)) {
					continue;
				}
			} else { // or check individual leafs
				int32_t i;
				for (i = 0; i < sent->num_clusters; i++) {
					const int32_t c = sent->clusters[i];
					if (vis[c >> 3] & (1 << (c & 7))) {
						break;
					}
				}
				if (i == sent->num_clusters) {
					continue;    // not visible
				}
			}
		}

		// copy it to the circular entity_state_t array
		entity_state_t *s = &svs.entity_states[svs.next_entity_state % svs.num_entity_states];
		if (ent->s.number != e) {
			Com_Warn("Fixing entity number: %d -> %d\n", ent->s.number, e);
			ent->s.number = e;
		}
		*s = ent->s;

		// don't mark our own missiles as solid for prediction
		if (ent->owner == client->entity) {
			s->solid = SOLID_NOT;
		}

		svs.next_entity_state++;
		frame->num_entities++;
	}
}