1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "nel/3d/landscapevb_allocator.h"
23 #include "nel/3d/driver.h"
24 #include "nel/misc/fast_mem.h"
28 using namespace NLMISC
;
38 Once a reallocation of a VBHard occurs, how many vertices we add to the re-allocation, to avoid
39 as possible reallocations.
41 #define NL3D_LANDSCAPE_VERTEX_ALLOCATE_SECURITY 1024
43 The start size of the array.
45 #define NL3D_LANDSCAPE_VERTEX_ALLOCATE_START 4048
48 #define NL3D_VERTEX_FREE_MEMORY_RESERVE 1024
49 /*// 65000 is a maximum because of GeForce limitations.
50 #define NL3D_VERTEX_MAX_VERTEX_VBHARD 40000*/
53 // ***************************************************************************
54 CLandscapeVBAllocator::CLandscapeVBAllocator(TType type
, const std::string
&vbName
)
58 _VertexFreeMemory
.reserve(NL3D_VERTEX_FREE_MEMORY_RESERVE
);
60 _ReallocationOccur
= false;
61 _NumVerticesAllocated
= 0;
66 for(uint i
=0;i
<MaxVertexProgram
;i
++)
67 _VertexProgram
[i
]= NULL
;
70 // ***************************************************************************
71 CLandscapeVBAllocator::~CLandscapeVBAllocator()
77 // ***************************************************************************
78 void CLandscapeVBAllocator::updateDriver(IDriver
*driver
)
80 // test change of driver.
82 if( _Driver
==NULL
|| driver
!=_Driver
)
88 // If change of driver, delete the VertexProgram first, if any
89 deleteVertexProgram();
90 // Then rebuild VB format, and VertexProgram, if needed.
91 // Do it only if VP supported by GPU.
92 setupVBFormatAndVertexProgram(!_Driver
->isVertexProgramEmulated() && (
93 _Driver
->supportVertexProgram(CVertexProgram::nelvp
)
94 // || _Driver->supportVertexProgram(CVertexProgram::glsl330v) // TODO_VP_GLSL
97 // must reallocate the VertexBuffer.
98 if( _NumVerticesAllocated
>0 )
99 allocateVertexBuffer(_NumVerticesAllocated
);
104 // ***************************************************************************
105 void CLandscapeVBAllocator::clear()
108 _VertexFreeMemory
.clear();
109 _NumVerticesAllocated
= 0;
112 deleteVertexBuffer();
114 // delete vertex Program, if any
115 deleteVertexProgram();
117 // clear other states.
118 _ReallocationOccur
= false;
124 // ***************************************************************************
125 void CLandscapeVBAllocator::resetReallocation()
127 _ReallocationOccur
= false;
132 // ***************************************************************************
133 // ***************************************************************************
135 // ***************************************************************************
136 // ***************************************************************************
140 // ***************************************************************************
141 uint
CLandscapeVBAllocator::allocateVertex()
143 // if no more free, allocate.
144 if( _VertexFreeMemory
.empty() )
148 if(_NumVerticesAllocated
==0)
149 newResize
= NL3D_LANDSCAPE_VERTEX_ALLOCATE_START
;
151 newResize
= NL3D_LANDSCAPE_VERTEX_ALLOCATE_SECURITY
;
152 _NumVerticesAllocated
+= newResize
;
154 #ifdef NL_LANDSCAPE_INDEX16
155 nlassert(_NumVerticesAllocated
<= 65535);
157 allocateVertexBuffer(_NumVerticesAllocated
);
158 // resize infos on vertices.
159 _VertexInfos
.resize(_NumVerticesAllocated
);
161 // Fill list of free elements.
162 for(uint i
=0;i
<newResize
;i
++)
164 // create a new entry which points to this vertex.
165 // the list is made so allocation is in growing order.
166 _VertexFreeMemory
.push_back( _NumVerticesAllocated
- (i
+1) );
168 // Mark as free the new vertices. (Debug).
169 _VertexInfos
[_NumVerticesAllocated
- (i
+1)].Free
= true;
173 // get a vertex (pop_back).
174 uint id
= _VertexFreeMemory
.back();
175 // delete this vertex free entry.
176 _VertexFreeMemory
.pop_back();
178 // check and Mark as not free the vertex. (Debug).
179 nlassert(id
<_NumVerticesAllocated
);
180 nlassert(_VertexInfos
[id
].Free
);
181 _VertexInfos
[id
].Free
= false;
189 // ***************************************************************************
190 void CLandscapeVBAllocator::deleteVertex(uint vid
)
192 // check and Mark as free the vertex. (Debug).
193 nlassert(vid
<_NumVerticesAllocated
);
194 nlassert(!_VertexInfos
[vid
].Free
);
195 _VertexInfos
[vid
].Free
= true;
197 // Add this vertex to the free list.
198 // create a new entry which points to this vertex.
199 _VertexFreeMemory
.push_back( vid
);
203 // ***************************************************************************
204 void CLandscapeVBAllocator::lockBuffer(CFarVertexBufferInfo
&farVB
)
206 nlassert( _Type
==Far0
|| _Type
==Far1
);
213 farVB
.setupVertexBuffer(_VB
, _VertexProgram
[0]!=NULL
);
217 // ***************************************************************************
218 void CLandscapeVBAllocator::lockBuffer(CNearVertexBufferInfo
&tileVB
)
220 nlassert(_Type
==Tile
);
225 _LastNearVB
= &tileVB
;
227 tileVB
.setupVertexBuffer(_VB
, _VertexProgram
[0]!=NULL
);
231 // ***************************************************************************
232 void CLandscapeVBAllocator::unlockBuffer()
237 _LastFarVB
->setupNullPointers();
240 _LastNearVB
->setupNullPointers();
242 _BufferLocked
= false;
247 // ***************************************************************************
248 // ***************************************************************************
250 // ***************************************************************************
251 // ***************************************************************************
254 // ***************************************************************************
255 void CLandscapeVBAllocator::activate(uint vpId
)
258 nlassert(!_BufferLocked
);
262 _Driver
->activeVertexBuffer(_VB
);
266 // ***************************************************************************
267 void CLandscapeVBAllocator::activateVP(uint vpId
)
271 // If enabled, activate Vertex program first.
272 if (_VertexProgram
[vpId
])
274 //nlinfo("\nSTARTVP\n%s\nENDVP\n", _VertexProgram[vpId]->getProgram().c_str());
275 nlverify(_Driver
->activeVertexProgram(_VertexProgram
[vpId
]));
280 // ***************************************************************************
281 void CLandscapeVBAllocator::deleteVertexBuffer()
283 // must unlock VBhard before.
286 // delete the soft one.
287 _VB
.deleteAllVertices();
291 // ***************************************************************************
292 void CLandscapeVBAllocator::allocateVertexBuffer(uint32 numVertices
)
294 // no allocation must be done if the Driver is not setuped, or if the driver has been deleted by refPtr.
297 // allocate() =>_ReallocationOccur= true;
298 _ReallocationOccur
= true;
299 // must unlock VBhard before.
302 // This always works.
303 _VB
.setPreferredMemory(CVertexBuffer::AGPPreferred
, false);
304 _VB
.setNumVertices(numVertices
);
305 _VB
.setName (_VBName
);
309 // ***************************************************************************
310 // ***************************************************************************
312 // ***************************************************************************
313 // ***************************************************************************
316 // ***************************************************************************
318 Common Part. Inputs and constants.
323 v[0] == StartPos. Hence, It is the SplitPoint of the father face.
325 v[9] == Tex1 (xy) (different meanings for Far and Tile).
326 v[13] == Tex2 (xy) (just for Tile mode).
329 v[10] == { GeomFactor, MaxNearLimit }
330 * where GeomFactor == max(SizeFaceA, SizeFaceB) * OORefineThreshold.
331 It's means vertices are re-computed when the RefineThreshold setup change.
333 * MaxNearLimit= max(nearLimitFaceA, nearLimitFaceB)
335 v[11].xyz == EndPos-StartPos
337 Alpha: NB: Since only useful for Far1, v[12] is not in the VB for Far0 and Tile VertexBuffer.
338 v[12] == { TransitionSqrMin, OOTransitionSqrDelta}
339 * TransitionSqrMin, OOTransitionSqrDelta : Alpha transition, see preRender().
340 There is only 3 values possibles. It depends on Far1 type. Changed in preRender()
345 Setuped at beginning of CLandscape::render()
346 c[0..3]= ModelViewProjection Matrix.
349 c[6]= {TileDistFarSqr, OOTileDistDeltaSqr, *, *}
351 c[8..11]= ModelView Matrix (for Fog).
352 c[12]= PZBModelPosition: landscape center / delta Position to apply before multipliying by mviewMatrix
357 Fog is computed on geomorphed position R1.
358 R1.w==1, and suppose that ModelViewMatrix has no Projection Part.
359 Then Homogenous-coordinate == Non-Homogenous-coordinate.
360 Hence we need only (FogVector*R1).z to get the FogC value.
361 => computed in just on instruction.
365 // ***********************
367 Common start of the program for Far0, Far1 and Tile mode.
368 It compute the good Geomorphed position.
369 At the end of this program, nothing is written in the output register, and we have in the Temp Registers:
372 - R1= CurrentPos geomorphed
373 - R2.x= sqrDist= (startPos - RefineCenter).sqrnorm(). Useful for alpha computing.
376 NB: 9 ope for normal errorMetric, and 9 ope for smoothing with TileNear.
378 The C code for this Program is: (v[] means data is a vertex input, c[] means it is a constant)
380 // Compute Basic ErrorMetric.
381 sqrDist= (v[StartPos] - c[RefineCenter]).sqrnorm()
382 ErrorMetric= v[GeomFactor] / sqrDist
384 // Compute ErrorMetric modified by TileNear transition.
385 f= (c[TileDistFarSqr] - sqrDist) * c[OOTileDistDeltaSqr]
387 // ^4 gives better smooth result
388 f= sqr(f); f= sqr(f);
389 // interpolate the errorMetric
390 ErrorMetricModified= v[MaxNearLimit]*f + ErrorMetric*(1-f);
392 // Take the max errorMetric.
393 ErrorMetric= max(ErrorMetric, ErrorMetricModified);
395 // Interpolate StartPos to EndPos, between 1 and 2.
398 R1= f * v[EndPos-StartPos] + StartPos;
402 const char* NL3D_LandscapeCommonStartProgram
=
404 # compute Basic geomorph into R0.x \n\
405 ADD R0, v[0], -c[5]; # R0 = startPos - RefineCenter \n\
406 DP3 R2.x, R0, R0; # R2.x= sqrDist= (startPos - RefineCenter).sqrnorm()\n\
407 RCP R0.x, R2.x; # R0.x= 1 / sqrDist \n\
408 MUL R0.x, v[10].x, R0.x; # R0.x= ErrorMetric= GeomFactor / sqrDist \n\
410 # compute Transition Factor To TileNear Geomorph, into R0.z \n\
411 ADD R0.z, c[6].x, -R2.x; # R0.z= TileDistFarSqr - sqrDist \n\
412 MUL R0.z, R0.z, c[6].y; # R0.z= f= (TileDistFarSqr - sqrDist ) * OOTileDistDeltaSqr \n\
413 MAX R0.z, R0.z, c[4].x; \n\
414 MIN R0.z, R0.z, c[4].y; # R0.z= f= clamp(f, 0, 1); \n\
415 MUL R0.z, R0.z, R0.z; \n\
416 MUL R0.z, R0.z, R0.z; # R0.z= finalFactor= f^4 \n\
418 # Apply the transition factor to the ErrorMetric => R0.w= ErrorMetricModified. \n\
419 ADD R0.w, v[10].y, -R0.x; # R0.w= maxNearLimit - ErrorMetric \n\
420 MAD R0.w, R0.z, R0.w, R0.x; # R0.w= finalFactor * (maxNearLimit - ErrorMetric) + ErrorMetric \n\
422 # R0.w may be < R0.x; (when the point is very near). Must take the bigger errorMetric. \n\
423 MAX R0.x, R0.x, R0.w; # R0.x= ErrorMetric Max \n\
425 # apply geomorph into R1 \n\
426 ADD R0.x, R0.x, -c[4].y; # R0.x= ErrorMetric Max - 1 \n\
427 MAX R0.x, R0.x, c[4].x; \n\
428 MIN R0.x, R0.x, c[4].y; # R0.x= geomBlend= clamp(R0.x, 0, 1); \n\
430 # NB: Can't use MAD R1.xyz, v[11], R0.x, v[0], because can't acces 2 inputs in one op. \n\
431 # Hence, can use a MAD to Sub the Landscape Center _PZBModelPosition \n\
432 # write to R1.w is useless (but needed to avoid a read error when multiplied by 0) \n\
433 # in the next instruction \n\
434 MAD R1.xyzw, v[11], R0.x, -c[12]; \n\
435 # set w to 1 by using c[4] = { 0, 1, 0.5, 0} \n\
436 MAD R1, R1, c[4].yyyx, v[0]; # R1= geomBlend * (EndPos-StartPos) + StartPos \n\
440 // ***********************
443 # compute Basic geomorph into R0.x \n\
444 ADD R0, v[0], -c[5]; # R0 = startPos - RefineCenter \n\
445 DP3 R2.x, R0, R0; # R2.x= sqrDist= (startPos - RefineCenter).sqrnorm()\n\
446 MOV R1, v[0]; # R1= geomBlend * (EndPos-StartPos) + StartPos \n\
449 const string NL3D_LandscapeTestSpeedProgram
=
450 " MOV R1, R1; \n MOV R1, R1; \n MOV R1, R1; \n MOV R1, R1; \n MOV R1, R1; \n\
451 MOV R1, R1; \n MOV R1, R1; \n MOV R1, R1; \n MOV R1, R1; \n MOV R1, R1; \n\
455 // ***********************
458 just project, copy uv0 and uv1
459 NB: leave o[COL0] undefined because the material don't care diffuse RGBA here
461 // ***********************
462 const char* NL3D_LandscapeFar0EndProgram
=
463 " # compute in Projection space \n\
464 DP4 o[HPOS].x, c[0], R1; \n\
465 DP4 o[HPOS].y, c[1], R1; \n\
466 DP4 o[HPOS].z, c[2], R1; \n\
467 DP4 o[HPOS].w, c[3], R1; \n\
468 MOV o[TEX0], v[8]; \n\
469 MOV o[TEX1], v[9]; \n\
470 DP4 o[FOGC].x, c[10], R1; \n\
475 // ***********************
478 Compute Alpha transition.
479 Project, copy uv0 and uv1,
480 NB: leave o[COL0] RGB undefined because the material don't care diffuse RGB
482 // ***********************
483 const char* NL3D_LandscapeFar1EndProgram
=
484 " # compute Alpha Transition \n\
485 ADD R0.x, R2.x, -v[12].x; # R0.x= sqrDist-TransitionSqrMin \n\
486 MUL R0.x, R0.x, v[12].y; # R0.x= (sqrDist-TransitionSqrMin) * OOTransitionSqrDelta \n\
487 MAX R0.x, R0.x, c[4].x; \n\
488 MIN o[COL0].w, R0.x, c[4].y; # col.A= clamp(R0.x, 0, 1); \n\
490 # compute in Projection space \n\
491 DP4 o[HPOS].x, c[0], R1; \n\
492 DP4 o[HPOS].y, c[1], R1; \n\
493 DP4 o[HPOS].z, c[2], R1; \n\
494 DP4 o[HPOS].w, c[3], R1; \n\
495 MOV o[TEX0], v[8]; \n\
496 MOV o[TEX1], v[9]; \n\
497 DP4 o[FOGC].x, c[10], R1; \n\
502 // ***********************
505 just project, copy uv0, uv1.
506 NB: leave o[COL0] undefined because the material don't care diffuse RGBA here
508 // ***********************
509 const char* NL3D_LandscapeTileEndProgram
=
510 " # compute in Projection space \n\
511 DP4 o[HPOS].x, c[0], R1; \n\
512 DP4 o[HPOS].y, c[1], R1; \n\
513 DP4 o[HPOS].z, c[2], R1; \n\
514 DP4 o[HPOS].w, c[3], R1; \n\
515 MOV o[TEX0], v[8]; \n\
516 MOV o[TEX1], v[9]; \n\
517 DP4 o[FOGC].x, c[10], R1; \n\
521 /// Same version but write Tex0 to take uv2, ie v[13], for lightmap pass
522 const char* NL3D_LandscapeTileLightMapEndProgram
=
523 " # compute in Projection space \n\
524 DP4 o[HPOS].x, c[0], R1; \n\
525 DP4 o[HPOS].y, c[1], R1; \n\
526 DP4 o[HPOS].z, c[2], R1; \n\
527 DP4 o[HPOS].w, c[3], R1; \n\
528 MOV o[TEX0], v[12]; \n\
529 MOV o[TEX1], v[9]; \n\
530 DP4 o[FOGC].x, c[10], R1; \n\
535 // ***************************************************************************
536 void CLandscapeVBAllocator::deleteVertexProgram()
538 for (uint i
= 0; i
< MaxVertexProgram
; ++i
)
540 if (_VertexProgram
[i
])
542 _VertexProgram
[i
] = NULL
; // smartptr
548 // ***************************************************************************
549 void CLandscapeVBAllocator::setupVBFormatAndVertexProgram(bool withVertexProgram
)
551 // If not vertexProgram mode
552 if(!withVertexProgram
)
554 // setup normal VB format.
557 _VB
.setVertexFormat(CVertexBuffer::PositionFlag
| CVertexBuffer::TexCoord0Flag
| CVertexBuffer::TexCoord1Flag
);
560 _VB
.setVertexFormat(CVertexBuffer::PositionFlag
| CVertexBuffer::TexCoord0Flag
| CVertexBuffer::TexCoord1Flag
| CVertexBuffer::PrimaryColorFlag
);
562 // v3f/t2f0/t2f1/t2f2
563 _VB
.setVertexFormat(CVertexBuffer::PositionFlag
| CVertexBuffer::TexCoord0Flag
| CVertexBuffer::TexCoord1Flag
| CVertexBuffer::TexCoord2Flag
);
567 // Else Setup our Vertex Program, and good VBuffers, according to _Type.
571 // Build the Vertex Format.
573 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_STARTPOS
, CVertexBuffer::Float3
); // v[0]= StartPos.
574 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_TEX0
, CVertexBuffer::Float2
); // v[8]= Tex0.
575 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_TEX1
, CVertexBuffer::Float2
); // v[9]= Tex1.
576 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_GEOMINFO
, CVertexBuffer::Float2
); // v[10]= GeomInfos.
577 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_DELTAPOS
, CVertexBuffer::Float3
); // v[11]= EndPos-StartPos.
580 // Init the Vertex Program.
581 _VertexProgram
[0] = new CVertexProgramLandscape(Far0
);
582 nlverify(_Driver
->compileVertexProgram(_VertexProgram
[0]));
586 // Build the Vertex Format.
588 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_STARTPOS
, CVertexBuffer::Float3
); // v[0]= StartPos.
589 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_TEX0
, CVertexBuffer::Float2
); // v[8]= Tex0.
590 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_TEX1
, CVertexBuffer::Float2
); // v[9]= Tex1.
591 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_GEOMINFO
, CVertexBuffer::Float2
); // v[10]= GeomInfos.
592 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_DELTAPOS
, CVertexBuffer::Float3
); // v[11]= EndPos-StartPos.
593 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_ALPHAINFO
, CVertexBuffer::Float2
); // v[12]= AlphaInfos.
596 // Init the Vertex Program.
597 _VertexProgram
[0] = new CVertexProgramLandscape(Far1
);
598 nlverify(_Driver
->compileVertexProgram(_VertexProgram
[0]));
602 // Build the Vertex Format.
604 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_STARTPOS
, CVertexBuffer::Float3
); // v[0]= StartPos.
605 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_TEX0
, CVertexBuffer::Float2
); // v[8]= Tex0.
606 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_TEX1
, CVertexBuffer::Float2
); // v[9]= Tex1.
607 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_TEX2
, CVertexBuffer::Float2
); // v[12]= Tex2.
608 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_GEOMINFO
, CVertexBuffer::Float2
); // v[10]= GeomInfos.
609 _VB
.addValueEx(NL3D_LANDSCAPE_VPPOS_DELTAPOS
, CVertexBuffer::Float3
); // v[11]= EndPos-StartPos.
612 // Init the Vertex Program.
613 _VertexProgram
[0] = new CVertexProgramLandscape(Tile
, false);
614 nlverify(_Driver
->compileVertexProgram(_VertexProgram
[0]));
616 // Init the Vertex Program for lightmap pass
617 _VertexProgram
[1] = new CVertexProgramLandscape(Tile
, true);
618 nlverify(_Driver
->compileVertexProgram(_VertexProgram
[1]));
624 CVertexProgramLandscape::CVertexProgramLandscape(CLandscapeVBAllocator::TType type
, bool lightMap
)
628 CSource
*source
= new CSource();
629 source
->Profile
= nelvp
;
630 source
->DisplayName
= "Landscape/nelvp";
633 case CLandscapeVBAllocator::Far0
:
634 source
->DisplayName
+= "/far0";
635 source
->setSource(std::string(NL3D_LandscapeCommonStartProgram
)
636 + std::string(NL3D_LandscapeFar0EndProgram
));
638 case CLandscapeVBAllocator::Far1
:
639 source
->DisplayName
+= "/far1";
640 source
->setSource(std::string(NL3D_LandscapeCommonStartProgram
)
641 + std::string(NL3D_LandscapeFar1EndProgram
));
643 case CLandscapeVBAllocator::Tile
:
644 source
->DisplayName
+= "/tile";
647 source
->DisplayName
+= "/lightmap";
648 source
->setSource(std::string(NL3D_LandscapeCommonStartProgram
)
649 + std::string(NL3D_LandscapeTileLightMapEndProgram
));
653 source
->setSource(std::string(NL3D_LandscapeCommonStartProgram
)
654 + std::string(NL3D_LandscapeTileEndProgram
));
658 source
->ParamIndices
["modelViewProjection"] = 0;
659 source
->ParamIndices
["programConstants0"] = 4;
660 source
->ParamIndices
["refineCenter"] = 5;
661 source
->ParamIndices
["tileDist"] = 6;
662 source
->ParamIndices
["fog"] = 10;
663 source
->ParamIndices
["pzbModelPosition"] = 12;
672 void CVertexProgramLandscape::buildInfo()
674 m_Idx
.ProgramConstants0
= getUniformIndex("programConstants0");
675 nlassert(m_Idx
.ProgramConstants0
!= std::numeric_limits
<uint
>::max());
676 m_Idx
.RefineCenter
= getUniformIndex("refineCenter");
677 nlassert(m_Idx
.RefineCenter
!= std::numeric_limits
<uint
>::max());
678 m_Idx
.TileDist
= getUniformIndex("tileDist");
679 nlassert(m_Idx
.TileDist
!= std::numeric_limits
<uint
>::max());
680 m_Idx
.PZBModelPosition
= getUniformIndex("pzbModelPosition");
681 nlassert(m_Idx
.PZBModelPosition
!= std::numeric_limits
<uint
>::max());