ray
Owner: IIIlllIIIllI URL: git@github.com:nyangkosense/ray.git
refine some commentary & license
Commit 73d931cddb753d6727aa9fbf5934d9f6860ba635 by SM <seb.michalk@gmail.com> on 2025-08-20 09:03:23 +0200
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0d75965
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) sebastian <sebastian@eingabeausgabe.io>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
index f28479a..5ce8976 100644
--- a/Makefile
+++ b/Makefile
@@ -5,24 +5,20 @@
CC = x86_64-w64-mingw32-gcc
CXX = x86_64-w64-mingw32-g++
-# Plugin name and output
PLUGIN_NAME = JTACCoords
OUTPUT_DIR = build
PLUGIN_DIR = $(OUTPUT_DIR)/$(PLUGIN_NAME)
PLUGIN_FILE = $(PLUGIN_DIR)/win_x64/$(PLUGIN_NAME).xpl
-# SDK paths
SDK_DIR = SDK
INCLUDE_DIRS = -I$(SDK_DIR)/CHeaders/XPLM -I$(SDK_DIR)/CHeaders/Widgets -I.
LIB_DIR = $(SDK_DIR)/Libraries/Win
LIBS = -L$(LIB_DIR) -lXPLM_64 -lXPWidgets_64
-# Compiler flags for C++
CXXFLAGS = -std=c++11 -Wall -O2 -fPIC -DXPLM200=1 -DXPLM210=1 -DXPLM300=1 -DXPLM301=1 -DXPLM302=1 -DXPLM400=1
CXXFLAGS += $(INCLUDE_DIRS)
CXXFLAGS += -DIBM=1 -DWIN32=1 -D_WIN32=1
-# Linker flags for Windows DLL
LDFLAGS = -shared -static-libgcc -static-libstdc++
LDFLAGS += -Wl,--kill-at -Wl,--no-undefined
LDFLAGS += $(LIBS)
@@ -80,4 +76,4 @@ install: $(PLUGIN_FILE)
.PHONY: all clean directories test-compile debug release check-mingw install
# Dependencies
-probe.o: probe.cpp
\ No newline at end of file
+probe.o: probe.cpp
diff --git a/README.md b/README.md
index 5f03e64..bb28d30 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,15 @@
-# X-Plane 12 JTAC Coordinate System Plugin with missile guidance
-
+# X-Plane 12 JTAC Coordinate System Plugin
+
+
## Overview
This plugin implements a realistic Joint Terminal Attack Controller (JTAC) coordinate designation system for X-Plane 12. It combines precise terrain probing with missile guidance capabilities, allowing pilots to designate targets and guide missiles to specific coordinates using real-world military procedures.
+## License
+
+This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
+
## How It Works
### 1. **Terrain Probing & Ray Casting**
@@ -174,16 +179,22 @@ This system evolved from a simple coordinate display to a complete JTAC solution
3. **Ray Casting Refinement**: Implemented binary search for precision
4. **Missile Integration**: Added weapon system integration
5. **Physics Optimization**: Simplified guidance to prevent oscillation
-6. **Final Polish**: Make the Plugin Toggable, create a Menuentry for convenience
+6. **Final Polish**: Made plugin toggleable and added menu entry for convenience
The key breakthrough was realizing that accurate targeting requires using the camera/view position rather than the aircraft position, combined with proper binary search terrain intersection rather than linear probing.
-## Future Enhancements
+## Contributign & Future Enhancements
+
+### Contributing
+
+If you want to help making this plugin more "mature" - you are welcome to do so!
+
+### Future Enhancements
- Integration with FLIR camera systems for visual targeting
- Support for moving targets
- Multiple simultaneous target designation
-- Advanced missile guidance modes (terminal homing, etc.)
+- Advanced missile guidance modes (maybe homing?, etc.)
- Integration with external navigation systems
This plugin demonstrates how precise mathematical modeling combined with X-Plane's robust API can create realistic military simulation capabilities within the flight simulator environment.
diff --git a/build/JTACCoords/win_x64/JTACCoords.xpl b/build/JTACCoords/win_x64/JTACCoords.xpl
index 64d1113..a7f22c3 100755
Binary files a/build/JTACCoords/win_x64/JTACCoords.xpl and b/build/JTACCoords/win_x64/JTACCoords.xpl differ
diff --git a/missile_guidance.cpp b/missile_guidance.cpp
index d23db97..07e016d 100644
--- a/missile_guidance.cpp
+++ b/missile_guidance.cpp
@@ -1,3 +1,27 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) sebastian <sebastian@eingabeausgabe.io>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
#include "XPLMDataAccess.h"
#include "XPLMUtilities.h"
#include "XPLMProcessing.h"
@@ -12,14 +36,14 @@
#define DEG_TO_RAD (M_PI / 180.0)
#define RAD_TO_DEG (180.0 / M_PI)
-// Missile guidance data refs (array datarefs)
+// X-Plane weapon datarefs are array-based to support multiple missiles
static XPLMDataRef gMissileX;
static XPLMDataRef gMissileY;
static XPLMDataRef gMissileZ;
static XPLMDataRef gMissileVX;
static XPLMDataRef gMissileVY;
static XPLMDataRef gMissileVZ;
-static XPLMDataRef gMissileQ1; // Quaternion components
+static XPLMDataRef gMissileQ1;
static XPLMDataRef gMissileQ2;
static XPLMDataRef gMissileQ3;
static XPLMDataRef gMissileQ4;
@@ -30,12 +54,12 @@ static XPLMDataRef gMissileTargetH;
static XPLMDataRef gMissileType;
static XPLMDataRef gWeaponCount;
-// Aircraft data refs
+// Aircraft position needed for relative calculations
static XPLMDataRef gPlaneLatitude;
static XPLMDataRef gPlaneLongitude;
static XPLMDataRef gPlaneElevation;
-// Target coordinates from JTAC system
+// Target coordinates storage - maintains both world and local coordinates
struct TargetCoords {
double latitude;
double longitude;
@@ -47,7 +71,7 @@ struct TargetCoords {
static TargetCoords gGuidanceTarget = {0};
static bool gGuidanceActive = false;
-// Track multiple active missiles
+// X-Plane supports up to 25 simultaneous weapons
#define MAX_MISSILES 25
struct ActiveMissile {
int index;
@@ -57,7 +81,7 @@ struct ActiveMissile {
static ActiveMissile gActiveMissiles[MAX_MISSILES];
static int gNumActiveMissiles = 0;
-// Quaternion utility functions
+// Quaternion math for missile orientation - needed because X-Plane uses quaternions internally
void NormalizeQuaternion(float* q1, float* q2, float* q3, float* q4) {
float magnitude = sqrt(*q1 * *q1 + *q2 * *q2 + *q3 * *q3 + *q4 * *q4);
if (magnitude > 0.0001f) {
@@ -70,8 +94,8 @@ void NormalizeQuaternion(float* q1, float* q2, float* q3, float* q4) {
void QuaternionFromDirectionVector(float dirX, float dirY, float dirZ,
float* q1, float* q2, float* q3, float* q4) {
- // Create quaternion that rotates missile to point in direction vector
- // This is a simplified version - assumes missile forward is along Z axis
+ // Converts direction vector to quaternion for missile orientation
+ // Assumes X-Plane missile model has forward axis along +Z
// Normalize direction vector
float length = sqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
@@ -85,19 +109,17 @@ void QuaternionFromDirectionVector(float dirX, float dirY, float dirZ,
float dot = dirZ; // dot product with (0,0,1)
if (dot > 0.9999f) {
- // Already pointing forward
*q1 = 1.0f; *q2 = 0.0f; *q3 = 0.0f; *q4 = 0.0f;
return;
}
if (dot < -0.9999f) {
- // Pointing backward - rotate 180 degrees around X axis
*q1 = 0.0f; *q2 = 1.0f; *q3 = 0.0f; *q4 = 0.0f;
return;
}
- // Cross product to get rotation axis
- float axisX = 0.0f - dirY; // (0,0,1) x (dirX,dirY,dirZ)
+ // Cross product (0,0,1) x direction gives rotation axis
+ float axisX = 0.0f - dirY;
float axisY = dirX - 0.0f;
float axisZ = 0.0f - 0.0f;
@@ -120,11 +142,11 @@ void QuaternionFromDirectionVector(float dirX, float dirY, float dirZ,
NormalizeQuaternion(q1, q2, q3, q4);
}
-// Flight callback
+// Forward declaration for X-Plane flight loop callback
float MissileGuidanceCallback(float inElapsedSinceLastCall, float inElapsedTimeSinceLastFlightLoop,
int inCounter, void* inRefcon);
-// Initialize missile guidance system
+// Finds X-Plane weapon datarefs and registers flight loop callback
bool InitializeMissileGuidance() {
// Get weapon count
gWeaponCount = XPLMFindDataRef("sim/weapons/weapon_count");
@@ -163,7 +185,7 @@ bool InitializeMissileGuidance() {
}
-// Set target coordinates for missile guidance
+// Internal function to update guidance target
void SetMissileTarget(const TargetCoords& target) {
gGuidanceTarget = target;
gGuidanceActive = target.valid;
@@ -177,7 +199,7 @@ void SetMissileTarget(const TargetCoords& target) {
}
}
-// Check if missile is already being tracked
+// Prevents duplicate tracking of same missile index
bool IsMissileTracked(int missileIndex) {
for (int i = 0; i < gNumActiveMissiles; i++) {
if (gActiveMissiles[i].index == missileIndex) {
@@ -187,7 +209,7 @@ bool IsMissileTracked(int missileIndex) {
return false;
}
-// Add a missile to the active tracking list
+// Starts guidance for newly detected missile
void AddActiveMissile(int missileIndex) {
if (gNumActiveMissiles >= MAX_MISSILES || IsMissileTracked(missileIndex)) {
return;
@@ -204,7 +226,7 @@ void AddActiveMissile(int missileIndex) {
XPLMDebugString(buffer);
}
-// Remove inactive missiles from tracking
+// Cleans up missiles that have exploded or disappeared
void CleanupInactiveMissiles() {
if (!gWeaponCount || !gMissileType) return;
@@ -237,7 +259,7 @@ void CleanupInactiveMissiles() {
}
}
-// Find new missiles that need to be tracked
+// Detects newly fired missiles and adds them to guidance
void FindNewMissiles() {
if (!gWeaponCount || !gMissileType) return;
@@ -253,7 +275,7 @@ void FindNewMissiles() {
}
}
-// Missile physics and guidance parameters
+// Per-missile state tracking for smooth guidance
struct MissileState {
float prevPosX, prevPosY, prevPosZ;
float prevVelX, prevVelY, prevVelZ;
@@ -264,7 +286,7 @@ struct MissileState {
static MissileState gMissileStates[25] = {0};
-// Simple, smooth guidance - don't touch speed, just direction
+// Proportional navigation guidance - preserves missile speed, only adjusts direction
void CalculateSmoothGuidance(int missileIndex, float deltaTime) {
if (!gGuidanceTarget.valid || missileIndex < 0 || missileIndex >= 25) return;
@@ -316,10 +338,10 @@ void CalculateSmoothGuidance(int missileIndex, float deltaTime) {
float currentDirY = missileVY / currentSpeed;
float currentDirZ = missileVZ / currentSpeed;
- // Very gentle steering - small correction factor
- float steeringGain = 0.05f; // Very small - smooth changes only
+ // Low gain prevents oscillation and maintains stable flight
+ float steeringGain = 0.05f;
- // Calculate new direction (interpolate very slowly toward target)
+ // Smooth interpolation prevents violent course corrections
float newDirX = currentDirX + (desiredDirX - currentDirX) * steeringGain;
float newDirY = currentDirY + (desiredDirY - currentDirY) * steeringGain;
float newDirZ = currentDirZ + (desiredDirZ - currentDirZ) * steeringGain;
@@ -332,21 +354,21 @@ void CalculateSmoothGuidance(int missileIndex, float deltaTime) {
newDirZ /= newDirLength;
}
- // Apply the same speed in the new direction
+ // Preserve original missile speed to maintain X-Plane physics
float newVX = newDirX * currentSpeed;
float newVY = newDirY * currentSpeed;
float newVZ = newDirZ * currentSpeed;
- // Set new velocity (preserving speed, only changing direction)
+ // Only modify direction vector, not magnitude
XPLMSetDatavf(gMissileVX, &newVX, missileIndex, 1);
XPLMSetDatavf(gMissileVY, &newVY, missileIndex, 1);
XPLMSetDatavf(gMissileVZ, &newVZ, missileIndex, 1);
- // Keep thrust steady
+ // Maintain consistent thrust for predictable flight
float steadyThrust = 0.8f;
XPLMSetDatavf(gMissileThrustRat, &steadyThrust, missileIndex, 1);
- // Update orientation to match velocity direction (very gently)
+ // Gradually align missile body with flight direction
if (gMissileQ1 && gMissileQ2 && gMissileQ3 && gMissileQ4) {
float q1, q2, q3, q4;
QuaternionFromDirectionVector(newDirX, newDirY, newDirZ, &q1, &q2, &q3, &q4);
@@ -358,8 +380,8 @@ void CalculateSmoothGuidance(int missileIndex, float deltaTime) {
XPLMGetDatavf(gMissileQ3, ¤tQ[2], missileIndex, 1);
XPLMGetDatavf(gMissileQ4, ¤tQ[3], missileIndex, 1);
- // Very gentle orientation changes
- float orientationGain = 0.02f; // Even gentler than before
+ // Even lower gain for orientation to prevent visual jittering
+ float orientationGain = 0.02f;
float newQ1 = currentQ[0] + (q1 - currentQ[0]) * orientationGain;
float newQ2 = currentQ[1] + (q2 - currentQ[1]) * orientationGain;
float newQ3 = currentQ[2] + (q3 - currentQ[2]) * orientationGain;
@@ -373,25 +395,25 @@ void CalculateSmoothGuidance(int missileIndex, float deltaTime) {
}
}
-// Flight loop callback for missile guidance
+// Called by X-Plane every 0.05 seconds during flight
float MissileGuidanceCallback(float inElapsedSinceLastCall, float inElapsedTimeSinceLastFlightLoop,
int inCounter, void* inRefcon) {
if (!gGuidanceActive) {
- return 0.1f; // Call again in 0.1 seconds
+ return 0.1f;
}
- // Clean up missiles that are no longer active
+ // Maintain active missile list
CleanupInactiveMissiles();
- // Find any new missiles that need guidance
+ // Track newly fired missiles
FindNewMissiles();
- // Guide all active missiles
+ // Apply guidance to each tracked missile
for (int i = 0; i < gNumActiveMissiles; i++) {
int missileIndex = gActiveMissiles[i].index;
- // Set target coordinates for this missile if it has a target
+ // Update X-Plane's internal target datarefs
if (gActiveMissiles[i].hasTarget && gMissileTargetLat && gMissileTargetLon && gMissileTargetH) {
float targetLat = (float)gGuidanceTarget.latitude;
float targetLon = (float)gGuidanceTarget.longitude;
@@ -402,16 +424,16 @@ float MissileGuidanceCallback(float inElapsedSinceLastCall, float inElapsedTimeS
XPLMSetDatavf(gMissileTargetH, &targetElev, missileIndex, 1);
}
- // Apply smooth guidance to this missile
+ // Calculate and apply course corrections
if (gActiveMissiles[i].hasTarget) {
CalculateSmoothGuidance(missileIndex, inElapsedSinceLastCall);
}
}
- return 0.05f; // Call again in 0.05 seconds for responsive guidance
+ return 0.05f; // 20Hz update rate for smooth guidance
}
-// External interface for JTAC system
+// C interface for integration with main plugin
extern "C" {
bool InitMissileGuidance() {
return InitializeMissileGuidance();
@@ -448,4 +470,4 @@ extern "C" {
XPLMDebugString("Missile Guidance: Target cleared, all missiles released\n");
}
-}
\ No newline at end of file
+}
diff --git a/missile_guidance.h b/missile_guidance.h
index fbe9d9a..3a3e3a1 100644
--- a/missile_guidance.h
+++ b/missile_guidance.h
@@ -1,3 +1,27 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) sebastian <sebastian@eingabeausgabe.io>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
#ifndef MISSILE_GUIDANCE_H
#define MISSILE_GUIDANCE_H
@@ -5,21 +29,18 @@
extern "C" {
#endif
-// Initialize missile guidance system
bool InitMissileGuidance();
-
-// Shutdown missile guidance system
void ShutdownMissileGuidance();
-// Set target from JTAC system
+// JTAC coordinates include both world (lat/lon/elev) and local X-Plane coordinates
+// because guidance calculations work faster in local space
void SetJTACTarget(double latitude, double longitude, double elevation,
float localX, float localY, float localZ);
-// Clear current missile target
void ClearMissileTarget();
#ifdef __cplusplus
}
#endif
-#endif // MISSILE_GUIDANCE_H
\ No newline at end of file
+#endif // MISSILE_GUIDANCE_H
diff --git a/probe.cpp b/probe.cpp
index 27d44a3..19e4ea6 100644
--- a/probe.cpp
+++ b/probe.cpp
@@ -1,3 +1,27 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) sebastian <sebastian@eingabeausgabe.io>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdio.h>
@@ -17,7 +41,7 @@
#define M_PI 3.14159265358979323846
#endif
-// OpenGL includes
+// OpenGL needed for coordinate transformations
#if IBM
#include <GL/gl.h>
#elif APL
@@ -26,7 +50,7 @@
#include <GL/gl.h>
#endif
-// Data refs for aircraft position and orientation
+// Aircraft datarefs for position calculations
static XPLMDataRef gPlaneLatitude = NULL;
static XPLMDataRef gPlaneLongitude = NULL;
static XPLMDataRef gPlaneElevation = NULL;
@@ -34,30 +58,30 @@ static XPLMDataRef gPlaneHeading = NULL;
static XPLMDataRef gPlanePitch = NULL;
static XPLMDataRef gPlaneRoll = NULL;
-// Data refs for view/camera direction
+// View system datarefs for ray casting
static XPLMDataRef gViewHeading = NULL;
static XPLMDataRef gViewPitch = NULL;
-// Hotkey handling
+// Hotkey IDs for JTAC controls
static XPLMHotKeyID gLaserHotkey = NULL;
static XPLMHotKeyID gClearTargetHotkey = NULL;
-// Menu handling
+// Plugin menu integration
static XPLMMenuID gMenu = NULL;
static int gMenuItemToggleWindow = 0;
-// Function declarations
+// Forward declarations
void LaserDesignationHotkey(void* refcon);
void ClearTargetHotkey(void* refcon);
void JTACMenuHandler(void* inMenuRef, void* inItemRef);
-// Terrain probe handle
+// X-Plane terrain intersection probe
static XPLMProbeRef gTerrainProbe = NULL;
-// Window for display
+// JTAC display window
static XPLMWindowID gWindow = NULL;
-// Target coordinates storage
+// Stores both world and local coordinates for efficiency
struct TargetCoords {
double latitude;
double longitude;
@@ -68,20 +92,20 @@ struct TargetCoords {
static TargetCoords gLastTarget = {0};
-// Convert degrees to radians
+// Mathematical constants
#define DEG_TO_RAD (M_PI / 180.0)
#define RAD_TO_DEG (180.0 / M_PI)
-// Earth radius in meters
+// WGS84 Earth radius for distance calculations
#define EARTH_RADIUS 6378137.0
-// Plugin lifecycle
+// X-Plane plugin initialization
PLUGIN_API int XPluginStart(char* outName, char* outSig, char* outDesc) {
strcpy(outName, "JTAC Coordinate System");
strcpy(outSig, "com.example.jtac_coords");
strcpy(outDesc, "Laser designation coordinate extraction system");
- // Get data refs
+ // Locate X-Plane datarefs
gPlaneLatitude = XPLMFindDataRef("sim/flightmodel/position/latitude");
gPlaneLongitude = XPLMFindDataRef("sim/flightmodel/position/longitude");
gPlaneElevation = XPLMFindDataRef("sim/flightmodel/position/elevation");
@@ -92,21 +116,21 @@ PLUGIN_API int XPluginStart(char* outName, char* outSig, char* outDesc) {
gViewHeading = XPLMFindDataRef("sim/graphics/view/view_heading");
gViewPitch = XPLMFindDataRef("sim/graphics/view/view_pitch");
- // Create terrain probe
+ // Y-probe for terrain height queries
gTerrainProbe = XPLMCreateProbe(xplm_ProbeY);
- // Register hotkey for laser designation (Ctrl+L)
+ // Ctrl+L for crosshair targeting
gLaserHotkey = XPLMRegisterHotKey(XPLM_VK_L, xplm_DownFlag | xplm_ControlFlag, "Laser Target Designation", LaserDesignationHotkey, NULL);
- // Register hotkey for clearing target (Ctrl+C)
+ // Ctrl+C to clear guidance target
gClearTargetHotkey = XPLMRegisterHotKey(XPLM_VK_C, xplm_DownFlag | xplm_ControlFlag, "Clear Missile Target", ClearTargetHotkey, NULL);
- // Create menu
+ // Add plugin menu entry
int pluginMenuIndex = XPLMAppendMenuItem(XPLMFindPluginsMenu(), "JTAC Coordinate System", NULL, 1);
gMenu = XPLMCreateMenu("JTAC Coordinate System", XPLMFindPluginsMenu(), pluginMenuIndex, JTACMenuHandler, NULL);
gMenuItemToggleWindow = XPLMAppendMenuItem(gMenu, "Toggle Window", (void*)1, 1);
- // Initialize missile guidance system
+ // Start missile guidance subsystem
if (!InitMissileGuidance()) {
XPLMDebugString("JTAC: Warning - Missile guidance system failed to initialize\n");
}
@@ -133,7 +157,7 @@ PLUGIN_API void XPluginStop(void) {
PLUGIN_API void XPluginDisable(void) {}
PLUGIN_API int XPluginEnable(void) { return 1; }
-// Matrix multiplication helper functions
+// 4x4 matrix operations for coordinate transformations
void MultiplyMatrix4x4(const float* a, const float* b, float* result) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
@@ -145,6 +169,8 @@ void MultiplyMatrix4x4(const float* a, const float* b, float* result) {
}
}
+// Matrix inversion using analytical method - unused but kept for reference
+// Current implementation uses direct trigonometric approach instead
void InvertMatrix4x4(const float* m, float* invOut) {
float inv[16], det;
int i;
@@ -176,18 +202,18 @@ void InvertMatrix4x4(const float* m, float* invOut) {
}
}
-// Convert screen coordinates to world ray using X-Plane's view system
+// Projects 2D screen coordinates to 3D world ray using camera parameters
bool ScreenToWorldRay(int screenX, int screenY,
float* rayStartX, float* rayStartY, float* rayStartZ,
float* rayDirX, float* rayDirY, float* rayDirZ) {
- // Get camera/view position (not aircraft position)
+ // Use actual camera position for accurate ray origin
XPLMDataRef camXRef = XPLMFindDataRef("sim/graphics/view/view_x");
XPLMDataRef camYRef = XPLMFindDataRef("sim/graphics/view/view_y");
XPLMDataRef camZRef = XPLMFindDataRef("sim/graphics/view/view_z");
if (!camXRef || !camYRef || !camZRef) {
- // Fallback to aircraft position if camera position not available
+ // Fallback when camera datarefs unavailable
double aircraftLat = XPLMGetDatad(gPlaneLatitude);
double aircraftLon = XPLMGetDatad(gPlaneLongitude);
double aircraftElev = XPLMGetDatad(gPlaneElevation);
@@ -196,60 +222,59 @@ bool ScreenToWorldRay(int screenX, int screenY,
XPLMWorldToLocal(aircraftLat, aircraftLon, aircraftElev, &localX, &localY, &localZ);
*rayStartX = (float)localX;
- *rayStartY = (float)localY + 2.0f; // Add 2m height for eye level
+ *rayStartY = (float)localY + 2.0f;
*rayStartZ = (float)localZ;
} else {
- // Use actual camera position
*rayStartX = XPLMGetDataf(camXRef);
*rayStartY = XPLMGetDataf(camYRef);
*rayStartZ = XPLMGetDataf(camZRef);
}
- // Get current screen dimensions
+ // Screen size needed for normalized coordinates
XPLMDataRef screenWidthRef = XPLMFindDataRef("sim/graphics/view/window_width");
XPLMDataRef screenHeightRef = XPLMFindDataRef("sim/graphics/view/window_height");
int screenWidth = XPLMGetDatai(screenWidthRef);
int screenHeight = XPLMGetDatai(screenHeightRef);
- // Convert to normalized coordinates (-1 to 1)
+ // OpenGL normalized device coordinates
float normalX = (2.0f * screenX / (float)screenWidth) - 1.0f;
float normalY = 1.0f - (2.0f * screenY / (float)screenHeight);
- // Get view parameters
+ // Current view orientation
float heading = XPLMGetDataf(gViewHeading) * DEG_TO_RAD;
float pitch = XPLMGetDataf(gViewPitch) * DEG_TO_RAD;
- // Get actual field of view
+ // FOV determines ray spread angle
XPLMDataRef fovRef = XPLMFindDataRef("sim/graphics/view/field_of_view_deg");
float fov = fovRef ? XPLMGetDataf(fovRef) * DEG_TO_RAD : (45.0f * DEG_TO_RAD);
float aspect = (float)screenWidth / (float)screenHeight;
- // Calculate ray direction based on screen position and view angles
+ // Transform screen position to world direction vector
float tanHalfFov = tan(fov * 0.5f);
- // Ray direction in view space (camera relative)
+ // Calculate direction in camera space
float viewX = normalX * tanHalfFov * aspect;
float viewY = normalY * tanHalfFov;
- float viewZ = -1.0f; // Forward direction (negative Z in OpenGL)
+ float viewZ = -1.0f;
- // Transform ray direction by view rotation
+ // Apply view orientation to ray direction
float cosHeading = cos(heading);
float sinHeading = sin(heading);
float cosPitch = cos(pitch);
float sinPitch = sin(pitch);
- // Apply pitch rotation (around X axis)
+ // Pitch rotation first
float tempY = viewY * cosPitch - viewZ * sinPitch;
float tempZ = viewY * sinPitch + viewZ * cosPitch;
viewY = tempY;
viewZ = tempZ;
- // Apply heading rotation (around Y axis)
+ // Then heading rotation
*rayDirX = viewX * cosHeading - viewZ * sinHeading;
*rayDirY = viewY;
*rayDirZ = viewX * sinHeading + viewZ * cosHeading;
- // Normalize direction vector
+ // Ensure unit vector for consistent scaling
float length = sqrt(*rayDirX * *rayDirX + *rayDirY * *rayDirY + *rayDirZ * *rayDirZ);
if (length > 0.0f) {
*rayDirX /= length;
@@ -261,14 +286,9 @@ bool ScreenToWorldRay(int screenX, int screenY,
return false;
}
-// Perform laser designation at screen coordinates
+// Main targeting function - converts screen position to terrain coordinates
bool DesignateLaser(int screenX, int screenY, TargetCoords* target) {
- // Get ray start and direction
- // double aircraftLat = XPLMGetDatad(gPlaneLatitude);
- // double aircraftLon = XPLMGetDatad(gPlaneLongitude);
- // double aircraftElev = XPLMGetDatad(gPlaneElevation);
-
- // Get ray start and direction using OpenGL matrices
+ // Convert screen coordinates to 3D ray
float rayStartX, rayStartY, rayStartZ;
float rayDirX, rayDirY, rayDirZ;
@@ -278,49 +298,48 @@ bool DesignateLaser(int screenX, int screenY, TargetCoords* target) {
return false;
}
- // Perform terrain intersection using divide-and-conquer ray casting
- // This is the proper way to do ray casting in X-Plane as discussed in forums
+ // Binary search for precise terrain intersection point
+ // More efficient than linear stepping and avoids missing terrain
- float minDistance = 1.0f; // 1 meter minimum
- float maxDistance = 30000.0f; // 30km maximum range
+ float minDistance = 1.0f;
+ float maxDistance = 30000.0f;
float currentDistance = maxDistance;
XPLMProbeInfo_t probeInfo;
probeInfo.structSize = sizeof(XPLMProbeInfo_t);
- // Binary search for terrain intersection
+ // Iterative refinement to 1-meter precision
int iterations = 0;
- const int maxIterations = 50; // Should be enough for 1-meter precision
+ const int maxIterations = 50;
while ((maxDistance - minDistance) > 1.0f && iterations < maxIterations) {
currentDistance = (minDistance + maxDistance) * 0.5f;
- // Calculate test point along ray
+ // Test point at current distance
float testX = rayStartX + rayDirX * currentDistance;
float testY = rayStartY + rayDirY * currentDistance;
float testZ = rayStartZ + rayDirZ * currentDistance;
- // Probe terrain at this point
+ // Query terrain height at test point
XPLMProbeResult result = XPLMProbeTerrainXYZ(gTerrainProbe, testX, testY, testZ, &probeInfo);
if (result == xplm_ProbeHitTerrain) {
- // We're above terrain, check if we're below ground level
if (testY < probeInfo.locationY) {
- // We're underground - intersection is closer
+ // Ray below terrain - intersection is closer
maxDistance = currentDistance;
} else {
- // We're above ground - intersection is further
+ // Ray above terrain - intersection is further
minDistance = currentDistance;
}
} else {
- // No terrain data - probably too far out
+ // No terrain data available at this location
maxDistance = currentDistance;
}
iterations++;
}
- // Final probe at the refined distance
+ // Final intersection calculation
currentDistance = (minDistance + maxDistance) * 0.5f;
float finalX = rayStartX + rayDirX * currentDistance;
float finalY = rayStartY + rayDirY * currentDistance;
@@ -329,12 +348,11 @@ bool DesignateLaser(int screenX, int screenY, TargetCoords* target) {
XPLMProbeResult finalResult = XPLMProbeTerrainXYZ(gTerrainProbe, finalX, finalY, finalZ, &probeInfo);
if (finalResult == xplm_ProbeHitTerrain) {
- // Store the terrain intersection point
target->localX = probeInfo.locationX;
target->localY = probeInfo.locationY;
target->localZ = probeInfo.locationZ;
- // Convert back to world coordinates
+ // Transform to lat/lon/elevation
XPLMLocalToWorld(probeInfo.locationX, probeInfo.locationY, probeInfo.locationZ,
&target->latitude, &target->longitude, &target->elevation);
@@ -346,13 +364,13 @@ bool DesignateLaser(int screenX, int screenY, TargetCoords* target) {
return false;
}
-// Calculate bearing and distance to target
+// Navigation calculations for target relative to aircraft
void CalculateTargetInfo(const TargetCoords* target, float* bearing, float* distance, float* elevation) {
double aircraftLat = XPLMGetDatad(gPlaneLatitude);
double aircraftLon = XPLMGetDatad(gPlaneLongitude);
double aircraftElev = XPLMGetDatad(gPlaneElevation);
- // Calculate bearing using great circle formula
+ // True bearing using spherical trigonometry
double dLon = (target->longitude - aircraftLon) * DEG_TO_RAD;
double lat1 = aircraftLat * DEG_TO_RAD;
double lat2 = target->latitude * DEG_TO_RAD;
@@ -363,19 +381,19 @@ void CalculateTargetInfo(const TargetCoords* target, float* bearing, float* dist
*bearing = atan2(y, x) * RAD_TO_DEG;
if (*bearing < 0) *bearing += 360.0f;
- // Calculate distance using haversine formula
+ // Great circle distance on Earth's surface
double dLat = (target->latitude - aircraftLat) * DEG_TO_RAD;
double a = sin(dLat/2) * sin(dLat/2) + cos(lat1) * cos(lat2) * sin(dLon/2) * sin(dLon/2);
double c = 2 * atan2(sqrt(a), sqrt(1-a));
*distance = (float)(EARTH_RADIUS * c);
- // Calculate elevation difference
+ // Height difference for angle calculations
*elevation = (float)(target->elevation - aircraftElev);
}
-// Format coordinates for military grid reference (simplified MGRS)
+// Convert decimal degrees to military coordinate format
void FormatMGRS(double latitude, double longitude, char* output, size_t outputSize) {
- // This is a simplified MGRS format - full implementation would require UTM conversion
+ // Simplified format - full MGRS requires UTM grid calculations
int latDeg = (int)latitude;
int latMin = (int)((latitude - latDeg) * 60);
float latSec = (float)(((latitude - latDeg) * 60 - latMin) * 60);
@@ -388,9 +406,9 @@ void FormatMGRS(double latitude, double longitude, char* output, size_t outputSi
abs(latDeg), latMin, latSec, abs(lonDeg), lonMin, lonSec);
}
-// Hotkey callback for laser designation (Ctrl+L)
+// Ctrl+L handler - designates target at screen center
void LaserDesignationHotkey(void* refcon) {
- // Use screen center as target point (crosshair designation)
+ // Target at crosshair (screen center)
XPLMDataRef screenWidthRef = XPLMFindDataRef("sim/graphics/view/window_width");
XPLMDataRef screenHeightRef = XPLMFindDataRef("sim/graphics/view/window_height");
@@ -398,15 +416,15 @@ void LaserDesignationHotkey(void* refcon) {
int screenWidth = XPLMGetDatai(screenWidthRef);
int screenHeight = XPLMGetDatai(screenHeightRef);
- // Use screen center (crosshair/center of view)
+ // Calculate screen center coordinates
int centerX = screenWidth / 2;
int centerY = screenHeight / 2;
- // Perform laser designation at screen center
+ // Execute targeting at crosshair
if (DesignateLaser(centerX, centerY, &gLastTarget)) {
XPLMDebugString("JTAC: Target designated successfully at crosshair\n");
- // Send target to missile guidance system
+ // Activate missile guidance
SetJTACTarget(gLastTarget.latitude, gLastTarget.longitude, gLastTarget.elevation,
gLastTarget.localX, gLastTarget.localY, gLastTarget.localZ);
} else {
@@ -417,15 +435,15 @@ void LaserDesignationHotkey(void* refcon) {
}
}
-// Clear target hotkey callback (Ctrl+C)
+// Ctrl+C handler - clears all guidance targets
void ClearTargetHotkey(void* refcon) {
ClearMissileTarget();
XPLMDebugString("JTAC: Missile target cleared\n");
}
-// Menu handler callback
+// Plugin menu item handler
void JTACMenuHandler(void* inMenuRef, void* inItemRef) {
- if ((intptr_t)inItemRef == 1) { // Toggle Window menu item
+ if ((intptr_t)inItemRef == 1) {
if (gWindow) {
int isVisible = XPLMGetWindowIsVisible(gWindow);
XPLMSetWindowIsVisible(gWindow, !isVisible);
@@ -434,18 +452,18 @@ void JTACMenuHandler(void* inMenuRef, void* inItemRef) {
}
}
-// Display window draw function
+// Renders JTAC coordinate display window
void DrawWindow(XPLMWindowID inWindowID, void* inRefcon) {
int left, top, right, bottom;
XPLMGetWindowGeometry(inWindowID, &left, &top, &right, &bottom);
- // Draw background
+ // Semi-transparent background
XPLMDrawTranslucentDarkBox(left, top, right, bottom);
char buffer[512];
int line = 0;
- // Display aircraft position
+ // Show current aircraft location
double aircraftLat = XPLMGetDatad(gPlaneLatitude);
double aircraftLon = XPLMGetDatad(gPlaneLongitude);
double aircraftElev = XPLMGetDatad(gPlaneElevation);
@@ -457,29 +475,29 @@ void DrawWindow(XPLMWindowID inWindowID, void* inRefcon) {
snprintf(buffer, sizeof(buffer), "Aircraft: %.6f, %.6f, %.0fm", aircraftLat, aircraftLon, aircraftElev);
XPLMDrawString(white, left + 10, top - 20 - (line++ * 15), buffer, NULL, xplmFont_Proportional);
- // Display target information if valid
+ // Show targeting data when available
if (gLastTarget.valid) {
- line++; // Skip a line
+ line++;
snprintf(buffer, sizeof(buffer), "TARGET COORDINATES:");
float yellow[] = {1.0f, 1.0f, 0.0f};
XPLMDrawString(yellow, left + 10, top - 20 - (line++ * 15), buffer, NULL, xplmFont_Proportional);
- // MGRS format
+ // Military grid format
char mgrs[128];
FormatMGRS(gLastTarget.latitude, gLastTarget.longitude, mgrs, sizeof(mgrs));
snprintf(buffer, sizeof(buffer), "MGRS: %s", mgrs);
XPLMDrawString(white, left + 10, top - 20 - (line++ * 15), buffer, NULL, xplmFont_Proportional);
- // Decimal degrees
+ // High precision coordinates
snprintf(buffer, sizeof(buffer), "LAT/LON: %.6f, %.6f", gLastTarget.latitude, gLastTarget.longitude);
XPLMDrawString(white, left + 10, top - 20 - (line++ * 15), buffer, NULL, xplmFont_Proportional);
- // Elevation
+ // Target altitude
snprintf(buffer, sizeof(buffer), "ELEVATION: %.0fm MSL", gLastTarget.elevation);
XPLMDrawString(white, left + 10, top - 20 - (line++ * 15), buffer, NULL, xplmFont_Proportional);
- // Bearing, distance, elevation difference
+ // Navigation information
float bearing, distance, elevDiff;
CalculateTargetInfo(&gLastTarget, &bearing, &distance, &elevDiff);
@@ -493,7 +511,7 @@ void DrawWindow(XPLMWindowID inWindowID, void* inRefcon) {
XPLMDrawString(white, left + 10, top - 20 - (line++ * 15), buffer, NULL, xplmFont_Proportional);
}
- // Instructions
+ // Usage instructions
line++;
snprintf(buffer, sizeof(buffer), "Press Ctrl+L to designate target at crosshair");
float gray[] = {0.78f, 0.78f, 0.78f};
@@ -506,7 +524,7 @@ void DrawWindow(XPLMWindowID inWindowID, void* inRefcon) {
XPLMDrawString(gray, left + 10, top - 20 - (line++ * 15), buffer, NULL, xplmFont_Proportional);
}
-// Create window
+// Initialize JTAC display window
void CreateJTACWindow() {
XPLMCreateWindow_t params;
params.structSize = sizeof(params);
@@ -534,4 +552,4 @@ PLUGIN_API void XPluginReceiveMessage(XPLMPluginID inFrom, int inMsg, void* inPa
CreateJTACWindow();
}
}
-}
\ No newline at end of file
+}