A tool for visualising particle distributions
March 18th, 2007 by Andy Nicholas - Viewed 8692 times - Popularity: 27% [?]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:
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | 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 member 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++; } } |
Popularity: 27% [?]




Ahh hummmm… what else can I say… you’re a genius.
What I’d probably do using this code is gather all initial attribute values and put them in a array (thus making sure they’re not updated each frame) so what you get is the birth state of all particles.
Otherwise it’s just a great tool to visualize what’s going on in your sim!
Thank you!
P/s: There are small typos with all