Source code: com/trapezium/chisel/cleaners/IFS_CoordRemover.java
1 /*
2 * @(#)IFS_CoordRemover.java
3 *
4 * Copyright (c) 1998-1999 by Trapezium Development LLC. All Rights Reserved.
5 *
6 * The information in this file is the property of Trapezium Development LLC
7 * and may be used only in accordance with the terms of the license granted
8 * by Trapezium.
9 *
10 */
11 package com.trapezium.chisel.cleaners;
12
13 import com.trapezium.parse.TokenTypes;
14 import com.trapezium.vrml.VrmlElement;
15 import com.trapezium.vrml.Value;
16 import com.trapezium.vrml.Scene;
17 import com.trapezium.vrml.node.Node;
18 import com.trapezium.vrml.node.UsageInfo;
19 import com.trapezium.vrml.node.IndexInfo;
20 import com.trapezium.vrml.node.DEFUSENode;
21 import com.trapezium.vrml.fields.Field;
22 import com.trapezium.vrml.fields.FieldValue;
23 import com.trapezium.vrml.fields.MFFieldValue;
24 import com.trapezium.vrml.node.generated.IndexedFaceSet;
25 import com.trapezium.chisel.*;
26
27 import java.util.BitSet;
28 import java.util.Hashtable;
29 import java.io.PrintStream;
30
31 //
32 // Base class for any optimizer that removes "coord" entries from an IndexedFaceSet
33 // or IndexedLineSet.
34 //
35 // Collects "coord", "coordIndex" ranges to optimize. Looks up any dependencies with
36 // normal/normalIndex, color/colorIndex, or texCoord/texCoordIndex. Replace range
37 // handles all these.
38 //
39 public class IFS_CoordRemover extends Optimizer {
40 static public int Coord = 0;
41 static public int CoordIndex = 1;
42 static public int TexCoord = 2;
43 static public int TexCoordIndex = 3;
44 static public int Color = 4;
45 static public int ColorIndex = 5;
46 static public int Normal = 6;
47 static public int NormalIndex = 7;
48
49 protected int numberCoordsRemoved = 0;
50 protected int numberTexCoordsRemoved = 0;
51 protected int numberNormalsRemoved = 0;
52 protected int numberColorsRemoved = 0;
53
54 public IFS_CoordRemover() {
55 super( "CoordinateOwner", "Removing unused values..." );
56 }
57
58 //
59 // Just records optimization for coord/coordIndex, plus any dependent nodes based
60 // on the IndexInfo associated with the node.
61 //
62 public void attemptOptimization( Node n ) {
63 Field coord = n.getField( "coord" );
64 Field coordIndex = n.getField( "coordIndex" );
65 if (( coord != null ) && ( coordIndex != null )) {
66 MFFieldValue mfv2 = (MFFieldValue)coordIndex.getFieldValue();
67 int coordIndexCount = 0;
68 if ( mfv2 != null ) {
69 coordIndexCount = mfv2.getRawValueCount();
70 if ( coordIndexCount == 0 ) {
71 return;
72 }
73 } else {
74 return;
75 }
76
77 FieldValue fv = coord.getFieldValue();
78 Node coordNode = (Node)fv.getChildAt( 0 );
79 if ( coordNode instanceof DEFUSENode ) {
80 DEFUSENode dun = (DEFUSENode)coordNode;
81 coordNode = dun.getNode();
82 }
83 if ( coordNode != null ) {
84 BitSet coordBits = null;
85 Scene s = (Scene)n.getScene();
86 if ( s != null ) {
87 Hashtable usageTable = s.getUsageTable();
88 UsageInfo ui = (UsageInfo)usageTable.get( coordNode );
89 if ( ui != null ) {
90 coordBits = ui.getUsageBits();
91 }
92 if ( coordBits == null ) {
93 return;
94 }
95 int numberFaces = 0;
96 if ( n instanceof IndexedFaceSet ) {
97 numberFaces = ((IndexedFaceSet)n).getNumberFaces();
98 }
99
100 Hashtable texCoordTable = s.getTexCoordTable();
101 IndexInfo texInfo = (IndexInfo)texCoordTable.get( n );
102 synchWithCoord( texInfo, TexCoord, coordBits, coordIndexCount, numberFaces );
103
104 Hashtable colorTable = s.getColorTable();
105 IndexInfo colorInfo = (IndexInfo)colorTable.get( n );
106 synchWithCoord( colorInfo, Color, coordBits, coordIndexCount, numberFaces );
107
108 Hashtable normalTable = s.getNormalTable();
109 IndexInfo normalInfo = (IndexInfo)normalTable.get( n );
110 synchWithCoord( normalInfo, Normal, coordBits, coordIndexCount, numberFaces );
111 }
112
113 Field coords = coordNode.getField( "point" );
114 FieldValue coordFieldValue = coords.getFieldValue();
115 int valcount = 0;
116 if ( coordFieldValue instanceof MFFieldValue ) {
117 MFFieldValue mfv = (MFFieldValue)coordFieldValue;
118 valcount = mfv.getRawValueCount();
119 }
120
121 if ( valcount > 0 ) {
122 replaceRange( coords.getFirstTokenOffset(),
123 coords.getLastTokenOffset(),
124 new CoordRemoveData( Coord, null, coordBits, coordIndexCount, 0 ));
125 replaceRange( coordIndex.getFirstTokenOffset(),
126 coordIndex.getLastTokenOffset(),
127 new CoordRemoveData( CoordIndex, null, coordBits, coordIndexCount, 0 ));
128 }
129 }
130 }
131 }
132
133 /** Synchronize IndexInfo with coord info.
134 *
135 * There are three types of synchronization:
136 * 1. values are synched with coord values. This means the "perVertex"
137 * setting is true, and there is no "..Index" field.
138 * 2. values are synched with index. This means there is an "..Index" field,
139 * with values in synch with the corresponding value node. There are two
140 * mutually exclusive further restrictions on the "..Index" field:
141 * a) the "..Index" field must be in synch with with the coord index,
142 * which happens when the "perVertex" setting is true.
143 * b) the "..Index" field must be in synch with the coord faces, which
144 * happens when the "perVertex" setting is false.
145 *
146 * @param ii IndexInfo contains relationship between value and index fields
147 * generic for texture, color, normal. The corresponding fields are:
148 * texCoord/texCoordIndex, color/colorIndex/colorPerVertex,
149 * normal/normalIndex/normalPerVertex.
150 * @param dataType the type of field being removed: Coord, CoordIndex,
151 * TexCoord, TexCoordIndex, Color, ColorIndex, Normal, NormalIndex.
152 * These constants follow the convention that the "Index" constant
153 * is one greater than the "Value" constant.
154 * @param coordBits BitSet indicating which coordinates are in use
155 * @param coordIndexCount count of the number of index values, needed for
156 * truncating index lists that are too long. This occurs if texCoordIndex
157 * has more values, or if colorIndex/normalIndex have more values and
158 * the colorPerVertex/normalPerVertex is true.
159 * @param numberFaces number of faces, which must match colorIndex/normalIndex
160 * if colorPerVertex/normalPerVertex is false.
161 */
162 void synchWithCoord( IndexInfo ii, int dataType, BitSet coordBits, int coordIndexCount, int numberFaces ) {
163 if ( ii == null ) {
164 return;
165 }
166 if ( coordBits == null ) {
167 return;
168 }
169 if ( ii.isValueSynchedWithCoord() ) {
170 Node valueNode = ii.getValueNode();
171 replaceRange( valueNode.getFirstTokenOffset(),
172 valueNode.getLastTokenOffset(),
173 new CoordRemoveData( dataType, ii, coordBits, coordIndexCount, 0 ));
174 } else if ( ii.isValueSynchedWithIndex() ) {
175 Node valueNode = ii.getValueNode();
176 MFFieldValue index = ii.getIndexValue();
177 coordBits = ii.getUsageBits();
178 if ( coordBits != null ) {
179 replaceRange( valueNode.getFirstTokenOffset(),
180 valueNode.getLastTokenOffset(),
181 new CoordRemoveData( dataType, ii, coordBits, coordIndexCount, 0 ));
182 if ( ii.isPerVertex() ) {
183 numberFaces = 0;
184 }
185 replaceRange( index.getFirstTokenOffset(), index.getLastTokenOffset(),
186 new CoordRemoveData( dataType+1, ii, coordBits, coordIndexCount, numberFaces ));
187 }
188 }
189 }
190
191 // RangeReplacer calls this when it has a range of tokens to replace
192 public void optimize( TokenPrinter tp, Object param, int startTokenOffset, int endTokenOffset ) {
193 CoordRemoveData cmd = (CoordRemoveData)param;
194 if ( cmd.isCoord() ) {
195 removeCoord( tp, cmd, startTokenOffset, endTokenOffset );
196 } else {
197 removeIndex( tp, cmd, startTokenOffset, endTokenOffset );
198 }
199 }
200
201 /** remove coord, texCoord, color, or normal entries based on the usage bitset */
202 void removeCoord( TokenPrinter tp, CoordRemoveData cmd, int startTokenOffset, int endTokenOffset ) {
203 BitSet keep = cmd.getCoordsToKeep();
204 int unitCount = cmd.getUnitCount();
205 int scannerOffset = startTokenOffset;
206 int idx = 0;
207 boolean hitEnd = false; // only useful in degenerate cases
208 while ( scannerOffset != -1 ) {
209 if ( dataSource.isNumber( scannerOffset )) {
210 if ( keep.get( idx )) {
211 for ( int i = 0; i < unitCount; i++ ) {
212 tp.print( dataSource, scannerOffset );
213 scannerOffset = dataSource.getNextToken( scannerOffset );
214 if ( scannerOffset == endTokenOffset ) hitEnd = true;
215 }
216 } else {
217 for ( int i = 0; i < unitCount; i++ ) {
218 scannerOffset = dataSource.getNextToken( scannerOffset );
219 if ( scannerOffset == endTokenOffset ) hitEnd = true;
220 }
221 numberCoordsRemoved++;
222 }
223 idx++;
224 } else {
225 tp.print( dataSource, scannerOffset );
226 if ( scannerOffset == endTokenOffset ) {
227 break;
228 }
229 scannerOffset = dataSource.getNextToken( scannerOffset );
230 }
231 if ( hitEnd ) {
232 tp.print( dataSource, scannerOffset );
233 break;
234 }
235 }
236 }
237
238 /** Remove index values based on CoordRemoveData parameter.
239 *
240 * There are three ways index values get removed. For coord/coordIndex,
241 * texCoord/texCoordIndex, color/colorIndex, normal/normalIndex, any removed
242 * coord affects the index. In this case we create an array to adjust all the
243 * offsets.
244 *
245 * The second way index values may be affected is in the case of color/colorIndex
246 * and normal/normalIndex when perVertex is true. In this case, there is a one
247 * to one correspondence between the index and the coord.
248 *
249 * The third way is when perVertex is false, and there are faces involved. In this
250 * case, the number of index values must match the number of faces. If there are
251 * more index values than faces, we remove the additional index values.
252 */
253 void removeIndex( TokenPrinter tp, CoordRemoveData cmd, int startTokenOffset, int endTokenOffset ) {
254 BitSet keep = cmd.getCoordsToKeep();
255 int keepSize = keep.size();
256 int[] offsets = new int[ keepSize ];
257 int idx = 0;
258 for ( int i = 0; i < keepSize; i++ ) {
259 offsets[i] = idx;
260 if ( keep.get( i )) {
261 idx++;
262 }
263 }
264 adjustIndex( tp, offsets, startTokenOffset, endTokenOffset, keepSize, cmd.getIndexLimit() );
265 }
266
267 /** Rewrite the index field, performing value adjustment along the way.
268 * Also limits the index field size to the maximum allowed by coordIndex
269 */
270 void adjustIndex( TokenPrinter tp, int[] offsets, int scannerOffset, int endTokenOffset, int keepSize, int indexLimit ) {
271 int indexCount = 0;
272 while ( scannerOffset != -1 ) {
273 int type = dataSource.getType( scannerOffset );
274 if ( type == TokenTypes.NumberToken ) {
275 // indexLimit of 0, temporary until face count limit implemented
276 if (( indexLimit == 0 ) || ( indexCount < indexLimit )) {
277 int ival = dataSource.getIntValue( scannerOffset );
278 if ( ival == -1 ) {
279 tp.print( dataSource, scannerOffset, TokenTypes.NumberToken );
280 } else {
281 if ( ival >= keepSize ) {
282 // out of range value, print it
283 tp.print( dataSource, scannerOffset );
284 System.out.println( "Index value at " + scannerOffset + " value is " + ival + " out of range, max value is " + keepSize );
285 } else {
286 tp.print( offsets[ ival ] );
287 }
288 }
289 indexCount++;
290 }
291 } else {
292 tp.print( dataSource, scannerOffset, type );
293 }
294 if ( scannerOffset == endTokenOffset ) {
295 break;
296 }
297 scannerOffset = dataSource.getNextToken( scannerOffset );
298 }
299 }
300
301 public void summarize( PrintStream ps ) {
302 if ( numberCoordsRemoved == 0 ) {
303 ps.println( "Did not remove any coordinates." );
304 } else if ( numberCoordsRemoved == 1 ) {
305 ps.println( "Removed 1 coordinate." );
306 } else {
307 ps.println( "Removed " + numberCoordsRemoved + " coordinates." );
308 }
309 }
310 }
311
312