History

move back into interface for now

ZappoMan committed 11 years ago

Commit 3de35401 parent 4ac673f10 files · +7 −8Browse files

Showing the changes to libraries/render-utils/src/GeometryCache.cpp (1 of 10 files). Show all

Unified · Split

libraries/render-utils/src/GeometryCache.cpp+0−1076
@@ -1,1076 +0,0 @@
1//
2// GeometryCache.cpp
3// interface/src/renderer
4//
5// Created by Andrzej Kapolka on 6/21/13.
6// Copyright 2013 High Fidelity, Inc.
7//
8// Distributed under the Apache License, Version 2.0.
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//
11
12#include <cmath>
13
14#include <QNetworkReply>
15#include <QRunnable>
16#include <QThreadPool>
17
18#include <SharedUtil.h>
19
20#include "TextureCache.h"
21
22#include "GeometryCache.h"
23
24GeometryCache* GeometryCache::getInstance() {
25 static GeometryCache instance;
26 return &instance;
27}
28
29GeometryCache::GeometryCache() {
30}
31
32GeometryCache::~GeometryCache() {
33 foreach (const VerticesIndices& vbo, _hemisphereVBOs) {
34 glDeleteBuffers(1, &vbo.first);
35 glDeleteBuffers(1, &vbo.second);
36 }
37}
38
39void GeometryCache::renderHemisphere(int slices, int stacks) {
40 VerticesIndices& vbo = _hemisphereVBOs[IntPair(slices, stacks)];
41 int vertices = slices * (stacks - 1) + 1;
42 int indices = slices * 2 * 3 * (stacks - 2) + slices * 3;
43 if (vbo.first == 0) {
44 GLfloat* vertexData = new GLfloat[vertices * 3];
45 GLfloat* vertex = vertexData;
46 for (int i = 0; i < stacks - 1; i++) {
47 float phi = PI_OVER_TWO * (float)i / (float)(stacks - 1);
48 float z = sinf(phi), radius = cosf(phi);
49
50 for (int j = 0; j < slices; j++) {
51 float theta = TWO_PI * (float)j / (float)slices;
52
53 *(vertex++) = sinf(theta) * radius;
54 *(vertex++) = cosf(theta) * radius;
55 *(vertex++) = z;
56 }
57 }
58 *(vertex++) = 0.0f;
59 *(vertex++) = 0.0f;
60 *(vertex++) = 1.0f;
61
62 glGenBuffers(1, &vbo.first);
63 glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
64 const int BYTES_PER_VERTEX = 3 * sizeof(GLfloat);
65 glBufferData(GL_ARRAY_BUFFER, vertices * BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW);
66 delete[] vertexData;
67
68 GLushort* indexData = new GLushort[indices];
69 GLushort* index = indexData;
70 for (int i = 0; i < stacks - 2; i++) {
71 GLushort bottom = i * slices;
72 GLushort top = bottom + slices;
73 for (int j = 0; j < slices; j++) {
74 int next = (j + 1) % slices;
75
76 *(index++) = bottom + j;
77 *(index++) = top + next;
78 *(index++) = top + j;
79
80 *(index++) = bottom + j;
81 *(index++) = bottom + next;
82 *(index++) = top + next;
83 }
84 }
85 GLushort bottom = (stacks - 2) * slices;
86 GLushort top = bottom + slices;
87 for (int i = 0; i < slices; i++) {
88 *(index++) = bottom + i;
89 *(index++) = bottom + (i + 1) % slices;
90 *(index++) = top;
91 }
92
93 glGenBuffers(1, &vbo.second);
94 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
95 const int BYTES_PER_INDEX = sizeof(GLushort);
96 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices * BYTES_PER_INDEX, indexData, GL_STATIC_DRAW);
97 delete[] indexData;
98
99 } else {
100 glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
101 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
102 }
103 glEnableClientState(GL_VERTEX_ARRAY);
104 glEnableClientState(GL_NORMAL_ARRAY);
105
106 glVertexPointer(3, GL_FLOAT, 0, 0);
107 glNormalPointer(GL_FLOAT, 0, 0);
108
109 glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertices - 1, indices, GL_UNSIGNED_SHORT, 0);
110
111 glDisableClientState(GL_VERTEX_ARRAY);
112 glDisableClientState(GL_NORMAL_ARRAY);
113
114 glBindBuffer(GL_ARRAY_BUFFER, 0);
115 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
116}
117
118const int NUM_VERTICES_PER_TRIANGLE = 3;
119const int NUM_TRIANGLES_PER_QUAD = 2;
120const int NUM_VERTICES_PER_TRIANGULATED_QUAD = NUM_VERTICES_PER_TRIANGLE * NUM_TRIANGLES_PER_QUAD;
121const int NUM_COORDS_PER_VERTEX = 3;
122const int NUM_BYTES_PER_VERTEX = NUM_COORDS_PER_VERTEX * sizeof(GLfloat);
123const int NUM_BYTES_PER_INDEX = sizeof(GLushort);
124
125void GeometryCache::renderSphere(float radius, int slices, int stacks) {
126 VerticesIndices& vbo = _sphereVBOs[IntPair(slices, stacks)];
127 int vertices = slices * (stacks - 1) + 2;
128 int indices = slices * stacks * NUM_VERTICES_PER_TRIANGULATED_QUAD;
129 if (vbo.first == 0) {
130 GLfloat* vertexData = new GLfloat[vertices * NUM_COORDS_PER_VERTEX];
131 GLfloat* vertex = vertexData;
132
133 // south pole
134 *(vertex++) = 0.0f;
135 *(vertex++) = 0.0f;
136 *(vertex++) = -1.0f;
137
138 //add stacks vertices climbing up Y axis
139 for (int i = 1; i < stacks; i++) {
140 float phi = PI * (float)i / (float)(stacks) - PI_OVER_TWO;
141 float z = sinf(phi), radius = cosf(phi);
142
143 for (int j = 0; j < slices; j++) {
144 float theta = TWO_PI * (float)j / (float)slices;
145
146 *(vertex++) = sinf(theta) * radius;
147 *(vertex++) = cosf(theta) * radius;
148 *(vertex++) = z;
149 }
150 }
151
152 // north pole
153 *(vertex++) = 0.0f;
154 *(vertex++) = 0.0f;
155 *(vertex++) = 1.0f;
156
157 glGenBuffers(1, &vbo.first);
158 glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
159 glBufferData(GL_ARRAY_BUFFER, vertices * NUM_BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW);
160 delete[] vertexData;
161
162 GLushort* indexData = new GLushort[indices];
163 GLushort* index = indexData;
164
165 // South cap
166 GLushort bottom = 0;
167 GLushort top = 1;
168 for (int i = 0; i < slices; i++) {
169 *(index++) = bottom;
170 *(index++) = top + i;
171 *(index++) = top + (i + 1) % slices;
172 }
173
174 // (stacks - 2) ribbons
175 for (int i = 0; i < stacks - 2; i++) {
176 bottom = i * slices + 1;
177 top = bottom + slices;
178 for (int j = 0; j < slices; j++) {
179 int next = (j + 1) % slices;
180
181 *(index++) = top + next;
182 *(index++) = bottom + j;
183 *(index++) = top + j;
184
185 *(index++) = bottom + next;
186 *(index++) = bottom + j;
187 *(index++) = top + next;
188 }
189 }
190
191 // north cap
192 bottom = (stacks - 2) * slices + 1;
193 top = bottom + slices;
194 for (int i = 0; i < slices; i++) {
195 *(index++) = bottom + (i + 1) % slices;
196 *(index++) = bottom + i;
197 *(index++) = top;
198 }
199
200 glGenBuffers(1, &vbo.second);
201 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
202 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices * NUM_BYTES_PER_INDEX, indexData, GL_STATIC_DRAW);
203 delete[] indexData;
204
205 } else {
206 glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
207 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
208 }
209 glEnableClientState(GL_VERTEX_ARRAY);
210 glEnableClientState(GL_NORMAL_ARRAY);
211
212 glVertexPointer(3, GL_FLOAT, 0, 0);
213 glNormalPointer(GL_FLOAT, 0, 0);
214
215 glPushMatrix();
216 glScalef(radius, radius, radius);
217 glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertices - 1, indices, GL_UNSIGNED_SHORT, 0);
218 glPopMatrix();
219
220 glDisableClientState(GL_VERTEX_ARRAY);
221 glDisableClientState(GL_NORMAL_ARRAY);
222
223 glBindBuffer(GL_ARRAY_BUFFER, 0);
224 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
225}
226
227void GeometryCache::renderSquare(int xDivisions, int yDivisions) {
228 VerticesIndices& vbo = _squareVBOs[IntPair(xDivisions, yDivisions)];
229 int xVertices = xDivisions + 1;
230 int yVertices = yDivisions + 1;
231 int vertices = xVertices * yVertices;
232 int indices = 2 * 3 * xDivisions * yDivisions;
233 if (vbo.first == 0) {
234 GLfloat* vertexData = new GLfloat[vertices * 3];
235 GLfloat* vertex = vertexData;
236 for (int i = 0; i <= yDivisions; i++) {
237 float y = (float)i / yDivisions;
238
239 for (int j = 0; j <= xDivisions; j++) {
240 *(vertex++) = (float)j / xDivisions;
241 *(vertex++) = y;
242 *(vertex++) = 0.0f;
243 }
244 }
245
246 glGenBuffers(1, &vbo.first);
247 glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
248 glBufferData(GL_ARRAY_BUFFER, vertices * NUM_BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW);
249 delete[] vertexData;
250
251 GLushort* indexData = new GLushort[indices];
252 GLushort* index = indexData;
253 for (int i = 0; i < yDivisions; i++) {
254 GLushort bottom = i * xVertices;
255 GLushort top = bottom + xVertices;
256 for (int j = 0; j < xDivisions; j++) {
257 int next = j + 1;
258
259 *(index++) = bottom + j;
260 *(index++) = top + next;
261 *(index++) = top + j;
262
263 *(index++) = bottom + j;
264 *(index++) = bottom + next;
265 *(index++) = top + next;
266 }
267 }
268
269 glGenBuffers(1, &vbo.second);
270 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
271 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices * NUM_BYTES_PER_INDEX, indexData, GL_STATIC_DRAW);
272 delete[] indexData;
273
274 } else {
275 glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
276 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
277 }
278 glEnableClientState(GL_VERTEX_ARRAY);
279
280 // all vertices have the same normal
281 glNormal3f(0.0f, 0.0f, 1.0f);
282
283 glVertexPointer(3, GL_FLOAT, 0, 0);
284
285 glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertices - 1, indices, GL_UNSIGNED_SHORT, 0);
286
287 glDisableClientState(GL_VERTEX_ARRAY);
288
289 glBindBuffer(GL_ARRAY_BUFFER, 0);
290 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
291}
292
293void GeometryCache::renderHalfCylinder(int slices, int stacks) {
294 VerticesIndices& vbo = _halfCylinderVBOs[IntPair(slices, stacks)];
295 int vertices = (slices + 1) * stacks;
296 int indices = 2 * 3 * slices * (stacks - 1);
297 if (vbo.first == 0) {
298 GLfloat* vertexData = new GLfloat[vertices * 2 * 3];
299 GLfloat* vertex = vertexData;
300 for (int i = 0; i <= (stacks - 1); i++) {
301 float y = (float)i / (stacks - 1);
302
303 for (int j = 0; j <= slices; j++) {
304 float theta = 3.0f * PI_OVER_TWO + PI * (float)j / (float)slices;
305
306 //normals
307 *(vertex++) = sinf(theta);
308 *(vertex++) = 0;
309 *(vertex++) = cosf(theta);
310
311 // vertices
312 *(vertex++) = sinf(theta);
313 *(vertex++) = y;
314 *(vertex++) = cosf(theta);
315 }
316 }
317
318 glGenBuffers(1, &vbo.first);
319 glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
320 glBufferData(GL_ARRAY_BUFFER, 2 * vertices * NUM_BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW);
321 delete[] vertexData;
322
323 GLushort* indexData = new GLushort[indices];
324 GLushort* index = indexData;
325 for (int i = 0; i < stacks - 1; i++) {
326 GLushort bottom = i * (slices + 1);
327 GLushort top = bottom + slices + 1;
328 for (int j = 0; j < slices; j++) {
329 int next = j + 1;
330
331 *(index++) = bottom + j;
332 *(index++) = top + next;
333 *(index++) = top + j;
334
335 *(index++) = bottom + j;
336 *(index++) = bottom + next;
337 *(index++) = top + next;
338 }
339 }
340
341 glGenBuffers(1, &vbo.second);
342 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
343 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices * NUM_BYTES_PER_INDEX, indexData, GL_STATIC_DRAW);
344 delete[] indexData;
345
346 } else {
347 glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
348 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
349 }
350 glEnableClientState(GL_VERTEX_ARRAY);
351 glEnableClientState(GL_NORMAL_ARRAY);
352
353 glNormalPointer(GL_FLOAT, 6 * sizeof(float), 0);
354 glVertexPointer(3, GL_FLOAT, (6 * sizeof(float)), (const void *)(3 * sizeof(float)));
355
356 glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertices - 1, indices, GL_UNSIGNED_SHORT, 0);
357
358 glDisableClientState(GL_VERTEX_ARRAY);
359 glDisableClientState(GL_NORMAL_ARRAY);
360
361 glBindBuffer(GL_ARRAY_BUFFER, 0);
362 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
363}
364
365void GeometryCache::renderCone(float base, float height, int slices, int stacks) {
366 VerticesIndices& vbo = _halfCylinderVBOs[IntPair(slices, stacks)];
367 int vertices = (stacks + 2) * slices;
368 int baseTriangles = slices - 2;
369 int indices = NUM_VERTICES_PER_TRIANGULATED_QUAD * slices * stacks + NUM_VERTICES_PER_TRIANGLE * baseTriangles;
370 if (vbo.first == 0) {
371 GLfloat* vertexData = new GLfloat[vertices * NUM_COORDS_PER_VERTEX * 2];
372 GLfloat* vertex = vertexData;
373 // cap
374 for (int i = 0; i < slices; i++) {
375 float theta = TWO_PI * i / slices;
376
377 //normals
378 *(vertex++) = 0.0f;
379 *(vertex++) = 0.0f;
380 *(vertex++) = -1.0f;
381
382 // vertices
383 *(vertex++) = cosf(theta);
384 *(vertex++) = sinf(theta);
385 *(vertex++) = 0.0f;
386 }
387 // body
388 for (int i = 0; i <= stacks; i++) {
389 float z = (float)i / stacks;
390 float radius = 1.0f - z;
391
392 for (int j = 0; j < slices; j++) {
393 float theta = TWO_PI * j / slices;
394
395 //normals
396 *(vertex++) = cosf(theta) / SQUARE_ROOT_OF_2;
397 *(vertex++) = sinf(theta) / SQUARE_ROOT_OF_2;
398 *(vertex++) = 1.0f / SQUARE_ROOT_OF_2;
399
400 // vertices
401 *(vertex++) = radius * cosf(theta);
402 *(vertex++) = radius * sinf(theta);
403 *(vertex++) = z;
404 }
405 }
406
407 glGenBuffers(1, &vbo.first);
408 glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
409 glBufferData(GL_ARRAY_BUFFER, 2 * vertices * NUM_BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW);
410 delete[] vertexData;
411
412 GLushort* indexData = new GLushort[indices];
413 GLushort* index = indexData;
414 for (int i = 0; i < baseTriangles; i++) {
415 *(index++) = 0;
416 *(index++) = i + 2;
417 *(index++) = i + 1;
418 }
419 for (int i = 1; i <= stacks; i++) {
420 GLushort bottom = i * slices;
421 GLushort top = bottom + slices;
422 for (int j = 0; j < slices; j++) {
423 int next = (j + 1) % slices;
424
425 *(index++) = bottom + j;
426 *(index++) = top + next;
427 *(index++) = top + j;
428
429 *(index++) = bottom + j;
430 *(index++) = bottom + next;
431 *(index++) = top + next;
432 }
433 }
434
435 glGenBuffers(1, &vbo.second);
436 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
437 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices * NUM_BYTES_PER_INDEX, indexData, GL_STATIC_DRAW);
438 delete[] indexData;
439
440 } else {
441 glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
442 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
443 }
444 glEnableClientState(GL_VERTEX_ARRAY);
445 glEnableClientState(GL_NORMAL_ARRAY);
446
447 int stride = NUM_VERTICES_PER_TRIANGULATED_QUAD * sizeof(float);
448 glNormalPointer(GL_FLOAT, stride, 0);
449 glVertexPointer(NUM_COORDS_PER_VERTEX, GL_FLOAT, stride, (const void *)(NUM_COORDS_PER_VERTEX * sizeof(float)));
450
451 glPushMatrix();
452 glScalef(base, base, height);
453
454 glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertices - 1, indices, GL_UNSIGNED_SHORT, 0);
455
456 glPopMatrix();
457
458 glDisableClientState(GL_VERTEX_ARRAY);
459 glDisableClientState(GL_NORMAL_ARRAY);
460
461 glBindBuffer(GL_ARRAY_BUFFER, 0);
462 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
463}
464
465void GeometryCache::renderGrid(int xDivisions, int yDivisions) {
466 QOpenGLBuffer& buffer = _gridBuffers[IntPair(xDivisions, yDivisions)];
467 int vertices = (xDivisions + 1 + yDivisions + 1) * 2;
468 if (!buffer.isCreated()) {
469 GLfloat* vertexData = new GLfloat[vertices * 2];
470 GLfloat* vertex = vertexData;
471 for (int i = 0; i <= xDivisions; i++) {
472 float x = (float)i / xDivisions;
473
474 *(vertex++) = x;
475 *(vertex++) = 0.0f;
476
477 *(vertex++) = x;
478 *(vertex++) = 1.0f;
479 }
480 for (int i = 0; i <= yDivisions; i++) {
481 float y = (float)i / yDivisions;
482
483 *(vertex++) = 0.0f;
484 *(vertex++) = y;
485
486 *(vertex++) = 1.0f;
487 *(vertex++) = y;
488 }
489 buffer.create();
490 buffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
491 buffer.bind();
492 buffer.allocate(vertexData, vertices * 2 * sizeof(GLfloat));
493 delete[] vertexData;
494
495 } else {
496 buffer.bind();
497 }
498 glEnableClientState(GL_VERTEX_ARRAY);
499
500 glVertexPointer(2, GL_FLOAT, 0, 0);
501
502 glDrawArrays(GL_LINES, 0, vertices);
503
504 glDisableClientState(GL_VERTEX_ARRAY);
505
506 buffer.release();
507}
508
509QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad) {
510 return getResource(url, fallback, delayLoad).staticCast<NetworkGeometry>();
511}
512
513QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url,
514 const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
515 QSharedPointer<NetworkGeometry> geometry(new NetworkGeometry(url, fallback.staticCast<NetworkGeometry>(), delayLoad),
516 &Resource::allReferencesCleared);
517 geometry->setLODParent(geometry);
518 return geometry.staticCast<Resource>();
519}
520
521const float NetworkGeometry::NO_HYSTERESIS = -1.0f;
522
523NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback, bool delayLoad,
524 const QVariantHash& mapping, const QUrl& textureBase) :
525 Resource(url, delayLoad),
526 _mapping(mapping),
527 _textureBase(textureBase.isValid() ? textureBase : url),
528 _fallback(fallback)
529{
530
531 if (url.isEmpty()) {
532 // make the minimal amount of dummy geometry to satisfy Model
533 FBXJoint joint = { false, QVector<int>(), -1 };
534 _geometry.joints.append(joint);
535 _geometry.leftEyeJointIndex = -1;
536 _geometry.rightEyeJointIndex = -1;
537 _geometry.neckJointIndex = -1;
538 _geometry.rootJointIndex = -1;
539 _geometry.leanJointIndex = -1;
540 _geometry.headJointIndex = -1;
541 _geometry.leftHandJointIndex = -1;
542 _geometry.rightHandJointIndex = -1;
543 }
544
545 connect(this, &Resource::loaded, this, &NetworkGeometry::replaceTexturesWithPendingChanges);
546}
547
548bool NetworkGeometry::isLoadedWithTextures() const {
549 if (!isLoaded()) {
550 return false;
551 }
552 foreach (const NetworkMesh& mesh, _meshes) {
553 foreach (const NetworkMeshPart& part, mesh.parts) {
554 if ((part.diffuseTexture && !part.diffuseTexture->isLoaded()) ||
555 (part.normalTexture && !part.normalTexture->isLoaded()) ||
556 (part.specularTexture && !part.specularTexture->isLoaded()) ||
557 (part.emissiveTexture && !part.emissiveTexture->isLoaded())) {
558 return false;
559 }
560 }
561 }
562 return true;
563}
564
565QSharedPointer<NetworkGeometry> NetworkGeometry::getLODOrFallback(float distance, float& hysteresis, bool delayLoad) const {
566 if (_lodParent.data() != this) {
567 return _lodParent.data()->getLODOrFallback(distance, hysteresis, delayLoad);
568 }
569 if (_failedToLoad && _fallback) {
570 return _fallback;
571 }
572 QSharedPointer<NetworkGeometry> lod = _lodParent;
573 float lodDistance = 0.0f;
574 QMap<float, QSharedPointer<NetworkGeometry> >::const_iterator it = _lods.upperBound(distance);
575 if (it != _lods.constBegin()) {
576 it = it - 1;
577 lod = it.value();
578 lodDistance = it.key();
579 }
580 if (hysteresis != NO_HYSTERESIS && hysteresis != lodDistance) {
581 // if we previously selected a different distance, make sure we've moved far enough to justify switching
582 const float HYSTERESIS_PROPORTION = 0.1f;
583 if (glm::abs(distance - qMax(hysteresis, lodDistance)) / fabsf(hysteresis - lodDistance) < HYSTERESIS_PROPORTION) {
584 lod = _lodParent;
585 lodDistance = 0.0f;
586 it = _lods.upperBound(hysteresis);
587 if (it != _lods.constBegin()) {
588 it = it - 1;
589 lod = it.value();
590 lodDistance = it.key();
591 }
592 }
593 }
594 if (lod->isLoaded()) {
595 hysteresis = lodDistance;
596 return lod;
597 }
598 // if the ideal LOD isn't loaded, we need to make sure it's started to load, and possibly return the closest loaded one
599 if (!delayLoad) {
600 lod->ensureLoading();
601 }
602 float closestDistance = FLT_MAX;
603 if (isLoaded()) {
604 lod = _lodParent;
605 closestDistance = distance;
606 }
607 for (it = _lods.constBegin(); it != _lods.constEnd(); it++) {
608 float distanceToLOD = glm::abs(distance - it.key());
609 if (it.value()->isLoaded() && distanceToLOD < closestDistance) {
610 lod = it.value();
611 closestDistance = distanceToLOD;
612 }
613 }
614 hysteresis = NO_HYSTERESIS;
615 return lod;
616}
617
618uint qHash(const QWeakPointer<Animation>& animation, uint seed = 0) {
619 return qHash(animation.data(), seed);
620}
621
622QVector<int> NetworkGeometry::getJointMappings(const AnimationPointer& animation) {
623 QVector<int> mappings = _jointMappings.value(animation);
624 if (mappings.isEmpty() && isLoaded() && animation && animation->isLoaded()) {
625 const FBXGeometry& animationGeometry = animation->getGeometry();
626 for (int i = 0; i < animationGeometry.joints.size(); i++) {
627 mappings.append(_geometry.jointIndices.value(animationGeometry.joints.at(i).name) - 1);
628 }
629 _jointMappings.insert(animation, mappings);
630 }
631 return mappings;
632}
633
634void NetworkGeometry::setLoadPriority(const QPointer<QObject>& owner, float priority) {
635 Resource::setLoadPriority(owner, priority);
636
637 for (int i = 0; i < _meshes.size(); i++) {
638 NetworkMesh& mesh = _meshes[i];
639 for (int j = 0; j < mesh.parts.size(); j++) {
640 NetworkMeshPart& part = mesh.parts[j];
641 if (part.diffuseTexture) {
642 part.diffuseTexture->setLoadPriority(owner, priority);
643 }
644 if (part.normalTexture) {
645 part.normalTexture->setLoadPriority(owner, priority);
646 }
647 if (part.specularTexture) {
648 part.specularTexture->setLoadPriority(owner, priority);
649 }
650 if (part.emissiveTexture) {
651 part.emissiveTexture->setLoadPriority(owner, priority);
652 }
653 }
654 }
655}
656
657void NetworkGeometry::setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities) {
658 Resource::setLoadPriorities(priorities);
659
660 for (int i = 0; i < _meshes.size(); i++) {
661 NetworkMesh& mesh = _meshes[i];
662 for (int j = 0; j < mesh.parts.size(); j++) {
663 NetworkMeshPart& part = mesh.parts[j];
664 if (part.diffuseTexture) {
665 part.diffuseTexture->setLoadPriorities(priorities);
666 }
667 if (part.normalTexture) {
668 part.normalTexture->setLoadPriorities(priorities);
669 }
670 if (part.specularTexture) {
671 part.specularTexture->setLoadPriorities(priorities);
672 }
673 if (part.emissiveTexture) {
674 part.emissiveTexture->setLoadPriorities(priorities);
675 }
676 }
677 }
678}
679
680void NetworkGeometry::clearLoadPriority(const QPointer<QObject>& owner) {
681 Resource::clearLoadPriority(owner);
682
683 for (int i = 0; i < _meshes.size(); i++) {
684 NetworkMesh& mesh = _meshes[i];
685 for (int j = 0; j < mesh.parts.size(); j++) {
686 NetworkMeshPart& part = mesh.parts[j];
687 if (part.diffuseTexture) {
688 part.diffuseTexture->clearLoadPriority(owner);
689 }
690 if (part.normalTexture) {
691 part.normalTexture->clearLoadPriority(owner);
692 }
693 if (part.specularTexture) {
694 part.specularTexture->clearLoadPriority(owner);
695 }
696 if (part.emissiveTexture) {
697 part.emissiveTexture->clearLoadPriority(owner);
698 }
699 }
700 }
701}
702
703void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& url) {
704 if (_meshes.size() > 0) {
705 for (int i = 0; i < _meshes.size(); i++) {
706 NetworkMesh& mesh = _meshes[i];
707 for (int j = 0; j < mesh.parts.size(); j++) {
708 NetworkMeshPart& part = mesh.parts[j];
709
710 QSharedPointer<NetworkTexture> matchingTexture = QSharedPointer<NetworkTexture>();
711 if (part.diffuseTextureName == name) {
712 part.diffuseTexture =
713 TextureCache::getInstance()->getTexture(url, DEFAULT_TEXTURE,
714 _geometry.meshes[i].isEye, QByteArray());
715 part.diffuseTexture->setLoadPriorities(_loadPriorities);
716 } else if (part.normalTextureName == name) {
717 part.normalTexture = TextureCache::getInstance()->getTexture(url, DEFAULT_TEXTURE,
718 false, QByteArray());
719 part.normalTexture->setLoadPriorities(_loadPriorities);
720 } else if (part.specularTextureName == name) {
721 part.specularTexture = TextureCache::getInstance()->getTexture(url, DEFAULT_TEXTURE,
722 false, QByteArray());
723 part.specularTexture->setLoadPriorities(_loadPriorities);
724 } else if (part.emissiveTextureName == name) {
725 part.emissiveTexture = TextureCache::getInstance()->getTexture(url, DEFAULT_TEXTURE,
726 false, QByteArray());
727 part.emissiveTexture->setLoadPriorities(_loadPriorities);
728 }
729 }
730 }
731 } else {
732 qDebug() << "Adding a name url pair to pending" << name << url;
733 // we don't have meshes downloaded yet, so hold this texture as pending
734 _pendingTextureChanges.insert(name, url);
735 }
736}
737
738QStringList NetworkGeometry::getTextureNames() const {
739 QStringList result;
740 for (int i = 0; i < _meshes.size(); i++) {
741 const NetworkMesh& mesh = _meshes[i];
742 for (int j = 0; j < mesh.parts.size(); j++) {
743 const NetworkMeshPart& part = mesh.parts[j];
744
745 if (!part.diffuseTextureName.isEmpty()) {
746 QString textureURL = part.diffuseTexture->getURL().toString();
747 result << part.diffuseTextureName + ":" + textureURL;
748 }
749
750 if (!part.normalTextureName.isEmpty()) {
751 QString textureURL = part.normalTexture->getURL().toString();
752 result << part.normalTextureName + ":" + textureURL;
753 }
754
755 if (!part.specularTextureName.isEmpty()) {
756 QString textureURL = part.specularTexture->getURL().toString();
757 result << part.specularTextureName + ":" + textureURL;
758 }
759
760 if (!part.emissiveTextureName.isEmpty()) {
761 QString textureURL = part.emissiveTexture->getURL().toString();
762 result << part.emissiveTextureName + ":" + textureURL;
763 }
764 }
765 }
766 return result;
767}
768
769void NetworkGeometry::replaceTexturesWithPendingChanges() {
770 QHash<QString, QUrl>::Iterator it = _pendingTextureChanges.begin();
771
772 while (it != _pendingTextureChanges.end()) {
773 setTextureWithNameToURL(it.key(), it.value());
774 it = _pendingTextureChanges.erase(it);
775 }
776}
777
778/// Reads geometry in a worker thread.
779class GeometryReader : public QRunnable {
780public:
781
782 GeometryReader(const QWeakPointer<Resource>& geometry, const QUrl& url,
783 QNetworkReply* reply, const QVariantHash& mapping);
784
785 virtual void run();
786
787private:
788
789 QWeakPointer<Resource> _geometry;
790 QUrl _url;
791 QNetworkReply* _reply;
792 QVariantHash _mapping;
793};
794
795GeometryReader::GeometryReader(const QWeakPointer<Resource>& geometry, const QUrl& url,
796 QNetworkReply* reply, const QVariantHash& mapping) :
797 _geometry(geometry),
798 _url(url),
799 _reply(reply),
800 _mapping(mapping) {
801}
802
803void GeometryReader::run() {
804 QSharedPointer<Resource> geometry = _geometry.toStrongRef();
805 if (geometry.isNull()) {
806 _reply->deleteLater();
807 return;
808 }
809 try {
810 if (!_reply) {
811 throw QString("Reply is NULL ?!");
812 }
813 std::string urlname = _url.path().toLower().toStdString();
814 bool urlValid = true;
815 urlValid &= !urlname.empty();
816 urlValid &= !_url.path().isEmpty();
817 urlValid &= _url.path().toLower().endsWith(".fbx")
818 || _url.path().toLower().endsWith(".svo");
819
820 if (urlValid) {
821 // Let's read the binaries from the network
822 FBXGeometry fbxgeo;
823 if (_url.path().toLower().endsWith(".svo")) {
824 QByteArray fileBinary = _reply->readAll();
825 if (fileBinary.isEmpty() || fileBinary.isNull()) {
826 throw QString("Read File binary is empty?!");
827 }
828 fbxgeo = readSVO(fileBinary);
829
830 } else if (_url.path().toLower().endsWith(".fbx")) {
831 bool grabLightmaps = true;
832 float lightmapLevel = 1.0f;
833 // HACK: For monday 12/01/2014 we need to kill lighmaps loading in starchamber...
834 if (_url.path().toLower().endsWith("loungev4_11-18.fbx")) {
835 grabLightmaps = false;
836 } else if (_url.path().toLower().endsWith("apt8_reboot.fbx")) {
837 lightmapLevel = 4.0f;
838 } else if (_url.path().toLower().endsWith("palaceoforinthilian4.fbx")) {
839 lightmapLevel = 3.5f;
840 }
841 fbxgeo = readFBX(_reply, _mapping, grabLightmaps, lightmapLevel);
842 }
843 QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo));
844 } else {
845 throw QString("url is invalid");
846 }
847
848 } catch (const QString& error) {
849 qDebug() << "Error reading " << _url << ": " << error;
850 QMetaObject::invokeMethod(geometry.data(), "finishedLoading", Q_ARG(bool, false));
851 }
852 _reply->deleteLater();
853}
854
855void NetworkGeometry::init() {
856 _mapping = QVariantHash();
857 _geometry = FBXGeometry();
858 _meshes.clear();
859 _lods.clear();
860 _pendingTextureChanges.clear();
861 _request.setUrl(_url);
862 Resource::init();
863}
864
865void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
866 QUrl url = reply->url();
867 if (url.path().toLower().endsWith(".fst")) {
868 // it's a mapping file; parse it and get the mesh filename
869 _mapping = readMapping(reply->readAll());
870 reply->deleteLater();
871 QString filename = _mapping.value("filename").toString();
872 if (filename.isNull()) {
873 qDebug() << "Mapping file " << url << " has no filename.";
874 finishedLoading(false);
875
876 } else {
877 QString texdir = _mapping.value("texdir").toString();
878 if (!texdir.isNull()) {
879 if (!texdir.endsWith('/')) {
880 texdir += '/';
881 }
882 _textureBase = url.resolved(texdir);
883 }
884 QVariantHash lods = _mapping.value("lod").toHash();
885 for (QVariantHash::const_iterator it = lods.begin(); it != lods.end(); it++) {
886 QSharedPointer<NetworkGeometry> geometry(new NetworkGeometry(url.resolved(it.key()),
887 QSharedPointer<NetworkGeometry>(), true, _mapping, _textureBase));
888 geometry->setSelf(geometry.staticCast<Resource>());
889 geometry->setLODParent(_lodParent);
890 _lods.insert(it.value().toFloat(), geometry);
891 }
892 _request.setUrl(url.resolved(filename));
893
894 // make the request immediately only if we have no LODs to switch between
895 _startedLoading = false;
896 if (_lods.isEmpty()) {
897 attemptRequest();
898 }
899 }
900 return;
901 }
902
903 // send the reader off to the thread pool
904 QThreadPool::globalInstance()->start(new GeometryReader(_self, url, reply, _mapping));
905}
906
907void NetworkGeometry::reinsert() {
908 Resource::reinsert();
909
910 _lodParent = qWeakPointerCast<NetworkGeometry, Resource>(_self);
911 foreach (const QSharedPointer<NetworkGeometry>& lod, _lods) {
912 lod->setLODParent(_lodParent);
913 }
914}
915
916void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
917 _geometry = geometry;
918
919 foreach (const FBXMesh& mesh, _geometry.meshes) {
920 NetworkMesh networkMesh;
921
922 int totalIndices = 0;
923 foreach (const FBXMeshPart& part, mesh.parts) {
924 NetworkMeshPart networkPart;
925 if (!part.diffuseTexture.filename.isEmpty()) {
926 networkPart.diffuseTexture = TextureCache::getInstance()->getTexture(
927 _textureBase.resolved(QUrl(part.diffuseTexture.filename)), DEFAULT_TEXTURE,
928 mesh.isEye, part.diffuseTexture.content);
929 networkPart.diffuseTextureName = part.diffuseTexture.name;
930 networkPart.diffuseTexture->setLoadPriorities(_loadPriorities);
931 }
932 if (!part.normalTexture.filename.isEmpty()) {
933 networkPart.normalTexture = TextureCache::getInstance()->getTexture(
934 _textureBase.resolved(QUrl(part.normalTexture.filename)), NORMAL_TEXTURE,
935 false, part.normalTexture.content);
936 networkPart.normalTextureName = part.normalTexture.name;
937 networkPart.normalTexture->setLoadPriorities(_loadPriorities);
938 }
939 if (!part.specularTexture.filename.isEmpty()) {
940 networkPart.specularTexture = TextureCache::getInstance()->getTexture(
941 _textureBase.resolved(QUrl(part.specularTexture.filename)), SPECULAR_TEXTURE,
942 false, part.specularTexture.content);
943 networkPart.specularTextureName = part.specularTexture.name;
944 networkPart.specularTexture->setLoadPriorities(_loadPriorities);
945 }
946 if (!part.emissiveTexture.filename.isEmpty()) {
947 networkPart.emissiveTexture = TextureCache::getInstance()->getTexture(
948 _textureBase.resolved(QUrl(part.emissiveTexture.filename)), EMISSIVE_TEXTURE,
949 false, part.emissiveTexture.content);
950 networkPart.emissiveTextureName = part.emissiveTexture.name;
951 networkPart.emissiveTexture->setLoadPriorities(_loadPriorities);
952 }
953 networkMesh.parts.append(networkPart);
954
955 totalIndices += (part.quadIndices.size() + part.triangleIndices.size());
956 }
957
958 {
959 networkMesh._indexBuffer = gpu::BufferPointer(new gpu::Buffer());
960 networkMesh._indexBuffer->resize(totalIndices * sizeof(int));
961 int offset = 0;
962 foreach(const FBXMeshPart& part, mesh.parts) {
963 networkMesh._indexBuffer->setSubData(offset, part.quadIndices.size() * sizeof(int),
964 (gpu::Resource::Byte*) part.quadIndices.constData());
965 offset += part.quadIndices.size() * sizeof(int);
966 networkMesh._indexBuffer->setSubData(offset, part.triangleIndices.size() * sizeof(int),
967 (gpu::Resource::Byte*) part.triangleIndices.constData());
968 offset += part.triangleIndices.size() * sizeof(int);
969 }
970 }
971
972 {
973 networkMesh._vertexBuffer = gpu::BufferPointer(new gpu::Buffer());
974 // if we don't need to do any blending, the positions/normals can be static
975 if (mesh.blendshapes.isEmpty()) {
976 int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3);
977 int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3);
978 int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3);
979 int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3);
980 int texCoords1Offset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2);
981 int clusterIndicesOffset = texCoords1Offset + mesh.texCoords1.size() * sizeof(glm::vec2);
982 int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4);
983
984 networkMesh._vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4));
985
986 networkMesh._vertexBuffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.vertices.constData());
987 networkMesh._vertexBuffer->setSubData(normalsOffset, mesh.normals.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.normals.constData());
988 networkMesh._vertexBuffer->setSubData(tangentsOffset,
989 mesh.tangents.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.tangents.constData());
990 networkMesh._vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.colors.constData());
991 networkMesh._vertexBuffer->setSubData(texCoordsOffset,
992 mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Resource::Byte*) mesh.texCoords.constData());
993 networkMesh._vertexBuffer->setSubData(texCoords1Offset,
994 mesh.texCoords1.size() * sizeof(glm::vec2), (gpu::Resource::Byte*) mesh.texCoords1.constData());
995 networkMesh._vertexBuffer->setSubData(clusterIndicesOffset,
996 mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Resource::Byte*) mesh.clusterIndices.constData());
997 networkMesh._vertexBuffer->setSubData(clusterWeightsOffset,
998 mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Resource::Byte*) mesh.clusterWeights.constData());
999
1000 // otherwise, at least the cluster indices/weights can be static
1001 networkMesh._vertexStream = gpu::BufferStreamPointer(new gpu::BufferStream());
1002 networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, 0, sizeof(glm::vec3));
1003 if (mesh.normals.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, normalsOffset, sizeof(glm::vec3));
1004 if (mesh.tangents.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, tangentsOffset, sizeof(glm::vec3));
1005 if (mesh.colors.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, colorsOffset, sizeof(glm::vec3));
1006 if (mesh.texCoords.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoordsOffset, sizeof(glm::vec2));
1007 if (mesh.texCoords1.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoords1Offset, sizeof(glm::vec2));
1008 if (mesh.clusterIndices.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4));
1009 if (mesh.clusterWeights.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4));
1010
1011 int channelNum = 0;
1012 networkMesh._vertexFormat = gpu::Stream::FormatPointer(new gpu::Stream::Format());
1013 networkMesh._vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::POS_XYZ), 0);
1014 if (mesh.normals.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
1015 if (mesh.tangents.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
1016 if (mesh.colors.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB));
1017 if (mesh.texCoords.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV));
1018 if (mesh.texCoords1.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV));
1019 if (mesh.clusterIndices.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW));
1020 if (mesh.clusterWeights.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW));
1021 }
1022 else {
1023 int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3);
1024 int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3);
1025 int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2);
1026 int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4);
1027
1028 networkMesh._vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4));
1029 networkMesh._vertexBuffer->setSubData(0, mesh.tangents.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.tangents.constData());
1030 networkMesh._vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.colors.constData());
1031 networkMesh._vertexBuffer->setSubData(texCoordsOffset,
1032 mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Resource::Byte*) mesh.texCoords.constData());
1033 networkMesh._vertexBuffer->setSubData(clusterIndicesOffset,
1034 mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Resource::Byte*) mesh.clusterIndices.constData());
1035 networkMesh._vertexBuffer->setSubData(clusterWeightsOffset,
1036 mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Resource::Byte*) mesh.clusterWeights.constData());
1037
1038 networkMesh._vertexStream = gpu::BufferStreamPointer(new gpu::BufferStream());
1039 if (mesh.tangents.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, 0, sizeof(glm::vec3));
1040 if (mesh.colors.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, colorsOffset, sizeof(glm::vec3));
1041 if (mesh.texCoords.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoordsOffset, sizeof(glm::vec2));
1042 if (mesh.clusterIndices.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4));
1043 if (mesh.clusterWeights.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4));
1044
1045 int channelNum = 0;
1046 networkMesh._vertexFormat = gpu::Stream::FormatPointer(new gpu::Stream::Format());
1047 networkMesh._vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::POS_XYZ));
1048 if (mesh.normals.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
1049 if (mesh.tangents.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
1050 if (mesh.colors.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB));
1051 if (mesh.texCoords.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV));
1052 if (mesh.clusterIndices.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW));
1053 if (mesh.clusterWeights.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW));
1054
1055 }
1056 }
1057
1058 _meshes.append(networkMesh);
1059 }
1060
1061 finishedLoading(true);
1062}
1063
1064bool NetworkMeshPart::isTranslucent() const {
1065 return diffuseTexture && diffuseTexture->isTranslucent();
1066}
1067
1068int NetworkMesh::getTranslucentPartCount(const FBXMesh& fbxMesh) const {
1069 int count = 0;
1070 for (int i = 0; i < parts.size(); i++) {
1071 if (parts.at(i).isTranslucent() || fbxMesh.parts.at(i).opacity != 1.0f) {
1072 count++;
1073 }
1074 }
1075 return count;
1076}