예제 #1
0
파일: sv_main.cpp 프로젝트: RkShaRkz/Quake2
/*
===============
SV_StatusString

Builds the string that is sent as heartbeats and status replies
===============
*/
static const char *SV_StatusString()
{
	guard(SV_StatusString);
	static char	status[MAX_MSGLEN - 16];

	if (sv.attractloop) return "";

	int statusLength = appSprintf(ARRAY_ARG(status), "%s\n", Cvar_BitInfo(CVAR_SERVERINFO));
	for (int i = 0; i < sv_maxclients->integer; i++)
	{
		client_t *cl = &svs.clients[i];
		if (cl->state == cs_connected || cl->state == cs_spawned)
		{
			char player[256];
			int playerLength = appSprintf(ARRAY_ARG(player), "%d %d \"%s\"\n",
				cl->edict->client->ps.stats[STAT_FRAGS], cl->ping, *cl->Name);
			if (statusLength + playerLength >= sizeof(status))
				break;		// can't hold any more
			memcpy(status + statusLength, player, playerLength+1);
			statusLength += playerLength;
		}
	}

	return status;
	unguard;
}
예제 #2
0
파일: CoreWin32.cpp 프로젝트: UIKit0/UModel
const char *appSymbolName(address_t addr)
{
	static char	buf[256];

#if USE_DBGHELP
	if (appSymbolName(addr, ARRAY_ARG(buf)))
		return buf;
#endif

#if GET_EXTENDED_INFO
	HMODULE hModule = NULL;
	char moduleName[256];
	char *s;

	MEMORY_BASIC_INFORMATION mbi;
	if (!VirtualQuery((void*)addr, &mbi, sizeof(mbi)))
		goto simple;
	if (!(hModule = (HMODULE)mbi.AllocationBase))
		goto simple;
	if (!GetModuleFileName(hModule, ARRAY_ARG(moduleName)))
		goto simple;

//	if (s = strrchr(moduleName, '.'))	// cut extension
//		*s = 0;
	if (s = strrchr(moduleName, '\\'))
		strcpy(moduleName, s+1);		// remove "path\" part
	appSprintf(ARRAY_ARG(buf), "%s+0x%X", moduleName, (int)(addr - (size_t)hModule));
	return buf;
#endif // GET_EXTENDED_INFO

simple:
	appSprintf(ARRAY_ARG(buf), "%08X", addr);
	return buf;
}
예제 #3
0
bool UIProgressDialog::Tick()
{
	char buffer[64];
	appSprintf(ARRAY_ARG(buffer), "%d MBytes", (int)(GTotalAllocationSize >> 20));
	MemoryLabel->SetText(buffer);
	appSprintf(ARRAY_ARG(buffer), "%d", UObject::GObjObjects.Num());
	ObjectsLabel->SetText(buffer);
	return PumpMessages();
}
예제 #4
0
static bool ScanGameDirectory(const char *dir, bool recurse)
{
	guard(ScanGameDirectory);

	char Path[MAX_PACKAGE_PATH];
	bool res = true;
//	printf("Scan %s\n", dir);
#if _WIN32
	appSprintf(ARRAY_ARG(Path), "%s/*.*", dir);
	_finddatai64_t found;
	long hFind = _findfirsti64(Path, &found);
	if (hFind == -1) return true;
	do
	{
		if (found.name[0] == '.') continue;			// "." or ".."
		appSprintf(ARRAY_ARG(Path), "%s/%s", dir, found.name);
		// directory -> recurse
		if (found.attrib & _A_SUBDIR)
		{
			if (recurse)
				res = ScanGameDirectory(Path, recurse);
			else
				res = true;
		}
		else
			res = RegisterGameFile(Path);
	} while (res && _findnexti64(hFind, &found) != -1);
	_findclose(hFind);
#else
	DIR *find = opendir(dir);
	if (!find) return true;
	struct dirent *ent;
	while (/*res &&*/ (ent = readdir(find)))
	{
		if (ent->d_name[0] == '.') continue;			// "." or ".."
		appSprintf(ARRAY_ARG(Path), "%s/%s", dir, ent->d_name);
		// directory -> recurse
		// note: using 'stat64' here because 'stat' ignores large files
		struct stat64 buf;
		if (stat64(Path, &buf) < 0) continue;			// or break?
		if (S_ISDIR(buf.st_mode))
		{
			if (recurse)
				res = ScanGameDirectory(Path, recurse);
			else
				res = true;
		}
		else
			res = RegisterGameFile(Path);
	}
	closedir(find);
#endif
	return res;

	unguard;
}
예제 #5
0
파일: Core.cpp 프로젝트: hui211314dd/UModel
void appUnwindPrefix(const char *fmt)
{
	char buf[512];
	appSprintf(ARRAY_ARG(buf), WasError ? " <- %s:" : "%s:", fmt);
	LogHistory(buf);
	WasError = false;
}
예제 #6
0
파일: gl_font.cpp 프로젝트: RkShaRkz/Quake2
bool CFont::Load(const char *name)
{
	TString<64> Filename;
	Filename.sprintf("fonts/%s.font", name);
	char* buf = (char*)GFileSystem->LoadFile(Filename);
	if (!buf)
	{
		appWPrintf("File %s does not exist\n", *Filename);
		return false;
	}
	CSimpleParser text;
	text.InitFromBuf(buf);
	loadingFont = this;
	while (const char *line = text.GetLine())
	{
		if (!ExecuteCommand(line, ARRAY_ARG(fontCommands)))
			appWPrintf("%s: invalid line [%s]\n", name, line);
	}
	// setup remaining fields
	Name    = name;
	spacing = FONT_SPACING;

	delete buf;

	return true;
}
예제 #7
0
static bool LoadWeaponInfo(const char *filename, weaponInfo_t &weap)
{
	char *buf = (char*) GFileSystem->LoadFile(va("models/weapons/%s.cfg", filename));
	if (!buf) return false;

	memset(&weap, 0, sizeof(weaponInfo_t));
	loadingWeap = &weap;
	// defaults
	weap.drawScale = 1;
	bool result = true;
	CSimpleParser text;
	text.InitFromBuf(buf);
	while (const char *line = text.GetLine())
	{
		if (!ExecuteCommand(line, ARRAY_ARG(weapCommands)))
		{
			appWPrintf("Invalid line [%s] in \"%s.cfg\"\n", line, filename);
			result = false;
			break;
		}
	}
	weap.origin.Scale(weap.drawScale);
	delete buf;
	return result && weap.model != NULL;
	//?? NOTE: currently, "false" result ignored ...
}
예제 #8
0
int SV_PointContents(const CVec3 &p)
{
	guard(SV_PointContents);

	// get base contents from world
	unsigned contents = CM_PointContents(p, 0);
	contents |= CM_PointModelContents(p);

	edict_t	*list[MAX_EDICTS];
	int num = SV_AreaEdicts(p, p, ARRAY_ARG(list), AREA_SOLID);

	for (int i = 0; i < num; i++)
	{
		edict_t *edict = list[i];
		entityHull_t &ent = ents[NUM_FOR_EDICT(edict)];

		if (ent.model)
			contents |= CM_TransformedPointContents(p, ent.model->headnode, edict->s.origin, ent.axis);
		else
			contents |= CM_TransformedPointContents(p, CM_HeadnodeForBox(ent.bounds), edict->s.origin, nullVec3);
	}
	return contents;

	unguard;
}
예제 #9
0
파일: cl_view.cpp 프로젝트: RkShaRkz/Quake2
void V_Init()
{
CVAR_BEGIN(vars)
#if !NO_DEBUG
	CVAR_VAR(cl_testblend, 0, 0),
	CVAR_VAR(cl_testentities, 0, 0),
	CVAR_VAR(cl_testlights, 0, CVAR_CHEAT),

	CVAR_VAR(r_playerpos, 0, CVAR_CHEAT),
	CVAR_VAR(r_surfinfo, 0, CVAR_CHEAT),
	CVAR_VAR(r_showBrush, -1, CVAR_CHEAT),
#endif // NO_DEBUG

	CVAR_VAR(r_drawfps, 0, 0),
	CVAR_VAR(scr_viewsize, 100, CVAR_ARCHIVE)	//?? should rename: not scr var
CVAR_END

	Cvar_GetVars(ARRAY_ARG(vars));

#if GUN_DEBUG
	RegisterCommand("gun_next",  Gun_Next_f);
	RegisterCommand("gun_prev",  Gun_Prev_f);
	RegisterCommand("gun_model", Gun_Model_f);
#endif
#if !NO_DEBUG
	RegisterCommand("setlight", SetLight_f);
#endif
	RegisterCommand("sky", Sky_f);
}
예제 #10
0
const char *appTimestamp()
{
	static char ctime[64];
	time_t itime;
	time(&itime);
	strftime(ARRAY_ARG(ctime), "%b %d %Y %H:%M:%S", localtime(&itime));
	return ctime;
}
예제 #11
0
파일: sv_main.cpp 프로젝트: RkShaRkz/Quake2
void SV_InitVars()
{
CVAR_BEGIN(vars)
	// LATCH cvars (note: all are SERVERINFO)
	CVAR_FULL(&sv_deathmatch, "deathmatch", "0", CVAR_SERVERINFO|CVAR_LATCH),
	CVAR_FULL(&sv_coop, "coop", "0", CVAR_SERVERINFO|CVAR_LATCH),
	CVAR_FULL(&sv_maxclients, "maxclients", "1", CVAR_SERVERINFO|CVAR_LATCH),
	CVAR_VAR(sv_extProtocol, 1, CVAR_SERVERINFO|CVAR_LATCH),
	// cvars from outside the server: here for LATCH updating only
	//?? may be, move this cvars here COMPLETELY?
	CVAR_FULL(NULL, "cheats", "", CVAR_SERVERINFO|CVAR_LATCH|CVAR_NODEFAULT),
	CVAR_FULL(NULL, "game", "", CVAR_SERVERINFO|CVAR_LATCH|CVAR_NODEFAULT),
	// other SERVERINFO vars
	CVAR_NULL(fraglimit,  0, CVAR_SERVERINFO),
	CVAR_NULL(timelimit,  0, CVAR_SERVERINFO),
	CVAR_VAR(hostname, noname, CVAR_SERVERINFO),
	CVAR_NULL(dmflags, 16, CVAR_SERVERINFO),		//?? old default value: 0 for game, DF_INSTANT_ITEMS (16) for server
	CVAR_VAR(sv_camperSounds, 1, CVAR_SERVERINFO),
	// read-only SERVERINFO
	CVAR_FULL(NULL, "protocol", STR(PROTOCOL_VERSION), CVAR_SERVERINFO|CVAR_NOSET),
	// non-archive LATCH vars
	CVAR_VAR(sv_airaccelerate, 0, CVAR_LATCH),		// send: CS_AIRACCEL -> CL_PredictMovement() -> pm_airaccelerate;
													// no other use (including server/game!)

	CVAR_VAR(timeout, 125, 0),
	CVAR_VAR(zombietime, 2, 0),

	CVAR_FULL(&rcon_password, "rcon_password", "", 0),
	CVAR_NULL(skill, 1, 0),

	CVAR_FULL(&sv_showclamp, "showclamp", "0", 0),
	CVAR_FULL(&sv_paused, "paused", "0", CVAR_CHEAT),
	CVAR_VAR(allow_download, 1, CVAR_ARCHIVE),
	CVAR_VAR(allow_download_players, 0, CVAR_ARCHIVE),
	CVAR_VAR(allow_download_models, 1, CVAR_ARCHIVE),
	CVAR_VAR(allow_download_sounds, 1, CVAR_ARCHIVE),
	CVAR_VAR(allow_download_maps, 1, CVAR_ARCHIVE),

	CVAR_VAR(sv_enforcetime, 0, 0),					//??
	CVAR_VAR(sv_noreload, 0, 0),					//??

	CVAR_FULL(&public_server, "public", "0", 0),
	CVAR_VAR(sv_shownet, 0, 0),

	CVAR_VAR(sv_reconnect_limit, 3, CVAR_ARCHIVE),

#if MAX_DEBUG
	CVAR_VAR(sv_labels, 0, CVAR_CHEAT),
#endif
#if FPU_EXCEPTIONS
	CVAR_VAR(g_fpuXcpt, 0, CVAR_ARCHIVE),
#endif
//	CVAR_VAR(sv_fps, 20, 0)	// archive/serverinfo ??
CVAR_END
	Cvar_GetVars(ARRAY_ARG(vars));
}
예제 #12
0
파일: CoreWin32.cpp 프로젝트: UIKit0/UModel
bool appSymbolName(address_t addr, char *buffer, int size)
{
	InitSymbols();

	char SymBuffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
	PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)SymBuffer;
	pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
	pSymbol->MaxNameLen   = MAX_SYM_NAME;

	DWORD64 dwDisplacement = 0;
	if (SymFromAddr(hProcess, addr, &dwDisplacement, pSymbol))
	{
		char OffsetBuffer[32];
		if (dwDisplacement)
			appSprintf(ARRAY_ARG(OffsetBuffer), "+%X", dwDisplacement);
		else
			OffsetBuffer[0] = 0;

#if EXTRA_UNDECORATE
		char undecBuffer[256];
		if (UnDecorateSymbolName(pSymbol->Name, ARRAY_ARG(undecBuffer),
			UNDNAME_NO_LEADING_UNDERSCORES|UNDNAME_NO_LEADING_UNDERSCORES|UNDNAME_NO_ALLOCATION_LANGUAGE|UNDNAME_NO_ACCESS_SPECIFIERS))
		{
			StripPrefix(undecBuffer, "virtual ");
			StripPrefix(undecBuffer, "class ");
			StripPrefix(undecBuffer, "struct ");
			appSprintf(buffer, size, "%s%s", undecBuffer, OffsetBuffer);
		}
		else
		{
			appSprintf(buffer, size, "%s%s", pSymbol->Name, OffsetBuffer);
		}
#else
		appSprintf(buffer, size, "%s%s", pSymbol->Name, OffsetBuffer);
#endif // EXTRA_UNDECORATE
	}
	else
	{
		appSprintf(buffer, size, "%08X", addr);
	}
	return true;
}
예제 #13
0
파일: fontgen.cpp 프로젝트: gildor2/Quake2
void Error(char *fmt, ...)
{
    va_list	argptr;
    va_start(argptr, fmt);
    char buf[4096];
    int len = _vsnprintf(ARRAY_ARG(buf), fmt, argptr);
    va_end(argptr);
    if (len < 0 || len >= sizeof(buf) - 1) exit(1);
    puts(buf);
    exit(1);
}
예제 #14
0
파일: Core.cpp 프로젝트: hui211314dd/UModel
void appSetNotifyHeader(const char *fmt, ...)
{
	if (!fmt)
	{
		NotifyBuf[0] = 0;
		return;
	}
	va_list	argptr;
	va_start(argptr, fmt);
	vsnprintf(ARRAY_ARG(NotifyBuf), fmt, argptr);
	va_end(argptr);
}
예제 #15
0
파일: Core.cpp 프로젝트: hui211314dd/UModel
void appError(const char *fmt, ...)
{
	va_list	argptr;
	va_start(argptr, fmt);
	char buf[4096];
	int len = vsnprintf(ARRAY_ARG(buf), fmt, argptr);
	va_end(argptr);
	assert(len >= 0 && len < ARRAY_COUNT(buf) - 1);

	GIsSwError = true;

#if DO_GUARD
//	appNotify("ERROR: %s\n", buf);
	strcpy(GErrorHistory, buf);
	appStrcatn(ARRAY_ARG(GErrorHistory), "\n");
	THROW;
#else
	fprintf(stderr, "Fatal Error: %s\n", buf);
	if (GLogFile) fprintf(GLogFile, "Fatal Error: %s\n", buf);
	exit(1);
#endif
}
예제 #16
0
const char* GetGameTag(int gameEnum)
{
	static char buf[64];

	int Count = ARRAY_COUNT(GListOfGames) - 1;	// exclude TABLE_END marker
	const char* value = NULL;
	for (int i = 0; i < Count; i++)
	{
		if (GListOfGames[i].Enum == gameEnum)
		{
			value = GListOfGames[i].Switch;
			break;
		}
	}
#if UNREAL4
	if (!value && gameEnum >= GAME_UE4_BASE)
	{
		// generate tag
		int ue4ver = GAME_UE4_GET_MINOR(gameEnum);
		if (gameEnum == GAME_UE4(ue4ver))
		{
			// exactly matching, i.e. not a custom UE4 version
			appSprintf(ARRAY_ARG(buf), "ue4.%d", ue4ver);
			return buf;
		}
	}
#endif // UNREAL4

	if (!value)
	{
		appSprintf(ARRAY_ARG(buf), "%X", gameEnum);
		return buf;
	}

	return value;
}
예제 #17
0
FArchive *appCreateFileReader(const CGameFileInfo *info)
{
	if (!info->FileSystem)
	{
		// regular file
		char buf[MAX_PACKAGE_PATH];
		appSprintf(ARRAY_ARG(buf), "%s/%s", RootDirectory, info->RelativeName);
		return new FFileReader(buf);
	}
	else
	{
		// file from virtual file system
		return info->FileSystem->CreateReader(info->RelativeName);
	}
}
예제 #18
0
void InitFileSystem()
{
	guard(InitFileSystem);

	// initialize interface
CVAR_BEGIN(vars)
	CVAR_FULL(&fs_configfile, "cfgfile", CONFIGNAME, CVAR_NOSET),	//?? config system will be changed later
	// cddir <path>	-- allow game to be partially installed - read missing data from CD
	CVAR_FULL(&fs_cddir, "cddir", "", CVAR_NOSET),
	CVAR_FULL(&fs_game, "game", "", CVAR_SERVERINFO|CVAR_LATCH),
	CVAR_VAR(fs_logFile, 0, 0)
CVAR_END
	Cvar_GetVars(ARRAY_ARG(vars));

	CFileContainer *Mnt;

	FSLog = GNull;
	appInitFileSystem(FS);
	CFileSystem::RegisterFormat(CFileContainerPak::Create);
	CFileSystem::RegisterFormat(CFileContainerZip::Create);
#if !DEDICATED_ONLY
	// mount resources
	Mnt = CZResource::Mount("resources", zresource, ".");
	Mnt->locked = true;
#endif
	// mount CDROM
	if (fs_cddir->string[0])
	{
		Mnt = FS.MountDirectory(fs_cddir->string);
		Mnt->locked = true;
		appPrintf("FS: using \"%s\" as CDROM image\n", fs_cddir->string);
	}
	// mount root file container
	Mnt = FS.MountDirectory(".");
	Mnt->locked = true;
	// mount basedir
	GDefMountPoint = BASEDIRNAME;
	FS.GameDir = BASEDIRNAME;
	FS.MountGame(BASEDIRNAME);
	// change game, if needed
	FS.SetGameDir(fs_game->string);
	// load configuration
	LoadGameConfig();

	unguard;
}
예제 #19
0
파일: Core.cpp 프로젝트: hui211314dd/UModel
void appPrintf(const char *fmt, ...)
{
	va_list	argptr;
	va_start(argptr, fmt);
	char buf[4096];
	int len = vsnprintf(ARRAY_ARG(buf), fmt, argptr);
	va_end(argptr);
	assert(len >= 0 && len < ARRAY_COUNT(buf) - 1);

	fwrite(buf, len, 1, stdout);
	if (GLogFile) fwrite(buf, len, 1, GLogFile);

#if VSTUDIO_INTEGRATION
	if (IsDebuggerPresent())
		OutputDebugString(buf);
#endif
}
예제 #20
0
bool UIProgressDialog::Progress(const char* package, int index, int total)
{
	// do not update UI too often
	int tick = appMilliseconds();
	if (tick - lastTick < 50)
		return true;
	lastTick = tick;

	char buffer[512];
	appSprintf(ARRAY_ARG(buffer), "%s %d/%d", DescriptionText, index+1, total);
	DescriptionLabel->SetText(buffer);

	PackageLabel->SetText(package);

	ProgressBar->SetValue((float)(index+1) / total);

	return Tick();
}
예제 #21
0
파일: snd_dma.cpp 프로젝트: RkShaRkz/Quake2
void S_Init(void)
{
	cvar_t *cv = Cvar_Get("nosound", "0", 0);
	if (cv->integer)
		appPrintf(S_CYAN"Sound disabled\n");
	else
	{
CVAR_BEGIN(vars)
		CVAR_VAR(s_volume, 0.7, CVAR_ARCHIVE),
		CVAR_VAR(s_khz, 22, CVAR_ARCHIVE),
		CVAR_VAR(s_loadas8bit, 0, CVAR_ARCHIVE),
		CVAR_VAR(s_reverse_stereo, 0, CVAR_ARCHIVE),
		CVAR_VAR(s_mixahead, 0.2, CVAR_ARCHIVE),
		CVAR_VAR(s_show, 0, 0),
		CVAR_VAR(s_testsound, 0, 0),
		CVAR_VAR(s_primary, 0, CVAR_ARCHIVE)		//?? win32 specific
CVAR_END

		Cvar_GetVars(ARRAY_ARG(vars));

		appPrintf("\n------- Sound initialization -------\n");

		if (!SNDDMA_Init()) return;

		RegisterCommand("play", S_Play_f);
		RegisterCommand("stopsound", S_StopAllSounds_f);
		RegisterCommand("soundlist", S_SoundList_f);
		RegisterCommand("soundinfo", S_SoundInfo_f);

		S_InitScaletable();

		sound_started = true;
		num_sfx = 0;

		soundtime   = 0;
		paintedtime = 0;

		appPrintf("sound sampling rate: %d\n", dma.speed);

		S_StopAllSounds_f();

		appPrintf("------------------------------------\n");
	}
}
예제 #22
0
파일: cl_view.cpp 프로젝트: RkShaRkz/Quake2
static void DecodeContents(int i)
{
	static const flagInfo_t contentsNames[] = {
#define T(name)		{CONTENTS_##name, #name}
		T(SOLID),	T(WINDOW),		T(AUX),		T(LAVA),	T(SLIME),		T(WATER),
		T(MIST),	T(ALPHA),		T(AREAPORTAL),	T(PLAYERCLIP),	T(MONSTERCLIP),
		T(CURRENT_0),	T(CURRENT_90),	T(CURRENT_180),	T(CURRENT_270),
		T(CURRENT_UP),	T(CURRENT_DOWN),T(ORIGIN),		T(MONSTER),
		T(DEADMONSTER),	T(DETAIL),		T(TRANSLUCENT),	T(LADDER)
#undef T
	};

//	RE_DrawTextLeft("Contents:", RGB(0.4, 0.4, 0.6));
//	RE_DrawTextLeft("---------", RGB(0.4, 0.4, 0.6));
	if (!i)
		RE_DrawTextLeft("CONTENTS_EMPTY", RGB(0.3, 0.6, 0.4));
	else
		DrawFlag(i, ARRAY_ARG(contentsNames), "CONTENTS_");
}
예제 #23
0
static void ExportMaterial(UUnrealMaterial* Mat, FArchive& Ar, int index, bool bLast)
{
	char dummyName[64];
	appSprintf(ARRAY_ARG(dummyName), "dummy_material_%d", index);

	CVec3 Color = GetMaterialDebugColor(index);
	Ar.Printf(
		"    {\n"
		"      \"name\" : \"%s\",\n"
		"      \"pbrMetallicRoughness\" : {\n"
		"        \"baseColorFactor\" : [ %g, %g, %g, 1.0 ],\n"
		"        \"metallicFactor\" : 0.1,\n"
		"        \"roughnessFactor\" : 0.5\n"
		"      }\n"
		"    }%s\n",
		Mat ? Mat->Name : dummyName,
		Color[0], Color[1], Color[2],
		bLast ? "" : ","
	);
}
예제 #24
0
파일: sv_main.cpp 프로젝트: RkShaRkz/Quake2
static void SV_ConnectionlessPacket()
{
	guard(SV_ConnectionlessPacket);
	net_message.BeginReading();
	MSG_ReadLong(&net_message);		// skip the -1 marker

	const char *s = MSG_ReadString(&net_message);
	//?? "s" may be too long (userinfo etc)
	if (sv_shownet->integer)
		appPrintf("SV: packet from %s: %s\n", NET_AdrToString(&net_from), s);

	if (SV_AddressBanned(&net_from))
	{
		Com_DPrintf("... banned\n");
		return;
	}

	if (!ExecuteCommand(s, ARRAY_ARG(connectionlessCmds)))
		appWPrintf("Bad connectionless packet from %s: \"%s\"\n", NET_AdrToString(&net_from), s);
	unguard;
}
예제 #25
0
파일: Core.cpp 프로젝트: hui211314dd/UModel
void appNotify(const char *fmt, ...)
{
	va_list	argptr;
	va_start(argptr, fmt);
	char buf[4096];
	int len = vsnprintf(ARRAY_ARG(buf), fmt, argptr);
	va_end(argptr);
	assert(len >= 0 && len < ARRAY_COUNT(buf) - 1);

	fflush(stdout);

	// a bit ugly code: printing the same thing into 3 streams

	// print to notify file
	if (FILE *f = fopen("notify.log", "a"))
	{
		if (NotifyBuf[0])
			fprintf(f, "\n******** %s ********\n\n", NotifyBuf);
		fprintf(f, "%s\n", buf);
		fclose(f);
	}
	// print to log file
	if (GLogFile)
	{
		if (NotifyBuf[0])
			fprintf(GLogFile, "******** %s ********\n", NotifyBuf);
		fprintf(GLogFile, "*** %s\n", buf);
		fflush(GLogFile);
	}
	// print to console
	if (NotifyBuf[0])
		fprintf(stderr, "******** %s ********\n", NotifyBuf);
	fprintf(stderr, "*** %s\n", buf);
	fflush(stderr);
	// clean notify header
	NotifyBuf[0] = 0;
}
예제 #26
0
static bool RegisterGameFile(const char *FullName, FVirtualFileSystem* parentVfs = NULL)
{
	guard(RegisterGameFile);

//	printf("..file %s\n", FullName);
	// return false when MAX_GAME_FILES
	if (GNumGameFiles >= ARRAY_COUNT(GameFiles))
		return false;

	if (!parentVfs)		// no nested VFSs
	{
		const char* ext = strrchr(FullName, '.');
		if (ext)
		{
			guard(MountVFS);

			ext++;
			FVirtualFileSystem* vfs = NULL;
			FArchive* reader = NULL;

#if SUPPORT_ANDROID
			if (!stricmp(ext, "obb"))
			{
				GForcePlatform = PLATFORM_ANDROID;
				reader = new FFileReader(FullName);
				if (!reader) return true;
				reader->Game = GAME_UE3;
				vfs = new FObbVFS();
			}
#endif // SUPPORT_ANDROID
#if UNREAL4
			if (!stricmp(ext, "pak"))
			{
				reader = new FFileReader(FullName);
				if (!reader) return true;
				reader->Game = GAME_UE4;
				vfs = new FPakVFS();
				//!! detect game by file name
			}
#endif // UNREAL4
			//!! process other VFS types here
			if (vfs)
			{
				assert(reader);
				// read VF directory
				if (!vfs->AttachReader(reader))
				{
					// something goes wrong
					delete vfs;
					delete reader;
					return true;
				}
				// add game files
				int NumVFSFiles = vfs->NumFiles();
				for (int i = 0; i < NumVFSFiles; i++)
				{
					if (!RegisterGameFile(vfs->FileName(i), vfs))
						return false;
				}
				return true;
			}

			unguard;
		}
	}

	bool IsPackage;
	if (FindExtension(FullName, ARRAY_ARG(PackageExtensions)))
	{
		IsPackage = true;
	}
	else
	{
#if HAS_SUPORT_FILES
		if (!FindExtension(FullName, ARRAY_ARG(KnownExtensions)))
#endif
		{
			// perhaps this file was exported by our tool - skip it
			if (FindExtension(FullName, ARRAY_ARG(SkipExtensions)))
				return true;
			// unknown file type
			if (++GNumForeignFiles >= MAX_FOREIGN_FILES)
				appError("Too much unknown files - bad root directory (%s)?", RootDirectory);
			return true;
		}
		IsPackage = false;
	}

	// create entry
	CGameFileInfo *info = new CGameFileInfo;
	GameFiles[GNumGameFiles++] = info;
	info->IsPackage = IsPackage;
	info->FileSystem = parentVfs;
	if (IsPackage) GNumPackageFiles++;

	if (!parentVfs)
	{
		// regular file
		FILE* f = fopen(FullName, "rb");
		if (f)
		{
			fseek(f, 0, SEEK_END);
			info->SizeInKb = (ftell(f) + 512) / 1024;
			fclose(f);
		}
		else
		{
			info->SizeInKb = 0;
		}
		// cut RootDirectory from filename
		const char *s = FullName + strlen(RootDirectory) + 1;
		assert(s[-1] == '/');
		appStrncpyz(info->RelativeName, s, ARRAY_COUNT(info->RelativeName));
	}
	else
	{
		// file in virtual file system
		info->SizeInKb = parentVfs->GetFileSize(FullName);
		appStrncpyz(info->RelativeName, FullName, ARRAY_COUNT(info->RelativeName));
	}

	// find filename
	const char* s = strrchr(info->RelativeName, '/');
	if (s) s++; else s = info->RelativeName;
	info->ShortFilename = s;
	// find extension
	s = strrchr(info->ShortFilename, '.');
	if (s) s++;
	info->Extension = s;
//	printf("..  -> %s (pkg=%d)\n", info->ShortFilename, info->IsPackage);

#if UNREAL3
	if (info->IsPackage && (strnicmp(info->ShortFilename, "startup", 7) == 0))
	{
		// Register a startup package
		// possible name variants:
		// - startup
		// - startup_int
		// - startup_*
		int startupWeight = 0;
		if (info->ShortFilename[7] == '.')
			startupWeight = 30;							// "startup.upk"
		else if (strnicmp(info->ShortFilename+7, "_int.", 5) == 0)
			startupWeight = 20;							// "startup_int.upk"
		else if (strnicmp(info->ShortFilename+7, "_loc_int.", 9) == 0)
			startupWeight = 20;							// "startup_int.upk"
		else if (info->ShortFilename[7] == '_')
			startupWeight = 1;							// non-int locale, lower priority - use if when other is not detected
		if (startupWeight > GStartupPackageInfoWeight)
		{
			GStartupPackageInfoWeight = startupWeight;
			GStartupPackageInfo = info;
		}
	}
#endif // UNREAL3

	// insert CGameFileInfo into hash table
	int hash = GetHashForFileName(info->ShortFilename);
	info->HashNext = GGameFileHash[hash];
	GGameFileHash[hash] = info;

	return true;

	unguardf("%s", FullName);
}
예제 #27
0
static void ExportAnimations(ExportContext& Context, FArchive& Ar)
{
	guard(ExportAnimations);

	const CAnimSet* Anim = Context.SkelMesh->Anim;
	int NumBones = Context.SkelMesh->RefSkeleton.Num();

	// Build mesh to anim bone map

	TArray<int> BoneMap;
	BoneMap.Init(-1, NumBones);
	TArray<int> AnimBones;
	AnimBones.Empty(NumBones);

	for (int i = 0; i < NumBones; i++)
	{
		const CSkelMeshBone &B = Context.SkelMesh->RefSkeleton[i];
		for (int j = 0; j < Anim->TrackBoneNames.Num(); j++)
		{
			if (!stricmp(B.Name, Anim->TrackBoneNames[j]))
			{
				BoneMap[i] = j;			// lookup CAnimSet bone by mesh bone index
				AnimBones.Add(i);		// indicate that the bone has animation
				break;
			}
		}
	}

	Ar.Printf(
		"  \"animations\" : [\n"
	);

	int FirstDataIndex = Context.Data.Num();

	// Iterate over all animations
	for (int SeqIndex = 0; SeqIndex < Anim->Sequences.Num(); SeqIndex++)
	{
		const CAnimSequence &Seq = *Anim->Sequences[SeqIndex];

		Ar.Printf(
			"    {\n"
			"      \"name\" : \"%s\",\n",
			*Seq.Name
		);

		struct AnimSampler
		{
			enum ChannelType
			{
				TRANSLATION,
				ROTATION
			};

			int BoneNodeIndex;
			ChannelType Type;
			const CAnimTrack* Track;
		};

		TArray<AnimSampler> Samplers;
		Samplers.Empty(AnimBones.Num() * 2);

		//!! Optimization:
		//!! 1. there will be missing tracks (AnimRotationOnly etc) - drop such samplers
		//!! 2. store all time tracks in a single BufferView, all rotation tracks in another, and all position track in 3rd one - this
		//!!    will reduce amount of BufferViews in json text (combine them by data type)

		// Prepare channels array
		Ar.Printf("      \"channels\" : [\n");
		for (int BoneIndex = 0; BoneIndex < AnimBones.Num(); BoneIndex++)
		{
			int MeshBoneIndex = AnimBones[BoneIndex];
			int AnimBoneIndex = BoneMap[MeshBoneIndex];

			const CAnimTrack* Track = Seq.Tracks[AnimBoneIndex];

			int TranslationSamplerIndex = Samplers.Num();
			AnimSampler* Sampler = new (Samplers) AnimSampler;
			Sampler->Type = AnimSampler::TRANSLATION;
			Sampler->BoneNodeIndex = MeshBoneIndex + FIRST_BONE_NODE;
			Sampler->Track = Track;

			int RotationSamplerIndex = Samplers.Num();
			Sampler = new (Samplers) AnimSampler;
			Sampler->Type = AnimSampler::ROTATION;
			Sampler->BoneNodeIndex = MeshBoneIndex + FIRST_BONE_NODE;
			Sampler->Track = Track;

			// Print glTF information. Not using usual formatting here to make output a little bit more compact.
			Ar.Printf(
				"        { \"sampler\" : %d, \"target\" : { \"node\" : %d, \"path\" : \"%s\" } },\n",
				TranslationSamplerIndex, MeshBoneIndex + FIRST_BONE_NODE, "translation"
			);
			Ar.Printf(
				"        { \"sampler\" : %d, \"target\" : { \"node\" : %d, \"path\" : \"%s\" } }%s\n",
				RotationSamplerIndex, MeshBoneIndex + FIRST_BONE_NODE, "rotation", BoneIndex == AnimBones.Num()-1 ? "" : ","
			);
		}
		Ar.Printf("      ],\n");

		// Prepare samplers
		Ar.Printf("      \"samplers\" : [\n");
		for (int SamplerIndex = 0; SamplerIndex < Samplers.Num(); SamplerIndex++)
		{
			const AnimSampler& Sampler = Samplers[SamplerIndex];

			// Prepare time array
			const TArray<float>* TimeArray = (Sampler.Type == AnimSampler::TRANSLATION) ? &Sampler.Track->KeyPosTime : &Sampler.Track->KeyQuatTime;
			if (TimeArray->Num() == 0)
			{
				// For this situation, use track's time array
				TimeArray = &Sampler.Track->KeyTime;
			}
			int NumKeys = Sampler.Type == (AnimSampler::TRANSLATION) ? Sampler.Track->KeyPos.Num() : Sampler.Track->KeyQuat.Num();

			int TimeBufIndex = Context.Data.AddZeroed();
			BufferData& TimeBuf = Context.Data[TimeBufIndex];
			TimeBuf.Setup(NumKeys, "SCALAR", BufferData::FLOAT, sizeof(float));

			float RateScale = 1.0f / Seq.Rate;
			float LastFrameTime = 0;
			if (TimeArray->Num() == 0 || NumKeys == 1)
			{
				// Fill with equally spaced values
				for (int i = 0; i < NumKeys; i++)
				{
					TimeBuf.Put(i * RateScale);
				}
				LastFrameTime = NumKeys-1;
			}
			else
			{
				for (int i = 0; i < TimeArray->Num(); i++)
				{
					TimeBuf.Put((*TimeArray)[i] * RateScale);
				}
				LastFrameTime = (*TimeArray)[TimeArray->Num()-1];
			}
			// Prepare min/max values for time track, it's required by glTF standard
			TimeBuf.BoundsMin = "[ 0 ]";
			char buf[64];
			appSprintf(ARRAY_ARG(buf), "[ %g ]", LastFrameTime * RateScale);
			TimeBuf.BoundsMax = buf;

			// Try to reuse TimeBuf from previous tracks
			TimeBufIndex = Context.GetFinalIndexForLastBlock(FirstDataIndex);

			// Prepare data
			int DataBufIndex = Context.Data.AddZeroed();
			BufferData& DataBuf = Context.Data[DataBufIndex];
			if (Sampler.Type == AnimSampler::TRANSLATION)
			{
				// Translation track
				DataBuf.Setup(NumKeys, "VEC3", BufferData::FLOAT, sizeof(CVec3));
				for (int i = 0; i < NumKeys; i++)
				{
					CVec3 Pos = Sampler.Track->KeyPos[i];
					TransformPosition(Pos);
					DataBuf.Put(Pos);
				}
			}
			else
			{
				// Rotation track
				DataBuf.Setup(NumKeys, "VEC4", BufferData::FLOAT, sizeof(CQuat));
				for (int i = 0; i < NumKeys; i++)
				{
					CQuat Rot = Sampler.Track->KeyQuat[i];
					TransformRotation(Rot);
					if (Sampler.BoneNodeIndex - FIRST_BONE_NODE == 0)
					{
						Rot.Conjugate();
					}
					DataBuf.Put(Rot);
				}
			}

			// Try to reuse data block as well
			DataBufIndex = Context.GetFinalIndexForLastBlock(FirstDataIndex);

			// Write glTF info
			Ar.Printf(
				"        { \"input\" : %d, \"output\" : %d }%s\n",
				TimeBufIndex, DataBufIndex, SamplerIndex == Samplers.Num()-1 ? "" : ","
			);
		}
		Ar.Printf("      ]\n");

		Ar.Printf("    }%s\n", SeqIndex == Anim->Sequences.Num()-1 ? "" : ",");
	}

	Ar.Printf("  ],\n");

	unguard;
}
예제 #28
0
static void ExportSection(ExportContext& Context, const CBaseMeshLod& Lod, const CMeshVertex* Verts, int SectonIndex, FArchive& Ar)
{
	guard(ExportSection);

	int VertexSize = Context.IsSkeletal() ? sizeof(CSkelMeshVertex) : sizeof(CStaticMeshVertex);

	const CMeshSection& S = Lod.Sections[SectonIndex];
	bool bLast = (SectonIndex == Lod.Sections.Num()-1);

	// Remap section indices to local indices
	CIndexBuffer::IndexAccessor_t GetIndex = Lod.Indices.GetAccessor();
	TArray<int> indexRemap; // old vertex index -> new vertex index
	indexRemap.Init(-1, Lod.NumVerts);
	int numLocalVerts = 0;
	int numLocalIndices = S.NumFaces * 3;
	for (int idx = 0; idx < numLocalIndices; idx++)
	{
		int vertIndex = GetIndex(S.FirstIndex + idx);
		if (indexRemap[vertIndex] == -1)
		{
			indexRemap[vertIndex] = numLocalVerts++;
		}
	}

	// Prepare buffers
	int IndexBufIndex = Context.Data.AddZeroed();
	int PositionBufIndex = Context.Data.AddZeroed();
	int NormalBufIndex = Context.Data.AddZeroed();
	int TangentBufIndex = Context.Data.AddZeroed();

	int BonesBufIndex = -1;
	int WeightsBufIndex = -1;
	if (Context.IsSkeletal())
	{
		BonesBufIndex = Context.Data.AddZeroed();
		WeightsBufIndex = Context.Data.AddZeroed();
	}

	int UVBufIndex[MAX_MESH_UV_SETS];
	for (int i = 0; i < Lod.NumTexCoords; i++)
	{
		UVBufIndex[i] = Context.Data.AddZeroed();
	}

	BufferData& IndexBuf = Context.Data[IndexBufIndex];
	BufferData& PositionBuf = Context.Data[PositionBufIndex];
	BufferData& NormalBuf = Context.Data[NormalBufIndex];
	BufferData& TangentBuf = Context.Data[TangentBufIndex];
	BufferData* UVBuf[MAX_MESH_UV_SETS];
	BufferData* BonesBuf = NULL;
	BufferData* WeightsBuf = NULL;

	PositionBuf.Setup(numLocalVerts, "VEC3", BufferData::FLOAT, sizeof(CVec3));
	NormalBuf.Setup(numLocalVerts, "VEC3", BufferData::FLOAT, sizeof(CVec3));
	TangentBuf.Setup(numLocalVerts, "VEC4", BufferData::FLOAT, sizeof(CVec4));
	for (int i = 0; i < Lod.NumTexCoords; i++)
	{
		UVBuf[i] = &Context.Data[UVBufIndex[i]];
		UVBuf[i]->Setup(numLocalVerts, "VEC2", BufferData::FLOAT, sizeof(CMeshUVFloat));
	}

	if (Context.IsSkeletal())
	{
		BonesBuf = &Context.Data[BonesBufIndex];
		WeightsBuf = &Context.Data[WeightsBufIndex];
		BonesBuf->Setup(numLocalVerts, "VEC4", BufferData::UNSIGNED_SHORT, sizeof(uint16)*4);
		WeightsBuf->Setup(numLocalVerts, "VEC4", BufferData::UNSIGNED_BYTE, sizeof(uint32), /*InNormalized=*/ true);
	}

	// Prepare and build indices
	TArray<int> localIndices;
	localIndices.AddUninitialized(numLocalIndices);
	int* pIndex = localIndices.GetData();
	for (int i = 0; i < numLocalIndices; i++)
	{
		*pIndex++ = GetIndex(S.FirstIndex + i);
	}

	if (numLocalVerts <= 65536)
	{
		IndexBuf.Setup(numLocalIndices, "SCALAR", BufferData::UNSIGNED_SHORT, sizeof(uint16));
		for (int idx = 0; idx < numLocalIndices; idx++)
		{
			IndexBuf.Put<uint16>(indexRemap[localIndices[idx]]);
		}
	}
	else
	{
		IndexBuf.Setup(numLocalIndices, "SCALAR", BufferData::UNSIGNED_INT, sizeof(uint32));
		for (int idx = 0; idx < numLocalIndices; idx++)
		{
			IndexBuf.Put<uint32>(indexRemap[localIndices[idx]]);
		}
	}

	// Build reverse index map for fast lookup of vertex by its new index.
	// It maps new vertex index to old vertex index.
	TArray<int> revIndexMap;
	revIndexMap.AddUninitialized(numLocalVerts);
	for (int i = 0; i < indexRemap.Num(); i++)
	{
		int newIndex = indexRemap[i];
		if (newIndex != -1)
		{
			revIndexMap[newIndex] = i;
		}
	}

	// Build vertices
	for (int i = 0; i < numLocalVerts; i++)
	{
		int vertIndex = revIndexMap[i];
		const CMeshVertex& V = VERT(vertIndex);

		CVec3 Position = V.Position;

		CVec4 Normal, Tangent;
		Unpack(Normal, V.Normal);
		Unpack(Tangent, V.Tangent);
		// Unreal (and we are) using normal.w for computing binormal. glTF
		// uses tangent.w for that. Make this value exactly 1.0 of -1.0 to make glTF
		// validator happy.
	#if 0
		// There's some problem: V.Normal.W == 0x80 -> -1.008 instead of -1.0
		if (Normal.w > 1.001 || Normal.w < -1.001)
		{
			appError("%X -> %g\n", V.Normal.Data, Normal.w);
		}
	#endif
		Tangent.w = (Normal.w < 0) ? -1 : 1;

		TransformPosition(Position);
		TransformDirection(Normal);
		TransformDirection(Tangent);

		Normal.Normalize();
		Tangent.Normalize();

		// Fill buffers
		PositionBuf.Put(Position);
		NormalBuf.Put(Normal.xyz);
		TangentBuf.Put(Tangent);
		UVBuf[0]->Put(V.UV);
	}

	// Compute bounds for PositionBuf
	CVec3 Mins, Maxs;
	ComputeBounds((CVec3*)PositionBuf.Data, numLocalVerts, sizeof(CVec3), Mins, Maxs);
	char buf[256];
	appSprintf(ARRAY_ARG(buf), "[ %g, %g, %g ]", VECTOR_ARG(Mins));
	PositionBuf.BoundsMin = buf;
	appSprintf(ARRAY_ARG(buf), "[ %g, %g, %g ]", VECTOR_ARG(Maxs));
	PositionBuf.BoundsMax = buf;

	if (Context.IsSkeletal())
	{
		for (int i = 0; i < numLocalVerts; i++)
		{
			int vertIndex = revIndexMap[i];
			const CMeshVertex& V0 = VERT(vertIndex);
			const CSkelMeshVertex& V = static_cast<const CSkelMeshVertex&>(V0);

			int16 Bones[NUM_INFLUENCES];
			static_assert(NUM_INFLUENCES == 4, "Code designed for 4 influences");
			static_assert(sizeof(Bones) == sizeof(V.Bone), "Unexpected V.Bones size");
			memcpy(Bones, V.Bone, sizeof(Bones));
			for (int j = 0; j < NUM_INFLUENCES; j++)
			{
				// We have INDEX_NONE as list terminator, should replace with something else for glTF
				if (Bones[j] == INDEX_NONE)
				{
					Bones[j] = 0;
				}
			}

			BonesBuf->Put(*(uint64*)&Bones);
			WeightsBuf->Put(V.PackedWeights);
		}
	}

	// Secondary UVs
	for (int uvIndex = 1; uvIndex < Lod.NumTexCoords; uvIndex++)
	{
		BufferData* pBuf = UVBuf[uvIndex];
		const CMeshUVFloat* srcUV = Lod.ExtraUV[uvIndex-1];
		for (int i = 0; i < numLocalVerts; i++)
		{
			int vertIndex = revIndexMap[i];
			pBuf->Put(srcUV[vertIndex]);
		}
	}

	// Write primitive information to json
	Ar.Printf(
		"        {\n"
		"          \"attributes\" : {\n"
		"            \"POSITION\" : %d,\n"
		"            \"NORMAL\" : %d,\n"
		"            \"TANGENT\" : %d,\n",
		PositionBufIndex, NormalBufIndex, TangentBufIndex
	);
	if (Context.IsSkeletal())
	{
		Ar.Printf(
			"            \"JOINTS_0\" : %d,\n"
			"            \"WEIGHTS_0\" : %d,\n",
			BonesBufIndex, WeightsBufIndex
		);
	}
	for (int i = 0; i < Lod.NumTexCoords; i++)
	{
		Ar.Printf(
			"            \"TEXCOORD_%d\" : %d%s\n",
			i, UVBufIndex[i], i < (Lod.NumTexCoords-1) ? "," : ""
		);
	}

	Ar.Printf(
		"          },\n"
		"          \"indices\" : %d,\n"
		"          \"material\" : %d\n"
		"        }%s\n",
		IndexBufIndex, SectonIndex,
		SectonIndex < (Lod.Sections.Num()-1) ? "," : ""
	);

	unguard;
}
예제 #29
0
static void SV_ClipMoveToEntities(trace_t &trace, const CVec3 &start, const CVec3 &end, const CBox &bounds, edict_t *passedict, int contentmask)
{
	guard(SV_ClipMoveToEntities);

	if (trace.allsolid) return;

	int		i;

	CVec3 amins, amaxs;
	for (i = 0; i < 3; i++)
	{
		if (start[i] < end[i])
		{
			amins[i] = bounds.mins[i] + start[i];
			amaxs[i] = bounds.maxs[i] + end[i];
		}
		else
		{
			amins[i] = bounds.mins[i] + end[i];
			amaxs[i] = bounds.maxs[i] + start[i];
		}
	}
	edict_t	*list[MAX_EDICTS];
	int num = SV_AreaEdicts(amins, amaxs, ARRAY_ARG(list), AREA_SOLID);
	if (!num) return;

	float b1 = dot(bounds.mins, bounds.mins);
	float b2 = dot(bounds.maxs, bounds.maxs);
	float t = max(b1, b2);
	float traceWidth = SQRTFAST(t);
	CVec3 traceDir;
	VectorSubtract(end, start, traceDir);
	float traceLen = traceDir.Normalize() + traceWidth;

	for (i = 0; i < num; i++)
	{
		edict_t *edict = list[i];
		entityHull_t &ent = ents[NUM_FOR_EDICT(edict)];
//		if (!ent->linked) continue;

		if (edict->solid == SOLID_NOT || edict == passedict) continue;
		if (passedict)
		{
		 	if (edict->owner == passedict)
				continue;	// don't clip against own missiles
			if (passedict->owner == edict)
				continue;	// don't clip against owner
		}
		if (!(contentmask & CONTENTS_DEADMONSTER) && (edict->svflags & SVF_DEADMONSTER))
			continue;

		CVec3 eCenter;
		VectorSubtract(ent.center, start, eCenter);
		// check position of point projection on line
		float entPos = dot(eCenter, traceDir);
		if (entPos < -traceWidth - ent.radius || entPos > traceLen + ent.radius)
			continue;		// too near / too far

		// check distance between point and line
		CVec3 tmp;
		VectorMA(eCenter, -entPos, traceDir, tmp);
		float dist2 = dot(tmp, tmp);
		float dist0 = ent.radius + traceWidth;
		if (dist2 >= dist0 * dist0) continue;

		trace_t	tr;
		if (ent.model)
			CM_TransformedBoxTrace(tr, start, end, bounds, ent.model->headnode, contentmask, edict->s.origin, ent.axis);
		else
			CM_TransformedBoxTrace(tr, start, end, bounds, CM_HeadnodeForBox(ent.bounds), contentmask, edict->s.origin, nullVec3);
		if (CM_CombineTrace(trace, tr))
			trace.ent = edict;
		if (trace.allsolid) return;
	}

	unguard;
}
예제 #30
0
void SV_LinkEdict(edict_t *ent)
{
	guard(SV_LinkEdict);

	int		i, j, k;

	//!! HOOK - move outside ?
	if (bspfile.type != map_q2)
	{
		if (ent->s.modelindex >= 0 && ent->s.modelindex < MAX_MODELS)
		{
			// link model hook
			const char *modelName = sv.configstrings[CS_MODELS + ent->s.modelindex];
			if (!strcmp(modelName, "models/objects/dmspot/tris.md2"))
			{
				// teleporter (source+target) was found
//				appPrintf(S_CYAN"teleport: %g %g %g\n", VECTOR_ARG(ent->s.origin));
				return;			// simply do not link model; trigger entity will be added anyway
			}
		}
	}

	if (ent->area.prev)
		SV_UnlinkEdict(ent);	// unlink from old position (i.e. relink edict)

	if (ent == ge->edicts)
		return;					// don't add the world

	if (!ent->inuse)
		return;

	entityHull_t &ex = ents[NUM_FOR_EDICT(ent)];
	memset(&ex, 0, sizeof(entityHull_t));
	ex.owner = ent;
	ex.axis.FromEuler(ent->s.angles);

	// set the size
	VectorSubtract(ent->bounds.maxs, ent->bounds.mins, ent->size);

	// encode the size into the entity_state for client prediction
	if (ent->solid == SOLID_BBOX)
	{
		// assume that x/y are equal and symetric
		i = appRound(ent->bounds.maxs[0] / 8);
		// z is not symetric
		j = appRound(-ent->bounds.mins[2] / 8);
		// and z maxs can be negative...
		k = appRound((ent->bounds.maxs[2] + 32) / 8);
		// original Q2 have bounded i/j/k/ with lower margin==1 (for client prediction only); this will
		// produce incorrect collision test when bbox mins/maxs is (0,0,0)
		i = bound(i, 0, 31);		// mins/maxs[0,1] range is -248..0/0..248
		j = bound(j, 0, 31);		// mins[2] range is [-248..0]
		k = bound(k, 0, 63);		// maxs[2] range is [-32..472]

		// if SVF_DEADMONSTER, s.solid should be 0
		ent->s.solid = (ent->svflags & SVF_DEADMONSTER) ? 0 : (k<<10) | (j<<5) | i;

		i *= 8;
		j *= 8;
		k *= 8;
		ex.bounds.mins.Set(-i, -i, -j);
		ex.bounds.maxs.Set(i, i, k - 32);
		ex.bounds.GetCenter(ex.center);
		ex.center.Add(ent->s.origin);
		ex.model  = NULL;
		ex.radius = VectorDistance(ex.bounds.maxs, ex.bounds.mins) / 2;
	}
	else if (ent->solid == SOLID_BSP)
	{
		ex.model = sv.models[ent->s.modelindex];
		if (!ex.model) Com_DropError("MOVETYPE_PUSH with a non bsp model");
		CVec3	v;
		ex.model->bounds.GetCenter(v);
		UnTransformPoint(ent->s.origin, ex.axis, v, ex.center);
		ex.radius = ex.model->radius;

		ent->s.solid = 31;		// a SOLID_BBOX will never create this value (mins=(-248,-248,0) maxs=(248,248,-32))
	}
	else if (ent->solid == SOLID_TRIGGER)
	{
		ent->s.solid = 0;
		// check for model link
		ex.model = sv.models[ent->s.modelindex];
		if (!ex.model)
		{
			// model not attached by game, check entstring
			//?? can optimize: add 'bool spawningEnts', set to 'true' before SpawnEntities()
			//?? and 'false' after; skip code below when 'false'
			for (triggerModelLink_t *link = bspfile.modelLinks; link; link = link->next)
#define CMP(n)	(fabs(ent->s.origin[n] - link->origin[n]) < 0.5f)
				if (CMP(0) && CMP(1) && CMP(2))
				{
					CBspModel *model = CM_InlineModel(link->modelIdx);
					VectorSubtract(model->bounds.maxs, ent->s.origin, ent->bounds.maxs);
					VectorSubtract(model->bounds.mins, ent->s.origin, ent->bounds.mins);
					break;
				}
#undef CMP
		}
	}
	else
		ent->s.solid = 0;

	// set the abs box
	if (ent->solid == SOLID_BSP && (ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2]))
	{
		// expand for rotation
		for (i = 0; i < 3 ; i++)
		{
			ent->absBounds.mins[i] = ex.center[i] - ex.radius;
			ent->absBounds.maxs[i] = ex.center[i] + ex.radius;
		}
	}
	else
	{	// normal
		VectorAdd(ent->s.origin, ent->bounds.mins, ent->absBounds.mins);
		VectorAdd(ent->s.origin, ent->bounds.maxs, ent->absBounds.maxs);
	}

	// because movement is clipped an epsilon away from an actual edge,
	// we must fully check even when bounding boxes don't quite touch
	for (i = 0; i < 3; i++)
	{
		ent->absBounds.mins[i] -= 1;
		ent->absBounds.maxs[i] += 1;
	}

	// link to PVS leafs
	ent->num_clusters = 0;
	ent->zonenum      = 0;
	ent->zonenum2     = 0;

	// get all leafs, including solids
	CBspLeaf *leafs[MAX_TOTAL_ENT_LEAFS];
	int topnode;
	int num_leafs = CM_BoxLeafs(ent->absBounds, ARRAY_ARG(leafs), &topnode);

	// set zones
	int clusters[MAX_TOTAL_ENT_LEAFS];
	for (i = 0; i < num_leafs; i++)
	{
		clusters[i] = leafs[i]->cluster;
		int zone = leafs[i]->zone;
		if (zone)
		{	// doors may legally straggle two zones,
			// but nothing should evern need more than that
			if (ent->zonenum && ent->zonenum != zone)
			{
				if (ent->zonenum2 && ent->zonenum2 != zone && sv.state == ss_loading)
					Com_DPrintf("Object touching 3 zones at %g %g %g\n", VECTOR_ARG(ent->absBounds.mins));
				ent->zonenum2 = zone;
			}
			else
				ent->zonenum = zone;
		}
	}

	if (num_leafs >= MAX_TOTAL_ENT_LEAFS)
	{	// assume we missed some leafs, and mark by headnode
		ent->num_clusters = -1;
		ent->headnode     = topnode;
	}
	else
	{
		ent->num_clusters = 0;
		for (i = 0; i < num_leafs; i++)
		{
			if (clusters[i] == -1)
				continue;		// not a visible leaf
			for (j = 0; j < i; j++)
				if (clusters[j] == clusters[i])
					break;
			if (j == i)
			{
				if (ent->num_clusters == MAX_ENT_CLUSTERS)
				{	// assume we missed some leafs, and mark by headnode
					ent->num_clusters = -1;
					ent->headnode     = topnode;
					break;
				}

				ent->clusternums[ent->num_clusters++] = clusters[i];
			}
		}
	}

	// if first time, make sure old_origin is valid
	if (!ent->linkcount)
		ent->s.old_origin = ent->s.origin;
	ent->linkcount++;

	if (ent->solid == SOLID_NOT)
		return;

	// find the first node that the ent's box crosses
	areanode_t *node = areaNodes;
	while (node->axis != -1)
	{
		if (ent->absBounds.mins[node->axis] > node->dist)
			node = node->children[0];
		else if (ent->absBounds.maxs[node->axis] < node->dist)
			node = node->children[1];
		else
			break;		// crosses the node
	}

	// link it in
	areanode_t *node2 = node;
	if (ent->solid == SOLID_TRIGGER)
	{
		InsertLinkBefore(ent->area, node->trigEdicts);
		for ( ; node2; node2 = node2->parent)
			node2->numTrigEdicts++;
	}
	else
	{
		InsertLinkBefore(ent->area, node->solidEdicts);
		for ( ; node2; node2 = node2->parent)
			node2->numSolidEdicts++;
	}
	ex.area = node;

	unguard;
}