A tool for visualising particle distributions

March 18th, 2007 by Andy Nicholas - Viewed 16002 times -




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.

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++;
}
}

One Response to “A tool for visualising particle distributions”

  1. Vince Fortin says:

    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