Download as rtf, pdf, or txt
Download as rtf, pdf, or txt
You are on page 1of 14

OK someomtimes people want to get IMAGE CAPTURE from X-Plane by

UDP over an ethernet cable.


We only have this in black-and-white to save bandwidth and simulate a
FLIR system, and here is how you activet it.

Open “Sending Data to X-Plane.rtfd”, and search on the FLIR message.


THAT is the message that you will send to X-Plane to request FLIR imagery
from X-Plane.
Once you have sent the FLIR message and the desired frame-rate of data,
according to the document above, you should start getting FLIR imageray
right back to the same IP address and port.

NOW, how do you DECODE that imagery?


Here is the source code!

//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
#define xdob double
#define xflt float
typedef int xint; // always 32-bits. must do it this way since apple has a type xint in
xpc.h!

//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
class flir_class
{
public:
flir_class(){memset(this,0,sizeof(*this));} // THIS is a POINTER. so the
first arg is just "this" (the pointer) the third arg is the thing the pointer points to,
thus the '*'
~flir_class(){}
void dis_flir (xflt x1,xflt y1,xflt x2,xflt y2);
void flir_grab_from_net (GLubyte* data_read,xint len_rcvd);
};
extern flir_class flir;
#endif

flir_class flir;

//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
void tman_class::load_scratch( enum_tex t ,
string path ,
GLenum tex_type ,
xint dx_vis ,
xint dy_vis ,
xint dx_tot ,
xint dy_tot ,GLubyte*
rgb_data,xint is_pvr)
{
if(!
intrange(tex_type,GL_RGB,GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
))IPAD_alert("Bad minify arg.");

//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
// SET THE PARAMETERS
//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
xint reduc =0;
xint mini =0; // ALL 2 ND 4 BIT PVRS HAVE MINIFICATION BUILT
IN. SO THIS IS AVAIL FOR ALL PVRS. PROBLEM IS: IT LOOKS LIKE
DONKEY-ASS. SO, LEAVE IT OFF!
// WE STILL GET A 15% RAM SAVINGS AND 10%
FR BOOST FROM SWITCHING TO PVR!

tex_dx_vis[t]=dx_vis;
tex_dy_vis[t]=dy_vis;
tex_dx_tot[t]=dx_tot;
tex_dy_tot[t]=dy_tot;

glBindTexture (GL_TEXTURE_2D,t);
glTexParameteri (GL_TEXTURE_2D,GL_TEXTURE_WRAP_S
,GL_REPEAT ); // wrapping is faster.
glTexParameteri (GL_TEXTURE_2D,GL_TEXTURE_WRAP_T
,GL_REPEAT ); // wrapping is faster.
glTexParameteri
(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR );
glTexParameteri
(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR );
glTexParameterf
(GL_TEXTURE_2D,GL_TEXTURE_MAX_ANISOTROPY_EXT,1.0); //
THIS IS FASTEST, AND WE ONLY NEED ANISO IF MIP-MAPPED, SINCE
MIP-MAPPING IS THE ONLY THING THAT TAKES US DOWN TO A LOW-
RES TEX THAT NEEDS ANISO!

// THIS IS WHAT MAKES MIPMAPS SUCK LESS!


// THIS LETS THEM STILL HAVE DECENTLY HI RES IN THE
DIRECTION YOU WANT IT,
// AND MAY ALLOW THE APP TO BE FASTER SINCE THERE WILL BE
LESS MEMORY THROUGHPUT ON THE CARD
// SETTING TO MAX ISO MIGHT MAKE PVR-MINIFACTION LOOK A
TINY BIT BETTER, AND HAS NO MEASURBALE REDUCTION IN FRAME-
RATE THAT I CAN SEE,
// AND OF COURSE HAS NO IMPACT ON RAM
if(mini)
{
xflt max_iso ;

glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT,&max_iso);

glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAX_ANISOTROPY_EX
T,max_iso ); // aniso makes mini suck a little less, with no change
in ram, and no change in fr that i can measure in the ipad mini
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEA
R_MIPMAP_LINEAR ); // THEORETICAL BEST MIPMAP QUALITY
COMES FROM: GL_LINEAR_MIPMAP_LINEAR
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,
GL_LINEAR ); // MAYBE YOU GET FASTER RESULTS WITH:
GL_LINEAR_MIPMAP_NEAREST
}

//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
// LOAD THE TEXTURE
//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
for(;;)
{
OGL_error_check("glTexImage2D-1");

xint tex_size=0;

if(is_pvr)
{
tex_size=vram_for_texture_any(tex_type,0,dx_tot,dy_tot,xfals);
// packing is never used by compressed texes

glCompressedTexImage2D( GL_TEXTURE_2D, // target


reduc, //
reduction... 0 for non-mip-mapped, and first level of mipmap
tex_type, // gives # color
components
dx_tot, // width
and height... this needs power of 2 size only!!!!!
dy_tot, // width
and height... this needs power of 2 size only!!!!!
0, // width
of border... 0 or 1 only
tex_size,
pvr_buffer);
}
else
{
glTexImage2D(GL_TEXTURE_2D, // target
reduc, // reduction...
0 for non-mip-mapped, and first level of mipmap
tex_type,
dx_tot, // width and height...
this needs power of 2 size only!!!!!
dy_tot, // width and height...
this needs power of 2 size only!!!!!
0, // width of
border... 0 or 1 only
tex_type, // GL_RGB,
GL_RGBA
GL_UNSIGNED_BYTE, // format type
rgb_data); // actual texture
buffer
}

OGL_error_check("glTexImage2D-2");

if(!mini )break; reduc++ ;


if(dx_tot<=1)break; dx_tot/=2 ;
if(dy_tot<=1)break; dy_tot/=2 ;

if(is_pvr) // i do not mini the pvr.. that was done by the grinder! all 2
and 4 bit pvr's are minified... there is no way around it!
{
pvr_buffer+=tex_size;
}
else // for non-pvr's, i have to minify the tex here if i want it
done
{
if(!intrange(dx_vis,1,TEX_DIMi))IPAD_alert("Trying to create a
NON-PVR either less than 1 or greater than 1024!",path); // PVRs do not have
these limits! runway is 2048x2048 PVR!
if(!intrange(dy_vis,1,TEX_DIMj))IPAD_alert("Trying to create a
NON-PVR either less than 1 or greater than 1024!",path); // PVRs do not have
these limits! runway is 2048x2048 PVR!
if(!intrange(dx_tot,1,TEX_DIMi))IPAD_alert("Trying to create a
NON-PVR either less than 1 or greater than 1024!",path); // PVRs do not have
these limits! runway is 2048x2048 PVR!
if(!intrange(dy_tot,1,TEX_DIMj))IPAD_alert("Trying to create a
NON-PVR either less than 1 or greater than 1024!",path); // PVRs do not have
these limits! runway is 2048x2048 PVR!

xint step=(tex_type==GL_RGB)?3:4;

for(xint j=0;j<dy_vis;j++) // go in right order so we do not step


on our data while we mod it
for(xint i=0;i<dx_vis;i++) // go in right order so we do not step
on our data while we mod it
{
xint nim1=intlim(i*2-1,0,dx_tot*2-1);
xint nip1=intlim(i*2+1,0,dx_tot*2-1);
xint njm1=intlim(j*2-1,0,dy_tot*2-1);
xint njp1=intlim(j*2+1,0,dy_tot*2-1);

xint addy_lo =( j * dx_tot + i )*step;


xint addy_hi1=(njm1*(dx_tot*2)+nim1)*step;
xint addy_hi2=(njm1*(dx_tot*2)+nip1)*step;
xint addy_hi3=(njp1*(dx_tot*2)+nim1)*step;
xint addy_hi4=(njp1*(dx_tot*2)+nip1)*step;

for(xint c=0;c<step;c++)
rgb_data[addy_lo+c]=( rgb_data[addy_hi1+c]+
rgb_data[addy_hi2+c]+
rgb_data[addy_hi3+c]+
rgb_data[addy_hi4+c])/4;
}
}
}
}

//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
structTEX_image_desc{
GLenum format ; // OpenGL texture format like GL_RGBA.
Defines the number of channels, total storage size, ordering, and encoding.
GLenum packing ;
xint width; // Width of image in pixels.
xint height ; // Height of image in pixels.
xint padding ; // Number of dead bytes between each row.
xint has_mips;}; // If true, eaech image is followed by quarter-size
mip-reduction.

enum{kDxt1=(1<<0)}; // Use DXT1 compression.

inline xint unpack_565( xbyt const* packed, xbyt* color )


{
xint value = ( xint )packed[0] | ( ( xint )packed[1] << 8 );
xbyt red = ( xbyt )( ( value >> 11 ) & 0x1f );
xbyt green = ( xbyt )( ( value >> 5 ) & 0x3f );
xbyt blue = ( xbyt )( value & 0x1f );
color[0] = ( red << 3 ) | ( red >> 2 );
color[1] = ( green << 2 ) | ( green>> 4 );
color[2] = ( blue << 3 ) | ( blue >> 2 );
color[3] = 255;
return value;
}

inline void decomp_AlphaDxt3( xbyt* rgba, void const* block )


{
xbyt const* bytes = reinterpret_cast< xbyt const* >( block );
for( xint i = 0; i < 8; ++i )
{
xbyt quant = bytes[i];
xbyt lo = quant & 0x0f;
xbyt hi = quant & 0xf0;
rgba[8*i + 3] = lo | ( lo << 4 );
rgba[8*i + 7] = hi | ( hi >> 4 );
}
}

inline void decomp_AlphaDxt5( xbyt* rgba, void const* block )


{
xbyt const* bytes = reinterpret_cast< xbyt const* >( block );
xint alpha0 = bytes[0];
xint alpha1 = bytes[1];
xbyt codes[8];
codes[0] = ( xbyt )alpha0;
codes[1] = ( xbyt )alpha1;
if( alpha0 <= alpha1 )
{
for( xint i = 1; i < 5; ++i )codes[1 + i] = ( xbyt )( ( ( 5 - i )*alpha0 +
i*alpha1 )/5 );
codes[6] = 0;
codes[7] = 255;
}
else
{
for( xint i = 1; i < 7; ++i )codes[1 + i] = ( xbyt )( ( ( 7 - i )*alpha0 +
i*alpha1 )/7 );
}
xbyt indices[16];
xbyt const* src = bytes + 2;
xbyt* dest = indices;
for( xint i = 0; i < 2; ++i )
{
xint value = 0;
for( xint j = 0; j < 3; ++j )
{
xint byte = *src++;
value |= ( byte << 8*j );
}
for( xint j = 0; j < 8; ++j )
{
xint index = ( value >> 3*j ) & 0x7;
*dest++ = ( xbyt )index;
}
}
for( xint i = 0; i < 16; ++i )rgba[4*i + 3] = codes[indices[i]];
}

inline void decomp_col( xbyt* rgba, void const* block, bool isDxt1 )
{
xbyt const* bytes = reinterpret_cast< xbyt const* >( block );
xbyt codes[16];
xint a = unpack_565( bytes, codes );
xint b = unpack_565( bytes + 2, codes + 4 );
for( xint i = 0; i < 3; ++i )
{
xint c = codes[i];
xint d = codes[4 + i];
if( isDxt1 && a <= b )
{
codes[8 + i] = ( xbyt )( ( c + d )/2 );
codes[12 + i] = 0;
}
else
{
codes[8 + i] = ( xbyt )( ( 2*c + d )/3 );
codes[12 + i] = ( xbyt )( ( c + 2*d )/3 );
}
}
codes[8 + 3] = 255;
codes[12 + 3] = ( isDxt1 && a <= b ) ? 0 : 255;
xbyt indices[16];
for( xint i = 0; i < 4; ++i )
{
xbyt* ind = indices + 4*i;
xbyt packed = bytes[4 + i];
ind[0] = packed & 0x3;
ind[1] = ( packed >> 2 ) & 0x3;
ind[2] = ( packed >> 4 ) & 0x3;
ind[3] = ( packed >> 6 ) & 0x3;
}
for( xint i = 0; i < 16; ++i )
{
xbyt offset = 4*indices[i];
for( xint j = 0; j < 4; ++j )
rgba[4*i + j] = codes[offset + j];

//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
// MAKE IT GRAY TO SIM AN IR IMAGE
//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
// if(1)
// {
// rgba[4*i+0]=
// rgba[4*i+1]=
// rgba[4*i+2]=(rgba[4*i+0]+
// rgba[4*i+1]+
// rgba[4*i+2])/3;
// }
}
}

inline void decomp(xbyt* rgba, void const* block, xint flags )


{
void const* colourBlock = block;
decomp_col( rgba, colourBlock, ( flags & kDxt1 ) != 0 );
}

void decomp_image(xbyt* rgba, xint width, xint height, void const* blocks, xint
flags )
{
xbyt const* sourceBlock = reinterpret_cast< xbyt const* >( blocks );
xint bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16;
for( xint y = 0; y < height; y += 4 )
{
for( xint x = 0; x < width; x += 4 )
{
xbyt targetRgba[4*16];
decomp( targetRgba, sourceBlock, flags );
xbyt const* sourcePixel = targetRgba;
for( xint py = 0; py < 4; ++py )
{
for( xint px = 0; px < 4; ++px )
{
xint sx = x + px;
xint sy = y + py;
if( sx < width && sy < height )
{
xbyt* targetPixel = rgba + 4*( width*sy + sx );
for( xint i = 0; i < 4; ++i )*targetPixel++ =
*sourcePixel++;
}
else
{
sourcePixel += 4;
}
}
}
sourceBlock += bytesPerBlock;
}
}
}

inline xint GetStorageRequirements( xint width, xint height, xint flags )


{
xint blockcount= ( ( width + 3 )/4 ) * ( ( height + 3 )/4 );
xint blocksize = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16;
return blockcount*blocksize;
}

xint FLIR_bytes_needed_for_image(const TEX_image_desc& desc)


{
xint w=desc.width ;
xint h=desc.height;
switch(desc.format)
{
case GL_RGB: return ( 3 * w) * h;
break;
case GL_RGBA: return ( 4 * w)
* h; break;
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return
GetStorageRequirements(w,h,kDxt1); break;
}
return 0;
}

void decomp_s3tc( const TEX_image_desc& src_image ,


const TEX_image_desc& dst_image ,
const GLubyte * src_memory,
GLubyte * dst_memory)
{
if(dst_image.format!=GL_RGBA
)IPAD_alert("bad flir options!");
if(src_image.format!=GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
)IPAD_alert("bad flir options!");
if(src_image.width !=dst_image.width
)IPAD_alert("bad flir options!");
if(src_image.height!=dst_image.height
)IPAD_alert("bad flir options!");
decomp_image( dst_memory,
src_image.width,
src_image.height,
src_memory,kDxt1);
}

//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
void flir_class::flir_grab_from_net(GLubyte* data_read,xint len_rcvd)
{
borg.set_input(recv_FLIR,enum_nite_vision);

xint rx=data_read[0]+data_read[1]*256;
xint ry=data_read[2]+data_read[3]*256; if(rx!=ry)IPAD_alert("wrong-
size flir image!");
xint flir_tex_dim_recv=rx;
xint this_pack=data_read[4];
xint num_packs=data_read[5];

TEX_image_desc src,dst;
src.format
=GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
dst.format =GL_RGBA
;
src.width =dst.width =flir_tex_dim_recv ;
src.height =dst.height=flir_tex_dim_recv ;
xint flir_len=FLIR_bytes_needed_for_image(src);
static vector<GLubyte> comp_data_buf;
// compressed buffer to hold incoming S3TC image
until it is built up.
if( comp_data_buf.size()<flir_len)
comp_data_buf.resize(flir_len);
static xint pack_len=0
; // ok we could send in several packs to form 1
image. the pack len is however much we sent in in the first packet. offset that much
for the NEXT packet!
if(this_pack==0) pack_len=len_rcvd -6
; // we simply copy each packet into the compressed data buffer
here, with an offset based on which packet this is in the sequence, and the size of
each packet.
memcpy(&*comp_data_buf.begin()
+this_pack*pack_len,data_read+6,len_rcvd-6); // copy this packet into
the buffer.

//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
//IF THIS IS THE LAST PACKET OF HOWEVER MANY MAKE UP THE
TEXTURE, THEN UNCOMPRESS IT AND LOAD IT INTO THE TMAN
TEXTURE!
//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
if(this_pack==num_packs-1)
// ok we have the last packet! get to work
uncompressing!
{
decomp_s3tc(src,dst,&*comp_data_buf.begin(),apps.scratch_array);
// decompress the image to RGB. (someday we can avoid this, but tman
doesn't speak s3tc yet.)

//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
// DO NOT UNLOAD THE TEX BEFORE LOADING IT... THERE IS NO
NEED! IT JUST THRASHES RAM LIKE CRAZY FOR NOTHING! JUST
LOAD FROM SCRATCH IS ALL WE WANT TO DO HERE!
//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••

tman.load_scratch(t_flir_texture,"FLIR",GL_RGBA,flir_tex_dim_recv,flir_tex_di
m_recv,
flir_tex_dim_recv,flir_tex_dim_recv);
}
}

You might also like