mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-23 07:15:35 +08:00
851 lines
27 KiB
C++
851 lines
27 KiB
C++
/*
|
|
* Copyright (c) 2005, Ben Supnik and Chris Serio.
|
|
*
|
|
* 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 "XPMPMultiplayerObj.h"
|
|
#include "XPMPMultiplayerVars.h"
|
|
|
|
//#include "PlatformUtils.h"
|
|
#include "XObjReadWrite.h"
|
|
#include "TexUtils.h"
|
|
#include "XOGLUtils.h"
|
|
|
|
#include <map>
|
|
#include <vector>
|
|
#include <math.h>
|
|
|
|
#include "XPLMGraphics.h"
|
|
#include "XPLMUtilities.h"
|
|
#include "XPLMDataAccess.h"
|
|
#include "XPLMProcessing.h"
|
|
|
|
#define DEBUG_NORMALS 0
|
|
#define DISABLE_SHARING 0
|
|
#define BLEND_NORMALS 1
|
|
|
|
using namespace std;
|
|
|
|
const double kMetersToNM = 0.000539956803;
|
|
// Some color constants
|
|
//const float kNavLightRed[] = {1.0, 0.0, 0.2, 0.6};
|
|
//const float kNavLightGreen[] = {0.0, 1.0, 0.3, 0.6};
|
|
//const float kLandingLight[] = {1.0, 1.0, 0.7, 0.6};
|
|
//const float kStrobeLight[] = {1.0, 1.0, 1.0, 0.4};
|
|
const float kNavLightRed[] = {1.0, 0.0, 0.2, 0.5};
|
|
const float kNavLightGreen[] = {0.0, 1.0, 0.3, 0.5};
|
|
const float kLandingLight[] = {1.0, 1.0, 0.7, 0.6};
|
|
const float kStrobeLight[] = {1.0, 1.0, 1.0, 0.7};
|
|
|
|
static int sLightTexture = -1;
|
|
|
|
static void MakePartialPathNativeObj(string& io_str)
|
|
{
|
|
// char sep = *XPLMGetDirectorySeparator();
|
|
for(int i = 0; i < io_str.size(); ++i)
|
|
if(io_str[i] == '/' || io_str[i] == ':' || io_str[i] == '\\')
|
|
io_str[i] = '/';
|
|
}
|
|
|
|
static XPLMDataRef sFOVRef = XPLMFindDataRef("sim/graphics/view/field_of_view_deg");
|
|
static float sFOV = 60.0;
|
|
|
|
bool NormalizeVec(float vec[3])
|
|
{
|
|
float len=sqrt(vec[0]*vec[0]+vec[1]*vec[1]+vec[2]*vec[2]);
|
|
if (len>0.0)
|
|
{
|
|
len = 1.0 / len;
|
|
vec[0] *= len;
|
|
vec[1] *= len;
|
|
vec[2] *= len;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CrossVec(float a[3], float b[3], float dst[3])
|
|
{
|
|
dst[0] = a[1] * b[2] - a[2] * b[1] ;
|
|
dst[1] = a[2] * b[0] - a[0] * b[2] ;
|
|
dst[2] = a[0] * b[1] - a[1] * b[0] ;
|
|
}
|
|
|
|
|
|
/*****************************************************
|
|
Ben's Crazy Point Pool Class
|
|
******************************************************/
|
|
|
|
class OBJ_PointPool {
|
|
public:
|
|
OBJ_PointPool(){};
|
|
~OBJ_PointPool(){};
|
|
|
|
int AddPoint(float xyz[3], float st[2]);
|
|
void PreparePoolToDraw();
|
|
void CalcTriNormal(int idx1, int idx2, int idx3);
|
|
void NormalizeNormals(void);
|
|
void DebugDrawNormals();
|
|
void Purge() { mPointPool.clear(); }
|
|
int Size() { return mPointPool.size(); }
|
|
private:
|
|
vector<float> mPointPool;
|
|
};
|
|
|
|
|
|
|
|
// Adds a point to our pool and returns it's index.
|
|
// If one already exists in the same location, we
|
|
// just return that index
|
|
int OBJ_PointPool::AddPoint(float xyz[3], float st[2])
|
|
{
|
|
#if !DISABLE_SHARING
|
|
// Use x as the key...see if we can find it
|
|
for(int n = 0; n < mPointPool.size(); n += 8)
|
|
{
|
|
if((xyz[0] == mPointPool[n]) &&
|
|
(xyz[1] == mPointPool[n+1]) &&
|
|
(xyz[2] == mPointPool[n+2]) &&
|
|
(st[0] == mPointPool[n+3]) &&
|
|
(st[1] == mPointPool[n+4]))
|
|
return n/8; // Clients care about point # not array index
|
|
}
|
|
#endif
|
|
|
|
// If we're here, no match was found so we add it to the pool
|
|
// Add XYZ
|
|
mPointPool.push_back(xyz[0]); mPointPool.push_back(xyz[1]); mPointPool.push_back(xyz[2]);
|
|
// Add ST
|
|
mPointPool.push_back(st[0]); mPointPool.push_back(st[1]);
|
|
// Allocate some space for the normal later
|
|
mPointPool.push_back(0.0); mPointPool.push_back(0.0); mPointPool.push_back(0.0);
|
|
return (mPointPool.size()/8)-1;
|
|
}
|
|
|
|
// This function sets up OpenGL for our point pool
|
|
void OBJ_PointPool::PreparePoolToDraw()
|
|
{
|
|
// Setup our triangle data (20 represents 5 elements of 4 bytes each
|
|
// namely s,t,xn,yn,zn)
|
|
glVertexPointer(3, GL_FLOAT, 32, &(*mPointPool.begin()));
|
|
// Set our texture data (24 represents 6 elements of 4 bytes each
|
|
// namely xn, yn, zn, x, y, z. We start 3 from the beginning to skip
|
|
// over x, y, z initially.
|
|
glClientActiveTextureARB(GL_TEXTURE1);
|
|
glTexCoordPointer(2, GL_FLOAT, 32, &(*(mPointPool.begin() + 3)));
|
|
glClientActiveTextureARB(GL_TEXTURE0);
|
|
glTexCoordPointer(2, GL_FLOAT, 32, &(*(mPointPool.begin() + 3)));
|
|
// Set our normal data...
|
|
glNormalPointer(GL_FLOAT, 32, &(*(mPointPool.begin() + 5)));
|
|
}
|
|
|
|
void OBJ_PointPool::CalcTriNormal(int idx1, int idx2, int idx3)
|
|
{
|
|
if (mPointPool[idx1*8 ]==mPointPool[idx2*8 ]&&
|
|
mPointPool[idx1*8+1]==mPointPool[idx2*8+1]&&
|
|
mPointPool[idx1*8+2]==mPointPool[idx2*8+2]) return;
|
|
if (mPointPool[idx1*8 ]==mPointPool[idx3*8 ]&&
|
|
mPointPool[idx1*8+1]==mPointPool[idx3*8+1]&&
|
|
mPointPool[idx1*8+2]==mPointPool[idx3*8+2]) return;
|
|
if (mPointPool[idx2*8 ]==mPointPool[idx3*8 ]&&
|
|
mPointPool[idx2*8+1]==mPointPool[idx3*8+1]&&
|
|
mPointPool[idx2*8+2]==mPointPool[idx3*8+2]) return;
|
|
|
|
// idx2->idx1 cross idx1->idx3 = normal product
|
|
float v1[3], v2[3], n[3];
|
|
v1[0] = mPointPool[idx2*8 ] - mPointPool[idx1*8 ];
|
|
v1[1] = mPointPool[idx2*8+1] - mPointPool[idx1*8+1];
|
|
v1[2] = mPointPool[idx2*8+2] - mPointPool[idx1*8+2];
|
|
|
|
v2[0] = mPointPool[idx2*8 ] - mPointPool[idx3*8 ];
|
|
v2[1] = mPointPool[idx2*8+1] - mPointPool[idx3*8+1];
|
|
v2[2] = mPointPool[idx2*8+2] - mPointPool[idx3*8+2];
|
|
|
|
// We do NOT normalize the cross product; we want larger triangles
|
|
// to make bigger normals. When we blend them, bigger sides will
|
|
// contribute more to the normals. We'll normalize the normals
|
|
// after the blend is done.
|
|
CrossVec(v1, v2, n);
|
|
mPointPool[idx1*8+5] += n[0];
|
|
mPointPool[idx1*8+6] += n[1];
|
|
mPointPool[idx1*8+7] += n[2];
|
|
|
|
mPointPool[idx2*8+5] += n[0];
|
|
mPointPool[idx2*8+6] += n[1];
|
|
mPointPool[idx2*8+7] += n[2];
|
|
|
|
mPointPool[idx3*8+5] += n[0];
|
|
mPointPool[idx3*8+6] += n[1];
|
|
mPointPool[idx3*8+7] += n[2];
|
|
}
|
|
|
|
inline void swapped_add(float& a, float& b)
|
|
{
|
|
float a_c = a;
|
|
float b_c = b;
|
|
a += b_c;
|
|
b += a_c;
|
|
}
|
|
|
|
void OBJ_PointPool::NormalizeNormals(void)
|
|
{
|
|
// Average all normals of same-point, different texture points? Why is this needed?
|
|
// Well...the problem is that we get non-blended normals around the 'seam' where the ACF fuselage
|
|
// is put together...at this point the separate top and bottom texes touch. Their color will
|
|
// be the same but the S&T coords won't. If we have slightly different normals and the sun is making
|
|
// shiney specular hilites, the discontinuity is real noticiable.
|
|
#if BLEND_NORMALS
|
|
for (int n = 0; n < mPointPool.size(); n += 8)
|
|
{
|
|
for (int m = 0; m < mPointPool.size(); m += 8)
|
|
if (mPointPool[n ]==mPointPool[m ] &&
|
|
mPointPool[n+1]==mPointPool[m+1] &&
|
|
mPointPool[n+2]==mPointPool[m+2] &&
|
|
m != n)
|
|
{
|
|
swapped_add(mPointPool[n+5], mPointPool[m+5]);
|
|
swapped_add(mPointPool[n+6], mPointPool[m+6]);
|
|
swapped_add(mPointPool[n+7], mPointPool[m+7]);
|
|
}
|
|
}
|
|
#endif
|
|
for (int n = 5; n < mPointPool.size(); n += 8)
|
|
{
|
|
NormalizeVec(&mPointPool[n]);
|
|
}
|
|
}
|
|
|
|
// This is a debug routine that will draw each vertex's normals
|
|
void OBJ_PointPool::DebugDrawNormals()
|
|
{
|
|
XPLMSetGraphicsState(0, 0, 0, 0, 0, 1, 0);
|
|
glColor3f(1.0, 0.0, 1.0);
|
|
glBegin(GL_LINES);
|
|
for(int n = 0; n < mPointPool.size(); n+=8)
|
|
{
|
|
glVertex3f(mPointPool[n], mPointPool[n+1], mPointPool[n+2]);
|
|
glVertex3f(mPointPool[n] + mPointPool[n+5], mPointPool[n+1] + mPointPool[n+1+5],
|
|
mPointPool[n+2] + mPointPool[n+2+5]);
|
|
}
|
|
glEnd();
|
|
}
|
|
|
|
/*****************************************************
|
|
Object and Struct Definitions
|
|
******************************************************/
|
|
static map<string, int> sTexes;
|
|
|
|
struct LightInfo_t {
|
|
float xyz[3];
|
|
int rgb[3];
|
|
};
|
|
|
|
// One of these structs per LOD read from the OBJ file
|
|
struct LODObjInfo_t {
|
|
|
|
float nearDist; // The visible range
|
|
float farDist; // of this LOD
|
|
vector<int> triangleList;
|
|
vector<LightInfo_t> lights;
|
|
OBJ_PointPool pointPool;
|
|
GLuint dl;
|
|
};
|
|
|
|
// One of these structs per OBJ file
|
|
struct ObjInfo_t {
|
|
|
|
string path;
|
|
int texnum;
|
|
int texnum_lit;
|
|
XObj obj;
|
|
vector<LODObjInfo_t> lods;
|
|
};
|
|
|
|
static vector<ObjInfo_t> sObjects;
|
|
|
|
/*****************************************************
|
|
Utility functions to handle OBJ stuff
|
|
******************************************************/
|
|
|
|
int OBJ_LoadTexture(const char * inFilePath, bool inForceMaxTex)
|
|
{
|
|
string path(inFilePath);
|
|
if (sTexes.count(path) > 0)
|
|
return sTexes[path];
|
|
|
|
int texNum;
|
|
XPLMGenerateTextureNumbers(&texNum, 1);
|
|
|
|
int derez = 5 - gIntPrefsFunc("planes", "resolution", 3);
|
|
if (inForceMaxTex)
|
|
derez = 0;
|
|
bool ok = LoadTextureFromFile(path.c_str(), texNum, true, false, true, NULL, NULL, derez);
|
|
if (!ok) return 0;
|
|
|
|
sTexes[path] = texNum;
|
|
return texNum;
|
|
}
|
|
|
|
bool OBJ_Init(const char * inTexturePath)
|
|
{
|
|
// Now that we've successfully loaded an aircraft,
|
|
// we can load our lighting texture
|
|
static bool firstTime = true;
|
|
if(firstTime)
|
|
{
|
|
sLightTexture = OBJ_LoadTexture(inTexturePath, true);
|
|
firstTime = false;
|
|
}
|
|
return sLightTexture != 0;
|
|
}
|
|
|
|
// Load one model - return -1 if it can't be loaded.
|
|
int OBJ_LoadModel(const char * inFilePath)
|
|
{
|
|
string path(inFilePath);
|
|
|
|
for (int n = 0; n < sObjects.size(); ++n)
|
|
{
|
|
if (path == sObjects[n].path)
|
|
return n;
|
|
}
|
|
|
|
sObjects.push_back(ObjInfo_t());
|
|
bool ok = XObjRead(path.c_str(), sObjects.back().obj);
|
|
if (!ok)
|
|
{
|
|
sObjects.pop_back();
|
|
return -1;
|
|
}
|
|
|
|
MakePartialPathNativeObj(sObjects.back().obj.texture);
|
|
|
|
sObjects.back().path = path;
|
|
string tex_path(path);
|
|
string::size_type p = tex_path.find_last_of("\\:/");//XPLMGetDirectorySeparator());
|
|
tex_path.erase(p+1);
|
|
tex_path += sObjects.back().obj.texture;
|
|
tex_path += ".png";
|
|
sObjects.back().texnum = OBJ_LoadTexture(tex_path.c_str(), false);
|
|
|
|
tex_path = path;
|
|
p = tex_path.find_last_of("\\:/");//XPLMGetDirectorySeparator());
|
|
tex_path.erase(p+1);
|
|
tex_path += sObjects.back().obj.texture;
|
|
tex_path += "_LIT.png";
|
|
sObjects.back().texnum_lit = OBJ_LoadTexture(tex_path.c_str(), false);
|
|
|
|
// We prescan all of the commands to see if there's ANY LOD. If there's
|
|
// not then we need to add one ourselves. If there is, we will find it
|
|
// naturally later.
|
|
bool foundLOD = false;
|
|
for (vector<XObjCmd>::iterator cmd = sObjects.back().obj.cmds.begin();
|
|
cmd != sObjects.back().obj.cmds.end(); ++cmd)
|
|
{
|
|
if((cmd->cmdType == type_Attr) && (cmd->cmdID == attr_LOD))
|
|
foundLOD = true;
|
|
}
|
|
if(foundLOD == false)
|
|
{
|
|
sObjects.back().lods.push_back(LODObjInfo_t());
|
|
sObjects.back().lods.back().nearDist = 0;
|
|
sObjects.back().lods.back().farDist = 40000;
|
|
}
|
|
// Go through all of the commands for this object and filter out the polys
|
|
// and the lights.
|
|
for (vector<XObjCmd>::iterator cmd = sObjects.back().obj.cmds.begin();
|
|
cmd != sObjects.back().obj.cmds.end(); ++cmd)
|
|
{
|
|
switch(cmd->cmdType) {
|
|
case type_Attr:
|
|
if(cmd->cmdID == attr_LOD)
|
|
{
|
|
// We've found a new LOD section so save this
|
|
// information in a new struct. From now on and until
|
|
// we hit this again, all data is for THIS LOD instance.
|
|
sObjects.back().lods.push_back(LODObjInfo_t());
|
|
// Save our visible LOD range
|
|
sObjects.back().lods.back().nearDist = cmd->attributes[0];
|
|
sObjects.back().lods.back().farDist = cmd->attributes[1];
|
|
}
|
|
break;
|
|
case type_PtLine:
|
|
if(cmd->cmdID == obj_Light)
|
|
{
|
|
// For each light we've found, copy the data into our
|
|
// own light vector
|
|
for(int n = 0; n < cmd->rgb.size(); n++)
|
|
{
|
|
sObjects.back().lods.back().lights.push_back(LightInfo_t());
|
|
sObjects.back().lods.back().lights.back().xyz[0] = cmd->rgb[n].v[0];
|
|
sObjects.back().lods.back().lights.back().xyz[1] = cmd->rgb[n].v[1];
|
|
sObjects.back().lods.back().lights.back().xyz[2] = cmd->rgb[n].v[2];
|
|
sObjects.back().lods.back().lights.back().rgb[0] = cmd->rgb[n].rgb[0];
|
|
sObjects.back().lods.back().lights.back().rgb[1] = cmd->rgb[n].rgb[1];
|
|
sObjects.back().lods.back().lights.back().rgb[2] = cmd->rgb[n].rgb[2];
|
|
}
|
|
}
|
|
break;
|
|
case type_Poly:
|
|
{
|
|
vector<int> indexes;
|
|
// First get our point pool setup with all verticies
|
|
for(int n = 0; n < cmd->st.size(); n++)
|
|
{
|
|
float xyz[3], st[2];
|
|
int index;
|
|
|
|
xyz[0] = cmd->st[n].v[0];
|
|
xyz[1] = cmd->st[n].v[1];
|
|
xyz[2] = cmd->st[n].v[2];
|
|
st[0] = cmd->st[n].st[0];
|
|
st[1] = cmd->st[n].st[1];
|
|
index = sObjects.back().lods.back().pointPool.AddPoint(xyz, st);
|
|
indexes.push_back(index);
|
|
}
|
|
|
|
switch(cmd->cmdID) {
|
|
case obj_Tri:
|
|
for(int n = 0; n < indexes.size(); ++n)
|
|
{
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[n]);
|
|
}
|
|
break;
|
|
case obj_Tri_Fan:
|
|
for(int n = 2; n < indexes.size(); n++)
|
|
{
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[0 ]);
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[n-1]);
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[n ]);
|
|
}
|
|
break;
|
|
case obj_Tri_Strip:
|
|
case obj_Quad_Strip:
|
|
for(int n = 2; n < indexes.size(); n++)
|
|
{
|
|
if((n % 2) == 1)
|
|
{
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[n - 2]);
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[n]);
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[n - 1]);
|
|
}
|
|
else
|
|
{
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[n - 2]);
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[n - 1]);
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[n]);
|
|
}
|
|
}
|
|
break;
|
|
case obj_Quad:
|
|
for(int n = 3; n < indexes.size(); n += 4)
|
|
{
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[n-3]);
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[n-2]);
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[n-1]);
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[n-3]);
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[n-1]);
|
|
sObjects.back().lods.back().triangleList.push_back(indexes[n ]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Calculate our normals for all LOD's
|
|
for (int i = 0; i < sObjects.back().lods.size(); i++)
|
|
{
|
|
for (int n = 0; n < sObjects.back().lods[i].triangleList.size(); n += 3)
|
|
{
|
|
sObjects.back().lods[i].pointPool.CalcTriNormal(
|
|
sObjects.back().lods[i].triangleList[n],
|
|
sObjects.back().lods[i].triangleList[n+1],
|
|
sObjects.back().lods[i].triangleList[n+2]);
|
|
}
|
|
sObjects.back().lods[i].pointPool.NormalizeNormals();
|
|
sObjects.back().lods[i].dl = 0;
|
|
}
|
|
sObjects.back().obj.cmds.clear();
|
|
return sObjects.size()-1;
|
|
}
|
|
|
|
/*****************************************************
|
|
Aircraft Model Drawing
|
|
******************************************************/
|
|
// Note that texID and litTexID are OPTIONAL! They will only be filled
|
|
// in if the user wants to override the default texture specified by the
|
|
// obj file
|
|
void OBJ_PlotModel(int model, int texID, int litTexID, float inDistance, double inX,
|
|
double inY, double inZ, double inPitch, double inRoll, double inHeading)
|
|
{
|
|
int tex, lit;
|
|
// Find out what LOD we need to draw
|
|
int lodIdx = -1;
|
|
for(int n = 0; n < sObjects[model].lods.size(); n++)
|
|
{
|
|
if((inDistance >= sObjects[model].lods[n].nearDist) &&
|
|
(inDistance <= sObjects[model].lods[n].farDist))
|
|
{
|
|
lodIdx = n;
|
|
break;
|
|
}
|
|
}
|
|
// If we didn't find a good LOD bin, we don't draw!
|
|
if(lodIdx == -1)
|
|
return;
|
|
|
|
// pointPool is and always was empty! returning early
|
|
if(sObjects[model].lods[lodIdx].pointPool.Size()==0 && sObjects[model].lods[lodIdx].dl == 0)
|
|
return;
|
|
|
|
static XPLMDataRef night_lighting_ref = XPLMFindDataRef("sim/graphics/scenery/percent_lights_on");
|
|
bool use_night = XPLMGetDataf(night_lighting_ref) > 0.25;
|
|
|
|
if (model == -1) return;
|
|
|
|
if(texID)
|
|
{
|
|
tex = texID;
|
|
lit = litTexID;
|
|
}
|
|
else
|
|
{
|
|
tex = sObjects[model].texnum;
|
|
lit = sObjects[model].texnum_lit;
|
|
}
|
|
if (!use_night) lit = 0;
|
|
if (tex == 0) lit = 0;
|
|
XPLMSetGraphicsState(1, (tex != 0) + (lit != 0), 1, 1, 1, 1, 1);
|
|
if (tex != 0) XPLMBindTexture2d(tex, 0);
|
|
if (lit != 0) XPLMBindTexture2d(lit, 1);
|
|
|
|
if (tex) { glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); }
|
|
if (lit) { glActiveTextureARB(GL_TEXTURE1); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD); glActiveTextureARB(GL_TEXTURE0); }
|
|
|
|
if (sObjects[model].lods[lodIdx].dl == 0)
|
|
{
|
|
sObjects[model].lods[lodIdx].dl = glGenLists(1);
|
|
|
|
GLint xpBuffer;
|
|
// See if the card even has VBO. If it does, save xplane's pointer
|
|
// and bind to 0 for us.
|
|
if(glBindBufferARB)
|
|
{
|
|
glGetIntegerv(GL_ARRAY_BUFFER_BINDING_ARB, &xpBuffer);
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
|
}
|
|
// Save XPlanes OpenGL state
|
|
glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
|
|
// Setup OpenGL pointers to our pool
|
|
sObjects[model].lods[lodIdx].pointPool.PreparePoolToDraw();
|
|
// Enable vertex data sucking
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
// Enable normal array sucking
|
|
glEnableClientState(GL_NORMAL_ARRAY);
|
|
// Enable texture coordinate data sucking
|
|
glClientActiveTextureARB(GL_TEXTURE1);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glClientActiveTextureARB(GL_TEXTURE0);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
// Disable colors - maybe x-plane left it around.
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
|
|
glNewList(sObjects[model].lods[lodIdx].dl, GL_COMPILE);
|
|
// Kick OpenGL and draw baby!
|
|
glDrawElements(GL_TRIANGLES, sObjects[model].lods[lodIdx].triangleList.size(),
|
|
GL_UNSIGNED_INT, &(*sObjects[model].lods[lodIdx].triangleList.begin()));
|
|
|
|
#if DEBUG_NORMALS
|
|
sObjects[model].lods[lodIdx].pointPool.DebugDrawNormals();
|
|
XPLMSetGraphicsState(1, (tex != 0) + (lit != 0), 1, 1, 1, 1, 1);
|
|
#endif
|
|
|
|
glEndList();
|
|
|
|
// Disable vertex data sucking
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
// Disable texture coordinate data sucking
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
// Disable normal array sucking
|
|
glDisableClientState(GL_NORMAL_ARRAY);
|
|
|
|
// Restore Xplane's OpenGL State
|
|
glPopClientAttrib();
|
|
|
|
// If we bound before, we need to put xplane back where it was
|
|
if(glBindBufferARB)
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, xpBuffer);
|
|
|
|
sObjects[model].lods[lodIdx].triangleList.clear();
|
|
sObjects[model].lods[lodIdx].pointPool.Purge();
|
|
}
|
|
glCallList(sObjects[model].lods[lodIdx].dl);
|
|
|
|
|
|
}
|
|
|
|
/*****************************************************
|
|
Textured Lights Drawing
|
|
|
|
Draw one or more lights on our OBJ.
|
|
RGB of 11,11,11 is a RED NAV light
|
|
RGB of 22,22,22 is a GREEN NAV light
|
|
RGB of 33,33,33 is a Red flashing BEACON light
|
|
RGB of 44,44,44 is a White flashing STROBE light
|
|
RGB of 55,55,55 is a landing light
|
|
******************************************************/
|
|
void OBJ_BeginLightDrawing()
|
|
{
|
|
sFOV = XPLMGetDataf(sFOVRef);
|
|
|
|
// Setup OpenGL for the drawing
|
|
XPLMSetGraphicsState(1, 1, 0, 1, 1 , 1, 0);
|
|
XPLMBindTexture2d(sLightTexture, 0);
|
|
}
|
|
|
|
void OBJ_DrawLights(int model, float inDistance, double inX, double inY,
|
|
double inZ, double inPitch, double inRoll, double inHeading,
|
|
xpmp_LightStatus lights)
|
|
{
|
|
bool navLights = lights.navLights == 1;
|
|
bool bcnLights = lights.bcnLights == 1;
|
|
bool strbLights = lights.strbLights == 1;
|
|
bool landLights = lights.landLights == 1;
|
|
|
|
int offset = lights.timeOffset;
|
|
|
|
// flash frequencies
|
|
if(bcnLights) {
|
|
bcnLights = false;
|
|
int x = (int)(XPLMGetElapsedTime() * 1000 + offset) % 1200;
|
|
switch(lights.flashPattern) {
|
|
case xpmp_Lights_Pattern_EADS:
|
|
// EADS pattern: two flashes every 1.2 seconds
|
|
if(x < 120 || ((x > 240 && x < 360))) bcnLights = true;
|
|
break;
|
|
|
|
case xpmp_Lights_Pattern_GA:
|
|
// GA pattern: 900ms / 1200ms
|
|
if((((int)(XPLMGetElapsedTime() * 1000 + offset) % 2100) < 900)) bcnLights = true;
|
|
break;
|
|
|
|
case xpmp_Lights_Pattern_Default:
|
|
default:
|
|
// default pattern: one flash every 1.2 seconds
|
|
if(x < 120) bcnLights = true;
|
|
break;
|
|
}
|
|
|
|
}
|
|
if(strbLights) {
|
|
strbLights = false;
|
|
int x = (int)(XPLMGetElapsedTime() * 1000 + offset) % 1700;
|
|
switch(lights.flashPattern) {
|
|
case xpmp_Lights_Pattern_EADS:
|
|
if(x < 80 || (x > 260 && x < 340)) strbLights = true;
|
|
break;
|
|
|
|
case xpmp_Lights_Pattern_GA:
|
|
// similar to the others.. but a little different frequency :)
|
|
x = (int)(XPLMGetElapsedTime() * 1000 + offset) % 1900;
|
|
if(x < 100) strbLights = true;
|
|
break;
|
|
|
|
case xpmp_Lights_Pattern_Default:
|
|
default:
|
|
if(x < 80) strbLights = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Find out what LOD we need to draw
|
|
int lodIdx = -1;
|
|
for(int n = 0; n < sObjects[model].lods.size(); n++)
|
|
{
|
|
if((inDistance >= sObjects[model].lods[n].nearDist) &&
|
|
(inDistance <= sObjects[model].lods[n].farDist))
|
|
{
|
|
lodIdx = n;
|
|
break;
|
|
}
|
|
}
|
|
// If we didn't find a good LOD bin, we don't draw!
|
|
if(lodIdx == -1)
|
|
return;
|
|
|
|
double size, distance;
|
|
// Where are we looking?
|
|
XPLMCameraPosition_t cameraPos;
|
|
XPLMReadCameraPosition(&cameraPos);
|
|
|
|
// We can have 1 or more lights on each aircraft
|
|
for(int n = 0; n < sObjects[model].lods[lodIdx].lights.size(); n++)
|
|
{
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
// First we translate to our coordinate system and move the origin
|
|
// to the center of our lights.
|
|
glTranslatef(sObjects[model].lods[lodIdx].lights[n].xyz[0],
|
|
sObjects[model].lods[lodIdx].lights[n].xyz[1],
|
|
sObjects[model].lods[lodIdx].lights[n].xyz[2]);
|
|
|
|
// Now we undo the rotation of the plane
|
|
glRotated(-inRoll, 0.0, 0.0, -1.0);
|
|
glRotated(-inPitch, 1.0, 0.0, 0.0);
|
|
glRotated(-inHeading, 0.0, -1.0, 0.0);
|
|
|
|
// Now we undo the rotation of the camera
|
|
// NOTE: The order and sign of the camera is backwards
|
|
// from what we'd expect (the plane rotations) because
|
|
// the camera works backwards. If you pan right, everything
|
|
// else moves left!
|
|
glRotated(cameraPos.heading, 0.0, -1.0, 0.0);
|
|
glRotated(cameraPos.pitch, 1.0, 0.0, 0.0);
|
|
glRotated(cameraPos.roll, 0.0, 0.0, -1.0);
|
|
|
|
// Find our distance from the camera
|
|
float dx = cameraPos.x - inX;
|
|
float dy = cameraPos.y - inY;
|
|
float dz = cameraPos.z - inZ;
|
|
distance = sqrt((dx * dx) + (dy * dy) + (dz * dz));
|
|
|
|
// Convert to NM
|
|
distance *= kMetersToNM;
|
|
|
|
// Scale based on our FOV and Zoom. I did my initial
|
|
// light adjustments at a FOV of 60 so thats why
|
|
// I divide our current FOV by 60 to scale it appropriately.
|
|
distance *= sFOV/60.0;
|
|
distance /= cameraPos.zoom;
|
|
|
|
// Calculate our light size. This is piecewise linear. I noticed
|
|
// that light size changed more rapidly when closer than 3nm so
|
|
// I have a separate equation for that.
|
|
if(distance <= 3.6)
|
|
size = (10 * distance) + 1;
|
|
else
|
|
size = (6.7 * distance) + 12;
|
|
|
|
// Finally we can draw our lights
|
|
// Red Nav
|
|
glBegin(GL_QUADS);
|
|
if((sObjects[model].lods[lodIdx].lights[n].rgb[0] == 11) &&
|
|
(sObjects[model].lods[lodIdx].lights[n].rgb[1] == 11) &&
|
|
(sObjects[model].lods[lodIdx].lights[n].rgb[2] == 11))
|
|
{
|
|
if(navLights) {
|
|
glColor4fv(kNavLightRed);
|
|
glTexCoord2f(0, 0.5); glVertex2f(-(size/2.0), -(size/2.0));
|
|
glTexCoord2f(0, 1.0); glVertex2f(-(size/2.0), (size/2.0));
|
|
glTexCoord2f(0.25, 1.0); glVertex2f((size/2.0), (size/2.0));
|
|
glTexCoord2f(0.25, 0.5); glVertex2f((size/2.0), -(size/2.0));
|
|
}
|
|
}
|
|
// Green Nav
|
|
else if((sObjects[model].lods[lodIdx].lights[n].rgb[0] == 22) &&
|
|
(sObjects[model].lods[lodIdx].lights[n].rgb[1] == 22) &&
|
|
(sObjects[model].lods[lodIdx].lights[n].rgb[2] == 22))
|
|
{
|
|
if(navLights) {
|
|
glColor4fv(kNavLightGreen);
|
|
glTexCoord2f(0, 0.5); glVertex2f(-(size/2.0), -(size/2.0));
|
|
glTexCoord2f(0, 1.0); glVertex2f(-(size/2.0), (size/2.0));
|
|
glTexCoord2f(0.25, 1.0); glVertex2f((size/2.0), (size/2.0));
|
|
glTexCoord2f(0.25, 0.5); glVertex2f((size/2.0), -(size/2.0));
|
|
}
|
|
}
|
|
// Beacon
|
|
else if((sObjects[model].lods[lodIdx].lights[n].rgb[0] == 33) &&
|
|
(sObjects[model].lods[lodIdx].lights[n].rgb[1] == 33) &&
|
|
(sObjects[model].lods[lodIdx].lights[n].rgb[2] == 33))
|
|
{
|
|
if(bcnLights)
|
|
{
|
|
glColor4fv(kNavLightRed);
|
|
glTexCoord2f(0, 0.5); glVertex2f(-(size/2.0), -(size/2.0));
|
|
glTexCoord2f(0, 1.0); glVertex2f(-(size/2.0), (size/2.0));
|
|
glTexCoord2f(0.25, 1.0); glVertex2f((size/2.0), (size/2.0));
|
|
glTexCoord2f(0.25, 0.5); glVertex2f((size/2.0), -(size/2.0));
|
|
}
|
|
}
|
|
// Strobes
|
|
else if((sObjects[model].lods[lodIdx].lights[n].rgb[0] == 44) &&
|
|
(sObjects[model].lods[lodIdx].lights[n].rgb[1] == 44) &&
|
|
(sObjects[model].lods[lodIdx].lights[n].rgb[2] == 44))
|
|
{
|
|
if(strbLights)
|
|
{
|
|
glColor4fv(kStrobeLight);
|
|
glTexCoord2f(0.25, 0.0); glVertex2f(-(size/1.5), -(size/1.5));
|
|
glTexCoord2f(0.25, 0.5); glVertex2f(-(size/1.5), (size/1.5));
|
|
glTexCoord2f(0.50, 0.5); glVertex2f((size/1.5), (size/1.5));
|
|
glTexCoord2f(0.50, 0.0); glVertex2f((size/1.5), -(size/1.5));
|
|
}
|
|
}
|
|
// Landing Lights
|
|
else if((sObjects[model].lods[lodIdx].lights[n].rgb[0] == 55) &&
|
|
(sObjects[model].lods[lodIdx].lights[n].rgb[1] == 55) &&
|
|
(sObjects[model].lods[lodIdx].lights[n].rgb[2] == 55))
|
|
{
|
|
if(landLights) {
|
|
// BEN SEZ: modulate the _alpha to make this dark, not
|
|
// the light color. Otherwise if the sky is fairly light the light
|
|
// will be darker than the sky, which looks f---ed during the day.
|
|
float color[4];
|
|
color[0] = kLandingLight[0];
|
|
if(color[0] < 0.0) color[0] = 0.0;
|
|
color[1] = kLandingLight[1];
|
|
if(color[0] < 0.0) color[0] = 0.0;
|
|
color[2] = kLandingLight[2];
|
|
if(color[0] < 0.0) color[0] = 0.0;
|
|
color[3] = kLandingLight[3] * ((distance * -0.05882) + 1.1764);
|
|
glColor4fv(color);
|
|
glTexCoord2f(0.25, 0.0); glVertex2f(-(size/2.0), -(size/2.0));
|
|
glTexCoord2f(0.25, 0.5); glVertex2f(-(size/2.0), (size/2.0));
|
|
glTexCoord2f(0.50, 0.5); glVertex2f((size/2.0), (size/2.0));
|
|
glTexCoord2f(0.50, 0.0); glVertex2f((size/2.0), -(size/2.0));
|
|
}
|
|
} else {
|
|
// rear nav light and others? I guess...
|
|
if(navLights) {
|
|
glColor3f(
|
|
sObjects[model].lods[lodIdx].lights[n].rgb[0] * 0.1,
|
|
sObjects[model].lods[lodIdx].lights[n].rgb[1] * 0.1,
|
|
sObjects[model].lods[lodIdx].lights[n].rgb[2] * 0.1);
|
|
glTexCoord2f(0, 0.5); glVertex2f(-(size/2.0), -(size/2.0));
|
|
glTexCoord2f(0, 1.0); glVertex2f(-(size/2.0), (size/2.0));
|
|
glTexCoord2f(0.25, 1.0); glVertex2f((size/2.0), (size/2.0));
|
|
glTexCoord2f(0.25, 0.5); glVertex2f((size/2.0), -(size/2.0));
|
|
}
|
|
}
|
|
glEnd();
|
|
// Put OpenGL back how we found it
|
|
glPopMatrix();
|
|
}
|
|
}
|
|
|
|
int OBJ_GetModelTexID(int model)
|
|
{
|
|
return sObjects[model].texnum;
|
|
}
|