mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-15 15:51:07 +02:00
569 lines
20 KiB
C++
569 lines
20 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
IceTech GPL Source Code
|
|
Copyright (C) 2026 Justin Marshall
|
|
|
|
This file is part of the IceTech GPL Source Code.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "qe3.h"
|
|
#include "CityBuilder.h"
|
|
|
|
extern int mapModified;
|
|
|
|
namespace {
|
|
|
|
struct cityStats_t {
|
|
int brushes;
|
|
int entities;
|
|
int buildings;
|
|
int streets;
|
|
};
|
|
|
|
struct cityBounds_t {
|
|
idVec3 mins;
|
|
idVec3 maxs;
|
|
};
|
|
|
|
static const float CITY_DEFAULT_HALF_SIZE = 4096.0f;
|
|
static const float CITY_MIN_SIZE = 3072.0f;
|
|
static const float CITY_MAX_SIZE = 65536.0f;
|
|
static const float CITY_ROAD_PITCH = 1152.0f;
|
|
static const float CITY_ROAD_WIDTH = 256.0f;
|
|
static const float CITY_SIDEWALK_WIDTH = 80.0f;
|
|
static const float CITY_CURB_WIDTH = 12.0f;
|
|
static const float CITY_CEILING_HEIGHT = 1536.0f;
|
|
static const float CITY_TEXTURE_SIZE = 1024.0f;
|
|
static const float CITY_ROAD_TEXTURE_LENGTH = 512.0f;
|
|
static const float CITY_SIDEWALK_TEXTURE_LENGTH = 256.0f;
|
|
static const float CITY_BUILDING_TEXTURE_WIDTH = 256.0f;
|
|
static const float CITY_BUILDING_TEXTURE_HEIGHT = 384.0f;
|
|
static const float CITY_BUILDING_EDGE_SETBACK = 96.0f;
|
|
static const float CITY_BUILDING_LOT_SIZE = 640.0f;
|
|
static const float CITY_BUILDING_ALLEY = 56.0f;
|
|
static const float CITY_POINT_LIGHT_RADIUS = 300000.0f;
|
|
|
|
static const char* CITY_MAT_MARS_DIRT = "textures/mars_citybuilder/mars_dirt";
|
|
static const char* CITY_MAT_MARS_ROAD = "textures/mars_citybuilder/mars_road_dusty_center";
|
|
static const char* CITY_MAT_MARS_ROAD_EAST_WEST = "textures/mars_citybuilder/mars_road_dusty_center_east_west";
|
|
static const char* CITY_MAT_MARS_ROAD_INTERSECTION = "textures/mars_citybuilder/mars_road_dusty_intersection";
|
|
static const char* CITY_MAT_MARS_SIDEWALK = "textures/mars_citybuilder/mars_sidewalk_dusty";
|
|
static const char* CITY_MAT_MARS_BUILDING_01 = "textures/mars_citybuilder/mars_building_dark_01";
|
|
static const char* CITY_MAT_MARS_BUILDING_02 = "textures/mars_citybuilder/mars_building_dark_02";
|
|
static const idVec3 CITY_POINT_LIGHT_ORIGIN(-3200.0f, 3336.0f, 0.0f);
|
|
static const idVec3 CITY_POINT_LIGHT_COLOR(242.0f / 255.0f, 160.0f / 255.0f, 130.0f / 255.0f);
|
|
|
|
static float City_ClampFloat(float v, float minValue, float maxValue) {
|
|
if (v < minValue) {
|
|
return minValue;
|
|
}
|
|
if (v > maxValue) {
|
|
return maxValue;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
static int City_ClampInt(int v, int minValue, int maxValue) {
|
|
if (v < minValue) {
|
|
return minValue;
|
|
}
|
|
if (v > maxValue) {
|
|
return maxValue;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
static unsigned int City_Hash(int x, int y, int salt) {
|
|
unsigned int h = (unsigned int)(x * 73856093) ^ (unsigned int)(y * 19349663) ^ (unsigned int)(salt * 83492791);
|
|
h ^= h >> 13;
|
|
h *= 1274126177u;
|
|
h ^= h >> 16;
|
|
return h;
|
|
}
|
|
|
|
static float City_Random01(int x, int y, int salt) {
|
|
return (City_Hash(x, y, salt) & 0xffff) / 65535.0f;
|
|
}
|
|
|
|
static float City_TexScale(float worldUnits) {
|
|
return worldUnits / CITY_TEXTURE_SIZE;
|
|
}
|
|
|
|
static float City_TexShift(float worldCoord, float scale) {
|
|
if (scale == 0.0f) {
|
|
return 0.0f;
|
|
}
|
|
|
|
float shift = fmod(-worldCoord / scale, CITY_TEXTURE_SIZE);
|
|
if (shift < 0.0f) {
|
|
shift += CITY_TEXTURE_SIZE;
|
|
}
|
|
return shift;
|
|
}
|
|
|
|
static void City_SetTexdef(texdef_t& td, const char* material, float scale0 = 0.5f, float scale1 = 0.5f, float rotate = 0.0f, float shift0 = 0.0f, float shift1 = 0.0f) {
|
|
td.SetName(material);
|
|
td.shift[0] = shift0;
|
|
td.shift[1] = shift1;
|
|
td.rotate = rotate;
|
|
td.scale[0] = scale0;
|
|
td.scale[1] = scale1;
|
|
}
|
|
|
|
static idEditorBrush* City_AddWorldBrushUV(const idVec3& mins, const idVec3& maxs, const char* material, float scale0, float scale1, float rotate, float shift0, float shift1, cityStats_t& stats) {
|
|
if (mins.x >= maxs.x || mins.y >= maxs.y || mins.z >= maxs.z || world_entity == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
texdef_t td;
|
|
City_SetTexdef(td, material, scale0, scale1, rotate, shift0, shift1);
|
|
|
|
idEditorBrush* b = Brush_Create(mins, maxs, &td);
|
|
if (b == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
b->Brush_AddToList(&active_brushes);
|
|
world_entity->Entity_LinkBrush(b);
|
|
b->Brush_Build();
|
|
Undo_EndBrush(b);
|
|
stats.brushes++;
|
|
return b;
|
|
}
|
|
|
|
static idEditorBrush* City_AddWorldBrush(const idVec3& mins, const idVec3& maxs, const char* material, cityStats_t& stats) {
|
|
return City_AddWorldBrushUV(mins, maxs, material, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, stats);
|
|
}
|
|
|
|
static idEditorBrush* City_AddVerticalRoadBrush(float x, float y0, float y1, const cityBounds_t& bounds, cityStats_t& stats) {
|
|
const float roadHalf = CITY_ROAD_WIDTH * 0.5f;
|
|
const float scale0 = City_TexScale(CITY_ROAD_WIDTH);
|
|
const float scale1 = City_TexScale(CITY_ROAD_TEXTURE_LENGTH);
|
|
const idVec3 mins(x - roadHalf, y0, bounds.mins.z);
|
|
const idVec3 maxs(x + roadHalf, y1, bounds.mins.z + 8.0f);
|
|
return City_AddWorldBrushUV(mins, maxs, CITY_MAT_MARS_ROAD_EAST_WEST, scale0, scale1, 0.0f, City_TexShift(mins.x, scale0), City_TexShift(mins.y, scale1), stats);
|
|
}
|
|
|
|
static idEditorBrush* City_AddHorizontalRoadBrush(float x0, float x1, float y, const cityBounds_t& bounds, cityStats_t& stats) {
|
|
const float roadHalf = CITY_ROAD_WIDTH * 0.5f;
|
|
const float scale0 = City_TexScale(CITY_ROAD_TEXTURE_LENGTH);
|
|
const float scale1 = City_TexScale(CITY_ROAD_WIDTH);
|
|
const idVec3 mins(x0, y - roadHalf, bounds.mins.z);
|
|
const idVec3 maxs(x1, y + roadHalf, bounds.mins.z + 8.0f);
|
|
return City_AddWorldBrushUV(mins, maxs, CITY_MAT_MARS_ROAD, scale0, scale1, 0.0f, City_TexShift(mins.x, scale0), City_TexShift(mins.y, scale1), stats);
|
|
}
|
|
|
|
static idEditorBrush* City_AddIntersectionRoadBrush(float x, float y, const cityBounds_t& bounds, cityStats_t& stats) {
|
|
const float roadHalf = CITY_ROAD_WIDTH * 0.5f;
|
|
const float scale = City_TexScale(CITY_ROAD_WIDTH);
|
|
const idVec3 mins(x - roadHalf, y - roadHalf, bounds.mins.z);
|
|
const idVec3 maxs(x + roadHalf, y + roadHalf, bounds.mins.z + 8.0f);
|
|
return City_AddWorldBrushUV(mins, maxs, CITY_MAT_MARS_ROAD_INTERSECTION, scale, scale, 0.0f, City_TexShift(mins.x, scale), City_TexShift(mins.y, scale), stats);
|
|
}
|
|
|
|
static idEditorBrush* City_AddVerticalSidewalkBrush(float x0, float x1, float y0, float y1, const cityBounds_t& bounds, cityStats_t& stats) {
|
|
const float scale0 = City_TexScale(CITY_SIDEWALK_WIDTH);
|
|
const float scale1 = City_TexScale(CITY_SIDEWALK_TEXTURE_LENGTH);
|
|
const idVec3 mins(x0, y0, bounds.mins.z);
|
|
const idVec3 maxs(x1, y1, bounds.mins.z + 8.0f);
|
|
return City_AddWorldBrushUV(mins, maxs, CITY_MAT_MARS_SIDEWALK, scale0, scale1, 0.0f, City_TexShift(mins.x, scale0), City_TexShift(mins.y, scale1), stats);
|
|
}
|
|
|
|
static idEditorBrush* City_AddHorizontalSidewalkBrush(float x0, float x1, float y0, float y1, const cityBounds_t& bounds, cityStats_t& stats) {
|
|
const float scale0 = City_TexScale(CITY_SIDEWALK_TEXTURE_LENGTH);
|
|
const float scale1 = City_TexScale(CITY_SIDEWALK_WIDTH);
|
|
const idVec3 mins(x0, y0, bounds.mins.z);
|
|
const idVec3 maxs(x1, y1, bounds.mins.z + 8.0f);
|
|
return City_AddWorldBrushUV(mins, maxs, CITY_MAT_MARS_SIDEWALK, scale0, scale1, 0.0f, City_TexShift(mins.x, scale0), City_TexShift(mins.y, scale1), stats);
|
|
}
|
|
|
|
static idEditorBrush* City_AddBuildingBrush(const idVec3& mins, const idVec3& maxs, const char* material, cityStats_t& stats) {
|
|
const float scale0 = City_TexScale(CITY_BUILDING_TEXTURE_WIDTH);
|
|
const float scale1 = City_TexScale(CITY_BUILDING_TEXTURE_HEIGHT);
|
|
return City_AddWorldBrushUV(mins, maxs, material, scale0, scale1, 0.0f, 0.0f, City_TexShift(mins.z, scale1), stats);
|
|
}
|
|
|
|
static void City_SetVecKey(idEditorEntity* ent, const char* key, const idVec3& value) {
|
|
ent->SetKeyValue(key, va("%i %i %i", (int)value.x, (int)value.y, (int)value.z));
|
|
}
|
|
|
|
static idEditorEntity* City_AddFixedEntity(const char* classname, const idVec3& origin, cityStats_t& stats) {
|
|
idEditorEntity* ent = idEditorEntity::Entity_New();
|
|
if (ent == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ent->brushes.onext = ent->brushes.oprev = &ent->brushes;
|
|
ent->origin = origin;
|
|
ent->SetKeyValue("classname", classname);
|
|
City_SetVecKey(ent, "origin", origin);
|
|
ent->Entity_Name(false);
|
|
ent->Entity_PostParse(&active_brushes);
|
|
ent->Entity_AddToList(&entities);
|
|
g_qeglobals.d_num_entities++;
|
|
|
|
Undo_EndEntity(ent);
|
|
for (idEditorBrush* b = ent->brushes.onext; b != &ent->brushes; b = b->onext) {
|
|
b->Brush_Build();
|
|
Undo_EndBrush(b);
|
|
stats.brushes++;
|
|
}
|
|
|
|
stats.entities++;
|
|
return ent;
|
|
}
|
|
|
|
static cityBounds_t City_GetTargetBounds() {
|
|
cityBounds_t bounds;
|
|
bounds.mins.Set(-CITY_DEFAULT_HALF_SIZE, -CITY_DEFAULT_HALF_SIZE, 0.0f);
|
|
bounds.maxs.Set(CITY_DEFAULT_HALF_SIZE, CITY_DEFAULT_HALF_SIZE, CITY_CEILING_HEIGHT);
|
|
|
|
if (selected_brushes.next != NULL && selected_brushes.next != &selected_brushes) {
|
|
idVec3 mins(999999.0f, 999999.0f, 999999.0f);
|
|
idVec3 maxs(-999999.0f, -999999.0f, -999999.0f);
|
|
for (idEditorBrush* b = selected_brushes.next; b != &selected_brushes; b = b->next) {
|
|
for (int i = 0; i < 3; i++) {
|
|
if (b->mins[i] < mins[i]) {
|
|
mins[i] = b->mins[i];
|
|
}
|
|
if (b->maxs[i] > maxs[i]) {
|
|
maxs[i] = b->maxs[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((maxs.x - mins.x) >= 128.0f && (maxs.y - mins.y) >= 128.0f) {
|
|
bounds.mins.x = mins.x;
|
|
bounds.mins.y = mins.y;
|
|
bounds.mins.z = mins.z;
|
|
bounds.maxs.x = maxs.x;
|
|
bounds.maxs.y = maxs.y;
|
|
bounds.maxs.z = mins.z + CITY_CEILING_HEIGHT;
|
|
}
|
|
}
|
|
|
|
const float centerX = (bounds.mins.x + bounds.maxs.x) * 0.5f;
|
|
const float centerY = (bounds.mins.y + bounds.maxs.y) * 0.5f;
|
|
float width = City_ClampFloat(bounds.maxs.x - bounds.mins.x, CITY_MIN_SIZE, CITY_MAX_SIZE);
|
|
float depth = City_ClampFloat(bounds.maxs.y - bounds.mins.y, CITY_MIN_SIZE, CITY_MAX_SIZE);
|
|
width = floor(width / 64.0f) * 64.0f;
|
|
depth = floor(depth / 64.0f) * 64.0f;
|
|
|
|
bounds.mins.x = centerX - width * 0.5f;
|
|
bounds.maxs.x = centerX + width * 0.5f;
|
|
bounds.mins.y = centerY - depth * 0.5f;
|
|
bounds.maxs.y = centerY + depth * 0.5f;
|
|
bounds.maxs.z = bounds.mins.z + CITY_CEILING_HEIGHT;
|
|
return bounds;
|
|
}
|
|
|
|
static void City_AddVerticalStreetEdges(float x, float y0, float y1, const cityBounds_t& bounds, cityStats_t& stats) {
|
|
if (y1 <= y0) {
|
|
return;
|
|
}
|
|
|
|
const float z0 = bounds.mins.z;
|
|
const float roadHalf = CITY_ROAD_WIDTH * 0.5f;
|
|
const float sideHalf = roadHalf + CITY_SIDEWALK_WIDTH;
|
|
|
|
City_AddVerticalSidewalkBrush(x - sideHalf, x - roadHalf, y0, y1, bounds, stats);
|
|
City_AddVerticalSidewalkBrush(x + roadHalf, x + sideHalf, y0, y1, bounds, stats);
|
|
City_AddWorldBrush(
|
|
idVec3(x - roadHalf - CITY_CURB_WIDTH, y0, z0 + 8.0f),
|
|
idVec3(x - roadHalf, y1, z0 + 14.0f),
|
|
"textures/base_wall/steel",
|
|
stats);
|
|
City_AddWorldBrush(
|
|
idVec3(x + roadHalf, y0, z0 + 8.0f),
|
|
idVec3(x + roadHalf + CITY_CURB_WIDTH, y1, z0 + 14.0f),
|
|
"textures/base_wall/steel",
|
|
stats);
|
|
}
|
|
|
|
static void City_AddHorizontalStreetEdges(float y, float x0, float x1, const cityBounds_t& bounds, cityStats_t& stats) {
|
|
if (x1 <= x0) {
|
|
return;
|
|
}
|
|
|
|
const float z0 = bounds.mins.z;
|
|
const float roadHalf = CITY_ROAD_WIDTH * 0.5f;
|
|
const float sideHalf = roadHalf + CITY_SIDEWALK_WIDTH;
|
|
|
|
City_AddHorizontalSidewalkBrush(x0, x1, y - sideHalf, y - roadHalf, bounds, stats);
|
|
City_AddHorizontalSidewalkBrush(x0, x1, y + roadHalf, y + sideHalf, bounds, stats);
|
|
City_AddWorldBrush(
|
|
idVec3(x0, y - roadHalf - CITY_CURB_WIDTH, z0 + 8.0f),
|
|
idVec3(x1, y - roadHalf, z0 + 14.0f),
|
|
"textures/base_wall/steel",
|
|
stats);
|
|
City_AddWorldBrush(
|
|
idVec3(x0, y + roadHalf, z0 + 8.0f),
|
|
idVec3(x1, y + roadHalf + CITY_CURB_WIDTH, z0 + 14.0f),
|
|
"textures/base_wall/steel",
|
|
stats);
|
|
}
|
|
|
|
static void City_AddVerticalStreetEdges(float x, const cityBounds_t& bounds, const idList<float>& blockers, cityStats_t& stats) {
|
|
const float clearHalf = CITY_ROAD_WIDTH * 0.5f + CITY_SIDEWALK_WIDTH;
|
|
float y0 = bounds.mins.y;
|
|
|
|
for (int i = 0; i < blockers.Num(); i++) {
|
|
const float y1 = blockers[i] - clearHalf;
|
|
City_AddVerticalStreetEdges(x, y0, y1, bounds, stats);
|
|
y0 = blockers[i] + clearHalf;
|
|
}
|
|
|
|
City_AddVerticalStreetEdges(x, y0, bounds.maxs.y, bounds, stats);
|
|
}
|
|
|
|
static void City_AddHorizontalStreetEdges(float y, const cityBounds_t& bounds, const idList<float>& blockers, cityStats_t& stats) {
|
|
const float clearHalf = CITY_ROAD_WIDTH * 0.5f + CITY_SIDEWALK_WIDTH;
|
|
float x0 = bounds.mins.x;
|
|
|
|
for (int i = 0; i < blockers.Num(); i++) {
|
|
const float x1 = blockers[i] - clearHalf;
|
|
City_AddHorizontalStreetEdges(y, x0, x1, bounds, stats);
|
|
x0 = blockers[i] + clearHalf;
|
|
}
|
|
|
|
City_AddHorizontalStreetEdges(y, x0, bounds.maxs.x, bounds, stats);
|
|
}
|
|
|
|
static void City_AddStreetSign(float x, float y, int avenue, int street, const cityBounds_t& bounds, cityStats_t& stats) {
|
|
const float z0 = bounds.mins.z;
|
|
const float poleX = x + CITY_ROAD_WIDTH * 0.5f + 44.0f;
|
|
const float poleY = y - CITY_ROAD_WIDTH * 0.5f - 44.0f;
|
|
|
|
City_AddWorldBrush(idVec3(poleX - 4.0f, poleY - 4.0f, z0 + 8.0f), idVec3(poleX + 4.0f, poleY + 4.0f, z0 + 128.0f), "textures/base_wall/steel", stats);
|
|
City_AddWorldBrush(idVec3(poleX - 56.0f, poleY - 5.0f, z0 + 112.0f), idVec3(poleX + 56.0f, poleY + 5.0f, z0 + 136.0f), "textures/base_wall/greenpanel1", stats);
|
|
City_AddWorldBrush(idVec3(poleX - 5.0f, poleY - 56.0f, z0 + 140.0f), idVec3(poleX + 5.0f, poleY + 56.0f, z0 + 164.0f), "textures/base_wall/greenpanel1", stats);
|
|
common->Printf("Automatic City Builder: street node A%i / S%i at %.0f %.0f\n", avenue + 1, street + 1, x, y);
|
|
}
|
|
|
|
static void City_AddRoads(const cityBounds_t& bounds, idList<float>& roadX, idList<float>& roadY, cityStats_t& stats) {
|
|
const float roadHalf = CITY_ROAD_WIDTH * 0.5f;
|
|
const int xRoads = City_ClampInt((int)((bounds.maxs.x - bounds.mins.x) / CITY_ROAD_PITCH), 2, 64);
|
|
const int yRoads = City_ClampInt((int)((bounds.maxs.y - bounds.mins.y) / CITY_ROAD_PITCH), 2, 64);
|
|
const float xStep = (bounds.maxs.x - bounds.mins.x) / (float)xRoads;
|
|
const float yStep = (bounds.maxs.y - bounds.mins.y) / (float)yRoads;
|
|
|
|
for (int i = 1; i < xRoads; i++) {
|
|
roadX.Append(bounds.mins.x + xStep * i);
|
|
}
|
|
|
|
for (int i = 1; i < yRoads; i++) {
|
|
roadY.Append(bounds.mins.y + yStep * i);
|
|
}
|
|
|
|
for (int i = 0; i < roadX.Num(); i++) {
|
|
const float x = roadX[i];
|
|
for (int segment = 0; segment <= roadY.Num(); segment++) {
|
|
const float y0 = (segment == 0) ? bounds.mins.y : roadY[segment - 1] + roadHalf;
|
|
const float y1 = (segment == roadY.Num()) ? bounds.maxs.y : roadY[segment] - roadHalf;
|
|
City_AddVerticalRoadBrush(x, y0, y1, bounds, stats);
|
|
}
|
|
City_AddVerticalStreetEdges(x, bounds, roadY, stats);
|
|
stats.streets++;
|
|
}
|
|
|
|
for (int i = 0; i < roadY.Num(); i++) {
|
|
const float y = roadY[i];
|
|
for (int segment = 0; segment <= roadX.Num(); segment++) {
|
|
const float x0 = (segment == 0) ? bounds.mins.x : roadX[segment - 1] + roadHalf;
|
|
const float x1 = (segment == roadX.Num()) ? bounds.maxs.x : roadX[segment] - roadHalf;
|
|
City_AddHorizontalRoadBrush(x0, x1, y, bounds, stats);
|
|
}
|
|
City_AddHorizontalStreetEdges(y, bounds, roadX, stats);
|
|
stats.streets++;
|
|
}
|
|
|
|
for (int x = 0; x < roadX.Num(); x++) {
|
|
for (int y = 0; y < roadY.Num(); y++) {
|
|
City_AddIntersectionRoadBrush(roadX[x], roadY[y], bounds, stats);
|
|
if (((x + y) & 1) == 0) {
|
|
City_AddStreetSign(roadX[x], roadY[y], x, y, bounds, stats);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void City_AddDirtLot(const cityBounds_t& bounds, float minX, float minY, float maxX, float maxY, cityStats_t& stats) {
|
|
if (maxX - minX <= 1.0f || maxY - minY <= 1.0f) {
|
|
return;
|
|
}
|
|
|
|
City_AddWorldBrush(
|
|
idVec3(minX, minY, bounds.mins.z),
|
|
idVec3(maxX, maxY, bounds.mins.z + 8.0f),
|
|
CITY_MAT_MARS_DIRT,
|
|
stats);
|
|
}
|
|
|
|
static void City_AddDirtLots(const cityBounds_t& bounds, const idList<float>& roadX, const idList<float>& roadY, cityStats_t& stats) {
|
|
const float clearHalf = CITY_ROAD_WIDTH * 0.5f + CITY_SIDEWALK_WIDTH;
|
|
float y0 = bounds.mins.y;
|
|
|
|
for (int y = 0; y <= roadY.Num(); y++) {
|
|
const float y1 = (y == roadY.Num()) ? bounds.maxs.y : roadY[y] - clearHalf;
|
|
float x0 = bounds.mins.x;
|
|
|
|
for (int x = 0; x <= roadX.Num(); x++) {
|
|
const float x1 = (x == roadX.Num()) ? bounds.maxs.x : roadX[x] - clearHalf;
|
|
City_AddDirtLot(bounds, x0, y0, x1, y1, stats);
|
|
|
|
if (x < roadX.Num()) {
|
|
x0 = roadX[x] + clearHalf;
|
|
}
|
|
}
|
|
|
|
if (y < roadY.Num()) {
|
|
y0 = roadY[y] + clearHalf;
|
|
}
|
|
}
|
|
}
|
|
|
|
static const char* City_BuildingMaterial(int x, int y) {
|
|
static const char* materials[] = {
|
|
CITY_MAT_MARS_BUILDING_01,
|
|
CITY_MAT_MARS_BUILDING_02
|
|
};
|
|
return materials[City_Hash(x, y, 17) % (sizeof(materials) / sizeof(materials[0]))];
|
|
}
|
|
|
|
static void City_AddBuilding(float minX, float minY, float maxX, float maxY, float baseZ, int gx, int gy, cityStats_t& stats) {
|
|
const int floors = 2 + (City_Hash(gx, gy, 31) % 8);
|
|
const float z0 = baseZ + 8.0f;
|
|
const float z1 = z0 + 96.0f * floors + City_Random01(gx, gy, 23) * 64.0f;
|
|
idVec3 mins(minX, minY, z0);
|
|
idVec3 maxs(maxX, maxY, z1);
|
|
|
|
City_AddBuildingBrush(mins, maxs, City_BuildingMaterial(gx, gy), stats);
|
|
City_AddWorldBrush(idVec3(minX - 8.0f, minY - 8.0f, z1), idVec3(maxX + 8.0f, maxY + 8.0f, z1 + 16.0f), CITY_MAT_MARS_DIRT, stats);
|
|
stats.buildings++;
|
|
}
|
|
|
|
static void City_AddBlockBuildings(const cityBounds_t& bounds, float minX, float minY, float maxX, float maxY, int blockX, int blockY, cityStats_t& stats) {
|
|
minX += CITY_BUILDING_EDGE_SETBACK;
|
|
minY += CITY_BUILDING_EDGE_SETBACK;
|
|
maxX -= CITY_BUILDING_EDGE_SETBACK;
|
|
maxY -= CITY_BUILDING_EDGE_SETBACK;
|
|
if (maxX - minX < 128.0f || maxY - minY < 128.0f) {
|
|
return;
|
|
}
|
|
|
|
const int lotsX = City_ClampInt((int)((maxX - minX) / CITY_BUILDING_LOT_SIZE), 1, 2);
|
|
const int lotsY = City_ClampInt((int)((maxY - minY) / CITY_BUILDING_LOT_SIZE), 1, 2);
|
|
const float stepX = (maxX - minX) / (float)lotsX;
|
|
const float stepY = (maxY - minY) / (float)lotsY;
|
|
|
|
for (int y = 0; y < lotsY; y++) {
|
|
for (int x = 0; x < lotsX; x++) {
|
|
if ((City_Hash(blockX * 17 + x, blockY * 19 + y, 7) % 100) < 22) {
|
|
continue;
|
|
}
|
|
|
|
float cellMinX = minX + x * stepX + CITY_BUILDING_ALLEY;
|
|
float cellMinY = minY + y * stepY + CITY_BUILDING_ALLEY;
|
|
float cellMaxX = minX + (x + 1) * stepX - CITY_BUILDING_ALLEY;
|
|
float cellMaxY = minY + (y + 1) * stepY - CITY_BUILDING_ALLEY;
|
|
if (cellMaxX - cellMinX < 96.0f || cellMaxY - cellMinY < 96.0f) {
|
|
continue;
|
|
}
|
|
|
|
const float shrinkX = City_Random01(blockX * 31 + x, blockY * 37 + y, 11) * 24.0f;
|
|
const float shrinkY = City_Random01(blockX * 31 + x, blockY * 37 + y, 13) * 24.0f;
|
|
City_AddBuilding(
|
|
cellMinX + shrinkX,
|
|
cellMinY + shrinkY,
|
|
cellMaxX - shrinkX,
|
|
cellMaxY - shrinkY,
|
|
bounds.mins.z,
|
|
blockX * 10 + x,
|
|
blockY * 10 + y,
|
|
stats);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void City_AddBuildings(const cityBounds_t& bounds, const idList<float>& roadX, const idList<float>& roadY, cityStats_t& stats) {
|
|
const float clearHalf = CITY_ROAD_WIDTH * 0.5f + CITY_SIDEWALK_WIDTH;
|
|
float y0 = bounds.mins.y;
|
|
|
|
for (int y = 0; y <= roadY.Num(); y++) {
|
|
const float y1 = (y == roadY.Num()) ? bounds.maxs.y : roadY[y] - clearHalf;
|
|
float x0 = bounds.mins.x;
|
|
|
|
for (int x = 0; x <= roadX.Num(); x++) {
|
|
const float x1 = (x == roadX.Num()) ? bounds.maxs.x : roadX[x] - clearHalf;
|
|
City_AddBlockBuildings(bounds, x0, y0, x1, y1, x, y, stats);
|
|
|
|
if (x < roadX.Num()) {
|
|
x0 = roadX[x] + clearHalf;
|
|
}
|
|
}
|
|
|
|
if (y < roadY.Num()) {
|
|
y0 = roadY[y] + clearHalf;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void City_AddPlayerStart(const cityBounds_t& bounds, cityStats_t& stats) {
|
|
City_AddFixedEntity("info_player_start", idVec3(bounds.mins.x + 192.0f, bounds.mins.y + 192.0f, bounds.mins.z + 48.0f), stats);
|
|
}
|
|
|
|
static void City_AddPointLight(cityStats_t& stats) {
|
|
idEditorEntity* ent = City_AddFixedEntity("light", CITY_POINT_LIGHT_ORIGIN, stats);
|
|
if (ent == NULL) {
|
|
return;
|
|
}
|
|
|
|
ent->SetKeyValue("_color", va("%.3f %.3f %.3f", CITY_POINT_LIGHT_COLOR.x, CITY_POINT_LIGHT_COLOR.y, CITY_POINT_LIGHT_COLOR.z));
|
|
ent->SetKeyValue("light_radius", va("%i %i %i", (int)CITY_POINT_LIGHT_RADIUS, (int)CITY_POINT_LIGHT_RADIUS, (int)CITY_POINT_LIGHT_RADIUS));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void Radiant_GenerateAutomaticCity() {
|
|
if (world_entity == NULL) {
|
|
Sys_Status("Automatic City Builder needs an open map\n");
|
|
Sys_Beep();
|
|
return;
|
|
}
|
|
|
|
Sys_BeginWait();
|
|
Undo_Start("automatic city");
|
|
|
|
cityStats_t stats;
|
|
stats.brushes = 0;
|
|
stats.entities = 0;
|
|
stats.buildings = 0;
|
|
stats.streets = 0;
|
|
|
|
idList<float> roadX;
|
|
idList<float> roadY;
|
|
const cityBounds_t bounds = City_GetTargetBounds();
|
|
|
|
Select_Deselect();
|
|
City_AddRoads(bounds, roadX, roadY, stats);
|
|
City_AddDirtLots(bounds, roadX, roadY, stats);
|
|
City_AddBuildings(bounds, roadX, roadY, stats);
|
|
City_AddPlayerStart(bounds, stats);
|
|
City_AddPointLight(stats);
|
|
|
|
Map_BuildBrushData();
|
|
Sys_UpdateWindows(W_ALL);
|
|
mapModified = 1;
|
|
|
|
Undo_End();
|
|
Sys_EndWait();
|
|
common->Printf("Automatic City Builder: %i streets, %i buildings, %i brushes, %i entities\n", stats.streets, stats.buildings, stats.brushes, stats.entities);
|
|
Sys_Status(va("Automatic City Builder: %i streets, %i buildings generated", stats.streets, stats.buildings), 0);
|
|
}
|