//----------------------------------------------------------------------------- // end //----------------------------------------------------------------------------- GFXVertexBuffer * endToBuffer( U32 &numPrims ) { mVertBuff.set(GFX, mTempVertBuff.size(), GFXBufferTypeVolatile); GFXVertexPCT *verts = mVertBuff.lock(); dMemcpy( verts, mTempVertBuff.address(), mTempVertBuff.size() * sizeof(GFXVertexPCT) ); mVertBuff.unlock(); VERTEX_SIZE_CHECK(); switch( mType ) { case GFXPointList: { numPrims = mCurVertIndex; break; } case GFXLineList: { numPrims = mCurVertIndex / 2; break; } case GFXLineStrip: { numPrims = mCurVertIndex - 1; break; } case GFXTriangleList: { numPrims = mCurVertIndex / 3; break; } case GFXTriangleStrip: case GFXTriangleFan: { numPrims = mCurVertIndex - 2; break; } case GFXPT_COUNT: // handle warning break; } return mVertBuff; }
void CameraSpline::renderTimeMap() { buildTimeMap(); gBuilding = true; // Build vertex buffer GFXVertexBufferHandle<GFXVertexPCT> vb; vb.set(GFX, mTimeMap.size(), GFXBufferTypeVolatile); void *ptr = vb.lock(); if(!ptr) return; MRandomLCG random(1376312589 * (uintptr_t)this); S32 index = 0; for(Vector<TimeMap>::iterator itr=mTimeMap.begin(); itr != mTimeMap.end(); itr++) { Knot a; value(itr->mTime, &a, true); S32 cr = random.randI(0,255); S32 cg = random.randI(0,255); S32 cb = random.randI(0,255); vb[index].color.set(cr, cg, cb); vb[index].point.set(a.mPosition.x, a.mPosition.y, a.mPosition.z); index++; } gBuilding = false; vb.unlock(); // Render the buffer GFX->pushWorldMatrix(); GFX->setupGenericShaders(); GFX->setVertexBuffer(vb); GFX->drawPrimitive(GFXLineStrip,0,index); GFX->popWorldMatrix(); }
void ScatterSky::_renderMoon( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) { if ( !mMoonMatInst ) return; Point3F moonlightPosition = state->getCameraPosition() - /*mLight->getDirection()*/ mMoonLightDir * state->getFarPlane() * 0.9f; F32 dist = (moonlightPosition - state->getCameraPosition()).len(); // worldRadius = screenRadius * dist / worldToScreen // screenRadius = worldRadius / dist * worldToScreen // F32 screenRadius = GFX->getViewport().extent.y * mMoonScale * 0.5f; F32 worldRadius = screenRadius * dist / state->getWorldToScreenScale().y; // Calculate Billboard Radius (in world units) to be constant, independent of distance. // Takes into account distance, viewport size, and specified size in editor F32 BBRadius = worldRadius; mMatrixSet->restoreSceneViewProjection(); if ( state->isReflectPass() ) mMatrixSet->setProjection( state->getSceneManager()->getNonClipProjection() ); mMatrixSet->setWorld( MatrixF::Identity ); // Initialize points with basic info Point3F points[4]; points[0] = Point3F(-BBRadius, 0.0, -BBRadius); points[1] = Point3F( -BBRadius, 0.0, BBRadius); points[2] = Point3F( BBRadius, 0.0, BBRadius); points[3] = Point3F( BBRadius, 0.0, -BBRadius); static const Point2F sCoords[4] = { Point2F( 0.0f, 0.0f ), Point2F( 0.0f, 1.0f ), Point2F( 1.0f, 1.0f ), Point2F( 1.0f, 0.0f ) }; // Get info we need to adjust points const MatrixF &camView = state->getCameraTransform(); // Finalize points for(int i = 0; i < 4; i++) { // align with camera camView.mulV(points[i]); // offset points[i] += moonlightPosition; } // Vertex color. ColorF moonVertColor( 1.0f, 1.0f, 1.0f, mNightInterpolant ); // Copy points to buffer. GFXVertexBufferHandle< GFXVertexPCT > vb; vb.set( GFX, 4, GFXBufferTypeVolatile ); GFXVertexPCT *pVert = vb.lock(); for ( S32 i = 0; i < 4; i++ ) { pVert->color.set( moonVertColor ); pVert->point.set( points[i] ); pVert->texCoord.set( sCoords[i].x, sCoords[i].y ); pVert++; } vb.unlock(); // Setup SceneData struct. SceneData sgData; sgData.wireframe = GFXDevice::getWireframe(); sgData.visibility = 1.0f; // Draw it while ( mMoonMatInst->setupPass( state, sgData ) ) { mMoonMatInst->setTransforms( *mMatrixSet, state ); mMoonMatInst->setSceneInfo( state, sgData ); GFX->setVertexBuffer( vb ); GFX->drawPrimitive( GFXTriangleFan, 0, 2 ); } }
void TerrainBlock::_updateBaseTexture(bool writeToCache) { if ( !mBaseShader && !_initBaseShader() ) return; // This can sometimes occur outside a begin/end scene. const bool sceneBegun = GFX->canCurrentlyRender(); if ( !sceneBegun ) GFX->beginScene(); GFXDEBUGEVENT_SCOPE( TerrainBlock_UpdateBaseTexture, ColorI::GREEN ); PROFILE_SCOPE( TerrainBlock_UpdateBaseTexture ); GFXTransformSaver saver; const U32 maxTextureSize = GFX->getCardProfiler()->queryProfile( "maxTextureSize", 1024 ); U32 baseTexSize = getNextPow2( mBaseTexSize ); baseTexSize = getMin( maxTextureSize, baseTexSize ); Point2I destSize( baseTexSize, baseTexSize ); // Setup geometry GFXVertexBufferHandle<GFXVertexPT> vb; { F32 copyOffsetX = 2.0f * GFX->getFillConventionOffset() / (F32)destSize.x; F32 copyOffsetY = 2.0f * GFX->getFillConventionOffset() / (F32)destSize.y; GFXVertexPT points[4]; points[0].point = Point3F(1.0 - copyOffsetX, -1.0 + copyOffsetY, 0.0); points[0].texCoord = Point2F(1.0, 1.0f); points[1].point = Point3F(1.0 - copyOffsetX, 1.0 + copyOffsetY, 0.0); points[1].texCoord = Point2F(1.0, 0.0f); points[2].point = Point3F(-1.0 - copyOffsetX, -1.0 + copyOffsetY, 0.0); points[2].texCoord = Point2F(0.0, 1.0f); points[3].point = Point3F(-1.0 - copyOffsetX, 1.0 + copyOffsetY, 0.0); points[3].texCoord = Point2F(0.0, 0.0f); vb.set( GFX, 4, GFXBufferTypeVolatile ); GFXVertexPT *ptr = vb.lock(); if(ptr) { dMemcpy( ptr, points, sizeof(GFXVertexPT) * 4 ); vb.unlock(); } } GFXTexHandle blendTex; // If the base texture is already a valid render target then // use it to render to else we create one. if ( mBaseTex.isValid() && mBaseTex->isRenderTarget() && mBaseTex->getFormat() == GFXFormatR8G8B8A8 && mBaseTex->getWidth() == destSize.x && mBaseTex->getHeight() == destSize.y ) blendTex = mBaseTex; else blendTex.set( destSize.x, destSize.y, GFXFormatR8G8B8A8, &GFXDefaultRenderTargetProfile, "" ); GFX->pushActiveRenderTarget(); // Set our shader stuff GFX->setShader( mBaseShader ); GFX->setShaderConstBuffer( mBaseShaderConsts ); GFX->setStateBlock( mBaseShaderSB ); GFX->setVertexBuffer( vb ); mBaseTarget->attachTexture( GFXTextureTarget::Color0, blendTex ); GFX->setActiveRenderTarget( mBaseTarget ); GFX->clear( GFXClearTarget, ColorI(0,0,0,255), 1.0f, 0 ); GFX->setTexture( 0, mLayerTex ); mBaseShaderConsts->setSafe( mBaseLayerSizeConst, (F32)mLayerTex->getWidth() ); for ( U32 i=0; i < mBaseTextures.size(); i++ ) { GFXTextureObject *tex = mBaseTextures[i]; if ( !tex ) continue; GFX->setTexture( 1, tex ); F32 baseSize = mFile->mMaterials[i]->getDiffuseSize(); F32 scale = 1.0f; if ( !mIsZero( baseSize ) ) scale = getWorldBlockSize() / baseSize; // A mistake early in development means that texture // coords are not flipped correctly. To compensate // we flip the y scale here. mBaseShaderConsts->setSafe( mBaseTexScaleConst, Point2F( scale, -scale ) ); mBaseShaderConsts->setSafe( mBaseTexIdConst, (F32)i ); GFX->drawPrimitive( GFXTriangleStrip, 0, 2 ); } mBaseTarget->resolve(); GFX->setShader( NULL ); //GFX->setStateBlock( NULL ); // WHY NOT? GFX->setShaderConstBuffer( NULL ); GFX->setVertexBuffer( NULL ); GFX->popActiveRenderTarget(); // End it if we begun it... Yeehaw! if ( !sceneBegun ) GFX->endScene(); /// Do we cache this sucker? if (mBaseTexFormat == NONE || !writeToCache) { // We didn't cache the result, so set the base texture // to the render target we updated. This should be good // for realtime painting cases. mBaseTex = blendTex; } else if (mBaseTexFormat == DDS) { String cachePath = _getBaseTexCacheFileName(); FileStream fs; if ( fs.open( _getBaseTexCacheFileName(), Torque::FS::File::Write ) ) { // Read back the render target, dxt compress it, and write it to disk. GBitmap blendBmp( destSize.x, destSize.y, false, GFXFormatR8G8B8A8 ); blendTex.copyToBmp( &blendBmp ); /* // Test code for dumping uncompressed bitmap to disk. { FileStream fs; if ( fs.open( "./basetex.png", Torque::FS::File::Write ) ) { blendBmp.writeBitmap( "png", fs ); fs.close(); } } */ blendBmp.extrudeMipLevels(); DDSFile *blendDDS = DDSFile::createDDSFileFromGBitmap( &blendBmp ); DDSUtil::squishDDS( blendDDS, GFXFormatDXT1 ); // Write result to file stream blendDDS->write( fs ); delete blendDDS; } fs.close(); } else { FileStream stream; if (!stream.open(_getBaseTexCacheFileName(), Torque::FS::File::Write)) { mBaseTex = blendTex; return; } GBitmap bitmap(blendTex->getWidth(), blendTex->getHeight(), false, GFXFormatR8G8B8); blendTex->copyToBmp(&bitmap); bitmap.writeBitmap(formatToExtension(mBaseTexFormat), stream); } }
bool DecalManager::prepRenderImage(SceneState* state, const U32 stateKey, const U32 /*startZone*/, const bool /*modifyBaseState*/) { PROFILE_SCOPE( DecalManager_RenderDecals ); if ( !smDecalsOn || !mData ) return false; if (isLastState(state, stateKey)) return false; setLastState(state, stateKey); if ( !state->isDiffusePass() && !state->isReflectPass() ) return false; PROFILE_START( DecalManager_RenderDecals_SphereTreeCull ); // Grab this before anything here changes it. mCuller = state->getFrustum(); // Populate vector of decal instances to be rendered with all // decals from visible decal spheres. mDecalQueue.clear(); const Vector<DecalSphere*> &grid = mData->getGrid(); for ( U32 i = 0; i < grid.size(); i++ ) { const DecalSphere *decalSphere = grid[i]; const SphereF &worldSphere = decalSphere->mWorldSphere; if ( !mCuller.sphereInFrustum( worldSphere.center, worldSphere.radius ) ) continue; // TODO: If each sphere stored its largest decal instance we // could do an LOD step on it here and skip adding any of the // decals in the sphere. mDecalQueue.merge( decalSphere->mItems ); } PROFILE_END(); PROFILE_START( DecalManager_RenderDecals_Update ); const U32 &curSimTime = Sim::getCurrentTime(); const Point2I &viewportExtent = state->getViewportExtent(); Point3F cameraOffset; F32 decalSize, pixelRadius; U32 delta, diff; DecalInstance *dinst; // Loop through DecalQueue once for preRendering work. // 1. Update DecalInstance fade (over time) // 2. Clip geometry if flagged to do so. // 3. Calculate lod - if decal is far enough away it will not render. for ( U32 i = 0; i < mDecalQueue.size(); i++ ) { dinst = mDecalQueue[i]; // LOD calculation. // See TSShapeInstance::setDetailFromDistance for more // information on these calculations. decalSize = getMax( dinst->mSize, 0.001f ); pixelRadius = dinst->calcPixelRadius( state ); // Need to clamp here. if ( pixelRadius < dinst->calcEndPixRadius( viewportExtent ) ) { mDecalQueue.erase_fast( i ); i--; continue; } // We're gonna try to render this decal... so do any // final adjustments to it before rendering. // Update fade and delete expired. if ( !( dinst->mFlags & PermanentDecal || dinst->mFlags & CustomDecal ) ) { delta = ( curSimTime - dinst->mCreateTime ); if ( delta > dinst->mDataBlock->lifeSpan ) { diff = delta - dinst->mDataBlock->lifeSpan; dinst->mVisibility = 1.0f - (F32)diff / (F32)dinst->mDataBlock->fadeTime; if ( dinst->mVisibility <= 0.0f ) { mDecalQueue.erase_fast( i ); removeDecal( dinst ); i--; continue; } } } // Build clipped geometry for this decal if needed. if ( dinst->mFlags & ClipDecal/* && !( dinst->mFlags & CustomDecal ) */) { // Turn off the flag so we don't continually try to clip // if it fails. if(!clipDecal( dinst )) { dinst->mFlags = dinst->mFlags & ~ClipDecal; if ( !(dinst->mFlags & CustomDecal) ) { // Clipping failed to get any geometry... // Remove it from the render queue. mDecalQueue.erase_fast( i ); i--; // If the decal is one placed at run-time (not the editor) // then we should also permanently delete the decal instance. if ( !(dinst->mFlags & SaveDecal) ) { removeDecal( dinst ); } } // If this is a decal placed by the editor it will be // flagged to attempt clipping again the next time it is // modified. For now we just skip rendering it. continue; } } // If we get here and the decal still does not have any geometry // skip rendering it. It must be an editor placed decal that failed // to clip any geometry but has not yet been flagged to try again. if ( !dinst->mVerts || dinst->mVertCount == 0 || dinst->mIndxCount == 0 ) { mDecalQueue.erase_fast( i ); i--; continue; } // F32 alpha = pixelRadius / (dinst->mDataBlock->startPixRadius * decalSize) - 1.0f; if ( dinst->mFlags & CustomDecal ) { alpha = mClampF( alpha, 0.0f, 1.0f ); alpha *= dinst->mVisibility; } else alpha = mClampF( alpha * dinst->mVisibility, 0.0f, 1.0f ); // for ( U32 v = 0; v < dinst->mVertCount; v++ ) dinst->mVerts[v].color.set( 255, 255, 255, alpha * 255.0f ); } PROFILE_END(); if ( mDecalQueue.empty() ) return false; // Sort queued decals... // 1. Editor decals - in render priority order first, creation time second, and material third. // 2. Dynamic decals - in render priority order first and creation time second. // // With the constraint that decals with different render priority cannot // be rendered together in the same draw call. PROFILE_START( DecalManager_RenderDecals_Sort ); dQsort( mDecalQueue.address(), mDecalQueue.size(), sizeof(DecalInstance*), cmpDecalRenderOrder ); PROFILE_END(); PROFILE_SCOPE( DecalManager_RenderDecals_RenderBatch ); mPrimBuffs.clear(); mVBs.clear(); RenderPassManager *renderPass = state->getRenderPass(); // Base render instance for convenience we use for convenience. // Data shared by all instances we allocate below can be copied // from the base instance at the same time. MeshRenderInst baseRenderInst; baseRenderInst.clear(); MatrixF *tempMat = renderPass->allocUniqueXform( MatrixF( true ) ); MathUtils::getZBiasProjectionMatrix( gDecalBias, mCuller, tempMat ); baseRenderInst.projection = tempMat; baseRenderInst.objectToWorld = &MatrixF::Identity; baseRenderInst.worldToCamera = renderPass->allocSharedXform(RenderPassManager::View); baseRenderInst.type = RenderPassManager::RIT_Decal; // Make it the sort distance the max distance so that // it renders after all the other opaque geometry in // the prepass bin. baseRenderInst.sortDistSq = F32_MAX; // Get the best lights for the current camera position. LightManager *lm = state->getLightManager(); if ( lm ) { lm->setupLights( NULL, mCuller.getPosition(), mCuller.getTransform().getForwardVector(), mCuller.getFarDist() ); lm->getBestLights( baseRenderInst.lights, 4 ); lm->resetLights(); } Vector<DecalBatch> batches; DecalBatch *currentBatch = NULL; // Loop through DecalQueue collecting them into render batches. for ( U32 i = 0; i < mDecalQueue.size(); i++ ) { DecalInstance *decal = mDecalQueue[i]; DecalData *data = decal->mDataBlock; Material *mat = data->getMaterial(); if ( currentBatch == NULL ) { // Start a new batch, beginning with this decal. batches.increment(); currentBatch = &batches.last(); currentBatch->startDecal = i; currentBatch->decalCount = 1; currentBatch->iCount = decal->mIndxCount; currentBatch->vCount = decal->mVertCount; currentBatch->mat = mat; currentBatch->matInst = decal->mDataBlock->getMaterialInstance(); currentBatch->priority = decal->getRenderPriority(); currentBatch->dynamic = !(decal->mFlags & SaveDecal); continue; } if ( currentBatch->iCount + decal->mIndxCount >= smMaxIndices || currentBatch->vCount + decal->mVertCount >= smMaxVerts || currentBatch->mat != mat || currentBatch->priority != decal->getRenderPriority() || decal->mCustomTex ) { // End batch. currentBatch = NULL; i--; continue; } // Add on to current batch. currentBatch->decalCount++; currentBatch->iCount += decal->mIndxCount; currentBatch->vCount += decal->mVertCount; } // Loop through batches allocating buffers and submitting render instances. for ( U32 i = 0; i < batches.size(); i++ ) { DecalBatch ¤tBatch = batches[i]; // Allocate buffers... GFXVertexBufferHandle<DecalVertex> vb; vb.set( GFX, currentBatch.vCount, GFXBufferTypeDynamic ); DecalVertex *vpPtr = vb.lock(); GFXPrimitiveBufferHandle pb; pb.set( GFX, currentBatch.iCount, 0, GFXBufferTypeDynamic ); U16 *pbPtr; pb.lock( &pbPtr ); // Copy data into the buffers from all decals in this batch... U32 lastDecal = currentBatch.startDecal + currentBatch.decalCount; U32 voffset = 0; U32 ioffset = 0; // This is an ugly hack for ProjectedShadow! GFXTextureObject *customTex = NULL; for ( U32 j = currentBatch.startDecal; j < lastDecal; j++ ) { DecalInstance *dinst = mDecalQueue[j]; for ( U32 k = 0; k < dinst->mIndxCount; k++ ) { *( pbPtr + ioffset + k ) = dinst->mIndices[k] + voffset; } ioffset += dinst->mIndxCount; dMemcpy( vpPtr + voffset, dinst->mVerts, sizeof( DecalVertex ) * dinst->mVertCount ); voffset += dinst->mVertCount; // Ugly hack for ProjectedShadow! if ( (dinst->mFlags & CustomDecal) && dinst->mCustomTex != NULL ) customTex = *dinst->mCustomTex; } AssertFatal( ioffset == currentBatch.iCount, "bad" ); AssertFatal( voffset == currentBatch.vCount, "bad" ); pb.unlock(); vb.unlock(); // DecalManager must hold handles to these buffers so they remain valid, // we don't actually use them elsewhere. mPrimBuffs.push_back( pb ); mVBs.push_back( vb ); // Submit render inst... MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>(); *ri = baseRenderInst; ri->primBuff = &mPrimBuffs.last(); ri->vertBuff = &mVBs.last(); ri->matInst = currentBatch.matInst; ri->prim = renderPass->allocPrim(); ri->prim->type = GFXTriangleList; ri->prim->minIndex = 0; ri->prim->startIndex = 0; ri->prim->numPrimitives = currentBatch.iCount / 3; ri->prim->startVertex = 0; ri->prim->numVertices = currentBatch.vCount; // Ugly hack for ProjectedShadow! if ( customTex ) ri->miscTex = customTex; // The decal bin will contain render instances for both decals and decalRoad's. // Dynamic decals render last, then editor decals and roads in priority order. // DefaultKey is sorted in descending order. ri->defaultKey = currentBatch.dynamic ? 0xFFFFFFFF : (U32)currentBatch.priority; ri->defaultKey2 = 1;//(U32)lastDecal->mDataBlock; renderPass->addInst( ri ); } return false; }
void Sun::_renderCorona( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) { // Calculate Billboard Radius (in world units) to be constant, independent of distance. // Takes into account distance, viewport size, and specified size in editor F32 BBRadius = mCoronaWorldRadius; mMatrixSet->restoreSceneViewProjection(); if ( state->isReflectPass() ) mMatrixSet->setProjection( state->getSceneManager()->getNonClipProjection() ); //mMatrixSet->setWorld( MatrixF::Identity ); // Initialize points with basic info Point3F points[4]; points[0] = Point3F(-BBRadius, 0.0, -BBRadius); points[1] = Point3F( -BBRadius, 0.0, BBRadius); points[2] = Point3F( BBRadius, 0.0, BBRadius); points[3] = Point3F( BBRadius, 0.0, -BBRadius); static const Point2F sCoords[4] = { Point2F( 0.0f, 0.0f ), Point2F( 0.0f, 1.0f ), Point2F( 1.0f, 1.0f ), Point2F( 1.0f, 0.0f ) }; // Get info we need to adjust points const MatrixF &camView = state->getCameraTransform(); // Finalize points for(S32 i = 0; i < 4; i++) { // align with camera camView.mulV(points[i]); // offset points[i] += mLightWorldPos; } ColorF vertColor; if ( mCoronaUseLightColor ) vertColor = mLightColor; else vertColor = mCoronaTint; GFXVertexBufferHandle< GFXVertexPCT > vb; vb.set( GFX, 4, GFXBufferTypeVolatile ); GFXVertexPCT *pVert = vb.lock(); if(!pVert) return; for ( S32 i = 0; i < 4; i++ ) { pVert->color.set( vertColor ); pVert->point.set( points[i] ); pVert->texCoord.set( sCoords[i].x, sCoords[i].y ); pVert++; } vb.unlock(); // Setup SceneData struct. SceneData sgData; sgData.wireframe = GFXDevice::getWireframe(); sgData.visibility = 1.0f; // Draw it while ( mCoronaMatInst->setupPass( state, sgData ) ) { mCoronaMatInst->setTransforms( *mMatrixSet, state ); mCoronaMatInst->setSceneInfo( state, sgData ); GFX->setVertexBuffer( vb ); GFX->drawPrimitive( GFXTriangleFan, 0, 2 ); } }
void end( bool useGenericShaders ) { if ( mCurVertIndex == 0 ) return; VERTEX_SIZE_CHECK(); U32 vertStride = 1; U32 stripStart = 0; AssertFatal( mType >= GFXPT_FIRST && mType < GFXPT_COUNT, "PrimBuilder::end() - Bad primitive type!" ); switch( mType ) { default: case GFXPointList: { vertStride = 1; break; } case GFXLineList: { vertStride = 2; break; } case GFXTriangleList: { vertStride = 3; break; } case GFXLineStrip: { stripStart = 1; vertStride = 1; break; } case GFXTriangleStrip: case GFXTriangleFan: { stripStart = 2; vertStride = 1; break; } } if ( useGenericShaders ) GFX->setupGenericShaders( GFXDevice::GSModColorTexture ); const GFXVertexPCT *srcVerts = mTempVertBuff.address(); U32 numVerts = mCurVertIndex; // Make sure we don't have a dirty prim buffer left. GFX->setPrimitiveBuffer( NULL ); if ( stripStart > 0 ) { // TODO: Fix this to allow > MAX_DYNAMIC_VERTS! U32 copyVerts = getMin( (U32)MAX_DYNAMIC_VERTS, numVerts ); mVertBuff.set( GFX, copyVerts, GFXBufferTypeVolatile ); GFXVertexPCT *verts = mVertBuff.lock(); dMemcpy( verts, srcVerts, copyVerts * sizeof( GFXVertexPCT ) ); mVertBuff.unlock(); U32 numPrims = ( copyVerts / vertStride ) - stripStart; GFX->setVertexBuffer( mVertBuff ); GFX->drawPrimitive( mType, 0, numPrims ); } else { while ( numVerts > 0 ) { U32 copyVerts = getMin( (U32)MAX_DYNAMIC_VERTS, numVerts ); copyVerts -= copyVerts % vertStride; mVertBuff.set( GFX, copyVerts, GFXBufferTypeVolatile ); GFXVertexPCT *verts = mVertBuff.lock(); dMemcpy( verts, srcVerts, copyVerts * sizeof( GFXVertexPCT ) ); mVertBuff.unlock(); U32 numPrims = copyVerts / vertStride; GFX->setVertexBuffer( mVertBuff ); GFX->drawPrimitive( mType, 0, numPrims ); srcVerts += copyVerts; numVerts -= copyVerts; } } }