2023-05-08 14:33:37 +02:00
|
|
|
/*
|
|
|
|
===========================================================================
|
2024-06-04 19:56:01 +02:00
|
|
|
Copyright (C) 2024 the OpenMoHAA team
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2023-05-09 19:18:16 +02:00
|
|
|
This file is part of OpenMoHAA source code.
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2023-05-09 19:18:16 +02:00
|
|
|
OpenMoHAA source code is free software; you can redistribute it
|
2023-05-08 14:33:37 +02:00
|
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
|
|
or (at your option) any later version.
|
|
|
|
|
2023-05-09 19:18:16 +02:00
|
|
|
OpenMoHAA source code is distributed in the hope that it will be
|
2023-05-08 14:33:37 +02:00
|
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
2023-05-09 19:18:16 +02:00
|
|
|
along with OpenMoHAA source code; if not, write to the Free Software
|
2023-05-08 14:33:37 +02:00
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
2023-05-09 19:18:16 +02:00
|
|
|
|
|
|
|
// tr_models.cpp -- model loading and caching
|
2023-05-08 14:33:37 +02:00
|
|
|
|
|
|
|
#include "tr_local.h"
|
|
|
|
#include "tiki.h"
|
|
|
|
#include <vector.h>
|
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
#define LL(x) x = LittleLong(x)
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
qboolean g_bInfoworldtris = qfalse;
|
2024-09-11 18:57:50 +02:00
|
|
|
static int entityNumIndexes[MAX_ENTITIES];
|
|
|
|
static int staticModelNumIndexes[4095];
|
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
static int R_CullSkelModel(dtiki_t *tiki, refEntity_t *e, skelAnimFrame_t *newFrame, float fScale, float *vLocalOrg);
|
2023-05-19 02:10:03 +02:00
|
|
|
|
2023-05-08 14:33:37 +02:00
|
|
|
/*
|
|
|
|
** R_GetModelByHandle
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
model_t *R_GetModelByHandle(qhandle_t hModel)
|
|
|
|
{
|
|
|
|
model_t *mod;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
// out of range gets the default model
|
|
|
|
if (hModel < 1 || hModel >= tr.numModels) {
|
|
|
|
return &tr.models[0];
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
mod = &tr.models[hModel];
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
return mod;
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** R_Model_GetHandle
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
dtiki_t *R_Model_GetHandle(qhandle_t handle)
|
|
|
|
{
|
|
|
|
model_t *model = R_GetModelByHandle(handle);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (model->type == MOD_TIKI) {
|
|
|
|
return model->d.tiki;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
return NULL;
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//===============================================================================
|
|
|
|
|
|
|
|
/*
|
|
|
|
** R_FreeModel
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void R_FreeModel(model_t *mod)
|
|
|
|
{
|
|
|
|
if (mod->type == MOD_TIKI) {
|
|
|
|
ri.CG_EndTiki(mod->d.tiki);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
memset(mod, 0, sizeof(model_t));
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** R_AllocModel
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
model_t *R_AllocModel(void)
|
|
|
|
{
|
|
|
|
int i;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
for (i = 0; i < tr.numModels; i++) {
|
|
|
|
if (!tr.models[i].name[0]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (i == tr.numModels) {
|
|
|
|
if (i == MAX_MOD_KNOWN) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
tr.numModels++;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
tr.models[i].index = i;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
return &tr.models[i];
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** RE_FreeModels
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void RE_FreeModels(void)
|
|
|
|
{
|
|
|
|
int hModel;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
for (hModel = 0; hModel < tr.numModels; hModel++) {
|
|
|
|
if (!tr.models[hModel].name[0]) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
R_FreeModel(&tr.models[hModel]);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** R_RegisterShaders
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void R_RegisterShaders(model_t *mod)
|
|
|
|
{
|
|
|
|
dtiki_t *tiki;
|
|
|
|
int i, j;
|
|
|
|
dtikisurface_t *psurface;
|
|
|
|
shader_t *sh;
|
|
|
|
|
|
|
|
tiki = mod->d.tiki;
|
|
|
|
|
|
|
|
for (i = 0; i < tiki->num_surfaces; i++) {
|
|
|
|
psurface = &tiki->surfaces[i];
|
|
|
|
|
|
|
|
assert(psurface->numskins <= MAX_TIKI_SHADER);
|
|
|
|
for (j = 0; j < psurface->numskins; j++) {
|
|
|
|
if (psurface->shader[j][0]) {
|
|
|
|
sh = R_FindShader(
|
|
|
|
psurface->shader[j],
|
|
|
|
LIGHTMAP_NONE,
|
|
|
|
!(psurface->flags & TIKI_SURF_NOMIPMAPS),
|
|
|
|
r_picmip->integer,
|
|
|
|
qtrue,
|
|
|
|
qtrue
|
|
|
|
);
|
|
|
|
psurface->hShader[j] = sh->index;
|
|
|
|
} else {
|
|
|
|
psurface->hShader[j] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** RE_UnregisterServerModel
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void RE_UnregisterServerModel(qhandle_t hModel)
|
|
|
|
{
|
|
|
|
if (hModel < 0 || hModel >= MAX_MOD_KNOWN) {
|
|
|
|
return;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (tr.models[hModel].serveronly) {
|
|
|
|
R_FreeModel(&tr.models[hModel]);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** R_RegisterModelInternal
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
static qhandle_t R_RegisterModelInternal(const char *name, qboolean bBeginTiki, qboolean use)
|
|
|
|
{
|
|
|
|
model_t *mod;
|
|
|
|
qhandle_t hModel;
|
|
|
|
const char *ptr;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (!name || !*name) {
|
|
|
|
ri.Printf(PRINT_ALL, "RE_RegisterModel: NULL name\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (strlen(name) >= 128) {
|
|
|
|
Com_Printf("Model name exceeds MAX_MODEL_NAME\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
//
|
|
|
|
// search the currently loaded models
|
|
|
|
//
|
|
|
|
for (hModel = 1; hModel < tr.numModels; hModel++) {
|
|
|
|
mod = &tr.models[hModel];
|
|
|
|
if (!Q_stricmp(mod->name, name)) {
|
|
|
|
if (mod->type == MOD_BAD) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return hModel;
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
// allocate a new model_t
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if ((mod = R_AllocModel()) == NULL) {
|
|
|
|
ri.Printf(PRINT_WARNING, "RE_RegisterModel: R_AllocModel() failed for '%s'\n", name);
|
|
|
|
return 0;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
// only set the name after the model has been successfully loaded
|
|
|
|
Q_strncpyz(mod->name, name, sizeof(mod->name));
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
// make sure the render thread is stopped
|
|
|
|
R_IssuePendingRenderCommands();
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
mod->serveronly = qtrue;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
//
|
|
|
|
// load the files
|
|
|
|
//
|
|
|
|
ptr = strrchr(name, '.');
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (ptr) {
|
|
|
|
ptr++;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (!stricmp(ptr, "spr")) {
|
|
|
|
mod->d.sprite = SPR_RegisterSprite(name);
|
|
|
|
Q_strncpyz(mod->name, name, sizeof(mod->name));
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (mod->d.sprite) {
|
|
|
|
mod->type = MOD_SPRITE;
|
|
|
|
return mod->index;
|
|
|
|
}
|
|
|
|
} else if (!stricmp(ptr, "tik")) {
|
|
|
|
mod->d.tiki = ri.TIKI_RegisterTikiFlags(name, use);
|
|
|
|
Q_strncpyz(mod->name, name, sizeof(mod->name));
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (mod->d.tiki) {
|
|
|
|
mod->type = MOD_TIKI;
|
|
|
|
R_RegisterShaders(mod);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (bBeginTiki) {
|
|
|
|
ri.CG_ProcessInitCommands(mod->d.tiki, NULL);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
return mod->index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
ri.Printf(PRINT_ERROR, "RE_RegisterModel: Registration failed for '%s'\n", name);
|
|
|
|
mod->type = MOD_BAD;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
return 0;
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** RE_RegisterServerModel
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
qhandle_t RE_RegisterServerModel(const char *name)
|
|
|
|
{
|
|
|
|
return R_RegisterModelInternal(name, qtrue, qfalse);
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** RE_SpawnEffectModel
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
qhandle_t RE_SpawnEffectModel(const char *szModel, vec3_t vPos, vec3_t *axis)
|
|
|
|
{
|
|
|
|
refEntity_t new_entity;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
memset(&new_entity, 0, sizeof(refEntity_t));
|
|
|
|
memset(&new_entity.shaderRGBA, 255, sizeof(byte) * 4);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
VectorCopy(vPos, new_entity.origin);
|
|
|
|
new_entity.scale = 1.0;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (axis) {
|
|
|
|
AxisCopy(axis, new_entity.axis);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
new_entity.hModel = R_RegisterModelInternal(szModel, qfalse, qtrue);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (new_entity.hModel) {
|
|
|
|
tr.models[new_entity.hModel].serveronly = qfalse;
|
|
|
|
ri.CG_ProcessInitCommands(tr.models[new_entity.hModel].d.tiki, &new_entity);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
return new_entity.hModel;
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** RE_RegisterModel
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
qhandle_t RE_RegisterModel(const char *name)
|
|
|
|
{
|
|
|
|
qhandle_t handle;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
handle = R_RegisterModelInternal(name, qtrue, qtrue);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (handle) {
|
|
|
|
tr.models[handle].serveronly = qfalse;
|
|
|
|
}
|
|
|
|
return handle;
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
R_ModelInit
|
|
|
|
===============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void R_ModelInit(void)
|
|
|
|
{
|
|
|
|
model_t *mod;
|
|
|
|
int i;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
// leave a space for NULL model
|
|
|
|
tr.numModels = 0;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
mod = R_AllocModel();
|
|
|
|
Q_strncpyz(mod->name, "** BAD MODEL **", sizeof(mod->name));
|
|
|
|
mod->type = MOD_BAD;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
for (i = 0; i < ARRAY_LEN(tr.skel_index); i++) {
|
|
|
|
tr.skel_index[i] = -1;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
================
|
|
|
|
R_Modellist_f
|
|
|
|
================
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void R_Modellist_f(void)
|
|
|
|
{
|
|
|
|
int i;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
for (i = 1; i < tr.numModels; i++) {
|
|
|
|
ri.Printf(PRINT_ALL, "%s\n", tr.models[i].name);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
R_ModelRadius
|
|
|
|
====================
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
float R_ModelRadius(qhandle_t handle)
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
vec3_t bounds[2];
|
|
|
|
model_t *model;
|
|
|
|
float radius, maxRadius;
|
|
|
|
vec3_t tmpVec;
|
|
|
|
float w;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
model = R_GetModelByHandle(handle);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
switch (model->type) {
|
|
|
|
case MOD_BRUSH:
|
|
|
|
maxRadius = 0.0;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
VectorCopy(model->d.bmodel->bounds[0], bounds[0]);
|
|
|
|
VectorCopy(model->d.bmodel->bounds[1], bounds[1]);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
for (j = 0; j < 8; j++) {
|
|
|
|
tmpVec[0] = bounds[j & 1 ? 1 : 0][0];
|
|
|
|
tmpVec[1] = bounds[j & 2 ? 1 : 0][1];
|
|
|
|
tmpVec[2] = bounds[j & 4 ? 1 : 0][2];
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
radius = VectorLength(tmpVec);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (maxRadius < radius) {
|
|
|
|
maxRadius = radius;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MOD_TIKI:
|
|
|
|
return ri.TIKI_GlobalRadius(model->d.tiki);
|
|
|
|
case MOD_SPRITE:
|
|
|
|
maxRadius = model->d.sprite->width * model->d.sprite->scale * 0.5;
|
|
|
|
w = model->d.sprite->height * model->d.sprite->scale * 0.5;
|
|
|
|
|
|
|
|
if (maxRadius <= w) {
|
|
|
|
maxRadius = w;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
maxRadius = 0.0;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
return maxRadius;
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
R_ModelBounds
|
|
|
|
====================
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void R_ModelBounds(qhandle_t handle, vec3_t mins, vec3_t maxs)
|
|
|
|
{
|
|
|
|
model_t *model;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
model = R_GetModelByHandle(handle);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
switch (model->type) {
|
|
|
|
default:
|
|
|
|
case MOD_BAD:
|
|
|
|
VectorClear(mins);
|
|
|
|
VectorClear(maxs);
|
|
|
|
break;
|
|
|
|
case MOD_BRUSH:
|
|
|
|
VectorCopy(model->d.bmodel->bounds[0], mins);
|
|
|
|
VectorCopy(model->d.bmodel->bounds[1], maxs);
|
|
|
|
break;
|
|
|
|
case MOD_TIKI:
|
|
|
|
ri.TIKI_CalculateBounds(model->d.tiki, 1.0, mins, maxs);
|
|
|
|
break;
|
|
|
|
case MOD_SPRITE:
|
|
|
|
mins[0] = -model->d.sprite->width * model->d.sprite->scale * 0.5;
|
|
|
|
mins[1] = -model->d.sprite->height * model->d.sprite->scale * 0.5;
|
|
|
|
mins[2] = -0.0;
|
|
|
|
maxs[0] = model->d.sprite->width * model->d.sprite->scale * 0.5;
|
|
|
|
maxs[1] = model->d.sprite->height * model->d.sprite->scale * 0.5;
|
|
|
|
maxs[2] = 0.0;
|
|
|
|
break;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
2024-12-06 00:09:07 +01:00
|
|
|
#if 0
|
|
|
|
// Replaced by TIKI_FindSkelByHeader
|
2023-05-08 14:33:37 +02:00
|
|
|
/*
|
|
|
|
====================
|
|
|
|
GetModelPath
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
const char *GetModelPath( skelHeaderGame_t *skelmodel ) {
|
|
|
|
int i;
|
|
|
|
int num;
|
|
|
|
skelcache_t *cache;
|
|
|
|
|
|
|
|
num = cache_numskel;
|
|
|
|
|
|
|
|
for( i = 0; i < TIKI_MAX_SKELCACHE; i++ )
|
|
|
|
{
|
|
|
|
cache = &skelcache[ i ];
|
|
|
|
|
|
|
|
if( cache->skel )
|
|
|
|
{
|
|
|
|
if( cache->skel == skelmodel ) {
|
|
|
|
return cache->path;
|
|
|
|
}
|
|
|
|
|
|
|
|
num--;
|
|
|
|
if( num < 0 ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2024-12-06 00:09:07 +01:00
|
|
|
#endif
|
2023-05-08 14:33:37 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
GetLodCutoff
|
|
|
|
====================
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
int GetLodCutoff(skelHeaderGame_t *skelmodel, float lod_val, int renderfx)
|
|
|
|
{
|
|
|
|
lodControl_t *LOD;
|
|
|
|
float f;
|
|
|
|
float fLODCap;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
LOD = skelmodel->pLOD;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (renderfx & RF_DEPTHHACK) {
|
|
|
|
fLODCap = LOD->maxMetric + (LOD->minMetric - LOD->maxMetric) * r_lodviewmodelcap->value;
|
|
|
|
} else {
|
|
|
|
f = (LOD->minMetric - LOD->maxMetric) * r_lodcap->value + LOD->maxMetric;
|
|
|
|
fLODCap = (lod_val - LOD->maxMetric) * r_lodscale->value + LOD->maxMetric;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (fLODCap > f) {
|
|
|
|
fLODCap = f;
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (fLODCap >= LOD->minMetric || !r_uselod->integer) {
|
|
|
|
return LOD->curve[0].val;
|
|
|
|
} else if (fLODCap <= LOD->maxMetric) {
|
|
|
|
return LOD->curve[4].val;
|
|
|
|
} else if (fLODCap <= LOD->consts[3].cutoff) {
|
|
|
|
return fLODCap * LOD->consts[3].scale + LOD->consts[3].base;
|
|
|
|
} else if (fLODCap <= LOD->consts[2].cutoff) {
|
|
|
|
return fLODCap * LOD->consts[2].scale + LOD->consts[2].base;
|
|
|
|
} else if (fLODCap <= LOD->consts[1].cutoff) {
|
|
|
|
return fLODCap * LOD->consts[1].scale + LOD->consts[1].base;
|
|
|
|
} else {
|
|
|
|
return fLODCap * LOD->consts[0].scale + LOD->consts[0].base;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
2024-12-06 00:09:07 +01:00
|
|
|
/*
|
|
|
|
===============
|
|
|
|
R_SaveLODFile
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
static void R_SaveLODFile(const char *path, lodControl_t *LOD)
|
|
|
|
{
|
|
|
|
fileHandle_t file = ri.FS_OpenFileWrite(path);
|
|
|
|
if (!file) {
|
|
|
|
ri.Printf(PRINT_WARNING, "SaveLODFile: Failed to open file %s\n", path);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ri.FS_Write(LOD, sizeof(lodControl_t), file);
|
|
|
|
}
|
|
|
|
|
2023-05-08 14:33:37 +02:00
|
|
|
/*
|
|
|
|
====================
|
|
|
|
GetToolLodCutoff
|
|
|
|
====================
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
int GetToolLodCutoff(skelHeaderGame_t *skelmodel, float lod_val)
|
|
|
|
{
|
|
|
|
lodControl_t *LOD;
|
|
|
|
float totalRange;
|
|
|
|
int i;
|
|
|
|
char lodPath[256];
|
|
|
|
char *ext;
|
|
|
|
|
|
|
|
LOD = skelmodel->pLOD;
|
|
|
|
totalRange = 0.0;
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
|
|
if (skelmodel->lodIndex[i] > 0) {
|
|
|
|
totalRange = skelmodel->lodIndex[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (lod_save->integer == 1) {
|
|
|
|
ri.Cvar_Set("lod_save", "0");
|
|
|
|
Q_strncpyz(lodPath, ri.TIKI_FindSkelByHeader(skelmodel)->path, sizeof(lodPath));
|
|
|
|
ext = strstr(lodPath, "skd");
|
|
|
|
strcpy(ext, "lod");
|
|
|
|
R_SaveLODFile(lodPath, LOD);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (lod_mesh->modified) {
|
|
|
|
lod_mesh->modified = qfalse;
|
|
|
|
ri.Cvar_Set("lod_minLOD", va("%f", LOD->minMetric));
|
|
|
|
ri.Cvar_Set("lod_maxLOD", va("%f", LOD->maxMetric));
|
|
|
|
ri.Cvar_Set("lod_LOD_slider", va("%f", 0.5));
|
|
|
|
ri.Cvar_Set("lod_curve_0_slider", va("%f", LOD->curve[0].val / totalRange));
|
|
|
|
ri.Cvar_Set("lod_curve_1_slider", va("%f", LOD->curve[1].val / totalRange));
|
|
|
|
ri.Cvar_Set("lod_curve_2_slider", va("%f", LOD->curve[2].val / totalRange));
|
|
|
|
ri.Cvar_Set("lod_curve_3_slider", va("%f", LOD->curve[3].val / totalRange));
|
|
|
|
ri.Cvar_Set("lod_curve_4_slider", va("%f", LOD->curve[4].val / totalRange));
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
ri.Cvar_Set("lod_curve_0_val", va("%f", lod_curve_0_slider->value * totalRange));
|
|
|
|
ri.Cvar_Set("lod_curve_1_val", va("%f", lod_curve_1_slider->value * totalRange));
|
|
|
|
ri.Cvar_Set("lod_curve_2_val", va("%f", lod_curve_2_slider->value * totalRange));
|
|
|
|
ri.Cvar_Set("lod_curve_3_val", va("%f", lod_curve_3_slider->value * totalRange));
|
|
|
|
ri.Cvar_Set("lod_curve_4_val", va("%f", lod_curve_4_slider->value * totalRange));
|
|
|
|
|
|
|
|
LOD->minMetric = lod_minLOD->value;
|
|
|
|
LOD->maxMetric = lod_maxLOD->value;
|
|
|
|
LOD->curve[0].val = lod_curve_0_val->value;
|
|
|
|
LOD->curve[1].val = lod_curve_1_val->value;
|
|
|
|
LOD->curve[2].val = lod_curve_2_val->value;
|
|
|
|
LOD->curve[3].val = lod_curve_3_val->value;
|
|
|
|
LOD->curve[4].val = lod_curve_4_val->value;
|
|
|
|
|
|
|
|
ri.TIKI_CalcLodConsts(LOD);
|
|
|
|
return GetLodCutoff(skelmodel, lod_val, 0);
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
R_GetTagPositionAndOrientation
|
|
|
|
==============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
orientation_t R_GetTagPositionAndOrientation(refEntity_t *ent, int tagnum)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
orientation_t tag_or, new_or;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
tag_or = RE_TIKI_Orientation(ent, tagnum);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
VectorCopy(ent->origin, new_or.origin);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
VectorMA(new_or.origin, tag_or.origin[i], ent->axis[i], new_or.origin);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
MatrixMultiply(tag_or.axis, ent->axis, new_or.axis);
|
|
|
|
return new_or;
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
RB_DrawSkeletor
|
|
|
|
==============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void RB_DrawSkeletor(trRefEntity_t *ent)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
dtiki_t *tiki;
|
|
|
|
skeletor_c *skeletor;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
tiki = R_Model_GetHandle(ent->e.hModel);
|
|
|
|
skeletor = (skeletor_c *)ri.TIKI_GetSkeletor(tiki, ENTITYNUM_NONE);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (r_showSkeleton->integer == 1) {
|
|
|
|
//vec3_t vForward, vRight, vUp;
|
|
|
|
orientation_t ori;
|
|
|
|
orientation_t parent_or;
|
|
|
|
int iParentBone;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
qglLineWidth(3);
|
|
|
|
qglBegin(GL_LINES);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
for (i = 0; i < ri.TIKI_GetNumChannels(tiki); i++) { // draw a skeleton
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
ori = R_GetTagPositionAndOrientation(&ent->e, i);
|
|
|
|
iParentBone = ri.SKEL_GetBoneParent(skeletor, i);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (iParentBone != -1) {
|
|
|
|
parent_or = R_GetTagPositionAndOrientation(&ent->e, iParentBone);
|
|
|
|
} else {
|
|
|
|
VectorCopy(ent->e.origin, parent_or.origin);
|
|
|
|
AxisCopy(ent->e.axis, parent_or.axis);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2023-05-26 23:32:27 +02:00
|
|
|
qglColor3f(1, 1, 1);
|
|
|
|
qglVertex3fv(parent_or.origin);
|
|
|
|
qglVertex3fv(ori.origin);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
// draw bone axis
|
|
|
|
/*glColor3f( 1, 0, 0 );
|
2023-05-08 14:33:37 +02:00
|
|
|
glVertex3fv( parent_or.origin );
|
|
|
|
VectorAdd( parent_or.origin, parent_or.axis[ 0 ], vForward );
|
|
|
|
glVertex3fv( vForward );
|
|
|
|
glVertex3fv( vForward );
|
2023-05-11 22:48:13 +02:00
|
|
|
glVertex3fv( ori.origin );
|
2023-05-08 14:33:37 +02:00
|
|
|
|
|
|
|
glColor3f( 0, 1, 0 );
|
|
|
|
glVertex3fv( parent_or.origin );
|
|
|
|
VectorAdd( parent_or.origin, parent_or.axis[ 1 ], vRight );
|
|
|
|
glVertex3fv( vRight );
|
|
|
|
glVertex3fv( vRight );
|
2023-05-11 22:48:13 +02:00
|
|
|
glVertex3fv( ori.origin );
|
2023-05-08 14:33:37 +02:00
|
|
|
|
|
|
|
glColor3f( 0, 0, 1 );
|
|
|
|
glVertex3fv( parent_or.origin );
|
|
|
|
VectorAdd( parent_or.origin, parent_or.axis[ 2 ], vUp );
|
|
|
|
glVertex3fv( vUp );
|
|
|
|
glVertex3fv( vUp );
|
2023-05-11 22:48:13 +02:00
|
|
|
glVertex3fv( ori.origin );*/
|
2024-12-19 20:20:43 +01:00
|
|
|
}
|
|
|
|
qglEnd();
|
|
|
|
qglLineWidth(1);
|
|
|
|
} else if (r_showSkeleton->integer == 2) { // draw skeleton with bones
|
|
|
|
orientation_t ori;
|
|
|
|
orientation_t parent_or;
|
|
|
|
int iParentBone;
|
|
|
|
|
|
|
|
qglLineWidth(3);
|
|
|
|
qglBegin(GL_LINES);
|
|
|
|
for (i = 0; i < ri.TIKI_GetNumChannels(tiki); i++) {
|
|
|
|
iParentBone = ri.SKEL_GetBoneParent(skeletor, i);
|
|
|
|
|
|
|
|
if (iParentBone > 0) {
|
|
|
|
ori = R_GetTagPositionAndOrientation(&ent->e, i);
|
|
|
|
parent_or = R_GetTagPositionAndOrientation(&ent->e, iParentBone);
|
|
|
|
qglColor3f(1, 1, 1);
|
|
|
|
qglVertex3fv(parent_or.origin);
|
|
|
|
qglVertex3fv(ori.origin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
qglEnd();
|
|
|
|
|
|
|
|
qglBegin(GL_LINES);
|
|
|
|
for (i = 0; i < ri.TIKI_GetNumChannels(tiki); i++) {
|
|
|
|
vec3_t up, down, front;
|
|
|
|
ori = R_GetTagPositionAndOrientation(&ent->e, i);
|
|
|
|
|
|
|
|
VectorCopy(ori.origin, up);
|
|
|
|
up[1] += 5;
|
|
|
|
VectorCopy(ori.origin, down);
|
|
|
|
down[1] += 5;
|
|
|
|
VectorCopy(ori.origin, front);
|
|
|
|
front[0] += 5;
|
|
|
|
|
|
|
|
qglColor3f(1, 0, 1);
|
|
|
|
qglVertex3fv(front);
|
|
|
|
qglVertex3fv(ori.origin);
|
|
|
|
qglVertex3fv(down);
|
|
|
|
qglVertex3fv(ori.origin);
|
|
|
|
qglVertex3fv(up);
|
|
|
|
qglVertex3fv(ori.origin);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
qglEnd();
|
|
|
|
qglLineWidth(1);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
2023-05-26 23:32:27 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
surfaceType_t skelSurface = SF_TIKI_SKEL;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
R_AddSkelSurfaces
|
|
|
|
==============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void R_AddSkelSurfaces(trRefEntity_t *ent)
|
|
|
|
{
|
|
|
|
dtiki_t *tiki;
|
|
|
|
qboolean personalModel;
|
|
|
|
float tiki_scale;
|
|
|
|
vec3_t tiki_localorigin;
|
|
|
|
vec3_t tiki_worldorigin;
|
|
|
|
skelBoneCache_t *outbones;
|
|
|
|
//int tikiSurfNumOffset;
|
|
|
|
static cvar_t *vmEntity = NULL;
|
|
|
|
skeletor_c *skeletor;
|
|
|
|
float radius;
|
|
|
|
SkelVec3 centroid;
|
|
|
|
skelAnimFrame_t *newFrame;
|
|
|
|
int added;
|
|
|
|
shader_t *shader;
|
|
|
|
dtikisurface_t *dsurf;
|
|
|
|
byte *bsurf;
|
|
|
|
//float range;
|
|
|
|
//int render_count, total_tris;
|
|
|
|
skelSurfaceGame_t *surface;
|
|
|
|
//int skinnum;
|
|
|
|
//float target;
|
|
|
|
Vector newDistance;
|
|
|
|
//vec3_t org;
|
|
|
|
int i;
|
|
|
|
int mesh;
|
|
|
|
int iRadiusCull = 0;
|
|
|
|
int num_tags;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
tiki = ent->e.tiki;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (!vmEntity) {
|
|
|
|
vmEntity = ri.Cvar_Get("viewmodelentity", "", 0);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
R_UpdatePoseInternal(&ent->e);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
// don't add third_person objects if in a portal
|
|
|
|
personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
outbones = &TIKI_Skel_Bones[TIKI_Skel_Bones_Index];
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
num_tags = ri.TIKI_GetNumChannels(tiki);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (num_tags + TIKI_Skel_Bones_Index > MAX_SKELBONES) {
|
|
|
|
ri.Printf(PRINT_DEVELOPER, "R_AddSkelSurfaces: too many skeleton models visible on '%s'\n", tiki->a->name);
|
|
|
|
return;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
tiki_scale = tiki->load_scale * ent->e.scale;
|
|
|
|
VectorScale(tiki->load_origin, tiki_scale, tiki_localorigin);
|
|
|
|
R_LocalPointToWorld(tiki_localorigin, tiki_worldorigin);
|
|
|
|
|
|
|
|
radius = R_GetRadius(&ent->e);
|
|
|
|
|
|
|
|
if (!lod_tool->integer) {
|
|
|
|
iRadiusCull = R_CullPointAndRadius(tiki_worldorigin, radius);
|
|
|
|
if (r_showcull->integer & 2) {
|
|
|
|
switch (iRadiusCull) {
|
|
|
|
case CULL_IN:
|
|
|
|
R_DebugCircle(tiki_worldorigin, radius * 1.2f, 0, 1, 0, 0.5f, qfalse);
|
2023-08-28 02:32:33 +02:00
|
|
|
break;
|
|
|
|
case CULL_CLIP:
|
2023-08-28 15:20:31 +02:00
|
|
|
R_DebugCircle(tiki_worldorigin, radius * 1.2f, 0, 1, 0, 0.5f, qfalse);
|
2023-08-28 02:32:33 +02:00
|
|
|
break;
|
|
|
|
case CULL_OUT:
|
2023-08-28 15:20:31 +02:00
|
|
|
R_DebugCircle(tiki_worldorigin, radius * 1.2f + 32.f, 1, 0.2f, 0.2f, 0.5f, qfalse);
|
2023-08-28 02:32:33 +02:00
|
|
|
break;
|
2024-12-19 20:20:43 +01:00
|
|
|
}
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
switch (iRadiusCull) {
|
2023-08-28 02:32:33 +02:00
|
|
|
case CULL_IN:
|
|
|
|
tr.pc.c_sphere_cull_md3_in++;
|
|
|
|
break;
|
|
|
|
case CULL_CLIP:
|
|
|
|
tr.pc.c_sphere_cull_md3_clip++;
|
|
|
|
break;
|
|
|
|
case CULL_OUT:
|
|
|
|
tr.pc.c_sphere_cull_md3_out++;
|
|
|
|
break;
|
|
|
|
}
|
2024-12-19 20:20:43 +01:00
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (tiki->a->bIsCharacter) {
|
2023-08-28 02:32:33 +02:00
|
|
|
if (tr.viewParms.isPortal) {
|
|
|
|
ent->lodpercentage[1] = R_CalcLod(tiki_worldorigin, 92.f / ent->e.scale);
|
|
|
|
} else {
|
|
|
|
ent->lodpercentage[0] = R_CalcLod(tiki_worldorigin, 92.f / ent->e.scale);
|
|
|
|
}
|
2024-12-19 20:20:43 +01:00
|
|
|
} else {
|
|
|
|
if (tr.viewParms.isPortal) {
|
|
|
|
ent->lodpercentage[1] = R_CalcLod(tiki_worldorigin, radius / ent->e.scale);
|
|
|
|
} else {
|
|
|
|
ent->lodpercentage[0] = R_CalcLod(tiki_worldorigin, radius / ent->e.scale);
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
newFrame = (skelAnimFrame_t *)ri.Hunk_AllocateTempMemory(
|
|
|
|
sizeof(skelAnimFrame_t) + ri.TIKI_GetNumChannels(tiki) * sizeof(SkelMat4)
|
|
|
|
);
|
|
|
|
R_GetFrame(&ent->e, newFrame);
|
|
|
|
|
|
|
|
if (lod_tool->integer || iRadiusCull != CULL_CLIP
|
|
|
|
|| R_CullSkelModel(tiki, &ent->e, newFrame, tiki_scale, tiki_localorigin) != CULL_OUT) {
|
|
|
|
//
|
|
|
|
// copy bones position and axis
|
|
|
|
//
|
|
|
|
for (i = 0; i < num_tags; i++) {
|
|
|
|
VectorCopy(newFrame->bones[i][3], outbones->offset);
|
|
|
|
outbones->matrix[0][0] = newFrame->bones[i][0][0];
|
|
|
|
outbones->matrix[0][1] = newFrame->bones[i][0][1];
|
|
|
|
outbones->matrix[0][2] = newFrame->bones[i][0][2];
|
|
|
|
outbones->matrix[0][3] = 0;
|
|
|
|
outbones->matrix[1][0] = newFrame->bones[i][1][0];
|
|
|
|
outbones->matrix[1][1] = newFrame->bones[i][1][1];
|
|
|
|
outbones->matrix[1][2] = newFrame->bones[i][1][2];
|
|
|
|
outbones->matrix[1][3] = 0;
|
|
|
|
outbones->matrix[2][0] = newFrame->bones[i][2][0];
|
|
|
|
outbones->matrix[2][1] = newFrame->bones[i][2][1];
|
|
|
|
outbones->matrix[2][2] = newFrame->bones[i][2][2];
|
|
|
|
outbones->matrix[2][3] = 0;
|
|
|
|
outbones++;
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
ri.Hunk_FreeTempMemory(newFrame);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
ent->e.bonestart = TIKI_Skel_Bones_Index;
|
|
|
|
TIKI_Skel_Bones_Index += num_tags;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
ent->e.hasMorph = qfalse;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
//
|
|
|
|
// get the skeletor
|
|
|
|
//
|
|
|
|
skeletor = (skeletor_c *)ri.TIKI_GetSkeletor(tiki, ent->e.entityNumber);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
//
|
|
|
|
// add morphs
|
|
|
|
//
|
|
|
|
added = ri.SKEL_GetMorphWeightFrame(
|
|
|
|
skeletor, ent->e.frameInfo[0].index, ent->e.frameInfo[0].time, &skeletorMorphCache[skeletorMorphCacheIndex]
|
|
|
|
);
|
|
|
|
ent->e.morphstart = skeletorMorphCacheIndex;
|
|
|
|
|
|
|
|
if (added) {
|
|
|
|
// found morphs
|
|
|
|
skeletorMorphCacheIndex += added;
|
|
|
|
ent->e.hasMorph = qtrue;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
//
|
|
|
|
// draw all meshes
|
|
|
|
//
|
|
|
|
dsurf = tiki->surfaces;
|
|
|
|
bsurf = &ent->e.surfaces[0];
|
|
|
|
for (mesh = 0; mesh < tiki->numMeshes; mesh++) {
|
|
|
|
skelHeaderGame_t *skelmodel = ri.TIKI_GetSkel(tiki->mesh[mesh]);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (!skelmodel) {
|
|
|
|
ri.Printf(PRINT_DEVELOPER, "R_AddSkelSurfaces: couldn't get skel model in '%s'\n", tiki->a->name);
|
|
|
|
return;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (lod_tool->integer && !stricmp(ent->e.tiki->a->name, lod_tikiname->string)) {
|
|
|
|
if (lod_mesh->integer > tiki->numMeshes - 1) {
|
|
|
|
ri.Cvar_Set("lod_mesh", va("%d", tiki->numMeshes - 1));
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (mesh == lod_mesh->integer) {
|
|
|
|
if (skelmodel->pLOD) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
//
|
|
|
|
// draw all surfaces
|
|
|
|
//
|
|
|
|
surface = skelmodel->pSurfaces;
|
|
|
|
for (i = 0; i < skelmodel->numSurfaces; i++, dsurf++, bsurf++, surface = surface->pNext) {
|
|
|
|
if (*bsurf & 4) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
shader = NULL;
|
|
|
|
surface->ident = SF_TIKI_SKEL;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
// use a custom shader if specified
|
|
|
|
if (!(ent->e.customShader) || (ent->e.renderfx & RF_CUSTOMSHADERPASS)) {
|
|
|
|
int iShaderNum = ent->e.skinNum + (*bsurf & 3);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (iShaderNum >= dsurf->numskins) {
|
|
|
|
iShaderNum = 0;
|
|
|
|
}
|
|
|
|
shader = tr.shaders[dsurf->hShader[iShaderNum]];
|
|
|
|
} else {
|
|
|
|
shader = R_GetShaderByHandle(ent->e.customShader);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (!personalModel) {
|
|
|
|
if ((*bsurf & 0x40) && (dsurf->numskins > 1)) {
|
|
|
|
int iShaderNum = ent->e.skinNum + (*bsurf & 2);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
R_AddDrawSurf((surfaceType_t *)surface, tr.shaders[dsurf->hShader[iShaderNum]], 0);
|
|
|
|
R_AddDrawSurf((surfaceType_t *)surface, tr.shaders[dsurf->hShader[iShaderNum + 1]], 0);
|
|
|
|
} else {
|
|
|
|
R_AddDrawSurf((surfaceType_t *)surface, shader, 0);
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if ((ent->e.customShader) && (ent->e.renderfx & RF_CUSTOMSHADERPASS)) {
|
|
|
|
shader = R_GetShaderByHandle(ent->e.customShader);
|
|
|
|
R_AddDrawSurf((surfaceType_t *)surface, shader, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
// FIXME: setup LOD
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
SkelVertGetNormal
|
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
inline static void SkelVertGetNormal(skeletorVertex_t *vert, skelBoneCache_t *bone, vec3_t out)
|
|
|
|
{
|
|
|
|
out[0] = vert->normal[0] * bone->matrix[0][0] + vert->normal[1] * bone->matrix[1][0]
|
|
|
|
+ vert->normal[2] * bone->matrix[2][0];
|
|
|
|
|
|
|
|
out[1] = vert->normal[0] * bone->matrix[0][1] + vert->normal[1] * bone->matrix[1][1]
|
|
|
|
+ vert->normal[2] * bone->matrix[2][1];
|
|
|
|
|
|
|
|
out[2] = vert->normal[0] * bone->matrix[0][2] + vert->normal[1] * bone->matrix[1][2]
|
|
|
|
+ vert->normal[2] * bone->matrix[2][2];
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
SkelMorphGetXyz
|
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
inline static void SkelMorphGetXyz(skeletorMorph_t *morph, int *morphcache, vec3_t out)
|
|
|
|
{
|
|
|
|
VectorMA(out, *morphcache, morph->offset, out);
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
SkelWeightGetXyz
|
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
inline static void SkelWeightGetXyz(skelWeight_t *weight, skelBoneCache_t *bone, vec3_t out)
|
|
|
|
{
|
|
|
|
out[0] += ((weight->offset[0] * bone->matrix[0][0] + weight->offset[1] * bone->matrix[1][0]
|
|
|
|
+ weight->offset[2] * bone->matrix[2][0])
|
|
|
|
+ bone->offset[0])
|
|
|
|
* weight->boneWeight;
|
|
|
|
|
|
|
|
out[1] += ((weight->offset[0] * bone->matrix[0][1] + weight->offset[1] * bone->matrix[1][1]
|
|
|
|
+ weight->offset[2] * bone->matrix[2][1])
|
|
|
|
+ bone->offset[1])
|
|
|
|
* weight->boneWeight;
|
|
|
|
|
|
|
|
out[2] += ((weight->offset[0] * bone->matrix[0][2] + weight->offset[1] * bone->matrix[1][2]
|
|
|
|
+ weight->offset[2] * bone->matrix[2][2])
|
|
|
|
+ bone->offset[2])
|
|
|
|
* weight->boneWeight;
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
SkelWeightMorphGetXyz
|
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
inline static void SkelWeightMorphGetXyz(skelWeight_t *weight, skelBoneCache_t *bone, vec3_t totalmorph, vec3_t out)
|
|
|
|
{
|
|
|
|
vec3_t point;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
VectorAdd(totalmorph, weight->offset, point);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
out[0] += ((point[0] * bone->matrix[0][0] + point[1] * bone->matrix[1][0] + point[2] * bone->matrix[2][0])
|
|
|
|
+ bone->offset[0])
|
|
|
|
* weight->boneWeight;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
out[1] += ((point[0] * bone->matrix[0][1] + point[1] * bone->matrix[1][1] + point[2] * bone->matrix[2][1])
|
|
|
|
+ bone->offset[1])
|
|
|
|
* weight->boneWeight;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
out[2] += ((point[0] * bone->matrix[0][2] + point[1] * bone->matrix[1][2] + point[2] * bone->matrix[2][2])
|
|
|
|
+ bone->offset[2])
|
|
|
|
* weight->boneWeight;
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
2023-08-28 02:32:33 +02:00
|
|
|
RB_SkelMesh
|
2023-05-08 14:33:37 +02:00
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void RB_SkelMesh(skelSurfaceGame_t *sf)
|
|
|
|
{
|
|
|
|
unsigned int baseIndex, baseVertex;
|
|
|
|
unsigned int render_count;
|
|
|
|
unsigned int indexes;
|
|
|
|
float *outXyz;
|
|
|
|
vec4_t *outNormal;
|
|
|
|
skelIndex_t *triangles;
|
|
|
|
skelIndex_t *collapse_map;
|
|
|
|
skeletorVertex_t *newVerts;
|
|
|
|
skeletorMorph_t *morph;
|
|
|
|
skelWeight_t *weight;
|
|
|
|
int vertNum;
|
|
|
|
int morphNum;
|
|
|
|
int weightNum;
|
|
|
|
skelBoneCache_t *bones;
|
|
|
|
skelBoneCache_t *bone;
|
|
|
|
int *morphs;
|
|
|
|
int *morphcache;
|
|
|
|
float scale;
|
|
|
|
dtiki_t *tiki;
|
|
|
|
int mesh;
|
|
|
|
int surf;
|
|
|
|
int i;
|
|
|
|
skelHeaderGame_t *skelmodel;
|
|
|
|
skelSurfaceGame_t *psurface;
|
|
|
|
qboolean bFound;
|
|
|
|
short collapse[TIKI_MAX_VERTEXES];
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (!r_drawentitypoly->integer) {
|
|
|
|
return;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
tiki = backEnd.currentEntity->e.tiki;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
scale = tiki->load_scale * backEnd.currentEntity->e.scale;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
//
|
|
|
|
// get the mesh associated with the surface
|
|
|
|
//
|
|
|
|
bFound = qfalse;
|
|
|
|
for (mesh = 0; mesh < tiki->numMeshes; mesh++) {
|
|
|
|
skelmodel = ri.TIKI_GetSkel(tiki->mesh[mesh]);
|
|
|
|
psurface = skelmodel->pSurfaces;
|
|
|
|
|
|
|
|
// find the surface
|
|
|
|
for (surf = 0; surf < skelmodel->numSurfaces; surf++) {
|
|
|
|
if (psurface == sf) {
|
|
|
|
bFound = qtrue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
psurface = psurface->pNext;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (bFound) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
assert(bFound);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
//
|
|
|
|
// Process LOD
|
|
|
|
//
|
|
|
|
if (skelmodel->pLOD) {
|
|
|
|
float lod_val;
|
|
|
|
int renderfx;
|
2023-08-28 02:32:33 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
lod_val = backEnd.currentEntity->lodpercentage[0];
|
2023-08-28 02:32:33 +02:00
|
|
|
renderfx = backEnd.currentEntity->e.renderfx;
|
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (sf->numVerts > 3) {
|
|
|
|
skelIndex_t *collapseIndex;
|
|
|
|
int mid, low, high;
|
|
|
|
int lod_cutoff;
|
2023-08-28 02:32:33 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (lod_tool->integer && !strcmp(backEnd.currentEntity->e.tiki->a->name, lod_tikiname->string)
|
|
|
|
&& mesh == lod_mesh->integer) {
|
|
|
|
lod_cutoff = GetToolLodCutoff(skelmodel, backEnd.currentEntity->lodpercentage[0]);
|
|
|
|
} else {
|
|
|
|
lod_cutoff = GetLodCutoff(skelmodel, backEnd.currentEntity->lodpercentage[0], renderfx);
|
|
|
|
}
|
2023-08-28 02:32:33 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
collapseIndex = sf->pCollapseIndex;
|
|
|
|
if (collapseIndex[2] < lod_cutoff) {
|
|
|
|
return;
|
|
|
|
}
|
2023-08-28 02:32:33 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
low = mid = 3;
|
|
|
|
high = sf->numVerts;
|
|
|
|
while (high >= low) {
|
|
|
|
mid = (low + high) >> 1;
|
|
|
|
if (collapseIndex[mid] < lod_cutoff) {
|
|
|
|
high = mid - 1;
|
|
|
|
if (collapseIndex[mid - 1] >= lod_cutoff) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mid++;
|
|
|
|
low = mid;
|
|
|
|
if (high == mid || collapseIndex[mid] < lod_cutoff) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-08-28 02:32:33 +02:00
|
|
|
}
|
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
render_count = mid;
|
|
|
|
} else {
|
|
|
|
render_count = sf->numVerts;
|
|
|
|
}
|
2023-08-28 02:32:33 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (!render_count) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
render_count = sf->numVerts;
|
2023-08-28 02:32:33 +02:00
|
|
|
}
|
|
|
|
|
2023-08-28 15:20:31 +02:00
|
|
|
indexes = sf->numTriangles * 3;
|
|
|
|
RB_CHECKOVERFLOW(render_count, indexes);
|
|
|
|
|
2023-08-28 02:32:33 +02:00
|
|
|
collapse_map = sf->pCollapse;
|
2024-12-19 20:20:43 +01:00
|
|
|
triangles = sf->pTriangles;
|
|
|
|
baseIndex = tess.numIndexes;
|
|
|
|
baseVertex = tess.numVertexes;
|
2023-08-28 02:32:33 +02:00
|
|
|
tess.numVertexes += render_count;
|
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
outXyz = tess.xyz[baseVertex];
|
2023-08-28 15:20:31 +02:00
|
|
|
outNormal = &tess.normal[baseVertex];
|
2024-12-19 20:20:43 +01:00
|
|
|
newVerts = sf->pVerts;
|
2023-08-28 15:20:31 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (render_count == sf->numVerts) {
|
|
|
|
for (i = 0; i < indexes; i++) {
|
|
|
|
tess.indexes[baseIndex + i] = baseVertex + triangles[i];
|
|
|
|
}
|
2023-08-28 02:32:33 +02:00
|
|
|
|
2024-09-11 19:03:41 +02:00
|
|
|
entityNumIndexes[backEnd.currentEntity - backEnd.refdef.entities] += indexes;
|
2024-12-19 20:20:43 +01:00
|
|
|
tess.numIndexes += indexes;
|
|
|
|
} else {
|
|
|
|
assert(sf->numVerts < TIKI_MAX_VERTEXES);
|
2024-05-20 13:10:12 +02:00
|
|
|
|
2023-08-28 02:32:33 +02:00
|
|
|
for (i = 0; i < render_count; i++) {
|
|
|
|
collapse[i] = i;
|
|
|
|
}
|
2024-05-20 13:10:12 +02:00
|
|
|
|
2023-08-28 02:32:33 +02:00
|
|
|
for (i = render_count; i < sf->numVerts; i++) {
|
|
|
|
collapse[i] = collapse[collapse_map[i]];
|
|
|
|
}
|
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
for (i = 0; i < indexes; i += 3) {
|
|
|
|
assert(collapse[triangles[i]] < sf->numVerts);
|
|
|
|
assert(collapse[triangles[i + 1]] < sf->numVerts);
|
|
|
|
assert(collapse[triangles[i + 2]] < sf->numVerts);
|
2024-05-20 13:10:12 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (collapse[triangles[i]] == collapse[triangles[i + 1]]
|
|
|
|
|| collapse[triangles[i + 1]] == collapse[triangles[i + 2]]
|
|
|
|
|| collapse[triangles[i + 2]] == collapse[triangles[i]]) {
|
2023-08-28 02:32:33 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
tess.indexes[baseIndex + i] = baseVertex + collapse[triangles[i]];
|
2023-08-28 02:32:33 +02:00
|
|
|
tess.indexes[baseIndex + i + 1] = baseVertex + collapse[triangles[i + 1]];
|
|
|
|
tess.indexes[baseIndex + i + 2] = baseVertex + collapse[triangles[i + 2]];
|
|
|
|
}
|
2024-09-11 19:03:41 +02:00
|
|
|
|
|
|
|
entityNumIndexes[backEnd.currentEntity - backEnd.refdef.entities] += indexes;
|
2023-08-28 02:32:33 +02:00
|
|
|
tess.numIndexes += i;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
//
|
|
|
|
// just copy the vertexes
|
|
|
|
//
|
|
|
|
bones = &TIKI_Skel_Bones[backEnd.currentEntity->e.bonestart];
|
|
|
|
morphs = &skeletorMorphCache[backEnd.currentEntity->e.morphstart];
|
|
|
|
|
|
|
|
if (backEnd.currentEntity->e.hasMorph) {
|
|
|
|
if (mesh > 0) {
|
|
|
|
for (vertNum = 0; vertNum < render_count; vertNum++) {
|
|
|
|
vec3_t normal;
|
|
|
|
vec3_t out;
|
|
|
|
vec3_t totalmorph;
|
|
|
|
int channelNum;
|
|
|
|
int boneNum;
|
|
|
|
|
|
|
|
VectorClear(out);
|
|
|
|
VectorClear(outXyz);
|
|
|
|
VectorClear(totalmorph);
|
|
|
|
|
|
|
|
weight = (skelWeight_t *)((byte *)newVerts + sizeof(skeletorVertex_t)
|
|
|
|
+ sizeof(skeletorMorph_t) * newVerts->numMorphs);
|
|
|
|
morph = (skeletorMorph_t *)((byte *)newVerts + sizeof(skeletorVertex_t));
|
|
|
|
|
|
|
|
for (morphNum = 0; morphNum < newVerts->numMorphs; morphNum++) {
|
|
|
|
morphcache = &morphs[morph->morphIndex];
|
|
|
|
|
|
|
|
if (*morphcache) {
|
|
|
|
SkelMorphGetXyz(morph, morphcache, totalmorph);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
morph++;
|
|
|
|
}
|
2024-06-04 19:56:01 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (newVerts->numMorphs) {
|
|
|
|
channelNum = skelmodel->pBones[morph->morphIndex].channel;
|
|
|
|
} else {
|
|
|
|
channelNum = skelmodel->pBones[weight->boneIndex].channel;
|
|
|
|
}
|
2024-06-04 19:56:01 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
boneNum = ri.TIKI_GetLocalChannel(tiki, channelNum);
|
|
|
|
bone = &bones[boneNum];
|
2024-06-04 19:56:01 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
SkelVertGetNormal(newVerts, bone, normal);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
for (weightNum = 0; weightNum < newVerts->numWeights; weightNum++) {
|
|
|
|
channelNum = skelmodel->pBones[weight->boneIndex].channel;
|
|
|
|
boneNum = ri.TIKI_GetLocalChannel(tiki, channelNum);
|
|
|
|
bone = &bones[boneNum];
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (!weightNum) {
|
|
|
|
SkelWeightMorphGetXyz(weight, bone, totalmorph, out);
|
|
|
|
} else {
|
|
|
|
SkelWeightGetXyz(weight, bone, out);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
weight++;
|
|
|
|
}
|
2024-06-04 19:52:32 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
VectorCopy(normal, *outNormal);
|
|
|
|
VectorScale(out, scale, outXyz);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
tess.texCoords[baseVertex + vertNum][0][0] = newVerts->texCoords[0];
|
|
|
|
tess.texCoords[baseVertex + vertNum][0][1] = newVerts->texCoords[1];
|
|
|
|
// FIXME: fill in lightmapST for completeness?
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
newVerts = (skeletorVertex_t *)((byte *)newVerts + sizeof(skeletorVertex_t)
|
|
|
|
+ sizeof(skeletorMorph_t) * newVerts->numMorphs
|
|
|
|
+ sizeof(skelWeight_t) * newVerts->numWeights);
|
|
|
|
outXyz += 4;
|
|
|
|
outNormal++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (vertNum = 0; vertNum < render_count; vertNum++) {
|
|
|
|
vec3_t normal;
|
|
|
|
vec3_t out;
|
|
|
|
vec3_t totalmorph;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
VectorClear(out);
|
|
|
|
VectorClear(outXyz);
|
|
|
|
VectorClear(totalmorph);
|
2024-06-04 19:56:01 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
weight = (skelWeight_t *)((byte *)newVerts + sizeof(skeletorVertex_t)
|
|
|
|
+ sizeof(skeletorMorph_t) * newVerts->numMorphs);
|
|
|
|
morph = (skeletorMorph_t *)((byte *)newVerts + sizeof(skeletorVertex_t));
|
2024-06-04 19:56:01 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
for (morphNum = 0; morphNum < newVerts->numMorphs; morphNum++) {
|
|
|
|
morphcache = &morphs[morph->morphIndex];
|
2024-06-04 19:56:01 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (*morphcache) {
|
|
|
|
SkelMorphGetXyz(morph, morphcache, totalmorph);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
morph++;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (newVerts->numMorphs) {
|
|
|
|
bone = &bones[morph->morphIndex];
|
|
|
|
} else {
|
|
|
|
bone = &bones[weight->boneIndex];
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
SkelVertGetNormal(newVerts, bone, normal);
|
2024-06-04 19:56:01 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
for (weightNum = 0; weightNum < newVerts->numWeights; weightNum++) {
|
|
|
|
bone = &bones[weight->boneIndex];
|
2024-06-04 19:56:01 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (!weightNum) {
|
|
|
|
SkelWeightMorphGetXyz(weight, bone, totalmorph, out);
|
|
|
|
} else {
|
|
|
|
SkelWeightGetXyz(weight, bone, out);
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
weight++;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
VectorCopy(normal, *outNormal);
|
|
|
|
VectorScale(out, scale, outXyz);
|
2024-06-04 19:56:01 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
tess.texCoords[baseVertex + vertNum][0][0] = newVerts->texCoords[0];
|
|
|
|
tess.texCoords[baseVertex + vertNum][0][1] = newVerts->texCoords[1];
|
|
|
|
// FIXME: fill in lightmapST for completeness?
|
2024-06-04 19:56:01 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
newVerts = (skeletorVertex_t *)((byte *)newVerts + sizeof(skeletorVertex_t)
|
|
|
|
+ sizeof(skeletorMorph_t) * newVerts->numMorphs
|
|
|
|
+ sizeof(skelWeight_t) * newVerts->numWeights);
|
|
|
|
outXyz += 4;
|
|
|
|
outNormal++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (mesh > 0) {
|
|
|
|
for (vertNum = 0; vertNum < render_count; vertNum++) {
|
|
|
|
vec3_t normal;
|
|
|
|
vec3_t out;
|
|
|
|
int channelNum;
|
|
|
|
int boneNum;
|
2024-06-04 19:56:01 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
VectorClear(out);
|
|
|
|
VectorClear(outXyz);
|
2024-06-04 19:56:01 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
weight = (skelWeight_t *)((byte *)newVerts + sizeof(skeletorVertex_t)
|
|
|
|
+ sizeof(skeletorMorph_t) * newVerts->numMorphs);
|
2024-06-04 19:56:01 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
channelNum = skelmodel->pBones[weight->boneIndex].channel;
|
|
|
|
boneNum = ri.TIKI_GetLocalChannel(tiki, channelNum);
|
|
|
|
bone = &bones[boneNum];
|
2024-06-04 19:56:01 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
SkelVertGetNormal(newVerts, bone, normal);
|
2023-07-15 21:21:27 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
for (weightNum = 0; weightNum < newVerts->numWeights; weightNum++) {
|
|
|
|
channelNum = skelmodel->pBones[weight->boneIndex].channel;
|
|
|
|
boneNum = ri.TIKI_GetLocalChannel(tiki, channelNum);
|
|
|
|
bone = &bones[boneNum];
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
SkelWeightGetXyz(weight, bone, out);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
weight++;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
VectorCopy(normal, *outNormal);
|
|
|
|
VectorScale(out, scale, outXyz);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
tess.texCoords[baseVertex + vertNum][0][0] = newVerts->texCoords[0];
|
|
|
|
tess.texCoords[baseVertex + vertNum][0][1] = newVerts->texCoords[1];
|
|
|
|
// FIXME: fill in lightmapST for completeness?
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
newVerts = (skeletorVertex_t *)((byte *)newVerts + sizeof(skeletorVertex_t)
|
|
|
|
+ sizeof(skeletorMorph_t) * newVerts->numMorphs
|
|
|
|
+ sizeof(skelWeight_t) * newVerts->numWeights);
|
|
|
|
outXyz += 4;
|
|
|
|
outNormal++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (vertNum = 0; vertNum < render_count; vertNum++) {
|
|
|
|
vec3_t normal;
|
|
|
|
vec3_t out;
|
|
|
|
|
|
|
|
VectorClear(out);
|
|
|
|
VectorClear(outXyz);
|
|
|
|
|
|
|
|
weight = (skelWeight_t *)((byte *)newVerts + sizeof(skeletorVertex_t)
|
|
|
|
+ sizeof(skeletorMorph_t) * newVerts->numMorphs);
|
|
|
|
|
|
|
|
bone = &bones[weight->boneIndex];
|
|
|
|
SkelVertGetNormal(newVerts, bone, normal);
|
|
|
|
|
|
|
|
for (weightNum = 0; weightNum < newVerts->numWeights; weightNum++) {
|
|
|
|
bone = &bones[weight->boneIndex];
|
|
|
|
|
|
|
|
SkelWeightGetXyz(weight, bone, out);
|
|
|
|
|
|
|
|
weight++;
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorCopy(normal, *outNormal);
|
|
|
|
VectorScale(out, scale, outXyz);
|
|
|
|
|
|
|
|
tess.texCoords[baseVertex + vertNum][0][0] = newVerts->texCoords[0];
|
|
|
|
tess.texCoords[baseVertex + vertNum][0][1] = newVerts->texCoords[1];
|
|
|
|
// FIXME: fill in lightmapST for completeness?
|
|
|
|
|
|
|
|
newVerts = (skeletorVertex_t *)((byte *)newVerts + sizeof(skeletorVertex_t)
|
|
|
|
+ sizeof(skeletorMorph_t) * newVerts->numMorphs
|
|
|
|
+ sizeof(skelWeight_t) * newVerts->numWeights);
|
|
|
|
outXyz += 4;
|
|
|
|
outNormal++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
if( backEnd.currentEntity->e.staticModelIndex ) {
|
|
|
|
mstaticModel_t *sm;
|
|
|
|
color4ub_t *out;
|
|
|
|
color3ub_t *in;
|
|
|
|
int cdofs;
|
|
|
|
skdSurface_t *sf2;
|
|
|
|
|
|
|
|
sm = &tr.world->staticModels[ backEnd.currentEntity->e.staticModelIndex - 1 ];
|
|
|
|
|
|
|
|
tess.useStaticModelVertexColors = qtrue;
|
|
|
|
|
|
|
|
cdofs = 0;
|
|
|
|
sf2 = tiki->surfs;
|
|
|
|
while( sf2 != sf ) {
|
|
|
|
cdofs += sf2->numVerts;
|
|
|
|
sf2 = ( skdSurface_t* )( ( ( byte* )sf2 ) + sf2->ofsEnd );
|
|
|
|
}
|
|
|
|
|
|
|
|
in = &tr.world->smColors[ sm->firstVert + cdofs ];
|
|
|
|
out = tess.vertexColors + baseVertex;
|
|
|
|
for( i = 0; i < sf->numVerts; i++, in++, out++ ) {
|
2024-12-19 20:20:43 +01:00
|
|
|
# if 1
|
2023-05-08 14:33:37 +02:00
|
|
|
( *out )[ 0 ] = ( *in )[ 0 ];
|
|
|
|
( *out )[ 1 ] = ( *in )[ 1 ];
|
|
|
|
( *out )[ 2 ] = ( *in )[ 2 ];
|
|
|
|
( *out )[ 3 ] = 255;
|
2024-12-19 20:20:43 +01:00
|
|
|
# elif 0
|
2023-05-08 14:33:37 +02:00
|
|
|
( ( int* )out ) = tr.identityLightByte;
|
2024-12-19 20:20:43 +01:00
|
|
|
# else
|
2023-05-08 14:33:37 +02:00
|
|
|
// su44: set it to something special so
|
|
|
|
// I can debug vertex colors rendering
|
|
|
|
( *out )[ 0 ] = 255;
|
|
|
|
( *out )[ 1 ] = 0;
|
|
|
|
( *out )[ 2 ] = 0;
|
|
|
|
( *out )[ 3 ] = 255;
|
2024-12-19 20:20:43 +01:00
|
|
|
# endif
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
} //else
|
|
|
|
#endif
|
2024-12-19 20:20:43 +01:00
|
|
|
//{
|
|
|
|
// // an attemp to fix bizarre vertex colors bug
|
|
|
|
// color4ub_t *col;
|
|
|
|
//
|
|
|
|
// col = &tess.vertexColors[baseVertex];
|
|
|
|
// for(i = 0; i < sf->numVerts; i++,col++) {
|
|
|
|
// (*col)[0] = 255;
|
|
|
|
// (*col)[1] = 255;
|
|
|
|
// (*col)[2] = 255;
|
|
|
|
// (*col)[3] = 255;
|
|
|
|
// }
|
|
|
|
//}
|
|
|
|
//tess.numVertexes += sf->numVerts;
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
2024-09-11 19:03:41 +02:00
|
|
|
/*
|
|
|
|
=============
|
|
|
|
RB_StaticMesh
|
|
|
|
=============
|
|
|
|
*/
|
|
|
|
void RB_StaticMesh(staticSurface_t *staticSurf)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
dtiki_t *tiki;
|
|
|
|
skelSurfaceGame_t *surf;
|
|
|
|
int meshNum;
|
|
|
|
skelHeaderGame_t *skelmodel;
|
|
|
|
int render_count;
|
|
|
|
skelIndex_t *collapse_map;
|
|
|
|
skelIndex_t *triangles;
|
|
|
|
int indexes;
|
|
|
|
int baseIndex, baseVertex;
|
|
|
|
short collapse[1000];
|
|
|
|
|
|
|
|
if (!r_drawstaticmodelpoly->integer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(backEnd.currentStaticModel);
|
|
|
|
tiki = backEnd.currentStaticModel->tiki;
|
|
|
|
surf = staticSurf->surface;
|
|
|
|
|
|
|
|
assert(surf->pStaticXyz);
|
2024-12-19 20:20:43 +01:00
|
|
|
if (!surf->pStaticXyz) {
|
|
|
|
return;
|
|
|
|
}
|
2024-09-11 19:03:41 +02:00
|
|
|
|
|
|
|
meshNum = staticSurf->meshNum;
|
2024-12-06 00:09:07 +01:00
|
|
|
skelmodel = ri.TIKI_GetSkel(tiki->mesh[meshNum]);
|
2024-09-11 19:03:41 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// Process LOD
|
|
|
|
//
|
|
|
|
if (skelmodel->pLOD && r_staticlod->integer) {
|
|
|
|
float lod_val;
|
|
|
|
|
|
|
|
lod_val = backEnd.currentStaticModel->lodpercentage[0];
|
|
|
|
|
|
|
|
if (surf->numVerts > 3) {
|
|
|
|
skelIndex_t *collapseIndex;
|
|
|
|
int mid, low, high;
|
|
|
|
int lod_cutoff;
|
|
|
|
|
|
|
|
if (lod_tool->integer && !strcmp(backEnd.currentStaticModel->tiki->a->name, lod_tikiname->string)
|
|
|
|
&& meshNum == lod_mesh->integer) {
|
|
|
|
lod_cutoff = GetToolLodCutoff(skelmodel, backEnd.currentStaticModel->lodpercentage[0]);
|
|
|
|
} else {
|
|
|
|
lod_cutoff = GetLodCutoff(skelmodel, backEnd.currentStaticModel->lodpercentage[0], 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
collapseIndex = surf->pCollapseIndex;
|
|
|
|
if (collapseIndex[2] < lod_cutoff) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
low = mid = 3;
|
|
|
|
high = surf->numVerts;
|
|
|
|
while (high >= low) {
|
|
|
|
mid = (low + high) >> 1;
|
|
|
|
if (collapseIndex[mid] < lod_cutoff) {
|
|
|
|
high = mid - 1;
|
|
|
|
if (collapseIndex[mid - 1] >= lod_cutoff) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mid++;
|
|
|
|
low = mid;
|
|
|
|
if (high == mid || collapseIndex[mid] < lod_cutoff) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
render_count = mid;
|
|
|
|
} else {
|
|
|
|
render_count = surf->numVerts;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!render_count) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
render_count = surf->numVerts;
|
|
|
|
}
|
|
|
|
|
|
|
|
indexes = surf->numTriangles * 3;
|
|
|
|
RB_CHECKOVERFLOW(render_count, surf->numTriangles);
|
|
|
|
|
|
|
|
collapse_map = surf->pCollapse;
|
|
|
|
triangles = surf->pTriangles;
|
|
|
|
baseIndex = tess.numIndexes;
|
|
|
|
baseVertex = tess.numVertexes;
|
|
|
|
tess.numVertexes += render_count;
|
|
|
|
|
|
|
|
if (render_count == surf->numVerts) {
|
|
|
|
for (j = 0; j < indexes; j++) {
|
|
|
|
tess.indexes[baseIndex + j] = baseVertex + triangles[j];
|
|
|
|
}
|
|
|
|
|
|
|
|
staticModelNumIndexes[backEnd.currentStaticModel - backEnd.refdef.staticModels] += indexes;
|
|
|
|
tess.numIndexes += indexes;
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < render_count; i++) {
|
|
|
|
collapse[i] = i;
|
|
|
|
}
|
|
|
|
for (i = render_count; i < surf->numVerts; i++) {
|
|
|
|
collapse[i] = collapse[collapse_map[i]];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = 0; j < indexes; j += 3) {
|
|
|
|
if (collapse[triangles[j]] == collapse[triangles[j + 1]]
|
|
|
|
|| collapse[triangles[j + 1]] == collapse[triangles[j + 2]]
|
|
|
|
|| collapse[triangles[j + 2]] == collapse[triangles[j]]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
tess.indexes[baseIndex + j] = baseVertex + collapse[triangles[j]];
|
|
|
|
tess.indexes[baseIndex + j + 1] = baseVertex + collapse[triangles[j + 1]];
|
|
|
|
tess.indexes[baseIndex + j + 2] = baseVertex + collapse[triangles[j + 2]];
|
|
|
|
}
|
|
|
|
|
|
|
|
staticModelNumIndexes[backEnd.currentStaticModel - backEnd.refdef.staticModels] += j;
|
|
|
|
tess.numIndexes += j;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = 0; j < render_count; j++) {
|
|
|
|
Vector4Copy(surf->pStaticXyz[j], tess.xyz[baseVertex + j]);
|
|
|
|
Vector4Copy(surf->pStaticNormal[j], tess.normal[baseVertex + j]);
|
|
|
|
tess.texCoords[baseVertex + j][0][0] = surf->pStaticTexCoords[j][0][0];
|
|
|
|
tess.texCoords[baseVertex + j][0][1] = surf->pStaticTexCoords[j][0][1];
|
|
|
|
tess.texCoords[baseVertex + j][1][0] = surf->pStaticTexCoords[j][1][0];
|
|
|
|
tess.texCoords[baseVertex + j][1][1] = surf->pStaticTexCoords[j][1][1];
|
|
|
|
}
|
|
|
|
|
2024-12-01 16:26:26 +01:00
|
|
|
if (backEndData->staticModelData) {
|
2024-09-11 19:03:41 +02:00
|
|
|
const size_t offset =
|
|
|
|
backEnd.currentStaticModel->firstVertexData + staticSurf->ofsStaticData * sizeof(color4ub_t);
|
|
|
|
assert(offset < tr.world->numStaticModelData * sizeof(color4ub_t));
|
|
|
|
assert(offset + render_count * sizeof(color4ub_t) <= tr.world->numStaticModelData * sizeof(color4ub_t));
|
|
|
|
|
2024-12-01 16:26:26 +01:00
|
|
|
const color4ub_t *in = (const color4ub_t *)&backEndData->staticModelData[offset];
|
2024-09-11 19:03:41 +02:00
|
|
|
|
|
|
|
for (i = 0; i < render_count; i++) {
|
|
|
|
tess.vertexColors[baseVertex + i][0] = in[i][0];
|
|
|
|
tess.vertexColors[baseVertex + i][1] = in[i][1];
|
|
|
|
tess.vertexColors[baseVertex + i][2] = in[i][2];
|
|
|
|
tess.vertexColors[baseVertex + i][3] = in[i][3];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < render_count; i++) {
|
|
|
|
tess.vertexColors[baseVertex + i][0] = 0xFF;
|
|
|
|
tess.vertexColors[baseVertex + i][1] = 0xFF;
|
|
|
|
tess.vertexColors[baseVertex + i][2] = 0xFF;
|
|
|
|
tess.vertexColors[baseVertex + i][3] = 0xFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tess.vertexColorValid = qtrue;
|
|
|
|
}
|
|
|
|
|
2024-09-11 18:48:47 +02:00
|
|
|
/*
|
|
|
|
=============
|
|
|
|
R_InfoWorldTris_f
|
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void R_InfoWorldTris_f(void)
|
|
|
|
{
|
2024-09-11 18:57:50 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
g_bInfoworldtris = qtrue;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_LEN(entityNumIndexes); i++) {
|
|
|
|
entityNumIndexes[i] = 0;
|
|
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_LEN(staticModelNumIndexes); i++) {
|
|
|
|
staticModelNumIndexes[i] = 0;
|
|
|
|
}
|
2024-09-11 18:48:47 +02:00
|
|
|
}
|
|
|
|
|
2023-05-08 14:33:37 +02:00
|
|
|
/*
|
|
|
|
=============
|
|
|
|
R_PrintInfoWorldtris
|
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void R_PrintInfoWorldtris(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int numTris;
|
|
|
|
int totalNumTris;
|
|
|
|
dtiki_t *tiki;
|
|
|
|
skelHeaderGame_t *skelmodel;
|
2024-09-11 18:57:50 +02:00
|
|
|
|
|
|
|
totalNumTris = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_LEN(entityNumIndexes); i++) {
|
|
|
|
numTris = entityNumIndexes[i] / 3;
|
|
|
|
if (!numTris) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
totalNumTris += numTris;
|
2024-12-19 20:20:43 +01:00
|
|
|
tiki = backEnd.refdef.entities[i].e.tiki;
|
2024-12-06 00:09:07 +01:00
|
|
|
skelmodel = ri.TIKI_GetSkel(tiki->mesh[0]);
|
2024-09-11 18:57:50 +02:00
|
|
|
Com_Printf("ent: %i, tris: %i, %s, version: %i\n", i, numTris, tiki->a->name, skelmodel->version);
|
|
|
|
}
|
|
|
|
|
|
|
|
Com_Printf("total entity tris: %i\n\n", totalNumTris);
|
|
|
|
|
|
|
|
totalNumTris = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_LEN(entityNumIndexes); i++) {
|
|
|
|
numTris = staticModelNumIndexes[i] / 3;
|
|
|
|
if (!numTris) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
totalNumTris += numTris;
|
2024-12-19 20:20:43 +01:00
|
|
|
tiki = backEnd.refdef.staticModels[i].tiki;
|
2024-12-06 00:09:07 +01:00
|
|
|
skelmodel = ri.TIKI_GetSkel(tiki->mesh[0]);
|
2024-09-11 18:57:50 +02:00
|
|
|
Com_Printf("sm: %i, tris: %i, %s, version: %i\n", i, numTris, tiki->a->name, skelmodel->version);
|
|
|
|
}
|
|
|
|
|
|
|
|
Com_Printf("total static model tris: %i\n\n", totalNumTris);
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
RE_SetFrameNumber
|
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void RE_SetFrameNumber(int frameNumber)
|
|
|
|
{
|
|
|
|
tr.frame_skel_index = frameNumber;
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
R_UpdatePoseInternal
|
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void R_UpdatePoseInternal(refEntity_t *model)
|
|
|
|
{
|
|
|
|
if (model->entityNumber != ENTITYNUM_NONE) {
|
|
|
|
if (tr.skel_index[model->entityNumber] == tr.frame_skel_index) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
tr.skel_index[model->entityNumber] = tr.frame_skel_index;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
ri.TIKI_SetPoseInternal(
|
|
|
|
ri.TIKI_GetSkeletor(model->tiki, model->entityNumber),
|
|
|
|
model->frameInfo,
|
|
|
|
model->bone_tag,
|
|
|
|
model->bone_quat,
|
|
|
|
model->actionWeight
|
|
|
|
);
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
RE_ForceUpdatePose
|
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void RE_ForceUpdatePose(refEntity_t *model)
|
|
|
|
{
|
|
|
|
if (model->entityNumber != ENTITYNUM_NONE) {
|
|
|
|
tr.skel_index[model->entityNumber] = tr.frame_skel_index;
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
ri.TIKI_SetPoseInternal(
|
|
|
|
ri.TIKI_GetSkeletor(model->tiki, model->entityNumber),
|
|
|
|
model->frameInfo,
|
|
|
|
model->bone_tag,
|
|
|
|
model->bone_quat,
|
|
|
|
model->actionWeight
|
|
|
|
);
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
RE_TIKI_Orientation
|
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
orientation_t RE_TIKI_Orientation(refEntity_t *model, int tagnum)
|
|
|
|
{
|
|
|
|
R_UpdatePoseInternal(model);
|
|
|
|
return ri.TIKI_OrientationInternal(model->tiki, model->entityNumber, tagnum, model->scale);
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
RE_TIKI_IsOnGround
|
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
qboolean RE_TIKI_IsOnGround(refEntity_t *model, int tagnum, float threshold)
|
|
|
|
{
|
|
|
|
R_UpdatePoseInternal(model);
|
|
|
|
return ri.TIKI_IsOnGroundInternal(model->tiki, model->entityNumber, tagnum, threshold);
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
R_GetRadius
|
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
float R_GetRadius(refEntity_t *model)
|
|
|
|
{
|
|
|
|
R_UpdatePoseInternal(model);
|
|
|
|
return ri.GetRadiusInternal(model->tiki, model->entityNumber, model->scale);
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
R_GetFrame
|
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void R_GetFrame(refEntity_t *model, struct skelAnimFrame_s *newFrame)
|
|
|
|
{
|
|
|
|
R_UpdatePoseInternal(model);
|
|
|
|
ri.GetFrameInternal(model->tiki, model->entityNumber, newFrame);
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
R_DebugSkeleton
|
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void R_DebugSkeleton(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
trRefEntity_t *ent;
|
|
|
|
model_t *model;
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-09-10 20:16:56 +02:00
|
|
|
if (!r_showSkeleton->integer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
R_IssuePendingRenderCommands();
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
GL_Bind(tr.whiteImage);
|
|
|
|
GL_State(GLS_POLYMODE_LINE);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
qglDisableClientState(GL_COLOR_ARRAY);
|
|
|
|
qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
for (i = 0; i < tr.refdef.num_entities; i++) {
|
|
|
|
ent = &tr.refdef.entities[i];
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (ent->e.reType == RT_MODEL) {
|
|
|
|
model = R_GetModelByHandle(ent->e.hModel);
|
2023-05-08 14:33:37 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (model->type == MOD_TIKI) {
|
|
|
|
RE_ForceUpdatePose(&ent->e);
|
|
|
|
RB_DrawSkeletor(ent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 14:33:37 +02:00
|
|
|
}
|
2024-09-11 18:57:50 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
ProjectRadius
|
|
|
|
=============
|
|
|
|
*/
|
2023-05-16 23:35:39 +02:00
|
|
|
static float ProjectRadius(float r, const vec3_t location)
|
|
|
|
{
|
2024-12-19 20:20:43 +01:00
|
|
|
Vector separation;
|
|
|
|
float projectedRadius;
|
2023-05-16 23:35:39 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
separation = Vector(tr.viewParms.ori.origin) - Vector(location);
|
|
|
|
projectedRadius = separation.length();
|
2023-05-16 23:35:39 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
return fabs(r) * (100.0 / tr.viewParms.fovX) / projectedRadius;
|
2023-05-16 23:35:39 +02:00
|
|
|
}
|
|
|
|
|
2024-09-11 18:57:50 +02:00
|
|
|
/*
|
|
|
|
=============
|
|
|
|
R_CalcLod
|
|
|
|
=============
|
|
|
|
*/
|
2023-05-16 23:35:39 +02:00
|
|
|
float R_CalcLod(const vec3_t origin, float radius)
|
|
|
|
{
|
2024-12-19 20:20:43 +01:00
|
|
|
return ProjectRadius(radius, origin);
|
2023-05-16 23:35:39 +02:00
|
|
|
}
|
2024-09-11 18:57:50 +02:00
|
|
|
|
2023-08-28 02:32:33 +02:00
|
|
|
/*
|
|
|
|
=============
|
|
|
|
R_CullTIKI
|
|
|
|
=============
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
static int R_CullSkelModel(dtiki_t *tiki, refEntity_t *e, skelAnimFrame_t *newFrame, float fScale, float *vLocalOrg)
|
|
|
|
{
|
|
|
|
vec3_t bounds[2];
|
|
|
|
vec3_t delta;
|
|
|
|
int i;
|
|
|
|
int cull;
|
2023-08-28 02:32:33 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
// FIXME: not working properly
|
|
|
|
return CULL_IN;
|
2023-08-28 02:32:33 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (tr.currentEntity->e.renderfx & RF_FRAMELERP) {
|
|
|
|
VectorSubtract(e->origin, e->oldorigin, delta);
|
|
|
|
} else {
|
|
|
|
VectorClear(delta);
|
|
|
|
}
|
2023-08-28 02:32:33 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
bounds[0][i] = newFrame->bounds[0][i] * fScale + vLocalOrg[i];
|
|
|
|
bounds[1][i] = newFrame->bounds[1][i] * fScale + vLocalOrg[i];
|
2023-08-28 02:32:33 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
if (delta[i] > 0) {
|
|
|
|
bounds[1][i] += delta[i];
|
|
|
|
} else {
|
|
|
|
bounds[0][i] += delta[i];
|
|
|
|
}
|
|
|
|
}
|
2023-08-28 02:32:33 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
cull = R_CullLocalBox(bounds);
|
|
|
|
|
|
|
|
if (r_showcull->integer & 1) {
|
|
|
|
float fR, fG, fB;
|
|
|
|
vec3_t vAngles;
|
|
|
|
|
|
|
|
switch (cull) {
|
2023-08-28 02:32:33 +02:00
|
|
|
case CULL_IN:
|
2024-12-19 20:20:43 +01:00
|
|
|
fR = 0;
|
|
|
|
fG = 1;
|
|
|
|
fB = 0;
|
2023-08-28 02:32:33 +02:00
|
|
|
break;
|
|
|
|
case CULL_CLIP:
|
|
|
|
fR = 1;
|
|
|
|
fG = 1;
|
|
|
|
fB = 0;
|
|
|
|
break;
|
|
|
|
case CULL_OUT:
|
|
|
|
fR = 1;
|
2023-08-28 15:20:31 +02:00
|
|
|
fG = 0.2f;
|
|
|
|
fB = 0.2f;
|
2023-08-28 02:32:33 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
bounds[0][i] -= 16;
|
|
|
|
bounds[1][i] += 16;
|
|
|
|
}
|
|
|
|
break;
|
2023-08-28 02:32:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
MatrixToEulerAngles(tr.ori.axis, vAngles);
|
|
|
|
R_DebugRotatedBBox(tr.ori.origin, vAngles, bounds[0], bounds[1], fR, fG, fB, 0.5);
|
2024-12-19 20:20:43 +01:00
|
|
|
}
|
2023-08-28 02:32:33 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
switch (cull) {
|
2023-08-28 02:32:33 +02:00
|
|
|
case CULL_IN:
|
|
|
|
tr.pc.c_box_cull_md3_in++;
|
|
|
|
return CULL_IN;
|
|
|
|
case CULL_CLIP:
|
|
|
|
tr.pc.c_box_cull_md3_clip++;
|
|
|
|
return CULL_CLIP;
|
|
|
|
case CULL_OUT:
|
|
|
|
default:
|
|
|
|
tr.pc.c_box_cull_md3_out++;
|
|
|
|
return CULL_OUT;
|
|
|
|
}
|
|
|
|
}
|
2023-05-16 23:35:39 +02:00
|
|
|
|
2023-07-17 00:24:56 +02:00
|
|
|
/*
|
|
|
|
==================
|
|
|
|
R_CountTikiLodTris
|
|
|
|
|
|
|
|
Computes the number of triangles to be rendered for the given TIKI model
|
|
|
|
based on the given LoD percentage, and passes it back with render_tris.
|
|
|
|
The total number of triangles in the TIKI model are passed back with total_tris.
|
|
|
|
Currently only used for debugging purposes.
|
|
|
|
|
|
|
|
FIXME: Shares some code with RB_StaticMesh, common parts could be extracted.
|
|
|
|
According to debug symbols, this was originally in tiki_mesh.cpp
|
|
|
|
==================
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
void R_CountTikiLodTris(dtiki_t *tiki, float lodpercentage, int *render_tris, int *total_tris)
|
2023-07-09 15:13:24 +02:00
|
|
|
{
|
2024-12-19 20:20:43 +01:00
|
|
|
*render_tris = 0;
|
|
|
|
*total_tris = 0;
|
|
|
|
int numtris = 0, totaltris = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < tiki->numMeshes; i++) {
|
|
|
|
skelHeaderGame_t *skelmodel = ri.TIKI_GetSkel(tiki->mesh[i]);
|
|
|
|
skelSurfaceGame_t *surface = skelmodel->pSurfaces;
|
|
|
|
|
|
|
|
for (int j = 0; j < skelmodel->numSurfaces; j++) {
|
|
|
|
int render_count = 0;
|
|
|
|
if (skelmodel->pLOD) {
|
|
|
|
int lod_cutoff = GetLodCutoff(skelmodel, lodpercentage, 0);
|
|
|
|
skelIndex_t *collapseIndex = surface->pCollapseIndex;
|
|
|
|
render_count = surface->numVerts;
|
|
|
|
|
|
|
|
// Determine the number of vertices to be rendered based on the LOD cutoff
|
|
|
|
while (render_count > 0 && collapseIndex[render_count - 1] < lod_cutoff) {
|
|
|
|
render_count--;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// The surf mesh doesn't have a LOD model, so all vertices will be rendered
|
|
|
|
render_count = surface->numVerts;
|
|
|
|
}
|
2023-07-17 00:24:56 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
skelIndex_t *triangles = surface->pTriangles;
|
|
|
|
int indexes = surface->numTriangles * 3; // 3 vertex/index for each tri
|
|
|
|
skelIndex_t *collapse_map = surface->pCollapse;
|
|
|
|
totaltris += surface->numTriangles;
|
|
|
|
skelIndex_t collapse[4096] {};
|
2023-07-17 00:24:56 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
// Initialize array for collapsed indices
|
|
|
|
int k;
|
|
|
|
for (k = 0; k < render_count; ++k) {
|
|
|
|
collapse[k] = k;
|
|
|
|
}
|
2023-07-17 00:24:56 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
// Map remaining vertices to their collapsed indices using the collapse map
|
|
|
|
for (k = render_count; k < surface->numVerts; ++k) {
|
|
|
|
collapse[k] = collapse[collapse_map[k]];
|
|
|
|
}
|
2023-07-17 00:24:56 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
// Check the first two collapsed indices of each triangle
|
|
|
|
for (k = 0; k < indexes; k += 3) {
|
|
|
|
if (collapse[triangles[k]] == collapse[triangles[k + 1]]) {
|
|
|
|
// The collapsed indices of the two vertices are the same:
|
|
|
|
// this, and any subsequent tris do not need to be rendered
|
|
|
|
// because the collapsed vertices (for this surface) coincide from here.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-07-17 00:24:56 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
surface = surface->pNext;
|
|
|
|
numtris += k / 3;
|
|
|
|
}
|
|
|
|
}
|
2023-07-17 00:24:56 +02:00
|
|
|
|
2024-12-19 20:20:43 +01:00
|
|
|
*render_tris = numtris;
|
|
|
|
*total_tris = totaltris;
|
2024-12-19 19:53:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
R_LerpTag
|
|
|
|
==================
|
|
|
|
*/
|
2024-12-19 20:20:43 +01:00
|
|
|
int R_LerpTag(orientation_t *tag, qhandle_t handle, int startFrame, int endFrame, float frac, const char *tagName)
|
|
|
|
{
|
|
|
|
// stub
|
|
|
|
return 0;
|
2023-07-09 15:13:24 +02:00
|
|
|
}
|