Home » XSI

A tool for visualising particle distributions in XSI

18 March 2007 6,757 views No Comment

Some of you may remember the Histogram plugin that I wrote for XSI’s rendertree. It’s a tool for visualising exactly what is happening in your shading networks and can be invaluable when trying to find out why your shader is behaving unexpectedly.

Since the Histogram display window is just a COM application launched by the equivalent mental ray shader, there’s nothing stopping you using the Histogram display tool for other uses. Indeed, I included in the original download example source scripts to demonstrate how to drive the COM interface.

Vince Fortin has asked me about whether it’s possible to get Histogram to show distributions for particle systems. So at his request (thanks for reminding me Vince!) I’m posting some example code to show you how to do it.

First though, here’s a quick screen shot of the sort of information it can show:

Histogram for particles

Here I’m using Histogram to show the distribution of the X, Y, and Z positions of the particles. I’ve also got it to display the particle age too. Note that the number of samples (1853) shown at the bottom, actually corresponds to the number of particles.

The JScript code below features a reusable object called HistogramData. This object has a member function called ProcessSample() which will take a sample value and take care of the histogram construction for you. You then push the curHist member to the Histogram COM application for display.

The first function in the code is a script callback for a particle event. To make it work, you just need to add a particle event to a PType and create an OnEveryFrame event with the trigger value set to 0 so that the script gets called every frame. You can then control the execution of the Histogram display inside the script by changing the trigger frame number.

You can download and find out more about Histogram by following this link:

www.andynicholas.com -> Histogram

Please feel free to modify this code for your own usage.

var HISTOGRAM_RES = 256; 
var INV_HISTOGRAM_RES = 0.00390625; 
var HISTOGRAM_ARRSIZE = 257;      

function OnEveryFrame(inParticleCloud, inTriggerParticleIndices, inSimFrame) 
{ 
    //Frame 99 is when the distribution will be viewed, and 
    //you can change it to whatever you want.      

    //Note that if you persist the histogram data objects, there's no 
    //reason you can't gather distributions across multiple frames.      

    //To make things a little easier to adjust, you may also want to 
    //create a custom property in the scene to tie this frame value to.      

    if(inSimFrame==99) 
    { 
        //Initialise the data 
        var histx = new HistogramData(); 
        var histy = new HistogramData(); 
        var histz = new HistogramData(); 
        var histage = new HistogramData();      

        //This is where we store the data whose distribution we want to look at. 
        //In this case, we're observing position and age.      

        var particles = inParticleCloud.particles; 
        var num_particles = particles.count; 
        for(var i=0;i<num_particles;++i) 
        { 
            var particle = particles(i); 
            histx.ProcessSample(particle.position.x); 
            histy.ProcessSample(particle.position.y); 
            histz.ProcessSample(particle.position.z); 
            histage.ProcessSample(particle.age); 
        }      

        //Display the data 
        var histWindow = new ActiveXObject("HistogramDisplay.Application"); 
        histWindow.SetHistogram(histx.curHist,"Pos.X", 0, histx.min, histx.max); 
        histWindow.SetHistogram(histy.curHist,"Pos.Y", 1, histy.min, histy.max); 
        histWindow.SetHistogram(histz.curHist,"Pos.Z", 2, histz.min, histz.max); 
        histWindow.SetHistogram(histage.curHist,"Age", 3, histage.min, histage.max);      

        histWindow.SetHistogramColor(0, 80, 0, 0); 
        histWindow.SetHistogramColor(1, 0, 80, 0); 
        histWindow.SetHistogramColor(2, 0, 0, 80); 
        histWindow.SetHistogramColor(3, 0, 0, 0);      

        histWindow.SetNumToDisplay(4); 
        histWindow.UpdateWindow(); 
    } 
}      

//Data object representing histogram data 
//and contains all important function called ProcessSample()      

function HistogramData() 
{ 
    this.min=0; 
    this.max=0; 
    this.samples=0;      

    this.curHist = new Array(HISTOGRAM_ARRSIZE); 
    this.otherHist = new Array(HISTOGRAM_ARRSIZE);      

    for(var i=0;i<HISTOGRAM_ARRSIZE;++i) 
    { 
        this.curHist[i]=0; 
        this.otherHist[i]=0; 
    }      

    this.ProcessSample = function(sample) 
    { 
        //This function does all the hard work      

        //It is a direct port from C++ of the functionality 
        //of the original Histogram rendertree shader      

        var oldMin = this.min; 
        var oldMax = this.max; 
        var oldBucketSize = (oldMax-oldMin)*INV_HISTOGRAM_RES;      

        var  scaleChanged=false;      

        if(this.samples==0) 
        { 
            this.min = sample; 
            this.max = sample; 
            this.samples++; 
            return; 
        }      

        if(sample<this.min) 
        { 
            this.min = sample; 
            scaleChanged=true; 
        } 
        else if(sample>this.max) 
        { 
            this.max = sample; 
            scaleChanged=true; 
        }      

        if(this.samples==1) 
        { 
            this.curHist[0]++; 
            this.curHist[HISTOGRAM_RES]++; 
            this.samples++; 
            return; 
        }      

        var bucketSize = (this.max-this.min)*INV_HISTOGRAM_RES;      

        var rangeRatio = HISTOGRAM_RES/(this.max-this.min); 
        if((this.max-this.min)==0) rangeRatio=0;      

        if(scaleChanged) 
        { 
            //Swap histograms 
            var tempHist; 
            tempHist = this.curHist; 
            this.curHist = this.otherHist; 
            this.otherHist = tempHist;      

            //Copy and rescale the histogram according to the new sample and resample 
            for(var i=0;i<HISTOGRAM_ARRSIZE;i++) 
            { 
                //Calculate sample value 
                var value = oldMin + oldBucketSize*i;      

                //Calculate new bucket 
                var newBucket = Math.floor((value - this.min)*rangeRatio);                  

                if(newBucket<0) newBucket=0; 
                if(newBucket>=HISTOGRAM_ARRSIZE) newBucket=HISTOGRAM_ARRSIZE-1;      

                this.curHist[newBucket]+=this.otherHist[i]; 
                this.otherHist[i]=0; 
            } 
        }      

        //Add sample 
        var inputBucket = Math.floor((sample - this.min)*rangeRatio); 
        if(inputBucket<0) inputBucket=0; 
        if(inputBucket>=HISTOGRAM_ARRSIZE) inputBucket=HISTOGRAM_ARRSIZE-1; 
        this.curHist[inputBucket]++;      

        this.samples++; 
    } 
}
1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...

Leave your response!

Add your comment below, or trackback from your own site. You can also subscribe to these comments via RSS.

You can use these tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This is a Gravatar-enabled weblog. To get your own globally-recognized-avatar, please register at Gravatar.