| 227 | } |
| 228 | |
| 229 | Result CameraScreenToWorld(HRenderContext render_context, HRenderCamera camera_handle, float screen_x, float screen_y, float z, dmVMath::Vector3* out_world) |
| 230 | { |
| 231 | RenderCamera* camera = render_context->m_RenderCameras.Get(camera_handle); |
| 232 | |
| 233 | // Window size |
| 234 | dmGraphics::HContext gc = GetGraphicsContext(render_context); |
| 235 | float win_w = (float) dmGraphics::GetWindowWidth(gc); |
| 236 | float win_h = (float) dmGraphics::GetWindowHeight(gc); |
| 237 | if (win_w <= 0.0f) |
| 238 | { |
| 239 | win_w = 1.0f; |
| 240 | } |
| 241 | if (win_h <= 0.0f) |
| 242 | { |
| 243 | win_h = 1.0f; |
| 244 | } |
| 245 | |
| 246 | // Viewport-aware screen -> Normalized Device Coordinates |
| 247 | const dmVMath::Vector4& vp = camera->m_Data.m_Viewport; |
| 248 | float vx = vp.getX() * win_w; |
| 249 | float vy = vp.getY() * win_h; |
| 250 | float vw = vp.getZ() * win_w; |
| 251 | float vh = vp.getW() * win_h; |
| 252 | if (vw <= 0.0f || vh <= 0.0f) |
| 253 | { |
| 254 | vx = 0.0f; vy = 0.0f; vw = win_w; vh = win_h; |
| 255 | } |
| 256 | float x_ndc = 2.0f * ((screen_x - vx) / vw) - 1.0f; |
| 257 | float y_ndc = 2.0f * ((screen_y - vy) / vh) - 1.0f; |
| 258 | |
| 259 | dmVMath::Matrix4 inv_vp = dmVMath::Inverse(camera->m_ViewProjection); |
| 260 | |
| 261 | // Unproject near/far points and build pixel ray (world) |
| 262 | dmVMath::Vector4 v_near_clip(x_ndc, y_ndc, -1.0f, 1.0f); |
| 263 | dmVMath::Vector4 v_far_clip (x_ndc, y_ndc, 1.0f, 1.0f); |
| 264 | dmVMath::Vector4 v0 = inv_vp * v_near_clip; |
| 265 | dmVMath::Vector4 v1 = inv_vp * v_far_clip; |
| 266 | float iw0 = 1.0f / v0.getW(); |
| 267 | float iw1 = 1.0f / v1.getW(); |
| 268 | dmVMath::Point3 p_near(v0.getX() * iw0, v0.getY() * iw0, v0.getZ() * iw0); |
| 269 | dmVMath::Point3 p_far (v1.getX() * iw1, v1.getY() * iw1, v1.getZ() * iw1); |
| 270 | dmVMath::Vector3 dir = dmVMath::Normalize(p_far - p_near); |
| 271 | |
| 272 | // Camera forward (world) |
| 273 | dmVMath::Vector3 forward = dmVMath::Rotate(camera->m_LastRotation, dmVMath::Vector3(0.0f, 0.0f, -1.0f)); |
| 274 | forward = dmVMath::Normalize(forward); |
| 275 | |
| 276 | // z is view-depth along forward (world units from camera plane) |
| 277 | float denom = dmVMath::Dot(dir, forward); |
| 278 | if (denom > -EPS && denom < EPS) |
| 279 | { |
| 280 | return RESULT_INVALID_PARAMETER; |
| 281 | } |
| 282 | |
| 283 | dmVMath::Point3 cam_pos = camera->m_LastPosition; |
| 284 | // Intersect the ray r(s) = p_near + s * dir with the view-depth plane at distance z along 'forward' |
| 285 | float dot0 = dmVMath::Dot(p_near - cam_pos, forward); |
| 286 | float s = (z - dot0) / denom; |
no test coverage detected