bugfix(view): Fix view ray cast behavior for mouse picks and drawable occlusion#2818
Conversation
|
| Filename | Overview |
|---|---|
| Core/Libraries/Source/WWVegas/WWMath/plane.h | Promotes Compute_Intersection return type from bool to new IntersectionResType enum distinguishing NO_INTERSECTION, INSIDE_SEGMENT, and OUTSIDE_LINE; all existing callers in the codebase either discard the return value or have been updated, so no behavioral regression. |
| Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp | Key fixes: screenToTerrain now returns Bool and only writes to world when a terrain or bridge intersection exists; screenToWorldAtZ now uses PlaneClass::Compute_Intersection and returns IntersectionResType; getPickRay and the lookAt ray scale by sqr(Get_Depth()) for a very long ray; getAxisAlignedViewRegion falls back to the drawn terrain region when screen corners do not fully intersect the Z=0 plane. |
| Core/GameEngine/Source/GameClient/View.cpp | getScreenCornerWorldPointsAtZ now accepts a ViewportClass parameter (defaults to full-screen), returns IntersectionResType reflecting the worst-case intersection result across all four corners, and correctly propagates OUTSIDE_LINE vs NO_INTERSECTION. |
| Core/GameEngineDevice/Source/W3DDevice/Common/System/W3DRadar.cpp | reconstructViewBox resets m_reconstructViewBox at the function start (preventing every-frame retry storms when the camera looks at sky) and returns early on NO_INTERSECTION; drawViewBox likewise guards its screenToWorldAtZ call; local variables renamed from start/end to pixelStart/pixelEnd for clarity. |
| Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp | All screenToTerrain call sites now guard with if(!...) break before dispatching guard/move commands; isPoint is now computed after the terrain check to avoid the unused calculation on early exit. |
| Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp | Radius cursor position, double-click attack-move hint, and build-placement icons are all now guarded on successful terrain intersection; when the cursor is over sky, icons freeze at their last valid position rather than jumping to the world origin. |
| Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp | Adds getDrawRegion2D() which consolidates the repeated inline heightmap-to-world-space region calculation into a single reusable method, used by both updateCameraClipPlanes and getAxisAlignedViewRegion. |
| Core/Libraries/Include/Lib/BaseType.h | Adds Region3D::setXY(const Region2D&) and renames setFromPointsNoZ to setXYFromPoints with updated doc-comments; callers are fully updated and Z is always explicitly assigned after these calls. |
| Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp | One-line update in getMaximumVisibleBox to explicitly compare Compute_Intersection against INSIDE_SEGMENT rather than relying on implicit bool conversion; semantically identical to the old code since the old function returned false for OUTSIDE_LINE. |
| Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DShaderManager.cpp | Adds centerPos.zero() before calling screenToTerrain so that when the cursor is over sky and the call returns false, the mask texture shader projects from world-origin rather than from an uninitialized value. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant User as Mouse Input
participant Translator as Command Translator
participant View as W3DView
participant Plane as PlaneClass
participant Terrain as TerrainRenderObj
User->>Translator: Mouse click / move event
Translator->>View: screenToTerrain(screen, world)
View->>View: getPickRay() → rayStart, rayEnd (scaled by sqr(depth))
View->>Terrain: Cast_Ray(raytest)
alt Terrain hit
Terrain-->>View: ContactPoint
View-->>Translator: true + world coords
Translator->>Translator: Dispatch game command
else No terrain hit (sky)
Terrain-->>View: no hit
View-->>Translator: false
Translator->>Translator: break / COMMAND_COMPLETE (no command sent)
end
User->>View: getAxisAlignedViewRegion(region)
View->>View: "getScreenCornerWorldPointsAtZ(corners, z=0)"
View->>Plane: Compute_Intersection(rayStart, rayEnd, t)
alt All 4 corners: INSIDE_SEGMENT
Plane-->>View: INSIDE_SEGMENT
View->>View: setXYFromPoints(corners)
else Any corner: OUTSIDE_LINE or NO_INTERSECTION
Plane-->>View: OUTSIDE_LINE / NO_INTERSECTION
View->>View: fallback to heightMap.getDrawRegion2D()
end
View-->>View: set Z extents from mapExtent +/- safeValue
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant User as Mouse Input
participant Translator as Command Translator
participant View as W3DView
participant Plane as PlaneClass
participant Terrain as TerrainRenderObj
User->>Translator: Mouse click / move event
Translator->>View: screenToTerrain(screen, world)
View->>View: getPickRay() → rayStart, rayEnd (scaled by sqr(depth))
View->>Terrain: Cast_Ray(raytest)
alt Terrain hit
Terrain-->>View: ContactPoint
View-->>Translator: true + world coords
Translator->>Translator: Dispatch game command
else No terrain hit (sky)
Terrain-->>View: no hit
View-->>Translator: false
Translator->>Translator: break / COMMAND_COMPLETE (no command sent)
end
User->>View: getAxisAlignedViewRegion(region)
View->>View: "getScreenCornerWorldPointsAtZ(corners, z=0)"
View->>Plane: Compute_Intersection(rayStart, rayEnd, t)
alt All 4 corners: INSIDE_SEGMENT
Plane-->>View: INSIDE_SEGMENT
View->>View: setXYFromPoints(corners)
else Any corner: OUTSIDE_LINE or NO_INTERSECTION
Plane-->>View: OUTSIDE_LINE / NO_INTERSECTION
View->>View: fallback to heightMap.getDrawRegion2D()
end
View-->>View: set Z extents from mapExtent +/- safeValue
Reviews (5): Last reviewed commit: "Replicate in Generals" | Re-trigger Greptile
7c85fda to
0db89be
Compare
| } | ||
| if( individualResults[i] == PlaneClass::OUTSIDE_LINE ) | ||
| { | ||
| combinedResult = PlaneClass::OUTSIDE_LINE; |
There was a problem hiding this comment.
No because NO_INTERSECTION will take precedence over OUTSIDE_LINE
stephanmeesters
left a comment
There was a problem hiding this comment.
Looks correct to me, also did a test using the map Arabia v2 and the issue is fixed as expected
… occlusion and radar box
2d92831 to
f824bb7
Compare
|
Replicated to Generals with one conflict in InGameUI.cpp |
This change fixes view ray casting behavior for mouse picks and drawable occlusion. The behavior for the radar view box is still not working correctly at low camera pitch but is otherwise still ok for regular top-down camera views. I do not yet know how to deal with the view box at low camera pitch.
The view ray now has a very long range making sure that the camera far clip plane is not limiting mouse picks. Also, view rays into the sky will no longer send units to 0,0,0: Invalid ray picks are now treated as no input.
The drawable updates made in the area created with
getAxisAlignedViewRegionare now working with low camera pitch. The function falls back to the terrain draw area if the view frustum does not fully intersect with the Z plane.Known issues
The Radar View Box still looks bad at low camera pitch as did always.
TODO