Refactored drawing code into Drawing class.

This commit is contained in:
Jason Watkins
2015-04-10 13:13:52 -07:00
committed by jason-watkins
parent a3a82f4a71
commit aa74bc57f5
9 changed files with 355 additions and 305 deletions

277
xpcPlugin/Drawing.cpp Normal file
View File

@@ -0,0 +1,277 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#include "Drawing.h"
#include "XPLMDisplay.h"
#include "XPLMGraphics.h"
#include "XPLMDataAccess.h"
#include <math.h>
#include <string.h>
#include <stdio.h>
//OpenGL includes
#if IBM
#include <windows.h>
#endif
#if __GNUC__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
namespace XPC
{
//Internal Structures
typedef struct
{
double x;
double y;
double z;
} LocalPoint;
//Internal Memory
static const size_t MSG_MAX = 1024;
static const size_t MSG_LINE_MAX = MSG_MAX / 16;
static bool msgEnabled = false;
static int msgX = -1;
static int msgY = -1;
static char msgVal[MSG_MAX] = { 0 };
static size_t newLineCount = 0;
static size_t newLines[MSG_LINE_MAX] = { 0 };
static float rgb[3] = { 0.25F, 1.0F, 0.25F };
static const size_t WAYPOINT_MAX = 128;
static bool routeEnabled = false;
static size_t numWaypoints = 0;
static Waypoint waypoints[WAYPOINT_MAX];
static LocalPoint localPoints[WAYPOINT_MAX];
XPLMDataRef planeXref;
XPLMDataRef planeYref;
XPLMDataRef planeZref;
//Internal Functions
static int cmp(const void * a, const void * b)
{
return (*(size_t*)a - *(size_t*)b);
}
static void gl_drawCube(float x, float y, float z, float d)
{
//tan(0.25) degrees. Should scale all markers to appear about the same size
const float TAN = 0.00436335082070156648652885284203;
float h = d * TAN;
glBegin(GL_QUAD_STRIP);
//Top
glVertex3f(x - h, y + h, z - h);
glVertex3f(x + h, y + h, z - h);
glVertex3f(x - h, y + h, z + h);
glVertex3f(x + h, y + h, z + h);
//Front
glVertex3f(x - h, y - h, z + h);
glVertex3f(x + h, y - h, z + h);
//Bottom
glVertex3f(x - h, y - h, z - h);
glVertex3f(x + h, y - h, z - h);
//Back
glVertex3f(x - h, y + h, z - h);
glVertex3f(x + h, y + h, z - h);
glEnd();
glBegin(GL_QUADS);
//Left
glVertex3f(x - h, y + h, z - h);
glVertex3f(x - h, y + h, z + h);
glVertex3f(x - h, y - h, z + h);
glVertex3f(x - h, y - h, z - h);
//Right
glVertex3f(x + h, y + h, z + h);
glVertex3f(x + h, y + h, z - h);
glVertex3f(x + h, y - h, z - h);
glVertex3f(x + h, y - h, z + h);
glEnd();
}
static int MessageDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon)
{
XPLMDrawString(rgb, msgX, msgY, msgVal, NULL, xplmFont_Basic);
int y = msgY - 16;
for (size_t i = 0; i < newLineCount; ++i)
{
XPLMDrawString(rgb, msgX, y, msgVal + newLines[i], NULL, xplmFont_Basic);
y -= 16;
}
return 1;
}
static int RouteDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon)
{
float px = XPLMGetDataf(planeXref);
float py = XPLMGetDataf(planeYref);
float pz = XPLMGetDataf(planeZref);
Waypoint* g;
LocalPoint* l;
//Convert to local
for (size_t i = 0; i < numWaypoints; ++i)
{
g = &waypoints[i];
l = &localPoints[i];
XPLMWorldToLocal(g->latitude, g->longitude, g->altitude,
&l->x, &l->y, &l->z);
}
//Draw posts
glColor3f(1.0F, 1.0F, 1.0F);
glBegin(GL_LINES);
for (size_t i = 0; i < numWaypoints; ++i)
{
l = &localPoints[i];
glVertex3f((float)l->x, (float)l->y, (float)l->z);
glVertex3f((float)l->x, -1000.0F, (float)l->z);
}
glEnd();
//Draw route
glColor3f(1.0F, 0.0F, 0.0F);
glBegin(GL_LINE_STRIP);
for (size_t i = 0; i < numWaypoints; ++i)
{
l = &localPoints[i];
glVertex3f((float)l->x, (float)l->y, (float)l->z);
}
glEnd();
//Draw markers
glColor3f(1.0F, 1.0F, 1.0F);
for (size_t i = 0; i < numWaypoints; ++i)
{
l = &localPoints[i];
float xoff = (float)l->x - px;
float yoff = (float)l->y - py;
float zoff = (float)l->z - pz;
float d = sqrtf(xoff*xoff + yoff*yoff + zoff*zoff);
gl_drawCube((float)l->x, (float)l->y, (float)l->z, d);
}
return 1;
}
//Public Functions
void Drawing::ClearMessage()
{
XPLMUnregisterDrawCallback(MessageDrawCallback, xplm_Phase_Window, 0, NULL);
msgEnabled = false;
}
void Drawing::SetMessage(int x, int y, char* msg)
{
//Determine size of message and clear instead if the message string
//is empty.
size_t len = strnlen(msg, MSG_MAX);
if (len == 0)
{
ClearMessage();
return;
}
//Set the message, location, and mark new lines.
strncpy(msgVal, msg, len);
newLineCount = 0;
for (size_t i = 0; i < len && newLineCount < MSG_LINE_MAX; ++i)
{
if (msgVal[i] == '\n' || msgVal[i] == '\r')
{
msgVal[i] = 0;
newLines[newLineCount++] = i + 1;
}
}
msgX = x < 0 ? 10 : x;
msgY = y < 0 ? 600 : y;
//Enable drawing if necessary
if (!msgEnabled)
{
XPLMRegisterDrawCallback(MessageDrawCallback, xplm_Phase_LastCockpit, 0, NULL);
msgEnabled = true;
}
}
void Drawing::ClearWaypoints()
{
numWaypoints = 0;
if (routeEnabled)
{
XPLMUnregisterDrawCallback(RouteDrawCallback, xplm_Phase_Objects, 0, NULL);
}
return;
}
void Drawing::AddWaypoints(Waypoint points[], size_t numPoints)
{
if (numWaypoints + numPoints > WAYPOINT_MAX)
{
numPoints = WAYPOINT_MAX - numWaypoints;
}
size_t finalNumWaypoints = numPoints + numWaypoints;
for (size_t i = 0; i < numPoints; ++i)
{
waypoints[numWaypoints + i] = points[i];
}
numWaypoints = finalNumWaypoints;
if (!routeEnabled)
{
XPLMRegisterDrawCallback(RouteDrawCallback, xplm_Phase_Objects, 0, NULL);
}
if (!planeXref)
{
planeXref = XPLMFindDataRef("sim/flightmodel/position/local_x");
planeYref = XPLMFindDataRef("sim/flightmodel/position/local_y");
planeZref = XPLMFindDataRef("sim/flightmodel/position/local_z");
}
}
void Drawing::RemoveWaypoints(Waypoint points[], size_t numPoints)
{
//Build a list of indices of waypoints we should delete.
size_t delPoints[WAYPOINT_MAX];
size_t delPointsCur = 0;
for (size_t i = 0; i < numPoints; ++i)
{
Waypoint p = points[i];
for (size_t j = 0; j < numWaypoints; ++j)
{
Waypoint q = waypoints[j];
if (p.latitude == q.latitude &&
p.longitude == q.longitude &&
p.altitude == q.altitude)
{
delPoints[delPointsCur++] = j;
break;
}
}
}
//Sort the indices so that we only have to iterate them once
qsort(delPoints, delPointsCur, sizeof(size_t), cmp);
//Copy the new array on top of the old array
size_t copyCur = 0;
size_t count = delPointsCur;
delPointsCur = 0;
for (size_t i = 0; i < numWaypoints; ++i)
{
if (i == delPoints[delPointsCur])
{
++delPointsCur;
continue;
}
waypoints[copyCur++] = waypoints[i];
}
numWaypoints -= count;
if (numWaypoints == 0)
{
ClearWaypoints();
}
}
}

55
xpcPlugin/Drawing.h Normal file
View File

@@ -0,0 +1,55 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPC_DRAWING_H
#define XPC_DRAWING_H
#include <stdlib.h>
#include "xplaneConnect.h"
namespace XPC
{
/// Handles tasks that involve drawing to the screen in X-Plane.
///
/// \author Jason Watkins
/// \version 1.0
/// \since 1.0
/// \date Intial Version: 2015-04-10
/// \date Last Updated: 2015-04-10
class Drawing
{
public:
/// Clears the current message on the screen if any and unregisters the
/// draw callback for message drawing.
static void ClearMessage();
/// Sets the message to be drawn on the screen.
///
/// \param x The x coordinate of the message relative to the left edge
/// of the screen.
/// \param y The y coordinate of the message relative to the bottom
/// edge of the screen
/// \param msg A C string containing the message to display. The message
/// value is copied into a local buffer.
static void SetMessage(int x, int y, char* msg);
/// Adds the given waypoints to the list of waypoints to draw.
///
/// \param points A pointer to an array of waypoints.
/// \param numPoints The number of points in the array.
static void AddWaypoints(Waypoint points[], size_t numPoints);
/// Removes all waypoints and unregisters the callback for waypoint
/// drawing.
static void ClearWaypoints();
/// Removes the given waypoints from the list of waypoints to draw.
/// If all waypoints are removed as a result of this action, unregisters
/// the callback for waypoint drawing.
///
/// \param points A pointer to an array of waypoints.
/// \param numPoints The number of points in the array.
static void RemoveWaypoints(Waypoint points[], size_t numPoints);
};
}
#endif

View File

@@ -60,18 +60,21 @@
#define _WINSOCKAPI_ #define _WINSOCKAPI_
// XPC Includes
#include "Log.h" #include "Log.h"
#include "Drawing.h"
#include "xpcPluginTools.h"
// XPLM Includes
//#include "XPLMPlanes.h"
#include "XPLMProcessing.h"
#include "XPLMGraphics.h"
// System Includes
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
//#include "XPLMPlanes.h"
#include "XPLMProcessing.h"
#include "XPLMGraphics.h"
#include "xpcDrawing.h"
#include "xpcPluginTools.h"
#ifdef _WIN32 /* WIN32 SYSTEM */ #ifdef _WIN32 /* WIN32 SYSTEM */
#include <windows.h> #include <windows.h>
#elif (__APPLE__) #elif (__APPLE__)
@@ -191,10 +194,10 @@ PLUGIN_API void XPluginDisable(void)
closeUDP(sendSocket); closeUDP(sendSocket);
// Stop rendering messages to screen. // Stop rendering messages to screen.
XPCClearMessage(); XPC::Drawing::ClearMessage();
// Stop rendering waypoints to screen. // Stop rendering waypoints to screen.
XPCClearWaypoints(); XPC::Drawing::ClearWaypoints();
XPC::Log::WriteLine("[EXEC] xpcPlugin Disabled, sockets closed"); XPC::Log::WriteLine("[EXEC] xpcPlugin Disabled, sockets closed");
} }
@@ -498,7 +501,7 @@ int handleTEXT(char *buf, int len)
size_t msgLen = (unsigned char)buf[13]; size_t msgLen = (unsigned char)buf[13];
if (msgLen == 0) if (msgLen == 0)
{ {
XPCClearMessage(); XPC::Drawing::ClearMessage();
XPC::Log::WriteLine("[TEXT] Text cleared"); XPC::Log::WriteLine("[TEXT] Text cleared");
} }
else else
@@ -506,7 +509,7 @@ int handleTEXT(char *buf, int len)
int x = *((int*)(buf + 5)); int x = *((int*)(buf + 5));
int y = *((int*)(buf + 9)); int y = *((int*)(buf + 9));
strncpy(msg, buf + 14, msgLen); strncpy(msg, buf + 14, msgLen);
XPCSetMessage(x, y, msg); XPC::Drawing::SetMessage(x, y, msg);
XPC::Log::WriteLine("[TEXT] Text set"); XPC::Log::WriteLine("[TEXT] Text set");
} }
return 0; return 0;
@@ -837,13 +840,13 @@ int handleWYPT(char buf[], int len)
switch (wypt.op) switch (wypt.op)
{ {
case xpc_WYPT_ADD: case xpc_WYPT_ADD:
XPCAddWaypoints(wypt.points, wypt.numPoints); XPC::Drawing::AddWaypoints(wypt.points, wypt.numPoints);
break; break;
case xpc_WYPT_DEL: case xpc_WYPT_DEL:
XPCRemoveWaypoints(wypt.points, wypt.numPoints); XPC::Drawing::RemoveWaypoints(wypt.points, wypt.numPoints);
break; break;
case xpc_WYPT_CLR: case xpc_WYPT_CLR:
XPCClearWaypoints(); XPC::Drawing::ClearWaypoints();
break; break;
default: //If parseWYPT is doing its job, we shouldn't ever hit this. default: //If parseWYPT is doing its job, we shouldn't ever hit this.
return -2; return -2;

Binary file not shown.

Binary file not shown.

View File

@@ -1,268 +0,0 @@
#include "xpcDrawing.h"
#include "XPLMDisplay.h"
#include "XPLMGraphics.h"
#include "XPLMDataAccess.h"
#include <string.h>
#include <stdio.h>
#include <math.h>
//OpenGL includes
#if IBM
#include <windows.h>
#endif
#if __GNUC__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
//Internal Structures
typedef struct
{
double x;
double y;
double z;
} LocalPoint;
//Internal Memory
static const size_t MSG_MAX = 1024;
static const size_t MSG_LINE_MAX = MSG_MAX / 16;
static bool msgEnabled = false;
static int msgX = -1;
static int msgY = -1;
static char msgVal[MSG_MAX] = { 0 };
static size_t newLineCount = 0;
static size_t newLines[MSG_LINE_MAX] = { 0 };
static float rgb[3] = { 0.25F, 1.0F, 0.25F };
static const size_t WAYPOINT_MAX = 128;
static bool routeEnabled = false;
static size_t numWaypoints = 0;
static Waypoint waypoints[WAYPOINT_MAX];
static LocalPoint localPoints[WAYPOINT_MAX];
XPLMDataRef planeXref;
XPLMDataRef planeYref;
XPLMDataRef planeZref;
//Internal Functions
int cmp(const void * a, const void * b)
{
return (*(size_t*)a - *(size_t*)b);
}
static void gl_drawCube(float x, float y, float z, float d)
{
//tan(0.25) degrees. Should scale all markers to appear about the same size
const float TAN = 0.00436335082070156648652885284203;
float h = d * TAN;
glBegin(GL_QUAD_STRIP);
//Top
glVertex3f(x - h, y + h, z - h);
glVertex3f(x + h, y + h, z - h);
glVertex3f(x - h, y + h, z + h);
glVertex3f(x + h, y + h, z + h);
//Front
glVertex3f(x - h, y - h, z + h);
glVertex3f(x + h, y - h, z + h);
//Bottom
glVertex3f(x - h, y - h, z - h);
glVertex3f(x + h, y - h, z - h);
//Back
glVertex3f(x - h, y + h, z - h);
glVertex3f(x + h, y + h, z - h);
glEnd();
glBegin(GL_QUADS);
//Left
glVertex3f(x - h, y + h, z - h);
glVertex3f(x - h, y + h, z + h);
glVertex3f(x - h, y - h, z + h);
glVertex3f(x - h, y - h, z - h);
//Right
glVertex3f(x + h, y + h, z + h);
glVertex3f(x + h, y + h, z - h);
glVertex3f(x + h, y - h, z - h);
glVertex3f(x + h, y - h, z + h);
glEnd();
}
static int MessageDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon)
{
XPLMDrawString(rgb, msgX, msgY, msgVal, NULL, xplmFont_Basic);
int y = msgY - 16;
for (size_t i = 0; i < newLineCount; ++i)
{
XPLMDrawString(rgb, msgX, y, msgVal + newLines[i], NULL, xplmFont_Basic);
y -= 16;
}
return 1;
}
static int RouteDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon)
{
float px = XPLMGetDataf(planeXref);
float py = XPLMGetDataf(planeYref);
float pz = XPLMGetDataf(planeZref);
Waypoint* g;
LocalPoint* l;
//Convert to local
for (size_t i = 0; i < numWaypoints; ++i)
{
g = &waypoints[i];
l = &localPoints[i];
XPLMWorldToLocal(g->latitude, g->longitude, g->altitude,
&l->x, &l->y, &l->z);
}
//Draw posts
glColor3f(1.0F, 1.0F, 1.0F);
glBegin(GL_LINES);
for (size_t i = 0; i < numWaypoints; ++i)
{
l = &localPoints[i];
glVertex3f((float)l->x, (float)l->y, (float)l->z);
glVertex3f((float)l->x, -1000.0F, (float)l->z);
}
glEnd();
//Draw route
glColor3f(1.0F, 0.0F, 0.0F);
glBegin(GL_LINE_STRIP);
for (size_t i = 0; i < numWaypoints; ++i)
{
l = &localPoints[i];
glVertex3f((float)l->x, (float)l->y, (float)l->z);
}
glEnd();
//Draw markers
glColor3f(1.0F, 1.0F, 1.0F);
for (size_t i = 0; i < numWaypoints; ++i)
{
l = &localPoints[i];
float xoff = (float)l->x - px;
float yoff = (float)l->y - py;
float zoff = (float)l->z - pz;
float d = sqrtf(xoff*xoff + yoff*yoff + zoff*zoff);
gl_drawCube((float)l->x, (float)l->y, (float)l->z, d);
}
return 1;
}
//Public Functions
void XPCClearMessage()
{
XPLMUnregisterDrawCallback(MessageDrawCallback, xplm_Phase_Window, 0, NULL);
msgEnabled = false;
}
void XPCSetMessage(int x, int y, char* msg)
{
//Determine size of message and clear instead if the message string
//is empty.
size_t len = strnlen(msg, MSG_MAX);
if (len == 0)
{
XPCClearMessage();
return;
}
//Set the message, location, and mark new lines.
strncpy(msgVal, msg, len);
newLineCount = 0;
for (size_t i = 0; i < len && newLineCount < MSG_LINE_MAX; ++i)
{
if (msgVal[i] == '\n' || msgVal[i] == '\r')
{
msgVal[i] = 0;
newLines[newLineCount++] = i + 1;
}
}
msgX = x < 0 ? 10 : x;
msgY = y < 0 ? 600 : y;
//Enable drawing if necessary
if (!msgEnabled)
{
XPLMRegisterDrawCallback(MessageDrawCallback, xplm_Phase_LastCockpit, 0, NULL);
msgEnabled = true;
}
}
void XPCClearWaypoints()
{
numWaypoints = 0;
if (routeEnabled)
{
XPLMUnregisterDrawCallback(RouteDrawCallback, xplm_Phase_Objects, 0, NULL);
}
return;
}
void XPCAddWaypoints(Waypoint points[], size_t numPoints)
{
if (numWaypoints + numPoints > WAYPOINT_MAX)
{
numPoints = WAYPOINT_MAX - numWaypoints;
}
size_t finalNumWaypoints = numPoints + numWaypoints;
for (size_t i = 0; i < numPoints; ++i)
{
waypoints[numWaypoints + i] = points[i];
}
numWaypoints = finalNumWaypoints;
if (!routeEnabled)
{
XPLMRegisterDrawCallback(RouteDrawCallback, xplm_Phase_Objects, 0, NULL);
}
if (!planeXref)
{
planeXref = XPLMFindDataRef("sim/flightmodel/position/local_x");
planeYref = XPLMFindDataRef("sim/flightmodel/position/local_y");
planeZref = XPLMFindDataRef("sim/flightmodel/position/local_z");
}
}
void XPCRemoveWaypoints(Waypoint points[], size_t numPoints)
{
//Build a list of indices of waypoints we should delete.
size_t delPoints[WAYPOINT_MAX];
size_t delPointsCur = 0;
for (size_t i = 0; i < numPoints; ++i)
{
Waypoint p = points[i];
for (size_t j = 0; j < numWaypoints; ++j)
{
Waypoint q = waypoints[j];
if (p.latitude == q.latitude &&
p.longitude == q.longitude &&
p.altitude == q.altitude)
{
delPoints[delPointsCur++] = j;
break;
}
}
}
//Sort the indices so that we only have to iterate them once
qsort(delPoints, delPointsCur, sizeof(size_t), cmp);
//Copy the new array on top of the old array
size_t copyCur = 0;
size_t count = delPointsCur;
delPointsCur = 0;
for (size_t i = 0; i < numWaypoints; ++i)
{
if (i == delPoints[delPointsCur])
{
++delPointsCur;
continue;
}
waypoints[copyCur++] = waypoints[i];
}
numWaypoints -= count;
}

View File

@@ -1,17 +0,0 @@
#ifndef xpcDrawing_h
#define xpcDrawing_h
#include <stdlib.h>
#include "xplaneConnect.h"
void XPCClearMessage();
void XPCSetMessage(int x, int y, char* msg);
void XPCClearWaypoints();
void XPCAddWaypoints(Waypoint points[], size_t numPoints);
void XPCRemoveWaypoints(Waypoint points[], size_t numPoints);
#endif

View File

@@ -99,14 +99,14 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\C\src\xplaneConnect.h" /> <ClInclude Include="..\..\C\src\xplaneConnect.h" />
<ClInclude Include="..\Drawing.h" />
<ClInclude Include="..\Log.h" /> <ClInclude Include="..\Log.h" />
<ClInclude Include="..\xpcDrawing.h" />
<ClInclude Include="..\xpcPluginTools.h" /> <ClInclude Include="..\xpcPluginTools.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\C\src\xplaneConnect.c" /> <ClCompile Include="..\..\C\src\xplaneConnect.c" />
<ClCompile Include="..\Drawing.cpp" />
<ClCompile Include="..\Log.cpp" /> <ClCompile Include="..\Log.cpp" />
<ClCompile Include="..\xpcDrawing.cpp" />
<ClCompile Include="..\XPCPlugin.cpp" /> <ClCompile Include="..\XPCPlugin.cpp" />
<ClCompile Include="..\xpcPluginTools.cpp" /> <ClCompile Include="..\xpcPluginTools.cpp" />
</ItemGroup> </ItemGroup>

View File

@@ -21,10 +21,10 @@
<ClInclude Include="..\xpcPluginTools.h"> <ClInclude Include="..\xpcPluginTools.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\xpcDrawing.h"> <ClInclude Include="..\Log.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\Log.h"> <ClInclude Include="..\Drawing.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
@@ -38,10 +38,10 @@
<ClCompile Include="..\xpcPluginTools.cpp"> <ClCompile Include="..\xpcPluginTools.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\xpcDrawing.cpp"> <ClCompile Include="..\Log.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\Log.cpp"> <ClCompile Include="..\Drawing.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
</ItemGroup> </ItemGroup>