//-------------------------------------------------------------------------- U32 blTerrainProxy::getResourceCRC() { TerrainBlock * terrain = getObject(); if(!terrain) return(0); return(terrain->getCRC()); }
//-------------------------------------------------------------------------- bool blTerrainProxy::setPersistInfo(PersistInfo::PersistChunk * info) { if(!Parent::setPersistInfo(info)) return(false); blTerrainChunk * chunk = dynamic_cast<blTerrainChunk*>(info); AssertFatal(chunk, "blTerrainProxy::setPersistInfo: invalid info chunk!"); TerrainBlock * terrain = getObject(); if(!terrain || !terrain->getLightMap()) return(false); terrain->setLightMap( new GBitmap( *chunk->mLightmap) ); return(true); }
void EditTSCtrl::renderMissionArea() { MissionArea* obj = MissionArea::getServerObject(); if ( !obj ) return; if ( !mRenderMissionArea && !obj->isSelected() ) return; GFXDEBUGEVENT_SCOPE( Editor_renderMissionArea, ColorI::WHITE ); F32 minHeight = 0.0f; F32 maxHeight = 0.0f; TerrainBlock* terrain = getActiveTerrain(); if ( terrain ) { terrain->getMinMaxHeight( &minHeight, &maxHeight ); Point3F pos = terrain->getPosition(); maxHeight += pos.z + mMissionAreaHeightAdjust; minHeight += pos.z - mMissionAreaHeightAdjust; } const RectI& area = obj->getArea(); Box3F areaBox( area.point.x, area.point.y, minHeight, area.point.x + area.extent.x, area.point.y + area.extent.y, maxHeight ); GFXDrawUtil* drawer = GFX->getDrawUtil(); GFXStateBlockDesc desc; desc.setCullMode( GFXCullNone ); desc.setBlend( true ); desc.setZReadWrite( false, false ); desc.setFillModeSolid(); drawer->drawCube( desc, areaBox, mMissionAreaFillColor ); desc.setFillModeWireframe(); drawer->drawCube( desc, areaBox, mMissionAreaFrameColor ); }
void TerrainBlock::Tessellate(uint32 *pCountStrips, Terrain * pTerrain) { //GeoMipmapCode if(m_useGeoMipmap) { TessellateGeoMipmap(pTerrain); return; } /** define to use depth first transversing*/ #define USE_DEPTH_FIRST_TESSELLATE #ifdef USE_DEPTH_FIRST_TESSELLATE // depth first using recursive functions. if(Tessellate_NonRecursive(pCountStrips, pTerrain)) { m_pChildren[0]->Tessellate(pCountStrips, pTerrain); m_pChildren[1]->Tessellate(pCountStrips, pTerrain); m_pChildren[2]->Tessellate(pCountStrips, pTerrain); m_pChildren[3]->Tessellate(pCountStrips, pTerrain); } #else queue_TerrainBlockPtr_Type queueBlocks; queueBlocks.push((TerrainBlock*)this); /// breadth first transversing the quad tree while(!queueBlocks.empty()) { TerrainBlock* pBlock = queueBlocks.front(); queueBlocks.pop(); if(pBlock->Tessellate_NonRecursive(pCountStrips, pTerrain)) { queueBlocks.push(pBlock->m_pChildren[0]); queueBlocks.push(pBlock->m_pChildren[1]); queueBlocks.push(pBlock->m_pChildren[2]); queueBlocks.push(pBlock->m_pChildren[3]); } } #endif }
// Given a ray, this will return the color from the lightmap of this object, return true if handled bool blTerrainSystem::getColorFromRayInfo(const RayInfo & collision, ColorF& result) const { TerrainBlock *terrain = dynamic_cast<TerrainBlock *>(collision.object); if (!terrain) return false; Point2F uv; F32 terrainlength = (F32)terrain->getBlockSize(); Point3F pos = terrain->getPosition(); uv.x = (collision.point.x - pos.x) / terrainlength; uv.y = (collision.point.y - pos.y) / terrainlength; // similar to x = x & width... uv.x = uv.x - F32(U32(uv.x)); uv.y = uv.y - F32(U32(uv.y)); const GBitmap* lightmap = terrain->getLightMap(); if (!lightmap) return false; result = lightmap->sampleTexel(uv.x, uv.y); // terrain lighting is dim - look into this (same thing done in shaders)... result *= 2.0f; return true; }
//RBP - Global function declared in Terrdata.h TerrainBlock* getTerrainUnderWorldPoint(const Point3F & wPos) { // Cast a ray straight down from the world position and see which // Terrain is the closest to our starting point Point3F startPnt = wPos; Point3F endPnt = wPos + Point3F(0.0f, 0.0f, -10000.0f); S32 blockIndex = -1; F32 nearT = 1.0f; SimpleQueryList queryList; gServerContainer.findObjects( TerrainObjectType, SimpleQueryList::insertionCallback, &queryList); for (U32 i = 0; i < queryList.mList.size(); i++) { Point3F tStartPnt, tEndPnt; TerrainBlock* terrBlock = dynamic_cast<TerrainBlock*>(queryList.mList[i]); terrBlock->getWorldTransform().mulP(startPnt, &tStartPnt); terrBlock->getWorldTransform().mulP(endPnt, &tEndPnt); RayInfo ri; if (terrBlock->castRayI(tStartPnt, tEndPnt, &ri, true)) { if (ri.t < nearT) { blockIndex = i; nearT = ri.t; } } } if (blockIndex > -1) return (TerrainBlock*)(queryList.mList[blockIndex]); return NULL; }
void SoftSelectAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type) { TerrainBlock *terrBlock = mTerrainEditor->getActiveTerrain(); if ( !terrBlock ) return; // allow process of current selection Selection tmpSel; if(sel == mTerrainEditor->getCurrentSel()) { tmpSel = *sel; sel = &tmpSel; } if(type == Begin || type == Process) mFilter.set(1, &mTerrainEditor->mSoftSelectFilter); // if(selChanged) { F32 radius = mTerrainEditor->mSoftSelectRadius; if(radius == 0.f) return; S32 squareSize = terrBlock->getSquareSize(); U32 offset = U32(radius / F32(squareSize)) + 1; for(U32 i = 0; i < sel->size(); i++) { GridInfo & info = (*sel)[i]; info.mPrimarySelect = true; info.mWeight = mFilter.getValue(0); if(!mTerrainEditor->getCurrentSel()->add(info)) mTerrainEditor->getCurrentSel()->setInfo(info); Point2F infoPos((F32)info.mGridPoint.gridPos.x, (F32)info.mGridPoint.gridPos.y); // for(S32 x = info.mGridPoint.gridPos.x - offset; x < info.mGridPoint.gridPos.x + (offset << 1); x++) for(S32 y = info.mGridPoint.gridPos.y - offset; y < info.mGridPoint.gridPos.y + (offset << 1); y++) { // Point2F pos((F32)x, (F32)y); F32 dist = Point2F(pos - infoPos).len() * F32(squareSize); if(dist > radius) continue; F32 weight = mFilter.getValue(dist / radius); // GridInfo gInfo; GridPoint gridPoint = info.mGridPoint; gridPoint.gridPos.set(x, y); if(mTerrainEditor->getCurrentSel()->getInfo(Point2I(x, y), gInfo)) { if(gInfo.mPrimarySelect) continue; if(gInfo.mWeight < weight) { gInfo.mWeight = weight; mTerrainEditor->getCurrentSel()->setInfo(gInfo); } } else { Vector<GridInfo> gInfos; mTerrainEditor->getGridInfos(gridPoint, gInfos); for (U32 z = 0; z < gInfos.size(); z++) { gInfos[z].mWeight = weight; gInfos[z].mPrimarySelect = false; mTerrainEditor->getCurrentSel()->add(gInfos[z]); } } } } } }
void BrushAdjustHeightAction::process(Selection * sel, const Gui3DMouseEvent & event, bool, Type type) { if(type == Process) return; TerrainBlock *terrBlock = mTerrainEditor->getActiveTerrain(); if ( !terrBlock ) return; if(type == Begin) { mTerrainEditor->lockSelection(true); mTerrainEditor->getRoot()->mouseLock(mTerrainEditor); // the way this works is: // construct a plane that goes through the collision point // with one axis up the terrain Z, and horizontally parallel to the // plane of projection // the cross of the camera ffdv and the terrain up vector produces // the cross plane vector. // all subsequent mouse actions are collided against the plane and the deltaZ // from the previous position is used to delta the selection up and down. Point3F cameraDir; EditTSCtrl::smCamMatrix.getColumn(1, &cameraDir); terrBlock->getTransform().getColumn(2, &mTerrainUpVector); // ok, get the cross vector for the plane: Point3F planeCross; mCross(cameraDir, mTerrainUpVector, &planeCross); planeCross.normalize(); Point3F planeNormal; Point3F intersectPoint; mTerrainEditor->collide(event, intersectPoint); mCross(mTerrainUpVector, planeCross, &planeNormal); mIntersectionPlane.set(intersectPoint, planeNormal); // ok, we have the intersection point... // project the collision point onto the up vector of the terrain mPreviousZ = mDot(mTerrainUpVector, intersectPoint); // add to undo // and record the starting heights for(U32 i = 0; i < sel->size(); i++) { mTerrainEditor->getUndoSel()->add((*sel)[i]); (*sel)[i].mStartHeight = (*sel)[i].mHeight; } } else if(type == Update) { // ok, collide the ray from the event with the intersection plane: Point3F intersectPoint; Point3F start = event.pos; Point3F end = start + event.vec * 1000; F32 t = mIntersectionPlane.intersect(start, end); m_point3F_interpolate( start, end, t, intersectPoint); F32 currentZ = mDot(mTerrainUpVector, intersectPoint); F32 diff = currentZ - mPreviousZ; for(U32 i = 0; i < sel->size(); i++) { (*sel)[i].mHeight = (*sel)[i].mStartHeight + diff * (*sel)[i].mWeight; // clamp it if((*sel)[i].mHeight < 0.f) (*sel)[i].mHeight = 0.f; if((*sel)[i].mHeight > 2047.f) (*sel)[i].mHeight = 2047.f; mTerrainEditor->setGridInfoHeight((*sel)[i]); } mTerrainEditor->scheduleGridUpdate(); } else if(type == End) { mTerrainEditor->getRoot()->mouseUnlock(mTerrainEditor); } }
bool SceneCullingState::isOccludedByTerrain( SceneObject* object ) const { PROFILE_SCOPE( SceneCullingState_isOccludedByTerrain ); // Don't try to occlude globally bounded objects. if( object->isGlobalBounds() ) return false; const Vector< SceneObject* >& terrains = getSceneManager()->getContainer()->getTerrains(); const U32 numTerrains = terrains.size(); for( U32 terrainIdx = 0; terrainIdx < numTerrains; ++ terrainIdx ) { TerrainBlock* terrain = dynamic_cast< TerrainBlock* >( terrains[ terrainIdx ] ); if( !terrain ) continue; MatrixF terrWorldTransform = terrain->getWorldTransform(); Point3F localCamPos = getCameraState().getViewPosition(); terrWorldTransform.mulP(localCamPos); F32 height; terrain->getHeight( Point2F( localCamPos.x, localCamPos.y ), &height ); bool aboveTerrain = ( height <= localCamPos.z ); // Don't occlude if we're below the terrain. This prevents problems when // looking out from underground bases... if( !aboveTerrain ) continue; const Box3F& oBox = object->getObjBox(); F32 minSide = getMin(oBox.len_x(), oBox.len_y()); if (minSide > 85.0f) continue; const Box3F& rBox = object->getWorldBox(); Point3F ul(rBox.minExtents.x, rBox.minExtents.y, rBox.maxExtents.z); Point3F ur(rBox.minExtents.x, rBox.maxExtents.y, rBox.maxExtents.z); Point3F ll(rBox.maxExtents.x, rBox.minExtents.y, rBox.maxExtents.z); Point3F lr(rBox.maxExtents.x, rBox.maxExtents.y, rBox.maxExtents.z); terrWorldTransform.mulP(ul); terrWorldTransform.mulP(ur); terrWorldTransform.mulP(ll); terrWorldTransform.mulP(lr); Point3F xBaseL0_s = ul - localCamPos; Point3F xBaseL0_e = lr - localCamPos; Point3F xBaseL1_s = ur - localCamPos; Point3F xBaseL1_e = ll - localCamPos; static F32 checkPoints[3] = {0.75, 0.5, 0.25}; RayInfo rinfo; for( U32 i = 0; i < 3; i ++ ) { Point3F start = (xBaseL0_s * checkPoints[i]) + localCamPos; Point3F end = (xBaseL0_e * checkPoints[i]) + localCamPos; if (terrain->castRay(start, end, &rinfo)) continue; terrain->getHeight(Point2F(start.x, start.y), &height); if ((height <= start.z) == aboveTerrain) continue; start = (xBaseL1_s * checkPoints[i]) + localCamPos; end = (xBaseL1_e * checkPoints[i]) + localCamPos; if (terrain->castRay(start, end, &rinfo)) continue; Point3F test = (start + end) * 0.5; if (terrain->castRay(localCamPos, test, &rinfo) == false) continue; return true; } } return false; }
virtual float getMaxHeight() { return fixedToFloat(mBlock->findSquare(TerrainBlock::BlockShift, Point2I(0,0))->maxHeight); }
virtual float getAltitude(int x, int y) { return fixedToFloat(mBlock->getHeight(x,y)); }
virtual float getSampleSpacing() { return mBlock->getSquareSize(); }
void EditTSCtrl::onRightMouseDown(const GuiEvent & event) { // always process the right mouse event first... mRightMouseDown = true; mLastBorderMoveTime = 0; make3DMouseEvent(mLastEvent, event); on3DRightMouseDown(mLastEvent); if(!mLeftMouseDown && mRightMousePassThru && mProfile->mCanKeyFocus) { GuiCanvas *pCanvas = getRoot(); if( !pCanvas ) return; PlatformWindow *pWindow = static_cast<GuiCanvas*>(getRoot())->getPlatformWindow(); if( !pWindow ) return; PlatformCursorController *pController = pWindow->getCursorController(); if( !pController ) return; // ok, gotta disable the mouse // script functions are lockMouse(true); Canvas.cursorOff(); pWindow->setMouseLocked(true); pCanvas->setCursorON( false ); if(mDisplayType != DisplayTypePerspective) { mouseLock(); mLastMousePos = event.mousePoint; pCanvas->setForceMouseToGUI(true); mLastMouseClamping = pCanvas->getClampTorqueCursor(); pCanvas->setClampTorqueCursor(false); } if(mDisplayType == DisplayTypeIsometric) { // Store the screen center point on the terrain for a possible rotation TerrainBlock* activeTerrain = getActiveTerrain(); if( activeTerrain ) { F32 extx, exty; if(event.modifier & SI_SHIFT) { extx = F32(event.mousePoint.x); exty = F32(event.mousePoint.y); } else { extx = getExtent().x * 0.5; exty = getExtent().y * 0.5; } Point3F sp(extx, exty, 0.0f); // Near plane projection Point3F start; unproject(sp, &start); Point3F end = start + mLastEvent.vec * 4000.0f; Point3F tStartPnt, tEndPnt; activeTerrain->getTransform().mulP(start, &tStartPnt); activeTerrain->getTransform().mulP(end, &tEndPnt); RayInfo info; bool result = activeTerrain->castRay(tStartPnt, tEndPnt, &info); if(result) { info.point.interpolate(start, end, info.t); mIsoCamRotCenter = info.point; } else { mIsoCamRotCenter = start; } } else { F32 extx = getExtent().x * 0.5; F32 exty = getExtent().y * 0.5; Point3F sp(extx, exty, 0.0f); // Near plane projection unproject(sp, &mIsoCamRotCenter); } } setFirstResponder(); } }
void blTerrainProxy::lightVector(LightInfo * light) { // Grab our terrain object TerrainBlock* terrain = getObject(); if (!terrain) return; // Get the direction to the light (the inverse of the direction // the light is pointing) Point3F lightDir = -light->getDirection(); lightDir.normalize(); // Get the ratio between the light map pixel and world space (used below) F32 lmTerrRatio = (F32)mTerrainBlockSize / (F32) mLightMapSize; lmTerrRatio *= terrain->getSquareSize(); // Get the terrain position Point3F terrPos( terrain->getTransform().getPosition() ); U32 i = 0; for (U32 y = 0; y < mLightMapSize; y++) { for (U32 x = 0; x < mLightMapSize; x++) { // Get the relative pixel position and scale it // by the ratio between lightmap and world space Point2F pixelPos(x, y); pixelPos *= lmTerrRatio; // Start with a default normal of straight up Point3F normal(0.0f, 0.0f, 1.0f); // Try to get the actual normal from the terrain. // Note: this won't change the default normal if // it can't find a normal. terrain->getNormal(pixelPos, &normal); // The terrain lightmap only contains shadows. F32 shadowed = 0.0f; // Get the height at the lightmap pixel's position F32 height = 0.0f; terrain->getHeight(pixelPos, &height); // Calculate the 3D position of the pixel Point3F pixelPos3F(pixelPos.x, pixelPos.y, height); // Translate that position by the terrain's transform terrain->getTransform().mulP(pixelPos3F); // Offset slighting along the normal so that we don't // raycast into ourself pixelPos3F += (normal * 0.1f); // Calculate the light's position. // If it is a vector light like the sun (no position // just direction) then translate along that direction // a reasonable distance to get a point sufficiently // far away Point3F lightPos = light->getPosition(); if(light->getType() == LightInfo::Vector) { lightPos = 1000.f * lightDir; lightPos = pixelPos3F + lightPos; } // Cast a ray from the world space position of the lightmap pixel to the light source. // If we hit something then we are in shadow. This allows us to be shadowed by anything // that supports a castRay operation. RayInfo info; if(terrain->getContainer()->castRay(pixelPos3F, lightPos, STATIC_COLLISION_TYPEMASK, &info)) { // Shadow the pixel. shadowed = 1.0f; } // Set the final lightmap color. mLightmap[i++] += ColorF::WHITE * mClampF( 1.0f - shadowed, 0.0f, 1.0f ); } } }
void blTerrainProxy::light(LightInfo * light) { // If we don't have terrain or its not a directional // light then skip processing. TerrainBlock * terrain = getObject(); if ( !terrain || light->getType() != LightInfo::Vector ) return; S32 time = Platform::getRealMilliseconds(); // reset mShadowVolume = new ShadowVolumeBSP; // build interior shadow volume for(ObjectProxy ** itr = gLighting->mLitObjects.begin(); itr != gLighting->mLitObjects.end(); itr++) { ObjectProxy* objproxy = *itr; if (markObjectShadow(objproxy)) objproxy->addToShadowVolume(mShadowVolume, light, SceneLighting::SHADOW_DETAIL); } lightVector(light); // set the lightmap... terrain->clearLightMap(); // Blur... F32 kernel[3][3] = { {1, 2, 1}, {2, 3, 2}, {1, 2, 1} }; F32 modifier = 1; F32 divisor = 0; for( U32 i=0; i<3; i++ ) { for( U32 j=0; j<3; j++ ) { if( i==1 && j==1 ) { kernel[i][j] = 1 + kernel[i][j] * modifier; } else { kernel[i][j] = kernel[i][j] * modifier; } divisor += kernel[i][j]; } } for( U32 i=0; i < mLightMapSize; i++ ) { for( U32 j=0; j < mLightMapSize; j++ ) { ColorF val; val = _getValue( i-1, j-1 ) * kernel[0][0]; val += _getValue( i-1, j ) * kernel[0][1]; val += _getValue( i-1, j+1 ) * kernel[0][2]; val += _getValue( i, j-1 ) * kernel[1][0]; val += _getValue( i, j ) * kernel[1][1]; val += _getValue( i, j+1 ) * kernel[1][2]; val += _getValue( i+1, j-1 ) * kernel[2][0]; val += _getValue( i+1, j ) * kernel[2][1]; val += _getValue( i+1, j+1 ) * kernel[2][2]; U32 edge = 0; if( j == 0 || j == mLightMapSize - 1 ) edge++; if( i == 0 || i == mLightMapSize - 1 ) edge++; if( !edge ) val = val / divisor; else val = mLightmap[ i * mLightMapSize + j ]; // clamp values mLightmap[ i * mLightMapSize + j ]= val; } } // And stuff it into the texture... GBitmap *terrLightMap = terrain->getLightMap(); for(U32 y = 0; y < mLightMapSize; y++) { for(U32 x = 0; x < mLightMapSize; x++) { ColorI color(255, 255, 255, 255); color.red = mLightmap[x + y * mLightMapSize].red * 255; color.green = mLightmap[x + y * mLightMapSize].green * 255; color.blue = mLightmap[x + y * mLightMapSize].blue * 255; terrLightMap->setColor(x, y, color); } } /* // This handles matching up the outer edges of the terrain // lightmap when it has neighbors if (!terrain->isTiling()) { for (S32 y = 0; y < terrLightMap->getHeight(); y++) { ColorI c; if (terrain->getFile()->mEdgeTerrainFiles[0]) { terrLightMap->getColor(terrLightMap->getWidth()-1,y,c); terrLightMap->setColor(0,y,c); terrLightMap->setColor(1,y,c); } else { terrLightMap->getColor(0,y,c); terrLightMap->setColor(terrLightMap->getWidth()-1,y,c); terrLightMap->setColor(terrLightMap->getWidth()-2,y,c); } } for (S32 x = 0; x < terrLightMap->getHeight(); x++) { ColorI c; if (terrain->getFile()->mEdgeTerrainFiles[1]) { terrLightMap->getColor(x,terrLightMap->getHeight()-1,c); terrLightMap->setColor(x,0,c); terrLightMap->setColor(x,1,c); } else { terrLightMap->getColor(x,0,c); terrLightMap->setColor(x,terrLightMap->getHeight()-1,c); terrLightMap->setColor(x,terrLightMap->getHeight()-2,c); } } } */ delete mShadowVolume; Con::printf(" = terrain lit in %3.3f seconds", (Platform::getRealMilliseconds()-time)/1000.f); }
void afxZodiacTerrainRenderer::render(SceneRenderState* state) { PROFILE_SCOPE(afxRenderZodiacTerrainMgr_render); // Early out if no terrain zodiacs to draw. if (terrain_zodes.size() == 0) return; initShader(); if (!zodiac_shader) return; bool is_reflect_pass = state->isReflectPass(); // Automagically save & restore our viewport and transforms. GFXTransformSaver saver; MatrixF proj = GFX->getProjectionMatrix(); // Set up world transform MatrixF world = GFX->getWorldMatrix(); proj.mul(world); shader_consts->set(projection_sc, proj); //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// // RENDER EACH ZODIAC // for (S32 zz = 0; zz < terrain_zodes.size(); zz++) { TerrainZodiacElem& elem = terrain_zodes[zz]; TerrainBlock* block = (TerrainBlock*) elem.block; afxZodiacMgr::ZodiacSpec* zode = &afxZodiacMgr::terr_zodes[elem.zode_idx]; if (!zode) continue; if (is_reflect_pass) { if ((zode->zflags & afxZodiacData::SHOW_IN_REFLECTIONS) == 0) continue; } else { if ((zode->zflags & afxZodiacData::SHOW_IN_NON_REFLECTIONS) == 0) continue; } F32 fadebias = zode->calcDistanceFadeBias(elem.camDist); if (fadebias < 0.01f) continue; F32 cos_ang = mCos(elem.ang); F32 sin_ang = mSin(elem.ang); GFXStateBlock* sb = chooseStateBlock(zode->zflags & afxZodiacData::BLEND_MASK, is_reflect_pass); GFX->setShader(zodiac_shader->getShader()); GFX->setStateBlock(sb); GFX->setShaderConstBuffer(shader_consts); // set the texture GFX->setTexture(0, *zode->txr); ColorF zode_color = (ColorF)zode->color; zode_color.alpha *= fadebias; shader_consts->set(color_sc, zode_color); Point3F half_size(zode->radius_xy,zode->radius_xy,zode->radius_xy); F32 inv_radius = 1.0f/zode->radius_xy; GFXPrimitive cell_prim; GFXVertexBufferHandle<TerrVertex> cell_verts; GFXPrimitiveBufferHandle primBuff; elem.cell->getRenderPrimitive(&cell_prim, &cell_verts, &primBuff); U32 n_nonskirt_tris = TerrCellSpy::getMinCellSize()*TerrCellSpy::getMinCellSize()*2; const Point3F* verts = ((TerrCell*)elem.cell)->getZodiacVertexBuffer(); const U16 *tris = block->getZodiacPrimitiveBuffer(); if (!tris) continue; PrimBuild::begin(GFXTriangleList, 3*n_nonskirt_tris); ///////////////////////////////// U32 n_overlapping_tris = 0; U32 idx = 0; for (U32 i = 0; i < n_nonskirt_tris; i++) { Point3F tri_v[3]; tri_v[0] = verts[tris[idx++]]; tri_v[1] = verts[tris[idx++]]; tri_v[2] = verts[tris[idx++]]; elem.mRenderObjToWorld.mulP(tri_v[0]); elem.mRenderObjToWorld.mulP(tri_v[1]); elem.mRenderObjToWorld.mulP(tri_v[2]); if (!afxTriBoxOverlap2D(zode->pos, half_size, tri_v[0], tri_v[1], tri_v[2])) continue; n_overlapping_tris++; for (U32 j = 0; j < 3; j++) { // compute UV F32 u1 = (tri_v[j].x - zode->pos.x)*inv_radius; F32 v1 = (tri_v[j].y - zode->pos.y)*inv_radius; F32 ru1 = u1*cos_ang - v1*sin_ang; F32 rv1 = u1*sin_ang + v1*cos_ang; F32 uu = (ru1 + 1.0f)/2.0f; F32 vv = 1.0f - (rv1 + 1.0f)/2.0f; PrimBuild::texCoord2f(uu, vv); PrimBuild::vertex3fv(tri_v[j]); } } ///////////////////////////////// PrimBuild::end(false); } // // RENDER EACH ZODIAC //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// }