/* Copyright (c) 2002-2012 Croteam Ltd. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "stdh.h" #include #include #include #include #include extern FLOAT3D _vViewerAbs; // Viewer pos extern CStaticStackArray _aiExtColors; // Selection preview static CTerrain *_ptrSelectionTerrain; // Terrain that needs to show vertex selection static CTextureData *_ptdSelectionBrush; // Brush that will be used for vertex selection preview static GFXColor _colSelection; // Selection color static Rect _rcSelectionExtract; // Rect of selection vertices that will be shown static FLOAT _fSelectionStrenght; // Selection preview strenght static SelectionFill _sfSelectionFill; // Type of fill for selection preview static FLOATaabbox3D CalculateAABBoxFromRect(CTerrain *ptrTerrain, Rect rcExtract) { ASSERT(ptrTerrain!=NULL); ASSERT(ptrTerrain->tr_penEntity!=NULL); // Get entity that holds this terrain CEntity *penEntity = ptrTerrain->tr_penEntity; FLOATaabbox3D bboxExtract; FLOATaabbox3D bboxAllTerrain; ptrTerrain->GetAllTerrainBBox(bboxAllTerrain); FLOAT fMinY = bboxAllTerrain.minvect(2); FLOAT fMaxY = bboxAllTerrain.maxvect(2); bboxExtract.minvect = FLOAT3D(rcExtract.rc_iLeft * ptrTerrain->tr_vStretch(1),fMinY,rcExtract.rc_iTop * ptrTerrain->tr_vStretch(3)); bboxExtract.maxvect = FLOAT3D(rcExtract.rc_iRight * ptrTerrain->tr_vStretch(1),fMaxY,rcExtract.rc_iBottom * ptrTerrain->tr_vStretch(3)); return bboxExtract; } // Find if there are any tiles in given rect that are not in lowest nor in highest lod without using TT_NO_LODING flag static INDEX GetFirstTileInMidLod(CTerrain *ptrTerrain, Rect &rcExtract) { FLOATaabbox3D bboxExtract = CalculateAABBoxFromRect(ptrTerrain,rcExtract); // for each terrain tile for(INDEX itt=0;itttr_ctTiles;itt++) { QuadTreeNode &qtn = ptrTerrain->tr_aqtnQuadTreeNodes[itt]; CTerrainTile &tt = ptrTerrain->tr_attTiles[itt]; // if it is coliding with given box if(qtn.qtn_aabbox.HasContactWith(bboxExtract)) { // calculate its real distance factor FLOAT fDistance = (qtn.qtn_aabbox.Center() - _vViewerAbs).Length(); INDEX iRealLod = Clamp((INDEX)(fDistance/ptrTerrain->tr_fDistFactor),(INDEX)0,ptrTerrain->tr_iMaxTileLod); if(iRealLod>0 && iRealLodtr_iMaxTileLod) { // found one return itt; } } } return -1; } // Add given flags to all tiles in rect static void AddFlagsToTilesInRect(CTerrain *ptrTerrain, Rect &rcExtract, ULONG ulFlags, BOOL bRegenerateTiles=FALSE) { ASSERT(ptrTerrain!=NULL); FLOATaabbox3D bboxExtract = CalculateAABBoxFromRect(ptrTerrain, rcExtract); // for each terrain tile for(INDEX itt=0;itttr_ctTiles;itt++) { QuadTreeNode &qtn = ptrTerrain->tr_aqtnQuadTreeNodes[itt]; CTerrainTile &tt = ptrTerrain->tr_attTiles[itt]; // if it is coliding with given box if(qtn.qtn_aabbox.HasContactWith(bboxExtract)) { // if tile must regenerate if(bRegenerateTiles) { // add tile to regen queue ptrTerrain->AddTileToRegenQueue(itt); } // add given flags to tile tt.AddFlag(ulFlags); } } } // Update given rect of topmap static void UpdateShadowMapRect(CTerrain *ptrTerrain, Rect &rcExtract) { FLOATaabbox3D bboxExtract = CalculateAABBoxFromRect(ptrTerrain,rcExtract); ptrTerrain->UpdateShadowMap(&bboxExtract); } // Update terrain top map static void UpdateTerrainGlobalTopMap(CTerrain *ptrTerrain, Rect &rcExtract) { // if there aren't any tiles in if(GetFirstTileInMidLod(ptrTerrain,rcExtract)==(-1)) { // update gloabal terrain top map now ptrTerrain->UpdateTopMap(-1); // else } else { // gloabal terrain top map will be updated when first tile chage its lod ptrTerrain->AddFlag(TR_REGENERATE_TOP_MAP); } } // void ShowSelectionInternal(CTerrain *ptrTerrain, Rect &rcExtract, CTextureData *ptdBrush, GFXColor colSelection, FLOAT fStrenght, SelectionFill sfFill) { ASSERT(ptrTerrain!=NULL); ASSERT(ptdBrush!=NULL); Rect rcSelection; FLOATaabbox3D bboxSelection; // Clamp rect used for extraction rcSelection.rc_iLeft = Clamp(rcExtract.rc_iLeft , 0L, ptrTerrain->tr_pixHeightMapWidth); rcSelection.rc_iTop = Clamp(rcExtract.rc_iTop , 0L, ptrTerrain->tr_pixHeightMapHeight); rcSelection.rc_iRight = Clamp(rcExtract.rc_iRight , 0L, ptrTerrain->tr_pixHeightMapWidth); rcSelection.rc_iBottom = Clamp(rcExtract.rc_iBottom , 0L, ptrTerrain->tr_pixHeightMapHeight); // Prepare box for vertex selection bboxSelection = FLOAT3D(rcSelection.rc_iLeft, 0, rcSelection.rc_iTop); bboxSelection |= FLOAT3D(rcSelection.rc_iRight, 0, rcSelection.rc_iBottom); // Stretch selection box bboxSelection.minvect(1) *= ptrTerrain->tr_vStretch(1); bboxSelection.minvect(3) *= ptrTerrain->tr_vStretch(3); bboxSelection.maxvect(1) *= ptrTerrain->tr_vStretch(1); bboxSelection.maxvect(3) *= ptrTerrain->tr_vStretch(3); // Set selection box height FLOATaabbox3D bboxAllTerrain; ptrTerrain->GetAllTerrainBBox(bboxAllTerrain); bboxSelection.minvect(2) = bboxAllTerrain.minvect(2); bboxSelection.maxvect(2) = bboxAllTerrain.maxvect(2); GFXVertex *pavVertices; INDEX *paiIndices; INDEX ctVertices; INDEX ctIndices; // Extract vertices in selection rect ExtractVerticesInRect(ptrTerrain, rcSelection, &pavVertices, &paiIndices, ctVertices, ctIndices); if(ctVertices!=rcSelection.Width()*rcSelection.Height()) { ASSERT(FALSE); return; } // if no vertices if(ctVertices==0) { return; } // Prepare vertex colors for selection preview PIX pixWidth = rcSelection.Width(); PIX pixHeight = rcSelection.Height(); INDEX iStepX = ptdBrush->GetWidth() - pixWidth; INDEX iFirst = 0; if(rcExtract.rc_iTop<0) { iFirst += -rcExtract.rc_iTop*ptdBrush->GetWidth(); } if(rcExtract.rc_iLeft<0) { iFirst += -rcExtract.rc_iLeft; } _aiExtColors.Push(ctVertices); GFXColor *pacolColor = (GFXColor*)&_aiExtColors[0]; GFXColor *pacolBrush = (GFXColor*)&ptdBrush->td_pulFrames[iFirst]; // Fill vertex colors for selection preview SLONG slStrength = Clamp(Abs(fStrenght),0.0f,1.0f) * 256.0f; // for each row for(INDEX iy=0;iyabgr = colSelection.abgr; pacolColor->a = (pacolBrush->r*slStrength)>>8; pacolColor++; pacolBrush++; } pacolBrush+=iStepX; } // Render selected polygons for selection preview if(sfFill == SF_WIREFRAME) { gfxPolygonMode(GFX_LINE); gfxEnableDepthBias(); } if(sfFill != SF_POINTS) { // Draw selection gfxDisableTexture(); gfxDisableAlphaTest(); gfxEnableBlend(); gfxBlendFunc(GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA); gfxSetVertexArray(pavVertices,ctVertices); gfxSetColorArray(&_aiExtColors[0]); gfxLockArrays(); gfxDrawElements(ctIndices,paiIndices); gfxUnlockArrays(); gfxDisableBlend(); } if(sfFill == SF_WIREFRAME) { gfxDisableDepthBias(); gfxPolygonMode(GFX_FILL); } if(sfFill == SF_POINTS) { DrawSelectedVertices(pavVertices,&_aiExtColors[0],ctVertices); } } void ShowSelectionInternal(CTerrain *ptrTerrain) { // just in case if(ptrTerrain!=_ptrSelectionTerrain) { return; } // Show selection ShowSelectionInternal(ptrTerrain,_rcSelectionExtract,_ptdSelectionBrush,_colSelection,_fSelectionStrenght,_sfSelectionFill); } void ShowSelection(CTerrain *ptrTerrain, Rect &rcExtract, CTextureData *ptdBrush, COLOR colSelection, FLOAT fStrenght, SelectionFill sfFill/*=SF_POLYGON*/) { _ptrSelectionTerrain = ptrTerrain; _ptdSelectionBrush = ptdBrush; _colSelection = colSelection; _rcSelectionExtract = rcExtract; _fSelectionStrenght = fStrenght; _sfSelectionFill = sfFill, // all tiles in rect must be in zero lod AddFlagsToTilesInRect(ptrTerrain,rcExtract,TT_NO_LODING); // Make sure selection is visible on next render ptrTerrain->AddFlag(TR_SHOW_SELECTION); } static void UpdateEditedTerrainTiles(CTerrain *ptrTerrain, Rect &rcExtract, BufferType btBufferType) { // Update terrain tiles if(btBufferType == BT_HEIGHT_MAP) { AddFlagsToTilesInRect(ptrTerrain, rcExtract, TT_NO_LODING|TT_QUADTREENODE_REGEN, TRUE); UpdateShadowMapRect(ptrTerrain, rcExtract); } else if(btBufferType == BT_LAYER_MASK) { AddFlagsToTilesInRect(ptrTerrain, rcExtract, TT_NO_LODING|TT_FORCE_TOPMAP_REGEN, TRUE); UpdateTerrainGlobalTopMap(ptrTerrain,rcExtract); } else if(btBufferType == BT_EDGE_MAP) { AddFlagsToTilesInRect(ptrTerrain, rcExtract, TT_NO_LODING|TT_FORCE_TOPMAP_REGEN, TRUE); UpdateTerrainGlobalTopMap(ptrTerrain,rcExtract); } else { ASSERTALWAYS("Ilegal buffer type"); return; } } UWORD *GetBufferForEditing(CTerrain *ptrTerrain, Rect &rcExtract, BufferType btBufferType, INDEX iBufferData/*=-1*/) { ASSERT(ptrTerrain!=NULL); ASSERT(rcExtract.Width()>0); ASSERT(rcExtract.Height()>0); PIX pixLeft = rcExtract.rc_iLeft; PIX pixRight = rcExtract.rc_iRight; PIX pixTop = rcExtract.rc_iTop; PIX pixBottom = rcExtract.rc_iBottom; PIX pixWidht = pixRight-pixLeft; PIX pixHeight = pixBottom-pixTop; PIX pixMaxWidth = ptrTerrain->tr_pixHeightMapWidth; PIX pixMaxHeight = ptrTerrain->tr_pixHeightMapHeight; // allocate memory for editing buffer UWORD *pauwEditingBuffer = (UWORD*)AllocMemory(pixWidht*pixHeight*sizeof(UWORD)); // Get pointer to first member in editing pointer UWORD *puwBufferData = &pauwEditingBuffer[0]; // if buffer type is height map if(btBufferType==BT_HEIGHT_MAP) { // Extract data from terrain height map UWORD *puwFirstInHeightMap = &ptrTerrain->tr_auwHeightMap[0]; // for each row for(PIX pixY=pixTop;pixYGetLayer(iBufferData); UBYTE *pubFirstInLayer = &tl.tl_aubColors[0]; // for each row for(PIX pixY=pixTop;pixYtr_aubEdgeMap[0]; // for each row for(PIX pixY=pixTop;pixY0); ASSERT(rcExtract.Height()>0); PIX pixLeft = rcExtract.rc_iLeft; PIX pixRight = rcExtract.rc_iRight; PIX pixTop = rcExtract.rc_iTop; PIX pixBottom = rcExtract.rc_iBottom; PIX pixWidht = pixRight-pixLeft; PIX pixHeight = pixBottom-pixTop; PIX pixMaxWidth = ptrTerrain->tr_pixHeightMapWidth; PIX pixMaxHeight = ptrTerrain->tr_pixHeightMapHeight; // Get pointer to first member in editing buffer UWORD *puwBufferData = &puwEditedBuffer[0]; // if buffer type is height map if(btBufferType==BT_HEIGHT_MAP) { // put data from buffer to terrain height map UWORD *puwFirstInHeightMap = &ptrTerrain->tr_auwHeightMap[0]; // for each row for(PIX pixY=pixTop;pixY=0 && pixY=0 && pixXGetLayer(iBufferData); UBYTE *pubFirstInLayer = &tl.tl_aubColors[0]; // for each row for(PIX pixY=pixTop;pixY=0 && pixY=0 && pixX>8; } puwBufferData++; } // else pixY is not inside terrain rect } else { // increment buffer data pointer puwBufferData+=pixRight-pixLeft; } } } else if(btBufferType==BT_EDGE_MAP) { // Extract data from edge map UBYTE *pubFirstInEdgeMap = &ptrTerrain->tr_aubEdgeMap[0]; // for each row for(PIX pixY=pixTop;pixY=0 && pixY=0 && pixX=1) { *pubMask = 255; } else { *pubMask = 0; } } puwBufferData++; } // else pixY is not inside terrain rect } else { // increment buffer data pointer puwBufferData+=pixRight-pixLeft; } } } else { ASSERTALWAYS("Ilegal buffer type"); return; } UpdateEditedTerrainTiles(ptrTerrain,rcExtract,btBufferType); } // check whether a polygon is below given point, but not too far away BOOL IsTerrainBelowPoint(CTerrain *ptrTerrain, const FLOAT3D &vPoint, FLOAT fMaxDist, const FLOAT3D &vGravityDir) { return TRUE; /* // get distance from point to the plane FLOAT fD = plPolygon.PointDistance(vPoint); // if the point is behind the plane if (fD<-0.01f) { // it cannot be below _pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON); return FALSE; } // find distance of point from the polygon along gravity vector FLOAT fDistance = -fD/fCos; // if too far away if (fDistance > fMaxDist) { // it cannot be below _pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON); return FALSE; } // project point to the polygon along gravity vector FLOAT3D vProjected = vPoint + en_vGravityDir*fDistance; // find major axes of the polygon plane INDEX iMajorAxis1, iMajorAxis2; GetMajorAxesForPlane(plPolygon, iMajorAxis1, iMajorAxis2); // create an intersector CIntersector isIntersector(vProjected(iMajorAxis1), vProjected(iMajorAxis2)); // for all edges in the polygon FOREACHINSTATICARRAY(pbpo->bpo_abpePolygonEdges, CBrushPolygonEdge, itbpePolygonEdge) { // get edge vertices (edge direction is irrelevant here!) const FLOAT3D &vVertex0 = itbpePolygonEdge->bpe_pbedEdge->bed_pbvxVertex0->bvx_vAbsolute; const FLOAT3D &vVertex1 = itbpePolygonEdge->bpe_pbedEdge->bed_pbvxVertex1->bvx_vAbsolute; // pass the edge to the intersector isIntersector.AddEdge( vVertex0(iMajorAxis1), vVertex0(iMajorAxis2), vVertex1(iMajorAxis1), vVertex1(iMajorAxis2)); } // if the point is inside polygon if (isIntersector.IsIntersecting()) { // it is below _pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON); return TRUE; // if the point is outside polygon } else { // it is not below _pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON); return FALSE; } */ }