1 /******************************************************************************
2
3 @File PVRTBoneBatch.cpp
4
5 @Title PVRTBoneBatch
6
7 @Version
8
9 @Copyright Copyright (c) Imagination Technologies Limited.
10
11 @Platform ANSI compatible
12
13 @Description Utility functions which process vertices.
14
15 ******************************************************************************/
16
17 /****************************************************************************
18 ** Includes
19 ****************************************************************************/
20 #include "PVRTGlobal.h"
21 #include "PVRTContext.h"
22
23 #include <vector>
24 #include <list>
25
26 #include "PVRTMatrix.h"
27 #include "PVRTVertex.h"
28 #include "PVRTBoneBatch.h"
29
30 /****************************************************************************
31 ** Defines
32 ****************************************************************************/
33
34 /****************************************************************************
35 ** Macros
36 ****************************************************************************/
37
38 /****************************************************************************
39 ** Structures
40 ****************************************************************************/
41 /*!***************************************************************************
42 @Class CBatch
43 @Brief Class to contain and manage batch information.
44 *****************************************************************************/
45 class CBatch
46 {
47 protected:
48 int m_nCapacity, // Maximum size of the batch
49 m_nCnt, // Number of elements currently contained in the batch
50 *m_pnPalette; // Array of palette indices
51
52 public:
53 /*!***************************************************************************
54 @Function CBatch
55 @Description The default constructor
56 *****************************************************************************/
CBatch()57 CBatch() : m_nCapacity(0),
58 m_nCnt(0),
59 m_pnPalette(0)
60 {
61 }
62
63 /*!***************************************************************************
64 @Function CBatch
65 @Input src CBatch to copy
66 @Description Copy constructor
67 *****************************************************************************/
CBatch(const CBatch & src)68 CBatch(const CBatch &src) : m_pnPalette(0)
69 {
70 SetSize(src.m_nCapacity);
71 *this = src;
72 }
73
74 /*!***************************************************************************
75 @Function ~CBatch
76 @Description Destructor
77 *****************************************************************************/
~CBatch()78 ~CBatch()
79 {
80 FREE(m_pnPalette);
81 }
82
83 /*!***************************************************************************
84 @Function operator=
85 @Description Operator overload for the '=' operand
86 *****************************************************************************/
operator =(const CBatch & src)87 CBatch& operator= (const CBatch &src)
88 {
89 _ASSERT(m_nCapacity == src.m_nCapacity);
90 m_nCnt = src.m_nCnt;
91 memcpy(m_pnPalette, src.m_pnPalette, m_nCnt * sizeof(*m_pnPalette));
92 return *this;
93 }
94
95 /*!***************************************************************************
96 @Function SetSize
97 @Input nSize The new size of the batch
98 @Description Delete all current information and resizes the batch
99 to the value that has been passed in.
100 *****************************************************************************/
SetSize(const int nSize)101 void SetSize(const int nSize)
102 {
103 FREE(m_pnPalette);
104
105 m_nCapacity = nSize;
106 m_nCnt = 0;
107 m_pnPalette = (int*)malloc(m_nCapacity * sizeof(*m_pnPalette));
108 }
109
110 /*!***************************************************************************
111 @Function Clear
112 @Description Resets the count
113 *****************************************************************************/
Clear()114 void Clear()
115 {
116 m_nCnt = 0;
117 }
118
119 /*!***************************************************************************
120 @Function Clear
121 @Input n The index of the new item
122 Return bool Returns true if the item already exists or has been added.
123 @Description Adds a new item to the batch, providing it has not already
124 been added to the batch and the count doesn't exceed the
125 maximum number of bones the batch can hold.
126 *****************************************************************************/
Add(const int n)127 bool Add(const int n)
128 {
129 int i;
130
131 if(n < 0)
132 return false;
133
134 // If we already have this item, do nothing
135 for(i = 0; i < m_nCnt; ++i)
136 {
137 if(m_pnPalette[i] == n)
138 return true;
139 }
140
141 // Add the new item
142 if(m_nCnt < m_nCapacity)
143 {
144 m_pnPalette[m_nCnt] = n;
145 ++m_nCnt;
146 return true;
147 }
148 else
149 {
150 return false;
151 }
152 }
153
154 /*!***************************************************************************
155 @Function Merge
156 @Input src The batch to merge with
157 @Description Merges the input batch with the current batch.
158 *****************************************************************************/
Merge(const CBatch & src)159 void Merge(const CBatch &src)
160 {
161 int i;
162
163 for(i = 0; i < src.m_nCnt; ++i)
164 Add(src.m_pnPalette[i]);
165 }
166
167 /*!***************************************************************************
168 @Function TestMerge
169 @Input src The batch to merge with
170 @Return int The number of items that are not already
171 present in the batch. -1 if the merge will
172 exceed the capacity of the batch
173 @Description Tests how many of the items of the input batch are not
174 already contained in the batch. This returns the number of
175 items that would need to be added, or -1 if the number
176 of additional items would exceed the capacity of the batch.
177 *****************************************************************************/
TestMerge(const CBatch & src)178 int TestMerge(const CBatch &src)
179 {
180 int i, nCnt;
181
182 nCnt = 0;
183 for(i = 0; i < src.m_nCnt; ++i)
184 if(!Contains(src.m_pnPalette[i]))
185 ++nCnt;
186
187 return m_nCnt+nCnt > m_nCapacity ? -1 : nCnt;
188 }
189
190 /*!***************************************************************************
191 @Function Contains
192 @Input src The batch to compare
193 @Return bool Returns true if the batch and the input batch
194 have at least one item in common
195 @Description Returns true if the batch's have at least one item in common
196 *****************************************************************************/
Contains(const CBatch & batch) const197 bool Contains(const CBatch &batch) const
198 {
199 int i;
200
201 for(i = 0; i < batch.m_nCnt; ++i)
202 if(!Contains(batch.m_pnPalette[i]))
203 return false;
204
205 return true;
206 }
207
208 /*!***************************************************************************
209 @Function Contains
210 @Input n The index of the new item
211 @Return bool Returns true if the batch contains the item
212 @Description Returns true if the batch contains the item.
213 *****************************************************************************/
Contains(const int n) const214 bool Contains(const int n) const
215 {
216 int i;
217
218 for(i = 0; i < m_nCnt; ++i)
219 if(m_pnPalette[i] == n)
220 return true;
221
222 return false;
223 }
224
225 /*!***************************************************************************
226 @Function Write
227 @Output pn The array of items to overwrite
228 @Output pnCnt The number of items in the array
229 @Description Writes the array of items and the number of items to the output
230 parameters.
231 *****************************************************************************/
Write(int * const pn,int * const pnCnt) const232 void Write(
233 int * const pn,
234 int * const pnCnt) const
235 {
236 memcpy(pn, m_pnPalette, m_nCnt * sizeof(*pn));
237 *pnCnt = m_nCnt;
238 }
239
240 /*!***************************************************************************
241 @Function GetVertexBoneIndices
242 @Modified pfI Returned index
243 @Input pfW Weight?
244 @Input n Length of index array
245 @Description For each element of the input array, the index value is compared
246 with the palette's index value. If the values are equal, the
247 value of the current input array element is replaced with the
248 palette index, otherwise the value is set to zero.
249 *****************************************************************************/
GetVertexBoneIndices(float * const pfI,const float * const pfW,const int n)250 void GetVertexBoneIndices(
251 float * const pfI,
252 const float * const pfW,
253 const int n)
254 {
255 int i, j;
256
257 for(i = 0; i < n; ++i)
258 {
259 if(pfW[i] != 0)
260 {
261 for(j = 0; j < m_nCnt; ++j)
262 {
263 if(pfI[i] != m_pnPalette[j])
264 continue;
265
266 pfI[i] = (float)j;
267 break;
268 }
269
270 // This batch *must* contain this vertex
271 _ASSERT(j != m_nCnt);
272 }
273 else
274 {
275 pfI[i] = 0;
276 }
277 }
278 }
279 };
280
281 /*!***************************************************************************
282 @Class CGrowableArray
283 @Brief Class that provides an array structure that can change its size dynamically.
284 *****************************************************************************/
285 class CGrowableArray
286 {
287 protected:
288 char *m_p;
289 int m_nSize;
290 int m_nCnt;
291
292 public:
293 /*!***************************************************************************
294 @Function CGrowableArray
295 @Input nSize The size of the data (in bytes) that the array will contain
296 @Description Initialises the size of the data the array will contain to the
297 value that has been passed in and initialises the remaining
298 data members with default values.
299 *****************************************************************************/
CGrowableArray(const int nSize)300 CGrowableArray(const int nSize)
301 {
302 m_p = NULL;
303 m_nSize = nSize;
304 m_nCnt = 0;
305 }
306
307 /*!***************************************************************************
308 @Function ~CGrowableArray
309 @Description The destructor
310 *****************************************************************************/
~CGrowableArray()311 ~CGrowableArray()
312 {
313 FREE(m_p);
314 }
315
316 /*!***************************************************************************
317 @Function Append
318 @Input pData The data to append
319 @Input nCnt The amount of data elements to append
320 @Description Resizes the array and appends the new data that has been passed in.
321 *****************************************************************************/
Append(const void * const pData,const int nCnt)322 void Append(const void * const pData, const int nCnt)
323 {
324 m_p = (char*)realloc(m_p, (m_nCnt + nCnt) * m_nSize);
325 _ASSERT(m_p);
326
327 memcpy(&m_p[m_nCnt * m_nSize], pData, nCnt * m_nSize);
328 m_nCnt += nCnt;
329 }
330
331 /*!***************************************************************************
332 @Function last
333 @Return char* The last element of the array
334 @Description Returns a pointer to the last element of the array.
335 *****************************************************************************/
last()336 char *last()
337 {
338 return at(m_nCnt-1);
339 }
340
341 /*!***************************************************************************
342 @Function at
343 @Input nIdx The index of the requested element
344 @Return char* The element at the specified index of the array
345 @Description Returns a pointer to the data at the specified index of the array.
346 *****************************************************************************/
at(const int nIdx)347 char *at(const int nIdx)
348 {
349 return &m_p[nIdx * m_nSize];
350 }
351
352 /*!***************************************************************************
353 @Function size
354 @Return int The number of elements contained in the array
355 @Description Returns the number of elements contained in the array.
356 *****************************************************************************/
size() const357 int size() const
358 {
359 return m_nCnt;
360 }
361
362 /*!***************************************************************************
363 @Function Surrender
364 @Output pData The pointer to surrender the data to
365 @Description Assigns the memory address of the data to the pointer that has
366 been passed in. Sets the class's number of elements and
367 data pointer back to their default values.
368 *****************************************************************************/
Surrender(char ** const pData)369 int Surrender(
370 char ** const pData)
371 {
372 int nCnt;
373
374 *pData = m_p;
375 nCnt = m_nCnt;
376
377 m_p = NULL;
378 m_nCnt = 0;
379
380 return nCnt;
381 }
382 };
383
384 /****************************************************************************
385 ** Constants
386 ****************************************************************************/
387
388 /****************************************************************************
389 ** Local function definitions
390 ****************************************************************************/
391 static bool FillBatch(
392 CBatch &batch,
393 const unsigned int * const pui32Idx, // input AND output; index array for triangle list
394 const char * const pVtx, // Input vertices
395 const int nStride, // Size of a vertex (in bytes)
396 const int nOffsetWeight, // Offset in bytes to the vertex bone-weights
397 EPVRTDataType eTypeWeight, // Data type of the vertex bone-weights
398 const int nOffsetIdx, // Offset in bytes to the vertex bone-indices
399 EPVRTDataType eTypeIdx, // Data type of the vertex bone-indices
400 const int nVertexBones); // Number of bones affecting each vertex
401
402 static bool BonesMatch(
403 const float * const pfIdx0,
404 const float * const pfIdx1);
405
406 /*****************************************************************************
407 ** Functions
408 *****************************************************************************/
409
410 /*!***************************************************************************
411 @Function Create
412 @Output pnVtxNumOut vertex count
413 @Output pVtxOut Output vertices (program must free() this)
414 @Modified pui32Idx index array for triangle list
415 @Input nVtxNum vertex count
416 @Input pVtx vertices
417 @Input nStride Size of a vertex (in bytes)
418 @Input nOffsetWeight Offset in bytes to the vertex bone-weights
419 @Input eTypeWeight Data type of the vertex bone-weights
420 @Input nOffsetIdx Offset in bytes to the vertex bone-indices
421 @Input eTypeIdx Data type of the vertex bone-indices
422 @Input nTriNum Number of triangles
423 @Input nBatchBoneMax Number of bones a batch can reference
424 @Input nVertexBones Number of bones affecting each vertex
425 @Returns PVR_SUCCESS if successful
426 @Description Fills the bone batch structure
427 *****************************************************************************/
Create(int * const pnVtxNumOut,char ** const pVtxOut,unsigned int * const pui32Idx,const int nVtxNum,const char * const pVtx,const int nStride,const int nOffsetWeight,const EPVRTDataType eTypeWeight,const int nOffsetIdx,const EPVRTDataType eTypeIdx,const int nTriNum,const int nBatchBoneMax,const int nVertexBones)428 EPVRTError CPVRTBoneBatches::Create(
429 int * const pnVtxNumOut,
430 char ** const pVtxOut,
431 unsigned int * const pui32Idx,
432 const int nVtxNum,
433 const char * const pVtx,
434 const int nStride,
435 const int nOffsetWeight,
436 const EPVRTDataType eTypeWeight,
437 const int nOffsetIdx,
438 const EPVRTDataType eTypeIdx,
439 const int nTriNum,
440 const int nBatchBoneMax,
441 const int nVertexBones)
442 {
443 int i, j, k, nTriCnt;
444 CBatch batch;
445 std::list<CBatch> lBatch;
446 std::list<CBatch>::iterator iBatch, iBatch2;
447 CBatch **ppBatch;
448 unsigned int *pui32IdxNew;
449 const char *pV, *pV2;
450 PVRTVECTOR4 vWeight, vIdx;
451 PVRTVECTOR4 vWeight2, vIdx2;
452 std::vector<int> *pvDup;
453 CGrowableArray *pVtxBuf;
454 unsigned int ui32SrcIdx;
455
456 memset(this, 0, sizeof(*this));
457
458 if(nVertexBones <= 0 || nVertexBones > 4)
459 {
460 _RPT0(_CRT_WARN, "CPVRTBoneBatching() will only handle 1..4 bones per vertex.\n");
461 return PVR_FAIL;
462 }
463
464 memset(&vWeight, 0, sizeof(vWeight));
465 memset(&vWeight2, 0, sizeof(vWeight2));
466 memset(&vIdx, 0, sizeof(vIdx));
467 memset(&vIdx2, 0, sizeof(vIdx2));
468
469 batch.SetSize(nBatchBoneMax);
470
471 // Allocate some working space
472 ppBatch = (CBatch**)malloc(nTriNum * sizeof(*ppBatch));
473 pui32IdxNew = (unsigned int*)malloc(nTriNum * 3 * sizeof(*pui32IdxNew));
474 pvDup = new std::vector<int>[nVtxNum];
475 pVtxBuf = new CGrowableArray(nStride);
476
477 // Check what batches are necessary
478 for(i = 0; i < nTriNum; ++i)
479 {
480 // Build the batch
481 if(!FillBatch(batch, &pui32Idx[i * 3], pVtx, nStride, nOffsetWeight, eTypeWeight, nOffsetIdx, eTypeIdx, nVertexBones))
482 {
483 free(pui32IdxNew);
484 return PVR_FAIL;
485 }
486
487 // Update the batch list
488 for(iBatch = lBatch.begin(); iBatch != lBatch.end(); ++iBatch)
489 {
490 // Do nothing if an existing batch is a superset of this new batch
491 if(iBatch->Contains(batch))
492 {
493 break;
494 }
495
496 // If this new batch is a superset of an existing batch, replace the old with the new
497 if(batch.Contains(*iBatch))
498 {
499 *iBatch = batch;
500 break;
501 }
502 }
503
504 // If no suitable batch exists, create a new one
505 if(iBatch == lBatch.end())
506 {
507 lBatch.push_back(batch);
508 }
509 }
510
511 // Group batches into fewer batches. This simple greedy algorithm could be improved.
512 int nCurrent, nShortest;
513 std::list<CBatch>::iterator iShortest;
514
515 for(iBatch = lBatch.begin(); iBatch != lBatch.end(); ++iBatch)
516 {
517 for(;;)
518 {
519 nShortest = nBatchBoneMax;
520 iBatch2 = iBatch;
521 ++iBatch2;
522 for(; iBatch2 != lBatch.end(); ++iBatch2)
523 {
524 nCurrent = iBatch->TestMerge(*iBatch2);
525
526 if(nCurrent >= 0 && nCurrent < nShortest)
527 {
528 nShortest = nCurrent;
529 iShortest = iBatch2;
530 }
531 }
532
533 if(nShortest < nBatchBoneMax)
534 {
535 iBatch->Merge(*iShortest);
536 lBatch.erase(iShortest);
537 }
538 else
539 {
540 break;
541 }
542 }
543 }
544
545 // Place each triangle in a batch.
546 for(i = 0; i < nTriNum; ++i)
547 {
548 if(!FillBatch(batch, &pui32Idx[i * 3], pVtx, nStride, nOffsetWeight, eTypeWeight, nOffsetIdx, eTypeIdx, nVertexBones))
549 {
550 free(pui32IdxNew);
551 return PVR_FAIL;
552 }
553
554 for(iBatch = lBatch.begin(); iBatch != lBatch.end(); ++iBatch)
555 {
556 if(iBatch->Contains(batch))
557 {
558 ppBatch[i] = &*iBatch;
559 break;
560 }
561 }
562
563 _ASSERT(iBatch != lBatch.end());
564 }
565
566 // Now that we know how many batches there are, we can allocate the output arrays
567 CPVRTBoneBatches::nBatchBoneMax = nBatchBoneMax;
568 pnBatches = (int*) calloc(lBatch.size() * nBatchBoneMax, sizeof(*pnBatches));
569 pnBatchBoneCnt = (int*) calloc(lBatch.size(), sizeof(*pnBatchBoneCnt));
570 pnBatchOffset = (int*) calloc(lBatch.size(), sizeof(*pnBatchOffset));
571
572 // Create the new triangle index list, the new vertex list, and the batch information.
573 nTriCnt = 0;
574 nBatchCnt = 0;
575
576 for(iBatch = lBatch.begin(); iBatch != lBatch.end(); ++iBatch)
577 {
578 // Write pnBatches, pnBatchBoneCnt and pnBatchOffset for this batch.
579 iBatch->Write(&pnBatches[nBatchCnt * nBatchBoneMax], &pnBatchBoneCnt[nBatchCnt]);
580 pnBatchOffset[nBatchCnt] = nTriCnt;
581 ++nBatchCnt;
582
583 // Copy any triangle indices for this batch
584 for(i = 0; i < nTriNum; ++i)
585 {
586 if(ppBatch[i] != &*iBatch)
587 continue;
588
589 for(j = 0; j < 3; ++j)
590 {
591 ui32SrcIdx = pui32Idx[3 * i + j];
592
593 // Get desired bone indices for this vertex/tri
594 pV = &pVtx[ui32SrcIdx * nStride];
595
596 PVRTVertexRead(&vWeight, &pV[nOffsetWeight], eTypeWeight, nVertexBones);
597 PVRTVertexRead(&vIdx, &pV[nOffsetIdx], eTypeIdx, nVertexBones);
598
599 iBatch->GetVertexBoneIndices(&vIdx.x, &vWeight.x, nVertexBones);
600 _ASSERT(vIdx.x == 0 || vIdx.x != vIdx.y);
601
602 // Check the list of copies of this vertex for one with suitable bone indices
603 for(k = 0; k < (int)pvDup[ui32SrcIdx].size(); ++k)
604 {
605 pV2 = pVtxBuf->at(pvDup[ui32SrcIdx][k]);
606
607 PVRTVertexRead(&vWeight2, &pV2[nOffsetWeight], eTypeWeight, nVertexBones);
608 PVRTVertexRead(&vIdx2, &pV2[nOffsetIdx], eTypeIdx, nVertexBones);
609
610 if(BonesMatch(&vIdx2.x, &vIdx.x))
611 {
612 pui32IdxNew[3 * nTriCnt + j] = pvDup[ui32SrcIdx][k];
613 break;
614 }
615 }
616
617 if(k != (int)pvDup[ui32SrcIdx].size())
618 continue;
619
620 // Did not find a suitable duplicate of the vertex, so create one
621 pVtxBuf->Append(pV, 1);
622 pvDup[ui32SrcIdx].push_back(pVtxBuf->size() - 1);
623
624 PVRTVertexWrite(&pVtxBuf->last()[nOffsetIdx], eTypeIdx, nVertexBones, &vIdx);
625
626 pui32IdxNew[3 * nTriCnt + j] = pVtxBuf->size() - 1;
627 }
628 ++nTriCnt;
629 }
630 }
631 _ASSERTE(nTriCnt == nTriNum);
632 _ASSERTE(nBatchCnt == (int)lBatch.size());
633
634 // Copy indices to output
635 memcpy(pui32Idx, pui32IdxNew, nTriNum * 3 * sizeof(*pui32IdxNew));
636
637 // Move vertices to output
638 *pnVtxNumOut = pVtxBuf->Surrender(pVtxOut);
639
640 // Free working memory
641 delete [] pvDup;
642 delete pVtxBuf;
643 FREE(ppBatch);
644 FREE(pui32IdxNew);
645
646 return PVR_SUCCESS;
647 }
648
649 /****************************************************************************
650 ** Local functions
651 ****************************************************************************/
652
653 /*!***********************************************************************
654 @Function FillBatch
655 @Modified batch The batch to fill
656 @Input pui32Idx Input index array for triangle list
657 @Input pVtx Input vertices
658 @Input nStride Size of a vertex (in bytes)
659 @Input nOffsetWeight Offset in bytes to the vertex bone-weights
660 @Input eTypeWeight Data type of the vertex bone-weights
661 @Input nOffsetIdx Offset in bytes to the vertex bone-indices
662 @Input eTypeIdx Data type of the vertex bone-indices
663 @Input nVertexBones Number of bones affecting each vertex
664 @Returns True if successful
665 @Description Creates a bone batch from a triangle.
666 *************************************************************************/
FillBatch(CBatch & batch,const unsigned int * const pui32Idx,const char * const pVtx,const int nStride,const int nOffsetWeight,EPVRTDataType eTypeWeight,const int nOffsetIdx,EPVRTDataType eTypeIdx,const int nVertexBones)667 static bool FillBatch(
668 CBatch &batch,
669 const unsigned int * const pui32Idx,
670 const char * const pVtx,
671 const int nStride,
672 const int nOffsetWeight,
673 EPVRTDataType eTypeWeight,
674 const int nOffsetIdx,
675 EPVRTDataType eTypeIdx,
676 const int nVertexBones)
677 {
678 PVRTVECTOR4 vWeight, vIdx;
679 const char *pV;
680 int i;
681 bool bOk;
682
683 bOk = true;
684 batch.Clear();
685 for(i = 0; i < 3; ++i)
686 {
687 pV = &pVtx[pui32Idx[i] * nStride];
688
689 memset(&vWeight, 0, sizeof(vWeight));
690 PVRTVertexRead(&vWeight, &pV[nOffsetWeight], eTypeWeight, nVertexBones);
691 PVRTVertexRead(&vIdx, &pV[nOffsetIdx], eTypeIdx, nVertexBones);
692
693 if(nVertexBones >= 1 && vWeight.x != 0) bOk &= batch.Add((int)vIdx.x);
694 if(nVertexBones >= 2 && vWeight.y != 0) bOk &= batch.Add((int)vIdx.y);
695 if(nVertexBones >= 3 && vWeight.z != 0) bOk &= batch.Add((int)vIdx.z);
696 if(nVertexBones >= 4 && vWeight.w != 0) bOk &= batch.Add((int)vIdx.w);
697 }
698 return bOk;
699 }
700
701 /*!***********************************************************************
702 @Function BonesMatch
703 @Input pfIdx0 A float 4 array
704 @Input pfIdx1 A float 4 array
705 @Returns True if the two float4 arraus are identical
706 @Description Checks if the two float4 arrays are identical.
707 *************************************************************************/
BonesMatch(const float * const pfIdx0,const float * const pfIdx1)708 static bool BonesMatch(
709 const float * const pfIdx0,
710 const float * const pfIdx1)
711 {
712 int i;
713
714 for(i = 0; i < 4; ++i)
715 {
716 if(pfIdx0[i] != pfIdx1[i])
717 return false;
718 }
719
720 return true;
721 }
722
723 /*****************************************************************************
724 End of file (PVRTBoneBatch.cpp)
725 *****************************************************************************/
726
727