Numerous performance fixes.

This commit is contained in:
Justin Marshall
2026-05-21 15:36:02 -07:00
parent 2e4265f5a1
commit a89d43628c
29 changed files with 1632 additions and 636 deletions
+259 -46
View File
@@ -36,6 +36,7 @@ If you have questions concerning this license or the applicable additional terms
#include "OutlinerDlg.h"
#include "splines.h"
#include <GL/glu.h>
#include <vector>
#include "../../renderer/tr_local.h"
#include "../../models/model_local.h" // for idRenderModelMD5
@@ -1564,24 +1565,38 @@ static bool CamWnd_IsTriggerLikeBrushEntity(brush_t* brush) {
CamWnd_StringStartsWithNoCase(className, "monsterclip");
}
static bool CamWnd_TargetLinesVisible(CCamWnd* cam) {
return (CamWnd_GetFilterMask(cam) & CAMWND_FILTER_HIDE_TARGET_LINES) == 0;
struct camWndBrushFilterContext_t {
CCamWnd* cam;
int mask;
bool hasHiddenEntities;
};
static camWndBrushFilterContext_t CamWnd_MakeBrushFilterContext(CCamWnd* cam) {
camWndBrushFilterContext_t ctx;
ctx.cam = cam;
ctx.mask = CamWnd_GetFilterMask(cam);
ctx.hasHiddenEntities = Outliner_HasHiddenEntities();
return ctx;
}
static bool CamWnd_MenuFilterBrush(CCamWnd* cam, brush_t* brush) {
static bool CamWnd_TargetLinesVisible(const camWndBrushFilterContext_t& filter) {
return (filter.mask & CAMWND_FILTER_HIDE_TARGET_LINES) == 0;
}
static bool CamWnd_MenuFilterBrush(const camWndBrushFilterContext_t& filter, brush_t* brush) {
if (!brush) {
return false;
}
if (brush->owner != NULL && !Outliner_IsEntityVisible(brush->owner)) {
if (filter.hasHiddenEntities && brush->owner != NULL && !Outliner_IsEntityVisible(brush->owner)) {
return true;
}
if (!cam) {
if (!filter.cam) {
return false;
}
const int mask = CamWnd_GetFilterMask(cam);
const int mask = filter.mask;
if (mask == 0) {
return false;
}
@@ -1618,6 +1633,11 @@ static bool CamWnd_MenuFilterBrush(CCamWnd* cam, brush_t* brush) {
return false;
}
static bool CamWnd_MenuFilterBrush(CCamWnd* cam, brush_t* brush) {
const camWndBrushFilterContext_t filter = CamWnd_MakeBrushFilterContext(cam);
return CamWnd_MenuFilterBrush(filter, brush);
}
/*
========================
@@ -3556,6 +3576,7 @@ void setGLMode(int mode) {
break;
case cd_texture:
case cd_light:
glCullFace(GL_FRONT);
glEnable(GL_CULL_FACE);
glShadeModel(GL_FLAT);
@@ -3577,6 +3598,215 @@ void setGLMode(int mode) {
}
}
struct camWndWorldFaceBatch_t {
const idMaterial* material;
std::vector<face_t*> faces;
};
static bool CamWnd_IsFastWorldBrush(brush_t* brush) {
if (brush == NULL || brush->hiddenBrush) {
return false;
}
if (brush->pPatch || brush->modelHandle > 0 || brush->entityModel || brush->forceWireFrame) {
return false;
}
if (brush->owner == NULL || brush->owner != world_entity || brush->owner->curve != NULL) {
return false;
}
if (brush->owner->eclass == NULL || !(brush->owner->eclass->nShowFlags & ECLASS_WORLDSPAWN)) {
return false;
}
return true;
}
static bool CamWnd_CanFastDrawWorldMode(int drawMode) {
return drawMode == cd_solid || drawMode == cd_texture || drawMode == cd_light;
}
static bool CamWnd_ShouldDrawFastFace(face_t* face) {
if (face == NULL || face->face_winding == NULL || face->face_winding->GetNumPoints() < 3) {
return false;
}
if ((g_qeglobals.d_savedinfo.exclude & EXCLUDE_CAULK) && strstr(face->texdef.name, "caulk")) {
return false;
}
if ((g_qeglobals.d_savedinfo.exclude & EXCLUDE_VISPORTALS) && strstr(face->texdef.name, "visportal")) {
return false;
}
if ((g_qeglobals.d_savedinfo.exclude & EXCLUDE_NODRAW) && strstr(face->texdef.name, "nodraw")) {
return false;
}
return true;
}
static camWndWorldFaceBatch_t* CamWnd_FindWorldFaceBatch(std::vector<camWndWorldFaceBatch_t>& batches, const idMaterial* material) {
for (size_t i = 0; i < batches.size(); i++) {
if (batches[i].material == material) {
return &batches[i];
}
}
camWndWorldFaceBatch_t batch;
batch.material = material;
batch.faces.reserve(128);
batches.push_back(batch);
return &batches.back();
}
static void CamWnd_AddFastWorldBrush(std::vector<camWndWorldFaceBatch_t>& batches, brush_t* brush, int drawMode) {
const bool textured = (drawMode == cd_texture || drawMode == cd_light);
for (face_t* face = brush->brush_faces; face; face = face->next) {
if (!CamWnd_ShouldDrawFastFace(face)) {
continue;
}
const idMaterial* material = textured ? face->d_texture : NULL;
CamWnd_FindWorldFaceBatch(batches, material)->faces.push_back(face);
}
}
static void CamWnd_EmitFastWorldFaceBatches(const std::vector<camWndWorldFaceBatch_t>& batches, int drawMode) {
const bool textured = (drawMode == cd_texture || drawMode == cd_light);
for (size_t batchIndex = 0; batchIndex < batches.size(); batchIndex++) {
const camWndWorldFaceBatch_t& batch = batches[batchIndex];
if (batch.faces.empty()) {
continue;
}
if (textured && batch.material != NULL) {
batch.material->GetEditorImage()->Bind();
}
else {
globalImages->BindNull();
}
glBegin(GL_TRIANGLES);
for (size_t faceIndex = 0; faceIndex < batch.faces.size(); faceIndex++) {
face_t* face = batch.faces[faceIndex];
idWinding* w = face->face_winding;
const int numPoints = w->GetNumPoints();
const float alpha = face->d_texture ? face->d_texture->GetEditorAlpha() : 1.0f;
glColor4f(face->d_color.x, face->d_color.y, face->d_color.z, alpha);
for (int i = 2; i < numPoints; i++) {
if (textured) {
glTexCoord2fv(&(*w)[0][3]);
}
glVertex3fv((*w)[0].ToFloatPtr());
if (textured) {
glTexCoord2fv(&(*w)[i - 1][3]);
}
glVertex3fv((*w)[i - 1].ToFloatPtr());
if (textured) {
glTexCoord2fv(&(*w)[i][3]);
}
glVertex3fv((*w)[i].ToFloatPtr());
}
}
glEnd();
}
globalImages->BindNull();
}
static void CamWnd_DrawActiveBrushes(CCamWnd* cam, bool renderMode, bool entityMode, const camWndBrushFilterContext_t& filter) {
std::vector<camWndWorldFaceBatch_t> batches;
std::vector<brush_t*> slowBrushes;
batches.reserve(64);
slowBrushes.reserve(64);
for (brush_t* brush = active_brushes.next; brush != &active_brushes; brush = brush->next) {
if (cam->CullBrush(brush, false)) {
continue;
}
if (FilterBrush(brush)) {
continue;
}
if (CamWnd_MenuFilterBrush(filter, brush)) {
continue;
}
if (renderMode) {
if (!(entityMode && brush->owner->eclass->fixedsize)) {
continue;
}
slowBrushes.push_back(brush);
continue;
}
if (CamWnd_CanFastDrawWorldMode(cam->Camera().draw_mode) && CamWnd_IsFastWorldBrush(brush)) {
CamWnd_AddFastWorldBrush(batches, brush, cam->Camera().draw_mode);
}
else {
slowBrushes.push_back(brush);
}
}
if (!batches.empty()) {
setGLMode(cam->Camera().draw_mode);
CamWnd_EmitFastWorldFaceBatches(batches, cam->Camera().draw_mode);
}
for (size_t i = 0; i < slowBrushes.size(); i++) {
setGLMode(cam->Camera().draw_mode);
Brush_Draw(slowBrushes[i]);
}
}
static void CamWnd_DrawRadiantLightFrustum(const renderLight_t& lightParms, const idVec3& color) {
idPlane planes[6];
R_RenderLightFrustum(lightParms, planes);
srfTriangles_t* tri = R_PolytopeSurface(6, planes, NULL);
if (tri == NULL) {
return;
}
glColor3fv(color.ToFloatPtr());
for (int i = 0; i < tri->numIndexes; i += 3) {
glBegin(GL_LINE_LOOP);
glVertex3fv(tri->verts[tri->indexes[i]].xyz.ToFloatPtr());
glVertex3fv(tri->verts[tri->indexes[i + 1]].xyz.ToFloatPtr());
glVertex3fv(tri->verts[tri->indexes[i + 2]].xyz.ToFloatPtr());
glEnd();
}
R_FreeStaticTriSurf(tri);
}
static void CamWnd_DrawRadiantLightFrustums(CCamWnd* cam) {
if (cam == NULL || !cam->GetRenderMode() || !(cam->GetEntityMode() || g_bShowLightVolumes)) {
return;
}
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glLineWidth(1.0f);
globalImages->BindNull();
for (entity_t* ent = entities.next; ent != &entities; ent = ent->next) {
if (ent->eclass == NULL || !(ent->eclass->nShowFlags & ECLASS_LIGHT) || ent->epairs.GetBool("start_off")) {
continue;
}
idDict spawnArgs = ent->epairs;
renderLight_t lightParms;
gameEdit->ParseSpawnArgsToRenderLight(&spawnArgs, &lightParms);
CamWnd_DrawRadiantLightFrustum(lightParms, idVec3(1.0f, 0.0f, 1.0f));
}
glEnable(GL_DEPTH_TEST);
}
static bool CamWnd_FaceHasAllPlanePointsSelected(face_t* face) {
if (face == NULL || face->face_winding == NULL) {
return false;
@@ -3590,13 +3820,13 @@ static bool CamWnd_FaceHasAllPlanePointsSelected(face_t* face) {
return true;
}
static void CamWnd_DrawMoveSelectedBrushSidesForList(CCamWnd* cam, brush_t* list) {
static void CamWnd_DrawMoveSelectedBrushSidesForList(const camWndBrushFilterContext_t& filter, brush_t* list) {
if (list == NULL || list->next == NULL) {
return;
}
for (brush_t* brush = list->next; brush != list; brush = brush->next) {
if (CamWnd_MenuFilterBrush(cam, brush)) {
if (CamWnd_MenuFilterBrush(filter, brush)) {
continue;
}
if (brush->pPatch || brush->modelHandle > 0 || brush->entityModel) {
@@ -3611,7 +3841,7 @@ static void CamWnd_DrawMoveSelectedBrushSidesForList(CCamWnd* cam, brush_t* list
}
}
static void CamWnd_DrawMoveSelectedBrushSides(CCamWnd* cam) {
static void CamWnd_DrawMoveSelectedBrushSides(const camWndBrushFilterContext_t& filter) {
glPushAttrib(GL_CURRENT_BIT);
globalImages->BindNull();
glDisable(GL_LIGHTING);
@@ -3621,8 +3851,8 @@ static void CamWnd_DrawMoveSelectedBrushSides(CCamWnd* cam) {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glColor4f(1.0f, 0.0f, 0.0f, 0.25f);
CamWnd_DrawMoveSelectedBrushSidesForList(cam, &active_brushes);
CamWnd_DrawMoveSelectedBrushSidesForList(cam, &selected_brushes);
CamWnd_DrawMoveSelectedBrushSidesForList(filter, &active_brushes);
CamWnd_DrawMoveSelectedBrushSidesForList(filter, &selected_brushes);
glPopAttrib();
}
@@ -3756,29 +3986,10 @@ void CCamWnd::Cam_Draw() {
Cam_BuildMatrix();
for (brush = active_brushes.next; brush != &active_brushes; brush = brush->next) {
const camWndBrushFilterContext_t brushFilter = CamWnd_MakeBrushFilterContext(this);
if (CullBrush(brush, false)) {
continue;
}
if (FilterBrush(brush)) {
continue;
}
if (CamWnd_MenuFilterBrush(this, brush)) {
continue;
}
if (renderMode) {
if (!(entityMode && brush->owner->eclass->fixedsize)) {
continue;
}
}
setGLMode(m_Camera.draw_mode);
Brush_Draw(brush);
}
CamWnd_DrawActiveBrushes(this, renderMode, entityMode, brushFilter);
CamWnd_DrawRadiantLightFrustums(this);
//glDepthMask ( 1 ); // Ok, write now
@@ -3791,7 +4002,7 @@ void CCamWnd::Cam_Draw() {
if (!renderMode) {
// draw normally
for (brush = pList->next; brush != pList; brush = brush->next) {
if (CamWnd_MenuFilterBrush(this, brush)) {
if (CamWnd_MenuFilterBrush(brushFilter, brush)) {
continue;
}
if (brush->pPatch) {
@@ -3812,7 +4023,7 @@ void CCamWnd::Cam_Draw() {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
globalImages->BindNull();
for (brush = pList->next; brush != pList; brush = brush->next) {
if (CamWnd_MenuFilterBrush(this, brush)) {
if (CamWnd_MenuFilterBrush(brushFilter, brush)) {
continue;
}
if (brush->pPatch || brush->modelHandle > 0) {
@@ -3860,7 +4071,7 @@ void CCamWnd::Cam_Draw() {
glColor4f(1.0f, 0.0f, 0.0f, 0.25f);
for (brush = pList->next; brush != pList; brush = brush->next) {
if (CamWnd_MenuFilterBrush(this, brush)) {
if (CamWnd_MenuFilterBrush(brushFilter, brush)) {
continue;
}
if (brush->pPatch || brush->modelHandle > 0) {
@@ -3872,7 +4083,7 @@ void CCamWnd::Cam_Draw() {
}
}
CamWnd_DrawMoveSelectedBrushSides(this);
CamWnd_DrawMoveSelectedBrushSides(brushFilter);
CamWnd_FaceExtrudeDrawPreview(this);
CamWnd_GizmoDraw(this);
@@ -3913,7 +4124,7 @@ void CCamWnd::Cam_Draw() {
// draw pointfile
glEnable(GL_DEPTH_TEST);
if (CamWnd_TargetLinesVisible(this)) {
if (CamWnd_TargetLinesVisible(brushFilter)) {
CamWnd_DrawVisiblePathLines();
}
@@ -4196,12 +4407,12 @@ static unsigned int EditorHashBrushGeometry(brush_t* brush, const idVec3& origin
return hash;
}
static unsigned int EditorHashBModelGeometry(CCamWnd* cam, entity_t* ent) {
static unsigned int EditorHashBModelGeometry(entity_t* ent, const camWndBrushFilterContext_t& filter) {
unsigned int hash = EDITOR_RENDER_HASH_INIT;
hash = EditorHashString(hash, ValueForKey(ent, "name"));
for (brush_t* brush = ent->brushes.onext; brush != &ent->brushes; brush = brush->onext) {
if (FilterBrush(brush) || CamWnd_MenuFilterBrush(cam, brush) || Map_IsBrushFiltered(brush)) {
if (FilterBrush(brush) || CamWnd_MenuFilterBrush(filter, brush) || Map_IsBrushFiltered(brush)) {
continue;
}
const unsigned int brushHash = EditorHashBrushGeometry(brush, ent->origin);
@@ -4255,12 +4466,12 @@ static idRenderModel* EditorBuildSingleBrushModel(brush_t* brush, const idVec3&
return model;
}
static idRenderModel* EditorBuildBModel(CCamWnd* cam, entity_t* ent, const char* name) {
static idRenderModel* EditorBuildBModel(entity_t* ent, const char* name, const camWndBrushFilterContext_t& filter) {
idTriList tris(1024);
idMatList mats(1024);
for (brush_t* brush = ent->brushes.onext; brush != &ent->brushes; brush = brush->onext) {
if (FilterBrush(brush) || CamWnd_MenuFilterBrush(cam, brush) || Map_IsBrushFiltered(brush)) {
if (FilterBrush(brush) || CamWnd_MenuFilterBrush(filter, brush) || Map_IsBrushFiltered(brush)) {
continue;
}
Brush_ToTris(brush, &tris, &mats, false, true);
@@ -4600,6 +4811,7 @@ void CCamWnd::BuildEntityRenderState(entity_t* ent, bool update) {
idDict spawnArgs;
const char* name = NULL;
editorRenderEntityState_t* entityState = EditorTouchEntityState(ent);
const camWndBrushFilterContext_t brushFilter = CamWnd_MakeBrushFilterContext(this);
// The old code used update=false as "tear down and recreate". The
// incremental path keeps the handles and uses UpdateEntityDef /
@@ -4611,7 +4823,7 @@ void CCamWnd::BuildEntityRenderState(entity_t* ent, bool update) {
// If the entity is no longer renderable, remove only this entity's defs.
if (ent->brushes.onext == &ent->brushes ||
FilterBrush(ent->brushes.onext) ||
CamWnd_MenuFilterBrush(this, ent->brushes.onext) ||
CamWnd_MenuFilterBrush(brushFilter, ent->brushes.onext) ||
CullBrush(ent->brushes.onext, true) ||
Map_IsBrushFiltered(ent->brushes.onext)) {
EditorFreeEntityModelDef(entityState, ent);
@@ -4636,7 +4848,7 @@ void CCamWnd::BuildEntityRenderState(entity_t* ent, bool update) {
// Brush model entity. Rebuild the renderModel only when the
// entity-local brush geometry/materials changed. Plain movement
// is handled by UpdateEntityDef below.
const unsigned int geometryHash = EditorHashBModelGeometry(this, ent);
const unsigned int geometryHash = EditorHashBModelGeometry(ent, brushFilter);
editorRenderBModelState_t* bmodelState = EditorTouchBModelState(ent);
idRenderModel* oldModel = NULL;
@@ -4646,7 +4858,7 @@ void CCamWnd::BuildEntityRenderState(entity_t* ent, bool update) {
idStr::Icmp(bmodelState->modelName.c_str(), name) != 0);
if (geometryChanged) {
idRenderModel* newModel = EditorBuildBModel(this, ent, name);
idRenderModel* newModel = EditorBuildBModel(ent, name, brushFilter);
if (newModel) {
oldModel = bmodelState->model;
bmodelState->model = newModel;
@@ -5348,6 +5560,7 @@ void CCamWnd::DrawEntityData() {
idVec3 color(0, 1, 0);
glColor3fv(color.ToFloatPtr());
const camWndBrushFilterContext_t brushFilter = CamWnd_MakeBrushFilterContext(this);
brush_t* brushList = &active_brushes;
int pass = 0;
while (brushList) {
@@ -5361,7 +5574,7 @@ void CCamWnd::DrawEntityData() {
continue;
}
if (CamWnd_MenuFilterBrush(this, brush)) {
if (CamWnd_MenuFilterBrush(brushFilter, brush)) {
continue;
}
+115 -72
View File
@@ -5474,97 +5474,140 @@ bool FilterBrush(brush_t *pb) {
the lines can be visible when neither end is. Called for both camera view and xy view.
=======================================================================================================================
*/
void DrawPathLines(void) {
int i, k;
idVec3 mid, mid1;
entity_t *se, *te;
brush_t *sb, *tb;
const char *psz;
idVec3 dir, s1, s2;
float len, f;
int arrows;
int num_entities;
const char *ent_target[MAX_MAP_ENTITIES];
entity_t *ent_entity[MAX_MAP_ENTITIES];
struct pathLineNamedEntity_t {
const char* name;
entity_t* entity;
brush_t* brush;
};
static bool DrawPathLines_IsTargetKey(const char* key) {
if (key == NULL) {
return false;
}
if (idStr::Cmp(key, "target") == 0) {
return true;
}
if (idStr::Cmpn(key, "target", 6) != 0 || key[6] == '\0') {
return false;
}
for (const char* c = key + 6; *c != '\0'; c++) {
if (*c < '0' || *c > '9') {
return false;
}
}
return true;
}
static brush_t* DrawPathLines_FirstBrush(entity_t* ent) {
if (ent == NULL) {
return NULL;
}
brush_t* brush = ent->brushes.onext;
return (brush != &ent->brushes) ? brush : NULL;
}
static void DrawPathLines_AppendNamedEntity(idList<pathLineNamedEntity_t>& namedEntities, idHashIndex& nameHash, entity_t* ent) {
const char* name = ValueForKey(ent, "name");
if (name == NULL || name[0] == '\0') {
return;
}
brush_t* brush = DrawPathLines_FirstBrush(ent);
if (brush == NULL) {
return;
}
pathLineNamedEntity_t named;
named.name = name;
named.entity = ent;
named.brush = brush;
const int index = namedEntities.Append(named);
nameHash.Add(nameHash.GenerateKey(name, true), index);
}
static void DrawPathLines_DrawConnection(const pathLineNamedEntity_t& namedTarget, entity_t* source, brush_t* sourceBrush) {
if (namedTarget.entity == NULL || namedTarget.entity->eclass == NULL || source == NULL || sourceBrush == NULL) {
return;
}
idVec3 mid = namedTarget.brush->owner->origin;
idVec3 mid1 = sourceBrush->owner->origin;
idVec3 dir, s1, s2;
VectorSubtract(mid1, mid, dir);
const float len = dir.Normalize();
s1[0] = -dir[1] * 8 + dir[0] * 8;
s2[0] = dir[1] * 8 + dir[0] * 8;
s1[1] = dir[0] * 8 + dir[1] * 8;
s2[1] = -dir[0] * 8 + dir[1] * 8;
glColor3f(namedTarget.entity->eclass->color[0], namedTarget.entity->eclass->color[1], namedTarget.entity->eclass->color[2]);
glBegin(GL_LINES);
glVertex3fv(mid.ToFloatPtr());
glVertex3fv(mid1.ToFloatPtr());
const int arrows = (int)(len / 256) + 1;
for (int i = 0; i < arrows; i++) {
const float f = len * (i + 0.5f) / arrows;
mid1 = mid + (f * dir);
glVertex3fv(mid1.ToFloatPtr());
glVertex3f(mid1[0] + s1[0], mid1[1] + s1[1], mid1[2]);
glVertex3fv(mid1.ToFloatPtr());
glVertex3f(mid1[0] + s2[0], mid1[1] + s2[1], mid1[2]);
}
glEnd();
}
void DrawPathLines(void) {
if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_PATHS) {
return;
}
num_entities = 0;
for (te = entities.next; te != &entities && num_entities != MAX_MAP_ENTITIES; te = te->next) {
for (int i = 0; i < 2048; i++) {
if (i == 0) {
ent_target[num_entities] = ValueForKey(te, "target");
} else {
ent_target[num_entities] = ValueForKey(te, va("target%i", i));
}
if (ent_target[num_entities][0]) {
ent_entity[num_entities] = te;
num_entities++;
} else if (i > 16) {
break;
}
}
idList<pathLineNamedEntity_t> namedEntities;
idHashIndex nameHash(1024, 1024);
namedEntities.SetGranularity(256);
for (entity_t* ent = entities.next; ent != &entities; ent = ent->next) {
DrawPathLines_AppendNamedEntity(namedEntities, nameHash, ent);
}
for (se = entities.next; se != &entities; se = se->next) {
psz = ValueForKey(se, "name");
if (psz == NULL || psz[0] == '\0') {
for (entity_t* source = entities.next; source != &entities; source = source->next) {
brush_t* sourceBrush = DrawPathLines_FirstBrush(source);
if (sourceBrush == NULL) {
continue;
}
sb = se->brushes.onext;
if (sb == &se->brushes) {
continue;
}
for (k = 0; k < num_entities; k++) {
if (strcmp(ent_target[k], psz)) {
const int numKeys = source->epairs.GetNumKeyVals();
for (int keyIndex = 0; keyIndex < numKeys; keyIndex++) {
const idKeyValue* kv = source->epairs.GetKeyVal(keyIndex);
if (kv == NULL || !DrawPathLines_IsTargetKey(kv->GetKey().c_str())) {
continue;
}
te = ent_entity[k];
tb = te->brushes.onext;
if (tb == &te->brushes) {
const char* targetName = kv->GetValue().c_str();
if (targetName == NULL || targetName[0] == '\0') {
continue;
}
mid = sb->owner->origin;
mid1 = tb->owner->origin;
VectorSubtract(mid1, mid, dir);
len = dir.Normalize();
s1[0] = -dir[1] * 8 + dir[0] * 8;
s2[0] = dir[1] * 8 + dir[0] * 8;
s1[1] = dir[0] * 8 + dir[1] * 8;
s2[1] = -dir[0] * 8 + dir[1] * 8;
glColor3f(se->eclass->color[0], se->eclass->color[1], se->eclass->color[2]);
glBegin(GL_LINES);
glVertex3fv(mid.ToFloatPtr());
glVertex3fv(mid1.ToFloatPtr());
arrows = (int)(len / 256) + 1;
for (i = 0; i < arrows; i++) {
f = len * (i + 0.5) / arrows;
mid1 = mid + (f * dir);
glVertex3fv(mid1.ToFloatPtr());
glVertex3f(mid1[0] + s1[0], mid1[1] + s1[1], mid1[2]);
glVertex3fv(mid1.ToFloatPtr());
glVertex3f(mid1[0] + s2[0], mid1[1] + s2[1], mid1[2]);
const int hashKey = nameHash.GenerateKey(targetName, true);
for (int index = nameHash.First(hashKey); index != -1; index = nameHash.Next(index)) {
const pathLineNamedEntity_t& namedTarget = namedEntities[index];
if (strcmp(namedTarget.name, targetName) == 0) {
DrawPathLines_DrawConnection(namedTarget, source, sourceBrush);
}
}
glEnd();
}
}
return;
}
//