Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions PROJECTS/ROLLER/drawtrk3.c
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,10 @@ void drawtrk3_editor_draw_helpers(GameRenderer *pRenderer)
ED_STUNT_MARKER_PALETTE_COLOUR);
}
}
if (roller_ed_overlay_enabled(ROLLER_ED_OVERLAY_SHOW_TOWER_MARKERS)) {
for (int iTowerIdx = 0; iTowerIdx < NumTowers; iTowerIdx++)
tower_emit_marker(iTowerIdx, 1.0f);
}
}

static void draw_emitted_surface(const tEdSurfaceEmission *pSurface,
Expand Down
61 changes: 61 additions & 0 deletions PROJECTS/ROLLER/editor_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,67 @@ eRollerEdResult ROLLER_ED_CALL RollerEd_QueryGeometrySizes(
return ROLLER_ED_RESULT_OK;
}

eRollerEdResult ROLLER_ED_CALL RollerEd_QueryTowerCount(
uint32_t *puiCountOut)
{
eRollerEdResult eResult = roller_ed_require_worker();
uint32_t uiCount;

if (eResult != ROLLER_ED_RESULT_OK)
return eResult;
if (!puiCountOut) {
roller_ed_set_error("RollerEd_QueryTowerCount requires puiCountOut");
return ROLLER_ED_RESULT_INVALID_ARGUMENT;
}
if (s_eSceneState != ROLLER_ED_SCENE_READY) {
roller_ed_set_error("there is no tower scene");
return ROLLER_ED_RESULT_NO_SCENE;
}

uiCount = roller_ed_legacy_scene_tower_count();
*puiCountOut = uiCount;
return ROLLER_ED_RESULT_OK;
}

eRollerEdResult ROLLER_ED_CALL RollerEd_QueryTower(
uint32_t uiTowerIndex, tEdTowerInfo *pInfoOut)
{
eRollerEdResult eResult = roller_ed_require_worker();
tEdTowerInfo Info;
uint32_t uiCount;

if (eResult != ROLLER_ED_RESULT_OK)
return eResult;
if (!pInfoOut) {
roller_ed_set_error("RollerEd_QueryTower requires pInfoOut");
return ROLLER_ED_RESULT_INVALID_ARGUMENT;
}
eResult = roller_ed_validate_struct(
pInfoOut->uiStructSize, pInfoOut->uiVersion,
ROLLER_ED_TOWER_INFO_VERSION, sizeof(*pInfoOut),
"tEdTowerInfo");
if (eResult != ROLLER_ED_RESULT_OK)
return eResult;
if (s_eSceneState != ROLLER_ED_SCENE_READY) {
roller_ed_set_error("there is no tower scene");
return ROLLER_ED_RESULT_NO_SCENE;
}

uiCount = roller_ed_legacy_scene_tower_count();
if (uiTowerIndex >= uiCount) {
roller_ed_set_error("tower index %u is not below tower count %u",
uiTowerIndex, uiCount);
return ROLLER_ED_RESULT_INVALID_ARGUMENT;
}

memset(&Info, 0, sizeof(Info));
Info.uiStructSize = sizeof(Info);
Info.uiVersion = ROLLER_ED_TOWER_INFO_VERSION;
roller_ed_legacy_scene_query_tower(uiTowerIndex, &Info);
*pInfoOut = Info;
return ROLLER_ED_RESULT_OK;
}

eRollerEdResult ROLLER_ED_CALL RollerEd_FillGeometry(
uint32_t uiExpectedGeometryEpoch,
tEdVertex *pVerts, uint32_t uiVertexCapacity,
Expand Down
42 changes: 40 additions & 2 deletions PROJECTS/ROLLER/editor_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,14 @@ enum
* rather than a design of its own. F1WACK and DEATH have no Y variant and
* ignore it, exactly as the game does.
*/
ROLLER_ED_OVERLAY_TEST_CAR_ADVANCED = 1u << 11
ROLLER_ED_OVERLAY_TEST_CAR_ADVANCED = 1u << 11,
/*
* E7-S3. Camera-tower markers reuse the game's constant-screen-size
* DrawTower billboard. They are editor furniture rather than authored
* export geometry, and their visibility is independent of the surface
* master and per-class masks.
*/
ROLLER_ED_OVERLAY_SHOW_TOWER_MARKERS = 1u << 12
};

/*
Expand All @@ -251,7 +258,9 @@ enum
* Overlay class masks. SHOW_SURFACES and SHOW_WIREFRAME are master switches;
* each mask then selects which surface classes that switch applies to, one bit
* per eRollerEdSurfaceClass value. A class is drawn only when both agree, so
* the host can blank the view without losing its per-class choices.
* the host can blank the view without losing its per-class choices. The tower
* marker is editor furniture and is the sole exception: its solid visibility
* follows ROLLER_ED_OVERLAY_SHOW_TOWER_MARKERS regardless of these masks.
*/
#define ROLLER_ED_OVERLAY_CLASS_BIT(surface_class) \
(1u << (uint32_t)(surface_class))
Expand Down Expand Up @@ -279,6 +288,7 @@ enum
#define ROLLER_ED_OVERLAY_STATE_VERSION 3u
#define ROLLER_ED_REFERENCE_MESH_VERSION 1u
#define ROLLER_ED_GEOMETRY_SIZES_VERSION 1u
#define ROLLER_ED_TOWER_INFO_VERSION 1u

#if defined(_MSC_VER) || defined(__GNUC__) || defined(__clang__)
# pragma pack(push, 8)
Expand Down Expand Up @@ -421,6 +431,15 @@ typedef struct
uint32_t uiMaterialStride;
} tEdGeometrySizes;

typedef struct
{
uint32_t uiStructSize;
uint32_t uiVersion;
uint32_t uiChunkId;
float fWorldPosition[3];
float fAnchorPosition[3];
} tEdTowerInfo;

#if defined(_MSC_VER) || defined(__GNUC__) || defined(__clang__)
# pragma pack(pop)
#endif
Expand Down Expand Up @@ -524,6 +543,15 @@ ROLLER_ED_STATIC_ASSERT(offsetof(tEdGeometrySizes, uiGeometryEpoch) == 8u,
"geometry epoch offset");
ROLLER_ED_STATIC_ASSERT(offsetof(tEdGeometrySizes, uiVertexStride) == 36u,
"geometry stride offset");
ROLLER_ED_STATIC_ASSERT(sizeof(tEdTowerInfo) == 36u, "tower info size");
ROLLER_ED_STATIC_ASSERT(ROLLER_ED_ALIGNOF(tEdTowerInfo) == 4u,
"tower info alignment");
ROLLER_ED_STATIC_ASSERT(offsetof(tEdTowerInfo, uiChunkId) == 8u,
"tower chunk offset");
ROLLER_ED_STATIC_ASSERT(offsetof(tEdTowerInfo, fWorldPosition) == 12u,
"tower world position offset");
ROLLER_ED_STATIC_ASSERT(offsetof(tEdTowerInfo, fAnchorPosition) == 24u,
"tower anchor position offset");
ROLLER_ED_STATIC_ASSERT(ROLLER_ED_ALIGNOF(tEdVertex) == 4u, "vertex alignment");
ROLLER_ED_STATIC_ASSERT(ROLLER_ED_ALIGNOF(tEdPrimitive) == 4u, "primitive alignment");
ROLLER_ED_STATIC_ASSERT(ROLLER_ED_ALIGNOF(tEdMaterial) == 4u, "material alignment");
Expand Down Expand Up @@ -608,6 +636,16 @@ ROLLER_ED_API eRollerEdResult ROLLER_ED_CALL RollerEd_SetOverlayState(
ROLLER_ED_API eRollerEdResult ROLLER_ED_CALL RollerEd_SetReferenceMesh(
const tEdReferenceMesh *pMesh);

/*
* Tower results are copied from the committed scene during the call; no
* pointer is retained. Both calls require a READY scene on the render worker.
* pInfoOut must carry sizeof(tEdTowerInfo) and TOWER_INFO_VERSION on entry.
*/
ROLLER_ED_API eRollerEdResult ROLLER_ED_CALL RollerEd_QueryTowerCount(
uint32_t *puiCountOut);
ROLLER_ED_API eRollerEdResult ROLLER_ED_CALL RollerEd_QueryTower(
uint32_t uiTowerIndex, tEdTowerInfo *pInfoOut);

ROLLER_ED_API eRollerEdResult ROLLER_ED_CALL RollerEd_QueryGeometrySizes(
tEdGeometrySizes *pSizesOut);
/* Caller-owned buffers are written only when this call returns OK. */
Expand Down
24 changes: 24 additions & 0 deletions PROJECTS/ROLLER/editor_legacy_scene.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "roller.h"
#include "roller_core_error.h"
#include "scene_render_gpu.h"
#include "tower.h"
#include "types.h"

#define SDL_MAIN_HANDLED 1
Expand Down Expand Up @@ -363,6 +364,29 @@ eRollerEdResult roller_ed_legacy_scene_set_graphics_settings(
return ROLLER_ED_RESULT_OK;
}

uint32_t roller_ed_legacy_scene_tower_count(void)
{
if (NumTowers <= 0)
return 0u;
if (NumTowers > MAX_TOWERS)
return MAX_TOWERS;
return (uint32_t)NumTowers;
}

void roller_ed_legacy_scene_query_tower(
uint32_t uiTowerIndex, tEdTowerInfo *pInfoOut)
{
int iChunkIdx = TowerBase[uiTowerIndex].iChunkIdx;

pInfoOut->uiChunkId = (uint32_t)iChunkIdx;
pInfoOut->fWorldPosition[0] = TowerX[uiTowerIndex];
pInfoOut->fWorldPosition[1] = TowerY[uiTowerIndex];
pInfoOut->fWorldPosition[2] = TowerZ[uiTowerIndex];
pInfoOut->fAnchorPosition[0] = -localdata[iChunkIdx].pointAy[3].fX;
pInfoOut->fAnchorPosition[1] = -localdata[iChunkIdx].pointAy[3].fY;
pInfoOut->fAnchorPosition[2] = -localdata[iChunkIdx].pointAy[3].fZ;
}

eRollerEdResult roller_ed_legacy_scene_install(
const char *szTrackPath,
const tEdTrackStage *pStage,
Expand Down
5 changes: 5 additions & 0 deletions PROJECTS/ROLLER/editor_legacy_scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ eRollerEdResult roller_ed_legacy_scene_set_graphics_settings(
char *szError,
size_t uiErrorCapacity);

/* Copies authoritative InitTowers results and their loaded chunk anchors. */
uint32_t roller_ed_legacy_scene_tower_count(void);
void roller_ed_legacy_scene_query_tower(
uint32_t uiTowerIndex, tEdTowerInfo *pInfoOut);

/*
* One extraction of the loaded scene's authored geometry, owned by whoever
* called extract and released through the matching call. The facade caches
Expand Down
4 changes: 4 additions & 0 deletions PROJECTS/ROLLER/editor_overlay.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ void roller_ed_overlay_get(tEdOverlayState *pStateOut)

bool roller_ed_overlay_surface_class_visible(uint16_t unSurfaceClass)
{
if (unSurfaceClass == ROLLER_ED_SURFACE_CLASS_TOWER) {
return roller_ed_overlay_enabled(
ROLLER_ED_OVERLAY_SHOW_TOWER_MARKERS);
}
return roller_ed_overlay_enabled(ROLLER_ED_OVERLAY_SHOW_SURFACES)
&& overlay_class_selected(s_uiSurfaceClassMask, unSurfaceClass);
}
Expand Down
13 changes: 8 additions & 5 deletions PROJECTS/ROLLER/editor_overlay.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
| ROLLER_ED_OVERLAY_SHOW_TEST_CAR \
| ROLLER_ED_OVERLAY_SHOW_REFERENCE_MESH \
| ROLLER_ED_OVERLAY_TEST_CAR_MILLION_PLUS \
| ROLLER_ED_OVERLAY_TEST_CAR_ADVANCED)
| ROLLER_ED_OVERLAY_TEST_CAR_ADVANCED \
| ROLLER_ED_OVERLAY_SHOW_TOWER_MARKERS)

/*
* The defaults reproduce the E1-S6 track-only view exactly: every surface
Expand Down Expand Up @@ -74,10 +75,12 @@ bool roller_ed_overlay_selection_range(uint32_t *puiFirstChunk,

/*
* E3A-S2. Both queries are the master flag AND the class's mask bit, so a host
* can blank the whole view without losing its per-class choices. A class value
* at or beyond ROLLER_ED_SURFACE_CLASS_COUNT is never drawn either way: the
* emitter refuses those identities, so seeing one here means something already
* went wrong upstream.
* can blank the whole view without losing its per-class choices. E7-S3's tower
* marker is the one surface-class exception: its solid visibility follows only
* SHOW_TOWER_MARKERS, allowing editor furniture to remain in a surface-free
* wireframe view. A class value at or beyond ROLLER_ED_SURFACE_CLASS_COUNT is
* never drawn either way: the emitter refuses those identities, so seeing one
* here means something already went wrong upstream.
*/
bool roller_ed_overlay_surface_class_visible(uint16_t unSurfaceClass);
bool roller_ed_overlay_wireframe_class_visible(uint16_t unSurfaceClass);
Expand Down
2 changes: 1 addition & 1 deletion PROJECTS/ROLLER/loadtrak.c
Original file line number Diff line number Diff line change
Expand Up @@ -1190,7 +1190,7 @@ static eRollerEdResult loadtrack_internal(
++NumBuildings;
p_fBuildingAnglesBase = p_fBuildingAngles + 2;
p_fBuildingAngles[1] = (float)iSignRoll;
} else {
} else if (NumTowers < MAX_TOWERS) {
*pTowerBasePtr++ = iChunkIdx;
pTowerBase = pTowerBasePtr;
*pTowerBasePtr = iSignHOffset;
Expand Down
112 changes: 79 additions & 33 deletions PROJECTS/ROLLER/tower.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
//-------------------------------------------------------------------------------------------------

int TowerSect[MAX_TRACK_CHUNKS]; //001A1FA0
float TowerX[32]; //001A2770
float TowerY[32]; //001A27F0
float TowerZ[32]; //001A2870
tTowerBase TowerBase[32]; //001A28F0
float TowerX[MAX_TOWERS]; //001A2770
float TowerY[MAX_TOWERS]; //001A27F0
float TowerZ[MAX_TOWERS]; //001A2870
tTowerBase TowerBase[MAX_TOWERS]; //001A28F0
tPolyParams TowerPol; //001A2B70
int NumTowers; //001A2B9C

Expand Down Expand Up @@ -78,6 +78,80 @@ void InitTowers()
TowerPol.uiNumVerts = 4; // Set tower polygon to 4 vertices (rectangular)
}

//-------------------------------------------------------------------------------------------------
/*
* E7-S3. Shared camera-facing, constant-screen-size tower marker. DrawTower
* retains the legacy gates and visibility heuristic around this seam, while
* the editor calls it directly for every loaded tower so authored camera
* modes, NearTow, and the game-only screen heuristic cannot suppress one.
*/
void tower_emit_marker(int iTowerIdx, float fScale)
{
double TowerMinusViewX;
double TowerMinusViewY;
double TowerMinusViewZ;
double dTransformed3DZ;
float fClampedZ;
GameRenderVertex verts[4];
float fHalfPixelSize;
float viewOffsetX[4];
float viewOffsetY[4];

if (iTowerIdx < 0 || iTowerIdx >= NumTowers || iTowerIdx >= MAX_TOWERS
|| !g_pGameRenderer || !(fScale > 0.0f) || !isfinite(fScale))
return;

TowerMinusViewX = TowerX[iTowerIdx] - viewx;
TowerMinusViewY = TowerY[iTowerIdx] - viewy;
TowerMinusViewZ = TowerZ[iTowerIdx] - viewz;
dTransformed3DZ = (float)TowerMinusViewX * vk3
+ (float)TowerMinusViewY * vk6
+ (float)TowerMinusViewZ * vk9;
fClampedZ = (float)dTransformed3DZ;
if (dTransformed3DZ < 80.0)
fClampedZ = 80.0f;

fHalfPixelSize = fClampedZ * 3.0f * 64.0f
/ ((float)scr_size * (float)VIEWDIST);
fHalfPixelSize *= fScale;
viewOffsetX[0] = fHalfPixelSize;
viewOffsetX[1] = -fHalfPixelSize;
viewOffsetX[2] = -fHalfPixelSize;
viewOffsetX[3] = fHalfPixelSize;
viewOffsetY[0] = fHalfPixelSize;
viewOffsetY[1] = fHalfPixelSize;
viewOffsetY[2] = -fHalfPixelSize;
viewOffsetY[3] = -fHalfPixelSize;

for (int vi = 0; vi < 4; vi++) {
verts[vi].x = TowerX[iTowerIdx]
+ viewOffsetX[vi] * vk1 + viewOffsetY[vi] * vk2;
verts[vi].y = TowerY[iTowerIdx]
+ viewOffsetX[vi] * vk4 + viewOffsetY[vi] * vk5;
verts[vi].z = TowerZ[iTowerIdx]
+ viewOffsetX[vi] * vk7 + viewOffsetY[vi] * vk8;
verts[vi].u = 0.0f;
verts[vi].v = 0.0f;
}
{
tEdSurfaceInfo SurfaceInfo;
memset(&SurfaceInfo, 0, sizeof(SurfaceInfo));
SurfaceInfo.uiChunkId = TowerBase[iTowerIdx].iChunkIdx >= 0
? (uint32_t)TowerBase[iTowerIdx].iChunkIdx
: ROLLER_ED_INVALID_CHUNK_ID;
SurfaceInfo.uiRenderFlags = SURFACE_FLAG_FLIP_BACKFACE | 0xE7;
SurfaceInfo.uiBackSurfaceFlags = ED_MATERIAL_ID_NONE;
SurfaceInfo.uiTextureSet = TEXTURE_BANK_TRACK;
SurfaceInfo.fSubdivideThreshold = 1.0f;
SurfaceInfo.unSurfaceClass = ROLLER_ED_SURFACE_CLASS_TOWER;
SurfaceInfo.unContentClass = ROLLER_ED_CONTENT_RUNTIME_SCENERY;
SurfaceInfo.byTopology = ROLLER_ED_TOPOLOGY_QUAD;
SurfaceInfo.byRenderUVLayout = ROLLER_ED_RENDER_UV_TILE;
SurfaceInfo.iRenderSubdivideType = GAME_RENDER_SUBDIVIDE_TYPE_AUTO;
drawtrk3_emit_surface_to_renderer(g_pGameRenderer, verts, &SurfaceInfo);
}
}

//-------------------------------------------------------------------------------------------------
//00075850
void DrawTower(int iTowerIdx, uint8 *pScrBuf)
Expand Down Expand Up @@ -124,35 +198,7 @@ void DrawTower(int iTowerIdx, uint8 *pScrBuf)
iPixelY = (iScreenSize * (199 - (int)dScreenY)) >> 6;// Complex visibility test for different distance ranges
if (fOriginalZ >= 5000.0 && xp >= -50 && xp < 370 && yp >= -50 && yp < 250
|| fOriginalZ > -1000.0 && fOriginalZ < 5000.0 && (fTransformed3DX > -1000.0 && fTransformed3DX < 1000.0 || xp > -200 && xp < 520)) {
GameRenderVertex verts[4];
float fHalfPixelSize = fClampedZ * 3.0f * 64.0f / ((float)scr_size * (float)VIEWDIST);
float viewOffsetX[4] = { fHalfPixelSize, -fHalfPixelSize, -fHalfPixelSize, fHalfPixelSize };
float viewOffsetY[4] = { fHalfPixelSize, fHalfPixelSize, -fHalfPixelSize, -fHalfPixelSize };
for (int vi = 0; vi < 4; vi++) {
verts[vi].x = TowerX[iTowerIdx] + viewOffsetX[vi] * vk1 + viewOffsetY[vi] * vk2;
verts[vi].y = TowerY[iTowerIdx] + viewOffsetX[vi] * vk4 + viewOffsetY[vi] * vk5;
verts[vi].z = TowerZ[iTowerIdx] + viewOffsetX[vi] * vk7 + viewOffsetY[vi] * vk8;
verts[vi].u = 0.0f;
verts[vi].v = 0.0f;
}
{
tEdSurfaceInfo SurfaceInfo;
memset(&SurfaceInfo, 0, sizeof(SurfaceInfo));
SurfaceInfo.uiChunkId = TowerBase[iTowerIdx].iChunkIdx >= 0
? (uint32_t)TowerBase[iTowerIdx].iChunkIdx
: ROLLER_ED_INVALID_CHUNK_ID;
SurfaceInfo.uiRenderFlags = SURFACE_FLAG_FLIP_BACKFACE | 0xE7;
SurfaceInfo.uiBackSurfaceFlags = ED_MATERIAL_ID_NONE;
SurfaceInfo.uiTextureSet = TEXTURE_BANK_TRACK;
SurfaceInfo.fSubdivideThreshold = 1.0f;
SurfaceInfo.unSurfaceClass = ROLLER_ED_SURFACE_CLASS_TOWER;
SurfaceInfo.unContentClass = ROLLER_ED_CONTENT_RUNTIME_SCENERY;
SurfaceInfo.byTopology = ROLLER_ED_TOPOLOGY_QUAD;
SurfaceInfo.byRenderUVLayout = ROLLER_ED_RENDER_UV_TILE;
SurfaceInfo.iRenderSubdivideType = GAME_RENDER_SUBDIVIDE_TYPE_AUTO;
drawtrk3_emit_surface_to_renderer(
g_pGameRenderer, verts, &SurfaceInfo);
}
tower_emit_marker(iTowerIdx, 1.0f);
}
}
}
Expand Down
Loading
Loading