This document covers the services registered by SC4RenderServices.dll:
ImGui, S3D Camera, Draw, and Terrain Decal. All services are currently gated to
SimCity 4 version 1.1.641. If the version check fails, the service is not
registered and cIGZFrameWork::GetSystemService will fail.
Acquire a service with cIGZFrameWork::GetSystemService using the service ID and IID. The returned interface is AddRef'd; callers must Release().
Example:
cIGZImGuiService* service = nullptr;
if (fw->GetSystemService(kImGuiServiceID, GZIID_cIGZImGuiService,
reinterpret_cast<void**>(&service))) {
// use service
service->Release();
}IDs and interface:
- Service ID:
kImGuiServiceIDinsrc/public/ImGuiServiceIds.h - Interface ID:
GZIID_cIGZImGuiServiceinsrc/public/ImGuiServiceIds.h - Interface header:
src/public/cIGZImGuiService.h
Threading and lifecycle:
- Initialization happens after the DirectX 7 driver and the game window are available.
- Panel callbacks and
QueueRendercallbacks run on the render thread betweenImGui::NewFrame()andImGui::EndFrame(). GetContext()returns the ImGui context pointer ornullptrwhen not ready.
Panel registration and callbacks:
RegisterPanelcopies the descriptor; update state through yourdatapointer or unregister and re-register.on_initruns once after ImGui initializes.on_updateandon_renderrun each frame whenvisibleis true.on_visible_changedruns whenSetPanelVisibletoggles the flag.on_shutdownruns during service shutdown.on_unregisterruns when you explicitly unregister.on_device_lostandon_device_restoredrun after a device loss or restore.- Callbacks are invoked without holding internal locks. If you register/unregister/modify visibility inside a callback, the change applies on the next frame because the render loop uses a snapshot.
Render queue:
QueueRenderruns a one-shot callback on the next frame.- The optional cleanup runs immediately after the callback or during shutdown if still queued.
Texture API:
CreateTexturestores RGBA32 source pixels and returns anImGuiTextureHandle.GetTextureIDreturns aIDirectDrawSurface7*asvoid*ornullptrif the device is lost or the handle is stale.ReleaseTexturefrees the underlying surface and removes the handle.IsTextureValidreturns false if the handle is stale or the device is lost.- Texture calls must be made on the render thread.
- Device resets increment
GetDeviceGeneration. Handles with older generations are invalid.
Fonts:
RegisterFontrequires ImGui to be initialized and a unique font ID.- Fonts are stored inside ImGui; the service only tracks IDs and pointers.
- Register/unregister fonts on the render thread.
DX7 access:
AcquireD3DInterfacesreturns AddRef'dIDirect3DDevice7andIDirectDraw7pointers.- Always
Release()both interfaces when done. - Prefer acquiring per operation instead of caching across frames.
Usage snippet:
static void RenderPanel(void* data) {
ImGui::Begin("My Panel");
ImGui::TextUnformatted("Hello from a client DLL.");
ImGui::End();
}
void RegisterPanel(cIGZFrameWork* fw) {
cIGZImGuiService* service = nullptr;
if (!fw->GetSystemService(kImGuiServiceID, GZIID_cIGZImGuiService,
reinterpret_cast<void**>(&service))) {
return;
}
ImGuiPanelDesc desc{};
desc.id = 0x12345678;
desc.order = 100;
desc.visible = true;
desc.on_render = &RenderPanel;
service->RegisterPanel(desc);
service->Release();
}Larger examples:
../src/sample/ImGuiSampleDirector.cpp../src/sample/ImGuiSampleDemoDirector.cpp../src/sample/ImGuiTextureSample.cpp../src/sample/OverlayManagerSampleDirector.cpp
IDs and interface:
- Service ID:
kS3DCameraServiceIDinsrc/public/S3DCameraServiceIds.h - Interface ID:
GZIID_cIGZS3DCameraServiceinsrc/public/S3DCameraServiceIds.h - Interface header:
src/public/cIGZS3DCameraService.h
Camera handles:
- Handles include a version tag to prevent cross-build use.
WrapCamerareturns a non-owned handle for a game-managed camera.WrapActiveRendererCamerareturns the active renderer camera if available.CreateCamerais not supported by the built-in service and returns a null handle.DestroyCamerais a no-op for wrapped handles.
Projection helpers:
Project,UnProject, andWorldToScreenare thin wrappers around the game camera.- Functions return false or null when the handle is invalid or the camera is unavailable.
Viewport and transforms:
- Viewport, subview, ortho, view volume, and transform helpers map to the underlying
cS3DCameramethods. - Call these on the main or render thread only.
Usage snippet:
cIGZS3DCameraService* cameraService = nullptr;
if (!fw->GetSystemService(kS3DCameraServiceID, GZIID_cIGZS3DCameraService,
reinterpret_cast<void**>(&cameraService))) {
return;
}
auto handle = cameraService->WrapActiveRendererCamera();
float sx = 0.0f;
float sy = 0.0f;
if (cameraService->WorldToScreen(handle, worldX, worldY, worldZ, sx, sy)) {
// use screen coordinates
}
cameraService->Release();Larger examples:
../src/sample/WorldProjectionSampleDirector.cpp../src/sample/S3DCameraDebugSampleDirector.cpp
IDs and interface:
- Service ID:
kDrawServiceIDinsrc/public/cIGZDrawService.h - Interface ID:
GZIID_cIGZDrawServiceinsrc/public/cIGZDrawService.h - Interface header:
src/public/cIGZDrawService.h
Draw context handles:
WrapDrawContextandWrapActiveRendererDrawContextreturn a version-tagged handle.- Most API calls are no-ops if the handle is invalid or from a different game version.
Render pass callbacks:
RegisterDrawPassCallbackinstalls call-site patches for the requested pass.- Callbacks are invoked twice per pass:
begin=truebefore the game pass andbegin=falseafter. - The callback list is snapshotted at the start of the pass. If you unregister during a callback, you may still receive the
begin=falsecall for that pass; the change takes effect on the next pass. - Keep callbacks short; they run on the render thread.
Render state helpers:
- The remaining methods are thin wrappers around the game render context (materials, textures, fog, lighting, primitives, etc.).
- These functions assume you pass a valid draw context handle.
Usage snippet:
static void OnDrawPass(DrawServicePass pass, bool begin, void* userData) {
if (pass == DrawServicePass::Dynamic && begin) {
// Inject custom draw setup/work before Dynamic pass.
}
}
uint32_t token = 0;
if (drawService->RegisterDrawPassCallback(DrawServicePass::Dynamic,
&OnDrawPass, myData, &token)) {
// Keep token and call UnregisterDrawPassCallback(token) during shutdown.
}Larger examples:
../src/sample/DrawServiceSampleDirector.cpp../src/sample/road-decal/RoadDecalSampleDirector.cpp
IDs and interface:
- Service ID:
kTerrainDecalServiceIDinsrc/public/TerrainDecalServiceIds.h - Interface ID:
GZIID_cIGZTerrainDecalServiceinsrc/public/TerrainDecalServiceIds.h - Interface header:
src/public/cIGZTerrainDecalService.h
Capabilities:
- Create, update, remove, and enumerate managed terrain decals.
- Persist managed decals into the city save and recreate them on load.
- Target static or dynamic land/water overlay managers.
- Apply optional UV sub-rectangle overrides for atlas-style textures.
Important behavior:
CreateDecalandReplaceDecaloperate on the active city only.GetDecalandCopyDecalsreturn service-managed snapshots.- The service is currently intended for plugin-managed decals, not for discovering or taking ownership of arbitrary pre-existing terrain overlays.
Minimal usage:
cIGZTerrainDecalService* terrainDecalService = nullptr;
if (!fw->GetSystemService(kTerrainDecalServiceID,
GZIID_cIGZTerrainDecalService,
reinterpret_cast<void**>(&terrainDecalService))) {
return;
}
TerrainDecalState state{};
state.textureKey = cGZPersistResourceKey{0x7AB50E44, 0x1ABE787D, 0xAA40173A};
state.overlayType = cISTETerrainView::tOverlayManagerType::DynamicLand;
state.decalInfo.center = cS3DVector2{512.0f, 512.0f};
state.decalInfo.baseSize = 16.0f;
state.decalInfo.aspectMultiplier = 1.0f;
state.decalInfo.uvScaleU = 1.0f;
state.decalInfo.uvScaleV = 1.0f;
state.opacity = 1.0f;
state.enabled = true;
TerrainDecalId id{};
terrainDecalService->CreateDecal(state, &id);
terrainDecalService->Release();Larger example:
../src/sample/TerrainDecalSampleDirector.cpp
Detailed reference:
terrain-decals.md