Search this blog

07 July, 2011

Little tool for debugging with Processing

Ok, this is very rough. But it's fun to play with and it won't take much effort to turn into a real application. The idea is to use processing to import data from Pix or dumped from your game about your framebuffer, and reconstruct a point cloud from it (we need the depth and some camera parameters...). 


Having a point cloud makes easy to navigate in the scene, but it also makes possible to visualize data in a convenient way, i.e. the sampling pattern of a SSAO effect or display lines for our normals and so on.





This is a stupid test I did in half an hour with Processing using Java in Eclipse, as described here: http://c0de517e.blogspot.com/2011/05/edit-and-continue-is-fun.html. I plan to expand this in a functional "inspector", capable of displaying values from the game but also, by saving images layers with different variables, debugging visually problems in the shaders (Pix) and in memory (a VS plugin? maybe I'm going too far).


Here is the source:

import processing.core.PApplet;
import processing.core.PImage;
import processing.core.PVector;


public class FrameBufferDebugger extends PApplet 
{
PVector deproject(int x, int y, float depth)
{
// Deprojecting from z-buffer to view-space, this is specific to a given renderer  
float deprojectX = 0.28739804f;
float deprojectY = 0.161661401f;
float deprojectZ = -1.0002501f;
float deprojectW = 0.500125051f;

float viewDepth = deprojectW / (1.f - depth + deprojectZ);
float xf = ((float)x/(float)cloudWidth) * 2.f - 1.f;
float yf = ((float)y/(float)cloudHeight) * 2.f - 1.f;
xf = xf*deprojectX*viewDepth;
yf = -yf*deprojectY*viewDepth;

return new PVector(xf, yf, -viewDepth);
}

// Out point cloud
int cloudWidth;
int cloudHeight;
PVector cloud[];
int cloudAttrib0[];
PImage cloudAttrib1;

PVector boundingMin, boundingMax;

    public void setup()
    {
        size(800,600,P3D);
        
        // Simple parser for a x,y,depth,stencil CSV file (generated by Xbox Pix)
        String lines[] = loadStrings("depth.csv");
        String[] firstLine = splitTokens(lines[lines.length-1], ", ");
        cloudWidth = Integer.parseInt(firstLine[0])+1;
        cloudHeight = Integer.parseInt(firstLine[1])+1;
        
        boundingMin = new PVector(Float.MAX_VALUE,Float.MAX_VALUE,Float.MAX_VALUE);
        boundingMax = new PVector(-Float.MAX_VALUE,-Float.MAX_VALUE,-Float.MAX_VALUE);
        
        cloud = new PVector[cloudWidth*cloudHeight];
        cloudAttrib0 = new int[cloudWidth*cloudHeight];
        for(int i=1; i < lines.length; i++)
        {
        String[] splitLine = splitTokens(lines[i], ", ");
            int x = Integer.parseInt(splitLine[0]);
            int y = Integer.parseInt(splitLine[1]);
            float d = Float.parseFloat(splitLine[2]);
            int offset = y*cloudWidth+x;
            cloudAttrib0[offset] = Integer.parseInt(splitLine[3]);
            if(d!=0.f)
            {
            PVector pnt = deproject(x, y, d);
            cloud[offset] = pnt;
            
            boundingMin.x = Math.min(boundingMin.x, pnt.x); boundingMax.x = Math.max(boundingMax.x, pnt.x);
            boundingMin.y = Math.min(boundingMin.y, pnt.y); boundingMax.y = Math.max(boundingMax.y, pnt.y);
            boundingMin.z = Math.min(boundingMin.z, pnt.z); boundingMax.z = Math.max(boundingMax.z, pnt.z);
            }
        }
        
        cloudAttrib1 = loadImage("normals.jpg"); // It's not a smart idea to use a jpg here...        
    }
    
    float cam1 = 3.14f;
    float cam2 = 0.f;
    float camR = 0.f;
    PVector cameraCenter = new PVector();
    
    public void mouseDragged()
    { // Yeah... this "camera" sucks
    if(mouseButton == LEFT)
    {
    cam1 += (pmouseX-mouseX)*0.01f;
    cam2 += (pmouseY-mouseY)*0.01f;
    }
    if(mouseButton == RIGHT)
    {
    cameraCenter.x += (pmouseX-mouseX)*0.1f;
    cameraCenter.y += (pmouseY-mouseY)*0.1f;
    }
    if(mouseButton == CENTER)
    {
    float disp = ((pmouseX-mouseX)+(pmouseY-mouseY))*0.1f; 
    cameraCenter.z += disp;
    }
    }
    
    public void draw()
    {
    background(255);


    PVector boundingCenter = PVector.mult(PVector.add(boundingMax,boundingMin),0.5f);
        PVector boundingExtent = PVector.sub(boundingMax,boundingMin);
   
    if(camR == 0.f) 
    {
    camR = 10.f;
    cameraCenter = new PVector(boundingCenter.x, boundingCenter.y, boundingMin.z);
    }
   
    camera(
    cameraCenter.x + (float)(Math.sin(cam1)*Math.cos(cam2)*camR),
    cameraCenter.y + (float)(Math.sin(cam1)*Math.sin(cam2)*camR),
    cameraCenter.z + (float)(Math.cos(cam1)*camR), 
    cameraCenter.x, cameraCenter.y, cameraCenter.z,
    0,1,0
    );


    pushMatrix();
        translate(cameraCenter.x,cameraCenter.y,cameraCenter.z);
        fill(128,0,0,255);
        box(0.1f);
        popMatrix();
   
        for(int x=0; x < cloudWidth; x+=4)
        for(int y=0; y < cloudHeight; y+=4)
        {
        int offset = y*cloudWidth+x;
        if(cloud[offset] != null)
        {
        //stroke(cloudAttrib0[offset]);
        stroke(cloudAttrib1.get(x,y));
        point(cloud[offset].x, cloud[offset].y, cloud[offset].z);
        }
        }
        
        noStroke();
        fill(255,0,0,32);
    }
}

No comments: