-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
Copy pathPrimitiveLoadPlan.js
280 lines (249 loc) · 7.95 KB
/
PrimitiveLoadPlan.js
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
import Check from "../Core/Check.js";
import ComponentDatatype from "../Core/ComponentDatatype.js";
import defined from "../Core/defined.js";
import IndexDatatype from "../Core/IndexDatatype.js";
import Buffer from "../Renderer/Buffer.js";
import BufferUsage from "../Renderer/BufferUsage.js";
import AttributeType from "./AttributeType.js";
import ModelComponents from "./ModelComponents.js";
import PrimitiveOutlineGenerator from "./Model/PrimitiveOutlineGenerator.js";
/**
* Simple struct for tracking whether an attribute will be loaded as a buffer
* or typed array after post-processing.
*
* @alias PrimitiveLoadPlan.AttributeLoadPlan
* @constructor
*
* @param {ModelComponents.Attribute} attribute The attribute to be updated
*
* @private
*/
function AttributeLoadPlan(attribute) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object("attribute", attribute);
//>>includeEnd('debug');
/**
* The attribute to track.
*
* @type {ModelComponents.Attribute}
* @readonly
* @private
*/
this.attribute = attribute;
/**
* Whether the attribute will be loaded as a GPU buffer by the time
* {@link PrimitiveLoadPlan#postProcess} is finished.
*
* @type {boolean}
* @private
*/
this.loadBuffer = false;
/**
* Whether the attribute will be loaded as a packed typed array by the time
* {@link PrimitiveLoadPlan#postProcess} is finished.
*
* @type {boolean}
* @private
*/
this.loadTypedArray = false;
}
/**
* Simple struct for tracking whether an index buffer will be loaded as a buffer
* or typed array after post-processing.
*
* @alias PrimitiveLoadPlan.IndicesLoadPlan
* @constructor
*
* @param {ModelComponents.Indices} indices The indices to be updated
*
* @private
*/
function IndicesLoadPlan(indices) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object("indices", indices);
//>>includeEnd('debug');
/**
* The indices to track.
*
* @type {ModelComponents.Indices}
* @readonly
* @private
*/
this.indices = indices;
/**
* Whether the indices will be loaded as a GPU buffer by the time
* {@link PrimitiveLoadPlan#postProcess} is finished.
*
* @type {boolean}
* @private
*/
this.loadBuffer = false;
/**
* Whether the indices will be loaded as a typed array copy of the GPU
* buffer by the time {@link PrimitiveLoadPlan#postProcess} is finished.
*
* @type {boolean}
* @private
*/
this.loadTypedArray = false;
}
/**
* Primitives may need post-processing steps after their attributes and indices
* have loaded, such as generating outlines for the CESIUM_primitive_outline glTF
* extension. This object tracks what indices and attributes need to be
* post-processed.
*
* @alias PrimitiveLoadPlan
* @constructor
*
* @param {ModelComponents.Primitive} primitive The primitive to track
*
* @private
*/
function PrimitiveLoadPlan(primitive) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object("primitive", primitive);
//>>includeEnd('debug');
/**
* The primitive to track.
*
* @type {ModelComponents.Primitive}
* @readonly
* @private
*/
this.primitive = primitive;
/**
* A flat list of attributes that need to be post-processed. This includes
* both regular attributes and morph target attributes.
*
* @type {PrimitiveLoadPlan.AttributeLoadPlan[]}
* @private
*/
this.attributePlans = [];
/**
* Information about the triangle indices that need to be post-processed,
* if they exist.
*
* @type {PrimitiveLoadPlan.IndicesLoadPlan}
* @private
*/
this.indicesPlan = undefined;
/**
* Set this true to indicate that the primitive has the
* CESIUM_primitive_outline extension and needs to be post-processed
*
* @type {boolean}
* @private
*/
this.needsOutlines = false;
/**
* The outline edge indices from the CESIUM_primitive_outline extension
*
* @type {number[]}
* @private
*/
this.outlineIndices = undefined;
}
/**
* Apply post-processing steps that may modify geometry such as generating
* outline coordinates. If no post-processing steps are needed, this function
* is a no-op.
*
* @param {Context} context The context for generating buffers on the GPU
*/
PrimitiveLoadPlan.prototype.postProcess = function (context) {
// Handle CESIUM_primitive_outline. This modifies indices and attributes and
// also generates a new attribute for the outline coordinates. These steps
// are synchronous.
if (this.needsOutlines) {
generateOutlines(this);
generateBuffers(this, context);
}
};
function generateOutlines(loadPlan) {
const primitive = loadPlan.primitive;
const indices = primitive.indices;
const vertexCount = primitive.attributes[0].count;
const generator = new PrimitiveOutlineGenerator({
triangleIndices: indices.typedArray,
outlineIndices: loadPlan.outlineIndices,
originalVertexCount: vertexCount,
});
// The generator modifies/adds indices. In some uncommon cases it may have
// to upgrade to 16- or 32-bit indices so the datatype may change.
indices.typedArray = generator.updatedTriangleIndices;
indices.indexDatatype = IndexDatatype.fromTypedArray(indices.typedArray);
// Some vertices may be copied due to the addition of the new attribute
// which may have multiple values at a vertex depending on the face
const attributePlans = loadPlan.attributePlans;
const attributesLength = loadPlan.attributePlans.length;
for (let i = 0; i < attributesLength; i++) {
const attribute = attributePlans[i].attribute;
attribute.typedArray = generator.updateAttribute(attribute.typedArray);
}
// The outline generator creates a new attribute for the outline coordinates
// that are used with a lookup texture.
const outlineCoordinates = makeOutlineCoordinatesAttribute(
generator.outlineCoordinates
);
const outlineCoordinatesPlan = new AttributeLoadPlan(outlineCoordinates);
outlineCoordinatesPlan.loadBuffer = true;
outlineCoordinatesPlan.loadTypedArray = false;
loadPlan.attributePlans.push(outlineCoordinatesPlan);
primitive.outlineCoordinates = outlineCoordinatesPlan.attribute;
}
function makeOutlineCoordinatesAttribute(outlineCoordinatesTypedArray) {
const attribute = new ModelComponents.Attribute();
attribute.name = "_OUTLINE_COORDINATES";
attribute.typedArray = outlineCoordinatesTypedArray;
attribute.componentDatatype = ComponentDatatype.FLOAT;
attribute.type = AttributeType.VEC3;
attribute.normalized = false;
attribute.count = outlineCoordinatesTypedArray.length / 3;
return attribute;
}
function generateBuffers(loadPlan, context) {
generateAttributeBuffers(loadPlan.attributePlans, context);
if (defined(loadPlan.indicesPlan)) {
generateIndexBuffers(loadPlan.indicesPlan, context);
}
}
function generateAttributeBuffers(attributePlans, context) {
const attributesLength = attributePlans.length;
for (let i = 0; i < attributesLength; i++) {
const attributePlan = attributePlans[i];
const attribute = attributePlan.attribute;
const typedArray = attribute.typedArray;
if (attributePlan.loadBuffer) {
const buffer = Buffer.createVertexBuffer({
typedArray: typedArray,
context: context,
usage: BufferUsage.STATIC_DRAW,
});
buffer.vertexArrayDestroyable = false;
attribute.buffer = buffer;
}
if (!attributePlan.loadTypedArray) {
attribute.typedArray = undefined;
}
}
}
function generateIndexBuffers(indicesPlan, context) {
const indices = indicesPlan.indices;
if (indicesPlan.loadBuffer) {
const buffer = Buffer.createIndexBuffer({
typedArray: indices.typedArray,
context: context,
usage: BufferUsage.STATIC_DRAW,
indexDatatype: indices.indexDatatype,
});
indices.buffer = buffer;
buffer.vertexArrayDestroyable = false;
}
if (!indicesPlan.loadTypedArray) {
indices.typedArray = undefined;
}
}
PrimitiveLoadPlan.AttributeLoadPlan = AttributeLoadPlan;
PrimitiveLoadPlan.IndicesLoadPlan = IndicesLoadPlan;
export default PrimitiveLoadPlan;