Ultimate Coder Challenge II : Lee Going Perceptual : Week Three

Welcome back to my humble attempt to re-write the rule book on teleconferencing software, a journey that will see it dragged from its complacent little rectangular world. It’s true we’ve had 3D for years, but we’ve never been able to communicate accurately and directly in that space. Thanks to the Gesture Camera, we now have the first in what will be a long line of high fidelity super accurate perceptual devices.  It is a pleasure to develop for this ground breaking device, and I hope my ramblings will light the way for future travels and travellers. So now, I will begin my crazy rant.

Latest Progress

You may recall that last week I proposed to turn that green face blob into a proper head and transmit it across to another device. The good news is that my 3D face looks a lot better, and the bad news is that getting it transmitted is going to have to wait.  In taking the advice of judges, I dug out a modern webcam product and realised the value-adds where nothing more than novelties. The market has stagnated, and the march of Skype and Google Talk do nothing more than perpetuate a flat and utilitarian experience.

I did come to appreciate however that teleconferencing cannot be taken lightly. It’s a massive industry and serious users want a reliable, quality experience that helps them get on with their job. Low latency, ease of use, backwards compatibility and essential conferencing features are all required if a new tool is to supplant the old ones.

Voice over I.P Technology

I was initially temped to write my own audio streaming system to carry audio data to the various participants in the conferencing call, but after careful study of existing solutions and the highly specialised disciplines required, I decided to go the path of least resistance and use an existing open source solution. At first I decided to use the same technology Google Talk uses for audio exchange but after a few hours of research and light development, it turns out a vital API was no longer available for download, mainly because Google had bought the company in question and moved the technology onto HTML5 and JavaScript. As luck would have it, Google partnered with another company who they did not buy called Linphone, and they provide a great open source solution that is also cross platform compatible with all the major desktops and mobiles.  A long story short, this new API is right up to date and my test across two Windows PCs, a Mac and an iPad in four way audio conferencing mode worked a treat.  Next week I shall be breaking down the sample provided to obtain the vital bits of code needed to implement audio and packet exchange between my users. As a bonus, I am going to write it in such a way that existing Linphone client apps can call into my software to join the conference call, so anyone with regular webcams or even mobile phones can join in. I will probably stick a large 3D handset in the chair in place of a 3D avatar, just for fun.

On a related note, I have decided to postpone even thinking about voice recognition until the surrounding challenges have been conquered. It never pays to spin too many plates!

 Gaze Solved? – Version One

In theory, this should be a relatively simple algorithm. Find the head, then find the eyes, then grab the RGB around the eyes only. Locate the pupil at each eye, take the average, and produce a look vector. Job’s a good one, right? Well, no. At first I decided to run away and find a sample I once saw at an early Beta preview of the Perceptual SDK which created a vector from face rotation which was pretty neat. Unfortunately that sample was not included in Beta 3, and it was soon apparent why. On exploring the commands for getting ‘landmark’ data, I noticed my nose was missing. And more strikingly, all the roll, pitch and yaw values where empty too. Finding this out from the sample saved me a bucket load of time had I proceeded to add the code to my main app first. Phew. I am sure it will be fixed in a future SDK (or I was doing something silly and it does work), but I can’t afford the time to write even one email to Intel support (who are great by the way). I needed Gaze now!

I plumbed for option two, write everything myself using only the depth data as my source.  I set to work and implemented my first version of the Gave Algorithm. I have detailed the steps in case you like it, and want to use it:

1.       Find the furthest depth point from the upper half of the camera depth data

2.       March left and right to find the points at which the ‘head’ depth data stops

3.       Now we know the width of the head, trace downwards to find the shoulder

4.       Once you have a shoulder coordinate, use that to align the Y vector of the head

5.       You now have a stable X and Y vector for head tracking (and Z of course)

6.       Scan all the depth between the ears of the face, down to the shoulder height

7.       Add all depth values together, weighting them as the coordinate moves left/right

8.       Do the same for top/bottom weighting them with a vertical multiplayer

9.       You are essentially using the nose and facial features to track the bulk of the head

10.   Happily, this bulk determines the general gaze direction of the face

11.   You have to enhance the depth around the nose to get better gaze tracking

I have included my entire source code to date for the two DBP commands you saw in the last blog so you can see how I access the depth and colour data, create 3D constructs and handle the interpretation of the depth information.  This current implementation is only good enough to determine which corner of the screen you are looking at, but I feel with more work this can be refined to provide almost pinpoint accurate gazing.

Interacting with Document

One thing I enjoyed when tinkering with the latest version was holding up a piece of paper, maybe with a sketch on it, and shout ‘scan’ in a firm voice.  Nothing happened of course, but I imaged what could happen. We still doodle on paper, or have some article or clipping during a meeting. It would be awesome if you could hold it up, bark a command, and the computer would turn it into a virtual item in the conference. Other attendees could then pick it up (copy it I guess), and once received could view it or print it during the call. It would be like fax but faster! I even thought of tying in your tablet to the conference call too, so when a document is shared, it instantly goes onto a tablet carousel so everyone who has a tablet can view the media. It could work in reverse too, so you could find a website or application, and then just wave the tablet in front of the camera, the camera would detect you are waving your tablet and instantly copy the contents of the tablet screen to the others in the meeting.  It was around this time I switched part of my brain off so I could finish up and record the video for your viewing pleasure.

Developer Tips

TIP 1 : Infrared gesture cameras and 6AM sun rise do not mix very well. As I was gluing Saturday and Sunday together, the sun’s rays blasted through the window and disintegrated my virtual me. Fortunately a wall helped a few hours later.  For accurate usage of the gesture camera, ensure you are not bathed in direct sunlight!

TIP 2 : If you think you can smooth out and tame the edges of your depth data, think again. I have this one about five hours of solid thought and tinkering, and I concluded that you can only get smoothing by substantially trimming the depth shape. As the edges of a shape leap from almost zero to full depth reading, it is very difficult to filter or accommodate it. In order to move on, I moved on, but I have a few more ideas and many more days to crack this one. The current fussy edges are not bad as such, but it is something you might associate with low quality and so I want to return to this. The fact is the depth data around the edges is very dodgy, and some serious edge cleaning techniques will need to be employed to overcome this feature of the hardware.

Next Time

Now I have the two main components running side by side, the 3D construction and the audio conferencing, next week should be a case of gluing them together in a tidy interface. One of the judges has thrown down the gauntlet that the app should support both Gesture Camera AND Ultrabook, so I am going to pretend the depth camera is ‘built’ into the Ultrabook and treat it as one device. As I am writing the app from scratch, my interface design will make full use of touch when touch makes sense and intuitive use of perception for everything else.

P.S. The judges’ video blog was a great idea and fun to watch!   Hope you all had a good time in Barcelona and managed to avoid getting run over by all those meals on wheels.


// DEPTH GLOBALS FOR NOW
 
PXCSmartPtr<PXCSession> session;
pxcStatus sts;
UtilCapture* pcapture = NULL;
UtilRender* pdepth_render = NULL;
UtilRender* puv_render = NULL;
UtilRender* pprj_render = NULL;
PXCCapture::VideoStream::ProfileInfo pcolor;
PXCCapture::VideoStream::ProfileInfo pdepth;
PXCCapture::VideoStream::DataDesc request;
pxcCHAR line[64];
PXCSmartPtr<PXCProjection> projection;
PXCSmartPtr<PXCImage> color2;   // the color image after projection
PXCPoint3DF32 *pos2d = 0;       // array of depth coordinates to be mapped onto color coordinates
PXCPointF32 *posc = 0;          // array of mapped color coordinates
pxcF32 dvalues[2];              // special depth values for saturated and low-confidence pixels
pxcUID prj_value;               // projection serializable identifier
 
DARKSDK_DLL void MakeObjectPercBlob ( int iID, float fSize, int fSize2, int fSize3 )
{
      // attempt to create a new object
      if ( !CreateNewObject ( iID, "percblob" ) )
            return;
 
      // setup general object data
      sMesh* pMesh = g_ObjectList [ iID ]->pFrame->pMesh;
 
      // Master dimensions
      float fWidth = fSize;
      float fHeight = fSize2;
      int gridwidth = 320;
      int gridheight = 240;
 
      // create memory
      DWORD dwVertexCount = gridwidth * gridheight * 6;
      if ( !SetupMeshFVFData ( pMesh, D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1, dwVertexCount, 0 ) )
      {
            RunTimeError ( RUNTIMEERROR_B3DMESHLOADFAILED );
            return;
      }
 
      // create a pure 320x240 grid of vertices
      DWORD dwVertIndex = 0;
      float fCenterX = fWidth/2;
      float fCenterY = fHeight/2;
      float fTileWidth = fWidth/gridwidth;
      float fTileHeight = fHeight/gridheight;
      float fUTileWidth = 1.0f/gridwidth;
      float fVTileHeight = 1.0f/gridheight;
      for ( int y=0; y<gridheight; y++ )
      {
            for ( int x=0; x<gridwidth; x++ )
            {
                  float u = (float)x/(float)gridwidth;
                  float v = (float)y/(float)gridheight;
                  float px, py, pu, pv;
                  for ( int p=0; p<6; p++ )
                  {
                        if ( p==0 ) { px = 0.0f; py = 0.0f; pu=0.0f; pv=0.0f; }
                        if ( p==1 ) { px = fTileWidth; py = 0.0f; pu=fUTileWidth; pv=0.0f; }
                        if ( p==2 ) { px = fTileWidth; py = fTileHeight; pu=fUTileWidth; pv=fVTileHeight; }
                        if ( p==3 ) { px = 0.0f; py = 0.0f; pu=0.0f; pv=0.0f; }
                        if ( p==4 ) { px = fTileWidth; py = fTileHeight; pu=fUTileWidth; pv=fVTileHeight; }
                        if ( p==5 ) { px = 0.0f; py = fTileHeight; pu=0.0f; pv=fVTileHeight; }
                        SetupStandardVertex ( pMesh->dwFVF, pMesh->pVertexData, dwVertIndex, px+(x*fTileWidth)-fCenterX, py+fCenterY-(y*fTileHeight), 0.0f,  0.0f,  0.0f,  1.0f, D3DCOLOR_ARGB ( 255, 0, 255, 255 ), pu+u, pv+v );
                        dwVertIndex++;
                  }
            }
      }
 
      // setup mesh drawing properties
      pMesh->iPrimitiveType   = D3DPT_TRIANGLELIST;
      pMesh->iDrawVertexCount = pMesh->dwVertexCount;
      pMesh->iDrawPrimitives  = pMesh->dwVertexCount / 3;
 
      // setup new object and introduce to buffers
      SetNewObjectFinalProperties ( iID, fSize );
 
      // give the object a default texture
      SetTexture ( iID, 0 );
 
      // DEPTH CAPTURE
 
    pxcStatus sts=PXCSession_Create(&session);
    if (sts<PXC_STATUS_NO_ERROR || session==NULL) {
        wprintf_s(L"Failed to create a sessionn");
        return;
    }
 
      UtilCmdLine cmdl(session);
 
      pcapture = new UtilCapture(session);
      for (std::list<PXCSizeU32>::iterator itr=cmdl.m_csize.begin();itr!=cmdl.m_csize.end();itr++)
            pcapture->SetFilter(PXCImage::IMAGE_TYPE_COLOR,*itr);
      if (cmdl.m_sdname) pcapture->SetFilter(cmdl.m_sdname);
   
    memset(&request, 0, sizeof(request));
    request.streams[0].format=PXCImage::COLOR_FORMAT_RGB32;
    request.streams[1].format=PXCImage::COLOR_FORMAT_DEPTH;
    sts = pcapture->LocateStreams (&request);
    if (sts<PXC_STATUS_NO_ERROR) {
        wprintf_s(L"Failed to locate video stream(s)n");
        return;
    }
    pcapture->QueryDevice()->SetProperty(PXCCapture::Device::PROPERTY_DEPTH_SMOOTHING,1);
 
    pcapture->QueryVideoStream(0)->QueryProfile(&pcolor);
    pcapture->QueryVideoStream(1)->QueryProfile(&pdepth);
 
    swprintf_s(line,sizeof(line)/sizeof(pxcCHAR),L"Depth %dx%d", pdepth.imageInfo.width, pdepth.imageInfo.height);
    pdepth_render = new UtilRender(line);
    swprintf_s(line,sizeof(line)/sizeof(pxcCHAR),L"UV %dx%d", pcolor.imageInfo.width, pcolor.imageInfo.height);
    puv_render = new UtilRender(line);
 
      sts=pcapture->QueryDevice()->QueryPropertyAsUID(PXCCapture::Device::PROPERTY_PROJECTION_SERIALIZABLE,&prj_value);
      if (sts>=PXC_STATUS_NO_ERROR)
      {
      pcapture->QueryDevice()->QueryProperty(PXCCapture::Device::PROPERTY_DEPTH_LOW_CONFIDENCE_VALUE,&dvalues[0]);
      pcapture->QueryDevice()->QueryProperty(PXCCapture::Device::PROPERTY_DEPTH_SATURATION_VALUE,&dvalues[1]);
 
        session->DynamicCast<PXCMetadata>()->CreateSerializable<PXCProjection>(prj_value, &projection);
        PXCSmartPtr<PXCAccelerator> accelerator;
        session->CreateAccelerator(&accelerator);
        accelerator->CreateImage(&pcolor.imageInfo,0,0,&color2);
 
          int npoints = pdepth.imageInfo.width*pdepth.imageInfo.height;
        pos2d=(PXCPoint3DF32 *)new PXCPoint3DF32[npoints];
        posc=(PXCPointF32 *)new PXCPointF32[npoints];
          for (unsigned int y=0,k=0;y<pdepth.imageInfo.height;y++)
            for (unsigned int x=0;x<pdepth.imageInfo.width;x++,k++)
                pos2d[k].x=x, pos2d[k].y=y;
    }
 
// CLEANUP LATER!
//    if (pcapture) delete pcapture;
//    if (pdepth_render) delete pdepth_render;
//    if (puv_render) delete puv_render;
//    if (pprj_render) delete pprj_render;
//    if (pos2d) delete [] pos2d;
//    if (posc) delete [] posc;
 
}
 
pxcU16 depthalpha[320][240];
//pxcU16 depthmemory[320][240];
D3DXVECTOR3 vHeadTrack[3];
 
DARKSDK_DLL void UpdateObjectPercBlob ( int iID, float fF, int iI1, int iI2 )
{
      int gridwidth = 320;
      int gridheight = 240;
 
    PXCSmartArray<PXCImage> images(2);
    PXCSmartSP sp;
    sts=pcapture->ReadStreamAsync(images,&sp);
      if (sts<PXC_STATUS_NO_ERROR) return;
    sts=sp->Synchronize();
    if (sts<PXC_STATUS_NO_ERROR) return;
    PXCImage::ImageData ddepth;
    images[1]->AcquireAccess(PXCImage::ACCESS_READ,&ddepth);
    int dwidth2=ddepth.pitches[0]/sizeof(pxcU16);
 
      bool bDebugMode = false;
 
    float *uvmap=(float*)ddepth.planes[2];
    if (uvmap)
      {
            // get color image
        PXCImage::ImageData dcolor;
        images[0]->AcquireAccess(PXCImage::ACCESS_READ_WRITE,PXCImage::COLOR_FORMAT_RGB32,&dcolor);
          int cwidth2=dcolor.pitches[0]/sizeof(pxcU32);
 
            // update the object
            sObject* pObject = g_ObjectList [ iID ];
            if ( pObject )
            {
                  // for all frames
                  for ( int iCurrentFrame = 0; iCurrentFrame < pObject->iFrameCount; iCurrentFrame++ )
                  {
                        // frame within object
                        sFrame* pFrame = pObject->ppFrameList [ iCurrentFrame ];
                        if ( pFrame )
                        {
                              // mesh within frame
                              sMesh* pMesh = pFrame->pMesh;
                              if ( pMesh )
                              {
                                    // get the offset map for the FVF
                                    sOffsetMap offsetMap;
                                    GetFVFOffsetMap ( pMesh, &offsetMap );
 
                                    // copy depth data
                                    for ( int y=0; y<gridheight; y++ )
                                    {
                                          for ( int x=0; x<gridwidth; x++ )
                                          {
                                                pxcU16 depthvalue = ((pxcU16*)ddepth.planes[0])[y*pdepth.imageInfo.width+x];
                                                //depthmemory[x][y]=depthvalue;
                                                depthalpha[x][y]=depthvalue;
                                          }
                                    }
 
                                    // clean edges
                                    for ( int y=0; y<gridheight; y++ )
                                    {
                                          for ( int x=0; x<gridwidth; x++ )
                                          {
                                                if ( depthalpha[x][y]==32001 || depthalpha[x][y]<100 )
                                                {
                                                      depthalpha[x][y] = 0;
                                                }
                                                else
                                                {
                                                      depthalpha[x][y] = 255;
                                                }
                                          }
                                    }
 
                                    // verts within mesh
                                    float fNearestToUser[3];
                                    fNearestToUser[0] = 0.0f;
                                    fNearestToUser[1] = 0.0f;
                                    fNearestToUser[2] = 0.0f;
                                    int iNearestX[3];
                                    iNearestX[0] = 0;
                                    iNearestX[1] = 0;
                                    iNearestX[2] = 0;
                                    int iNearestY[3];
                                    iNearestY[0] = 0;
                                    iNearestY[1] = 0;
                                    iNearestY[2] = 0;
                                    DWORD dwVertSize = pMesh->dwFVFSize;
                                    DWORD v=0;
                                    for ( int y=0; y<gridheight; y++ )
                                    {
                                          for ( int x=0; x<gridwidth; x++ )
                                          {
                                                float vecz = *((float*)(pMesh->pVertexData+(v*dwVertSize))+offsetMap.dwZ);
                                                int colx=(int)(uvmap[(y*dwidth2+x)*2+0]*pcolor.imageInfo.width+0.5f);
                                                int coly=(int)(uvmap[(y*dwidth2+x)*2+1]*pcolor.imageInfo.height+0.5f);
                                                pxcU32 colorvalue = ((pxcU32*)dcolor.planes[0])[coly*cwidth2+colx];
                                                pxcU16 iColShift = depthalpha[x][y];
                                                colorvalue = (colorvalue & 0x00FFFFFF) + (iColShift<<24);
                                                pxcU16 depthvalue = ((pxcU16*)ddepth.planes[0])[y*pdepth.imageInfo.width+x];
                                                if ( depthvalue>10 && depthvalue<1500 && x>40 && x<260 )
                                                {
                                                      vecz = 70.0f-((((float)depthvalue)/1500.0f)*70.0f);
                                                      if ( vecz > fNearestToUser[0] && y>20 && y<gridheight*0.35f )
                                                      {
                                                            // check above point if still solid
                                                            pxcU16 depthvalue = ((pxcU16*)ddepth.planes[0])[(y-20)*pdepth.imageInfo.width+x];
                                                            if ( depthvalue>10 && depthvalue<1500 && x>100 && x<220 )
                                                            {
                                                                  fNearestToUser[0] = vecz;
                                                                  iNearestX[0] = x;
                                                                  iNearestY[0] = y;
                                                            }
                                                      }
                                                      if ( vecz > fNearestToUser[1] && y>=gridheight*0.75f && x>100 && x<220 && y<220 )
                                                      {
                                                            // lower screen
                                                            fNearestToUser[1] = vecz;
                                                            iNearestX[1] = x;
                                                            iNearestY[1] = y;
                                                      }
                                                }
                                                else
                                                {
                                                      vecz = 0.0f;
                                                      colorvalue = 0;
                                                }
                                                for ( int p=0; p<6; p++ )
                                                {
                                                      *((float*)(pMesh->pVertexData+(v*dwVertSize))+offsetMap.dwZ) = vecz;
                                                      *((DWORD*)(pMesh->pVertexData+(v*dwVertSize))+offsetMap.dwDiffuse) = colorvalue;
                                                      v++;
                                                }
                                          }
                                    }
 
                                    // Once we have nearest from top quadrand NOSE/FOREHEAD (work out edges)
                                    int ty = iNearestY[0];
                                    int iLeftX = iNearestX[0];
                                    for (; iLeftX>0; iLeftX-- ) if ( depthalpha[iLeftX][ty]==0 ) break;
                                    int tv = (((ty*gridwidth)+iLeftX)*6);
                                    if ( bDebugMode )
                                    {
                                          for ( int tvo = 0; tvo<6; tvo++ )
                                          {
                                                *((float*)(pMesh->pVertexData+((tv+tvo)*dwVertSize))+offsetMap.dwZ) = 50.0f;
                                                *((DWORD*)(pMesh->pVertexData+((tv+tvo)*dwVertSize))+offsetMap.dwDiffuse) = (255<<24)+(255<<16)+(255<<8)+255;
                                          }
                                    }
                                    int iRightX = iNearestX[0];
                                    for (; iRightX<320; iRightX++ ) if ( depthalpha[iRightX][ty]==0 ) break;
                                    tv = (((ty*gridwidth)+iRightX)*6);
                                    if ( bDebugMode )
                                    {
                                          for ( int tvo = 0; tvo<6; tvo++ )
                                          {
                                                *((float*)(pMesh->pVertexData+((tv+tvo)*dwVertSize))+offsetMap.dwZ) = 50.0f;
                                                *((DWORD*)(pMesh->pVertexData+((tv+tvo)*dwVertSize))+offsetMap.dwDiffuse) = (255<<24)+(255<<16)+(255<<8)+255;
                                          }
                                    }
 
                                    // new head center X
                                    int headwidth = (iRightX-iLeftX);
                                    iNearestX[0] = iLeftX + headwidth/2;
 
                                    // find shoulders
                                    int iLeftShoulderX = iNearestX[0] - headwidth/1.35;
                                    int iLeftShoulderY = iNearestY[0] + 0;
                                    for (; iLeftShoulderY<238; iLeftShoulderY++ ) if ( depthalpha[iLeftShoulderX][iLeftShoulderY]!=0 ) break;
                                    tv = (((iLeftShoulderY*gridwidth)+iLeftShoulderX)*6);
                                    if ( bDebugMode )
                                    {
                                          for ( int tvo = 0; tvo<6; tvo++ )
                                          {
                                                *((float*)(pMesh->pVertexData+((tv+tvo)*dwVertSize))+offsetMap.dwZ) = 50.0f;
                                                *((DWORD*)(pMesh->pVertexData+((tv+tvo)*dwVertSize))+offsetMap.dwDiffuse) = (255<<24)+(255<<16)+(255<<8)+255;
                                          }
                                    }
                                    iNearestY[0] = iLeftShoulderY;
 
                                    // work out which way we are looking
                                    if ( iLeftShoulderY < 41 ) iLeftShoulderY=41;
                                    float fVertWeight = 0.0f;
                                    float fHorizWeight = 0.0f;
                                    int iShoulderCenterY = iLeftShoulderY-(40/2);
                                    for ( int iNoseScanY=iLeftShoulderY; iNoseScanY>iLeftShoulderY-40; iNoseScanY-- )
                                    {
                                          for ( int iNoseScanX=iLeftX+2; iNoseScanX<iRightX-2; iNoseScanX++ )
                                          {
                                                int tv = (((iNoseScanY*gridwidth)+iNoseScanX)*6);
                                                for ( int tvo = 0; tvo<6; tvo++ )
                                                {
                                                      float fDistFromNose = fNearestToUser[0] - *((float*)(pMesh->pVertexData+((tv+tvo)*dwVertSize))+offsetMap.dwZ);
                                                      int iR = 255 - ( (fDistFromNose*fDistFromNose*2) * 75.0f);
                                                      if ( iR < 0 ) iR=0;
                                                      int iG = 0;
                                                      int iB = 0;
                                                      float fMultiplier = (iNoseScanX-iNearestX[0])*3;
                                                      fHorizWeight = fHorizWeight + ((iR/255.0f)*fMultiplier);
                                                      if ( iNoseScanY < iLeftShoulderY-12 && iNoseScanY > iLeftShoulderY-34 )
                                                      {
                                                            if ( iNoseScanX > iLeftX+(headwidth/3) && iNoseScanX < iRightX-(headwidth/3) )
                                                            {
                                                                  // a subset of the whole area
                                                                  fMultiplier = (iNoseScanY-iShoulderCenterY)*3;
                                                                  fVertWeight = fVertWeight + ((iR/255.0f)*fMultiplier);
                                                            }
                                                      }
                                                      if ( bDebugMode )
                                                            *((DWORD*)(pMesh->pVertexData+((tv+tvo)*dwVertSize))+offsetMap.dwDiffuse) = (255<<24)+(iR<<16)+(iG<<8)+iB;
                                                }
                                          }
                                    }
                                    for ( int iNoseScanY=iLeftShoulderY; iNoseScanY>iLeftShoulderY-40; iNoseScanY-- )
                                    {
                                          for ( int iNoseScanX=iLeftX+2; iNoseScanX<iRightX-2; iNoseScanX++ )
                                          {
                                                int tv = (((iNoseScanY*gridwidth)+iNoseScanX)*6);
                                                for ( int tvo = 0; tvo<6; tvo++ )
                                                {
                                                      float fDistFromNose = fNearestToUser[0] - *((float*)(pMesh->pVertexData+((tv+tvo)*dwVertSize))+offsetMap.dwZ);
                                                      int iR = 0;
                                                      int iG = 128+(fHorizWeight/250.0f);
                                                      if ( iG < 0 ) iG=0;
                                                      if ( iG > 255 ) iG=255;
                                                      int iB = 128+(fVertWeight/200.0f);
                                                      if ( iB < 0 ) iB=0;
                                                      if ( iB > 255 ) iB=255;
//                                                    if ( bDebugMode )
//                                                          *((DWORD*)(pMesh->pVertexData+((tv+tvo)*dwVertSize))+offsetMap.dwDiffuse) = (255<<24)+(iR<<16)+(iG<<8)+iB;
                                                }
                                          }
                                    }
 
                                    // SHOW NEAREST
                                    for ( int n=0; n<3; n++ )
                                    {
                                          int nearv;
                                          float fNowAtX, fNowAtY, fNowAtZ;
                                          if ( n==2 )
                                          {
                                                fNowAtX = 100.0f+(fHorizWeight/100.0f);
                                                if ( fNowAtX>=0 ) fNowAtX = 100.0f;
                                                if ( fNowAtX<0 ) fNowAtX = -100.0f;
                                                fNowAtY = 30.0f+(fVertWeight/250.0f);
                                                if ( fNowAtY>=0 ) fNowAtY = 50.0f;
                                                if ( fNowAtY<0 ) fNowAtY = -35.0f;
                                                fNowAtZ = -15.0f;
                                          }
                                          else
                                          {
                                                nearv = (((iNearestY[n]*gridwidth)+iNearestX[n])*6);
                                                fNowAtX = *((float*)(pMesh->pVertexData+((nearv+0)*dwVertSize))+offsetMap.dwX);
                                                fNowAtY = *((float*)(pMesh->pVertexData+((nearv+0)*dwVertSize))+offsetMap.dwY);
                                                fNowAtZ = *((float*)(pMesh->pVertexData+((nearv+0)*dwVertSize))+offsetMap.dwZ);
                                          }
                                          if ( n==0 || (n==1 && fNowAtZ>32.0) || n==2 )
                                          {
                                                if ( n==0 ) fNowAtZ -= 10.0f;
                                                if ( n==1 ) fNowAtZ += 5.0f;
                                                if ( n==1 && fNowAtY<-20.0f ) fNowAtY = -20.0f;
                                                float fDistX = fNowAtX - vHeadTrack[n].x;
                                                float fDistY = fNowAtY - vHeadTrack[n].y;
                                                float fDistZ = fNowAtZ - vHeadTrack[n].z;
                                                if ( n==2 )
                                                {
                                                      vHeadTrack[n].x += fDistX/3.0f;
                                                      vHeadTrack[n].y += fDistY/3.0f;
                                                      vHeadTrack[n].z += fDistZ/3.0f;
                                                }
                                                else
                                                {
                                                      vHeadTrack[n].x += fDistX/20.0f;
                                                      vHeadTrack[n].y += fDistY/20.0f;
                                                      vHeadTrack[n].z += fDistZ/20.0f;
                                                }
                                                sObject* pTracker = g_ObjectList [ 2+n ];
                                                if ( pTracker )
                                                {
                                                      pTracker->position.vecPosition.x = vHeadTrack[n].x;
                                                      pTracker->position.vecPosition.y = vHeadTrack[n].y;
                                                      pTracker->position.vecPosition.z = vHeadTrack[n].z;
                                                }
                                                if ( n<2 )
                                                {
                                                      if ( bDebugMode )
                                                      {
                                                            for ( int nearvo = 0; nearvo<6; nearvo++ )
                                                            {
                                                                  *((float*)(pMesh->pVertexData+((nearv+nearvo)*dwVertSize))+offsetMap.dwZ) = 50.0f;
                                                                  *((DWORD*)(pMesh->pVertexData+((nearv+nearvo)*dwVertSize))+offsetMap.dwDiffuse) = (255<<24)+(255<<16)+(255<<8)+255;
                                                            }
                                                      }
                                                }
                                          }
                                    }
 
                                    // stitch vertices together if they are within a threshold
                                    float fThreshold = 3.5f;
                                    v=0;
                                    for ( int y=0; y<gridheight; y++ )
                                    {
                                          for ( int x=0; x<gridwidth; x++ )
                                          {
                                                for ( int p=0; p<6; p++ )
                                                {
                                                      // get this Z (top left corner)
                                                      float vecz = *((float*)(pMesh->pVertexData+(v*dwVertSize))+offsetMap.dwZ);
                                                      if ( p==2 )
                                                      {
                                                            if ( vecz > 1.0f )
                                                            {
                                                                  // top right
                                                                  int comparex = x+1;
                                                                  int comparey = y;
                                                                  if ( comparex>=2 && comparex<=gridwidth-2 )
                                                                  {
                                                                        if ( comparey>=2 && comparey<=gridheight-2 )
                                                                        {
                                                                              int comparev = (((comparey*gridwidth)+comparex)*6);
                                                                              int comparevo = 5;
                                                                              float fOldZ = *((float*)(pMesh->pVertexData+((comparev+comparevo)*dwVertSize))+offsetMap.dwZ);
                                                                              if ( fOldZ>1.0f && vecz-fOldZ<fThreshold )
                                                                                    *((float*)(pMesh->pVertexData+((comparev+comparevo)*dwVertSize))+offsetMap.dwZ) = vecz;                                                                 
                                                                        }
                                                                  }
 
                                                                  // bottom left
                                                                  comparex = x;
                                                                  comparey = y-1;
                                                                  if ( comparex>=2 && comparex<=gridwidth-2 )
                                                                  {
                                                                        if ( comparey>=2 && comparey<=gridheight-2 )
                                                                        {
                                                                              int comparev = (((comparey*gridwidth)+comparex)*6);
                                                                              int comparevo = 1;
                                                                              float fOldZ = *((float*)(pMesh->pVertexData+((comparev+comparevo)*dwVertSize))+offsetMap.dwZ);
                                                                              if ( fOldZ>1.0f && vecz-fOldZ<fThreshold )
                                                                                    *((float*)(pMesh->pVertexData+((comparev+comparevo)*dwVertSize))+offsetMap.dwZ) = vecz;
                                                                        }
                                                                  }
 
                                                                  // bottom right
                                                                  comparex = x+1;
                                                                  comparey = y-1;
                                                                  if ( comparex>=2 && comparex<=gridwidth-2 )
                                                                  {
                                                                        if ( comparey>=2 && comparey<=gridheight-2 )
                                                                        {
                                                                              int comparev = (((comparey*gridwidth)+comparex)*6);
                                                                              int comparevo = 0;
                                                                              float fOldZ = *((float*)(pMesh->pVertexData+((comparev+comparevo)*dwVertSize))+offsetMap.dwZ);
                                                                              if ( fOldZ>1.0f && vecz-fOldZ<fThreshold )
                                                                              {
                                                                                    *((float*)(pMesh->pVertexData+((comparev+comparevo)*dwVertSize))+offsetMap.dwZ) = vecz;
                                                                                    comparevo = 3;
                                                                                    *((float*)(pMesh->pVertexData+((comparev+comparevo)*dwVertSize))+offsetMap.dwZ) = vecz;
                                                                              }
                                                                        }
                                                                  }
                                                            }
                                                      }
 
                                                      // change alpha of vertex
                                                      //pxcU16 iColShift = vecz*4;
                                                      //int colorvalue = (*((DWORD*)(pMesh->pVertexData+(v*dwVertSize))+offsetMap.dwDiffuse) & 0x00FFFFFF) + (iColShift<<24);
                                                      //*((DWORD*)(pMesh->pVertexData+(v*dwVertSize))+offsetMap.dwDiffuse) = colorvalue + (iColShift<<24);
 
                                                      // next vertex
                                                      v++;
                                                }
                                          }
                                    }
 
                                    /*
                                    // clear normals
                                    v=0;
                                    for ( int y=0; y<gridheight; y++ )
                                    {
                                          for ( int x=0; x<gridwidth; x++ )
                                          {
                                                for ( int p=0; p<6; p++ )
                                                {
                                                      D3DXVECTOR3 vecNormal = D3DXVECTOR3(0,0,0);
                                                      *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+(v*dwVertSize))+offsetMap.dwNX) = vecNormal;
                                                      v++;
                                                }
                                          }
                                    }
 
                                    // update normals based on new face direction
                                    v=0;
                                    for ( int y=0; y<gridheight; y++ )
                                    {
                                          for ( int x=0; x<gridwidth; x++ )
                                          {
                                                for ( int p=0; p<2; p++ )
                                                {
                                                      D3DXVECTOR3 vec0 = *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+((v+0)*dwVertSize))+offsetMap.dwX);
                                                      D3DXVECTOR3 vec1 = *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+((v+1)*dwVertSize))+offsetMap.dwX);
                                                      D3DXVECTOR3 vec2 = *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+((v+2)*dwVertSize))+offsetMap.dwX);
                                                      D3DXVECTOR3 edge1 = ( vec1 ) - ( vec0 );
                                                      D3DXVECTOR3 edge2 = ( vec2 ) - ( vec0 );
                                                      D3DXVECTOR3 vecNormal;
                                                      D3DXVec3Cross     ( &vecNormal, &edge1, &edge2 );
                                                      D3DXVec3Normalize ( &vecNormal, &vecNormal );
                                                      *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+((v+0)*dwVertSize))+offsetMap.dwNX) += vecNormal/6.0f;
                                                      *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+((v+1)*dwVertSize))+offsetMap.dwNX) += vecNormal/6.0f;
                                                      *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+((v+2)*dwVertSize))+offsetMap.dwNX) += vecNormal/6.0f;
                                                      v+=3;
                                                }
                                          }
                                    }
                                    */
 
                                    /*
                                    // smooth out vertices based on it's face neihgbors
                                    WORD* pFacePtr = pMesh->pIndices;
                                    for ( int y=0; y<40; y++ )
                                    {
                                          for ( int x=0; x<45; x++ )
                                          {
                                                for ( int f=0; f<2; f++ )
                                                {
                                                      DWORD wFaceV0 = *(pFacePtr++);
                                                      DWORD wFaceV1 = *(pFacePtr++);
                                                      DWORD wFaceV2 = *(pFacePtr++);
                                                      D3DXVECTOR3 vec0 = *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+(wFaceV0*dwVertSize))+offsetMap.dwX);
                                                      D3DXVECTOR3 vec1 = *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+(wFaceV1*dwVertSize))+offsetMap.dwX);
                                                      D3DXVECTOR3 vec2 = *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+(wFaceV2*dwVertSize))+offsetMap.dwX);
                                                      D3DXVECTOR3 vecNew0 = vec0;
                                                      D3DXVECTOR3 vecNew1 = vec1;
                                                      D3DXVECTOR3 vecNew2 = vec2;
                                                      vecNew0.z = (vec0.z+vec1.z+vec2.z)/3.0f;
                                                      vecNew1.z = (vec0.z+vec1.z+vec2.z)/3.0f;
                                                      vecNew2.z = (vec0.z+vec1.z+vec2.z)/3.0f;
                                                      *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+(wFaceV0*dwVertSize))+offsetMap.dwX) = vecNew0;
                                                      *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+(wFaceV1*dwVertSize))+offsetMap.dwX) = vecNew1;
                                                      *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+(wFaceV2*dwVertSize))+offsetMap.dwX) = vecNew2;
                                                }
                                          }
                                    }
 
                                    // clear face normals
                                    pFacePtr = pMesh->pIndices;
                                    for ( int y=0; y<40; y++ )
                                    {
                                          for ( int x=0; x<45; x++ )
                                          {
                                                for ( int f=0; f<2; f++ )
                                                {
                                                      DWORD wFaceV0 = *(pFacePtr++);
                                                      DWORD wFaceV1 = *(pFacePtr++);
                                                      DWORD wFaceV2 = *(pFacePtr++);
                                                      D3DXVECTOR3 vecNormal = D3DXVECTOR3(0,0,0);
                                                      *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+(wFaceV2*dwVertSize))+offsetMap.dwNX) = vecNormal;
                                                }
                                          }
                                    }
 
                                    // update normals based on new face direction
                                    pFacePtr = pMesh->pIndices;
                                    for ( int y=0; y<40; y++ )
                                    {
                                          for ( int x=0; x<45; x++ )
                                          {
                                                for ( int f=0; f<2; f++ )
                                                {
                                                      DWORD wFaceV0 = *(pFacePtr++);
                                                      DWORD wFaceV1 = *(pFacePtr++);
                                                      DWORD wFaceV2 = *(pFacePtr++);
                                                      D3DXVECTOR3 vec0 = *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+(wFaceV0*dwVertSize))+offsetMap.dwX);
                                                      D3DXVECTOR3 vec1 = *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+(wFaceV1*dwVertSize))+offsetMap.dwX);
                                                      D3DXVECTOR3 vec2 = *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+(wFaceV2*dwVertSize))+offsetMap.dwX);
                                                      D3DXVECTOR3 edge1 = ( vec1 ) - ( vec0 );
                                                      D3DXVECTOR3 edge2 = ( vec2 ) - ( vec0 );
                                                      D3DXVECTOR3 vecNormal;
                                                      D3DXVec3Cross     ( &vecNormal, &edge1, &edge2 );
                                                      D3DXVec3Normalize ( &vecNormal, &vecNormal );
                                                      *(D3DXVECTOR3*)((float*)(pMesh->pVertexData+(wFaceV2*dwVertSize))+offsetMap.dwNX) += vecNormal/6.0f;
                                                }
                                          }
                                    }
                                    */
 
                                    // flag to update real object
                                    pMesh->bVBRefreshRequired = true;
                              }
                        }
                  }
        }
            images[0]->ReleaseAccess(&dcolor);
    }
    //if (uvmap) if (!puv_render->RenderFrame(images[0])) return;
 
      // DEPTH
      // only rlease now
    images[1]->ReleaseAccess(&ddepth);
 
}

For more complete information about compiler optimizations, see our Optimization Notice.

Comments

Peter O&#039;Hanlon's picture

Good job Lee, but wow - an even longer blog than mine. Impressive.

sixense_danny's picture

Wow very impressive Lee. I think accepting the data fluctuations and using them to your advantage as a hologram filter is the way to go. Great work!

Bob Duffy (Intel)'s picture

Nice job Lee, you went from Han Solo carbonite to Princess Leia Hologram. Video demo is very impressive

Lee Bamber's picture

Yes, if no-one bashes me over the head with a large stick, I get carried away and blog until my remote mouse runs out of power (actually happened in this case) ;)

The Game Creators (www.thegamecreators.com)
Dmitry Oganezov (Intel)'s picture

This is really cool! And thanks for the source code, a blog is always better with a code ;)

BTW, don't you mind if I apply syntax highlighter to the code in this blog?

Lee Bamber's picture

Thanks for the comments! My problem now is my next demo has to be even better ;) Dmitry, you are welcome to highlight the code text, and don't look too close, it's pretty hacky stuff!

The Game Creators (www.thegamecreators.com)
Rupam D.'s picture

Hi Lee, this is exactly what I envisioned and did communicated to you if you remember! Looks very nice indeed. Wow man! I wish I could have got the results you have achieved. Well done mate.

When you replace 'I' with 'We', even illness becomes wellness
Bob Duffy (Intel)'s picture

Only missing a red belt comment on this and we have the full set

Infrared5's picture

We'll also be thinking on the boundaries for the depth data, although we don't need them to be as smooth as you do. Still, I really think there has to be a way to get a good contour around that. If I see anything that may help, I'll send it your way.

-steff

Lee Bamber's picture

Thanks Steff! I have a few ideas, but they all require time away from finishing the app which is a pity really as I would have liked to crack it completely within the allotted time. I am sure a coder familiar with writing 2D pixel filters would crack it in 5 minutes :) Alas, I'm not that guy :( We'll get there!

The Game Creators (www.thegamecreators.com)