Object oriented programming, once again.

May 20th, 2008 by Helge Mathee - Viewed 11590 times -




So the keen folks at the german XSI forum (http://www.xsiforum.de) discussed the topic of generating separate objects out of all of the polygon islands making up a polygonmesh. Even though this doesn’t seem to be a too tricky scripting problem, using commands to achieve this split-up can slow down XSI quite alot, especially if the source mesh contains a huge amount of polygon islands. Therefore I tried to come up with a object oriented solution, and here it is.

I put some effort into commenting the script accordingly, so that it may be used for learning purposes.

cheers!

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
// query the geometry data
var obj = selection(0);
var geo = obj.Activeprimitive.Geometry;
var polygons = geo.Polygons;
 
// create an array to track which polygon has been visited
var polygonVisited = [];
var polygonsLeft = polygons.Count;
 
// fill up the array to say: "no polygon has been visited yet"
for(var i=0;i<polygons .count;i++)
{
	polygonVisited[i] = false;
}
 
// create some helper variables
var clusterIndices = [];
var currentRow = [];
 
// log the start of the process
logmessage("Starting with "+polygonsLeft+" polygons...");
 
// loop until there are no polygons left
// or there are still items in the cluster array
while(polygonsLeft > 0 || clusterIndices.length > 0)
{
	// if there are no items in the next row...
	if(currentRow.length == 0)
	{
		// if there are items in the cluster index array
		if(clusterIndices.length > 0)
		{
			// log the creation of the cluster
			logmessage("Creating cluster with "+clusterIndices.length+" elements...");
 
			// create a new mesh containing all of these polygons
			// first, create an array containing all pointindices
			// and two additional point hash arrays
			// of which one is used to determine if a point is already used
			// and the second one is used to map the original index to the new one
			var pointIndices = [];
			var pointUsed = [];
			var pointNewIndex = [];
 
			for(var i=0;i<clusterIndices.length;i++)
			{
				var poly = polygons.item(clusterIndices[i]);
				var pointIndexArray = poly.points.indexArray.toArray(); //(VBS to JScript conversion)
 
				// loop through all points and check if it they are used
				for(var j=0;j<pointIndexArray.length;j++)
				{
					var index = pointIndexArray[j];
					if(!pointUsed[index])
					{
						pointNewIndex[index] = pointIndices.length;
						pointIndices.push(index);
						pointUsed[index] = true;	
					}
				}
			}
 
			// now, create the array of the positions of all points of this island
			var islandPos = [];
			for(var i=0;i<pointIndices.length;i++)
			{
				var point = geo.points.item(pointIndices[i]);
				islandPos.push(point.position.x);
				islandPos.push(point.position.y);
				islandPos.push(point.position.z);
			}
 
			// now we need to create the description of the polygons
			var islandPoly = [];
			// for that we loop over the polygons of the island again
			// and record their pointindices, well, actually the remapped
			// indices (given by the pointNewIndex hash array)
			for(var i=0;i<clusterIndices.length;i++)
			{
				var poly = polygons.item(clusterIndices[i]);
				var pointIndexArray = poly.points.indexArray.toArray(); //(VBS to JScript conversion)
 
				// first add the number of points of this polygon 
				// to this polygon's description
				islandPoly.push(pointIndexArray.length);
 
				// now add the remapped index for each point to the description
				for(var j=0;j<pointIndexArray.length;j++)
				{
					var index = pointIndexArray[j];
					islandPoly.push(pointNewIndex[index]);
				}
			}
 
			obj.Parent.AddPolygonMesh(islandPos,islandPoly,obj.name+"_island");
			clusterIndices = [];
 
			// if there are no polyons left we can stop now
			if(polygonsLeft == 0)
				break;
		}
 
		// loop over the polygons and try to find 
		// one which hasn't been visited yet
		for(var i=0;i<polygons.Count;i++)
		{
			if(polygonVisited[i] == false)
			{
				currentRow.push(i);
				clusterIndices.push(i);
				polygonVisited[i] = true;
				polygonsLeft--;
				break;
			}
		}
	}
 
	// define the nextrow after the current row
	var nextRow = [];
 
	// loop over all items in the current row
	for(var i=0;i<currentRow.length;i++)
	{
		var polygon = polygons.item(currentRow[i]);
		var neighbors = polygon.NeighborPolygons();
 
		// loop through all of the neighbors and add them to the nextRow
		for(var j=0;j<neighbors.count;j++)
		{
			// check if the neighbor has been
			// visited yet
			var neighbor = neighbors.item(j).index;
			if(polygonVisited[neighbor]==false)
			{
				// add the neighbor to the next row
				nextRow.push(neighbor);
				clusterIndices.push(neighbor);
				polygonVisited[neighbor] = true;
				polygonsLeft--;
				break;
			}
		}
	}
 
	currentRow = nextRow;
}
 
selection.SetAsText(obj.name+"_island*");

4 Responses to “Object oriented programming, once again.”

  1. Cool stuff as usual :-)

    Just wondered if you had any performance stats? Not only to see how fast it runs on a dense mesh, but also to see how it compares to writing a similar script with commands?

    Cheers

    Andy

  2. mab says:

    Hey Helge,

    If you ever port this to C++ to get better performance, you should consider using CGeometryAccessor instead of the geometry API. CGeometryAccessor is more scalable and much faster to access the geometry data than the geometry API which is a pain when dealing with very large meshes.

    -mab

  3. [...] More >>PHP Object-Oriented SolutionsSome Interesting BlogsRecruitment to PHP DEVELOPER | WalkinFeedSoftimage Blog » Blog Archive » Object oriented programming, once again.A gentle introduction to the 5th Polymath project « The Math Less TraveledAlley Tramp/Over [...]

  4. Goggles says:

    Hey Helge – nice script, thanks mate. If you want to make it work on linux, you need to add the following at the top of the script, xsi linux does otherwise not support the push method on arrays :

    // @cc_on
    // @if (@_jscript_version <= 5.5)
    var push = function(){
    for( var i = 0; arguments[ i ] != null; i++ )
    this[this.length++] = arguments[ i ];
    return( this );
    }
    Array.prototype.push = push;
    // @end

    Also note that it seems (on linux / firefox at least) that the symbols are replaced by ‘>’ and ‘<’ in your script above, so I had to do a quick search and replace before I got it working.

    Thanks again.