/** * Creates a D3DXMESH from a FStaticMeshRenderData * @param Triangles The triangles to create the mesh from. * @param bRemoveDegenerateTriangles True if degenerate triangles should be removed * @param OutD3DMesh Mesh to create * @return Boolean representing success or failure */ bool ConvertRawMeshToD3DXMesh( IDirect3DDevice9* Device, FRawMesh& RawMesh, const bool bRemoveDegenerateTriangles, TRefCountPtr<ID3DXMesh>& OutD3DMesh ) { TArray<D3DVERTEXELEMENT9> VertexElements; GetD3D9MeshVertexDeclarations(VertexElements); TArray<FUtilVertex> Vertices; TArray<uint16> Indices; TArray<uint32> Attributes; int32 NumWedges = RawMesh.WedgeIndices.Num(); int32 NumTriangles = NumWedges / 3; int32 NumUVs = 0; for (; NumUVs < 8; ++NumUVs) { if (RawMesh.WedgeTexCoords[NumUVs].Num() != RawMesh.WedgeIndices.Num()) { break; } } bool bHaveColors = RawMesh.WedgeColors.Num() == NumWedges; bool bHaveNormals = RawMesh.WedgeTangentZ.Num() == NumWedges; bool bHaveTangents = bHaveNormals && RawMesh.WedgeTangentX.Num() == NumWedges && RawMesh.WedgeTangentY.Num() == NumWedges; for(int32 TriangleIndex = 0;TriangleIndex < NumTriangles;TriangleIndex++) { bool bTriangleIsDegenerate = false; if( bRemoveDegenerateTriangles ) { // Detect if the triangle is degenerate. for(int32 EdgeIndex = 0;EdgeIndex < 3;EdgeIndex++) { const int32 Wedge0 = TriangleIndex * 3 + EdgeIndex; const int32 Wedge1 = TriangleIndex * 3 + ((EdgeIndex + 1) % 3); if((RawMesh.GetWedgePosition(Wedge0) - RawMesh.GetWedgePosition(Wedge1)).IsNearlyZero(THRESH_POINTS_ARE_SAME * 4.0f)) { bTriangleIsDegenerate = true; break; } } } if(!bTriangleIsDegenerate) { Attributes.Add(RawMesh.FaceMaterialIndices[TriangleIndex]); for(int32 J=0;J<3;J++) { FUtilVertex* Vertex = new(Vertices) FUtilVertex; FMemory::Memzero(Vertex,sizeof(FUtilVertex)); int32 WedgeIndex = TriangleIndex * 3 + J; Vertex->Position = RawMesh.GetWedgePosition(WedgeIndex); if (bHaveColors) { Vertex->Color = RawMesh.WedgeColors[WedgeIndex]; } else { Vertex->Color = FColor::White; } //store the smoothing mask per vertex since there is only one per-face attribute that is already being used (materialIndex) Vertex->SmoothingMask = RawMesh.FaceSmoothingMasks[TriangleIndex]; if (bHaveTangents) { Vertex->TangentX = RawMesh.WedgeTangentX[WedgeIndex]; Vertex->TangentY = RawMesh.WedgeTangentY[WedgeIndex]; } if (bHaveNormals) { Vertex->TangentZ = RawMesh.WedgeTangentZ[WedgeIndex]; } for(int32 UVIndex = 0; UVIndex < NumUVs; UVIndex++) { Vertex->UVs[UVIndex] = RawMesh.WedgeTexCoords[UVIndex][WedgeIndex]; } Indices.Add(Vertices.Num() - 1); } } } // This code uses the raw triangles. Needs welding, etc. const int32 NumFaces = Indices.Num() / 3; const int32 NumVertices = NumFaces*3; check(Attributes.Num() == NumFaces); check(NumFaces * 3 == Indices.Num()); // Create mesh for source data if (FAILED(D3DXCreateMesh(NumFaces,NumVertices,D3DXMESH_SYSTEMMEM,(D3DVERTEXELEMENT9 *)VertexElements.GetData(),Device,OutD3DMesh.GetInitReference()) ) ) { UE_LOG(LogD3D9MeshUtils, Warning, TEXT("D3DXCreateMesh() Failed!")); return false; } // Fill D3DMesh mesh FUtilVertex* D3DVertices; uint16* D3DIndices; ::DWORD * D3DAttributes; OutD3DMesh->LockVertexBuffer(0,(LPVOID*)&D3DVertices); OutD3DMesh->LockIndexBuffer(0,(LPVOID*)&D3DIndices); OutD3DMesh->LockAttributeBuffer(0, &D3DAttributes); FMemory::Memcpy(D3DVertices,Vertices.GetTypedData(),Vertices.Num() * sizeof(FUtilVertex)); FMemory::Memcpy(D3DIndices,Indices.GetTypedData(),Indices.Num() * sizeof(uint16)); FMemory::Memcpy(D3DAttributes,Attributes.GetTypedData(),Attributes.Num() * sizeof(uint32)); OutD3DMesh->UnlockIndexBuffer(); OutD3DMesh->UnlockVertexBuffer(); OutD3DMesh->UnlockAttributeBuffer(); return true; }
void FStaticMeshEditorViewportClient::ProcessClick(class FSceneView& InView, class HHitProxy* HitProxy, FKey Key, EInputEvent Event, uint32 HitX, uint32 HitY) { const bool bCtrlDown = Viewport->KeyState(EKeys::LeftControl) || Viewport->KeyState(EKeys::RightControl); bool ClearSelectedSockets = true; bool ClearSelectedPrims = true; bool ClearSelectedEdges = true; if( HitProxy ) { if(HitProxy->IsA( HSMESocketProxy::StaticGetType() ) ) { HSMESocketProxy* SocketProxy = (HSMESocketProxy*)HitProxy; UStaticMeshSocket* Socket = NULL; if(SocketProxy->SocketIndex < StaticMesh->Sockets.Num()) { Socket = StaticMesh->Sockets[SocketProxy->SocketIndex]; } if(Socket) { StaticMeshEditorPtr.Pin()->SetSelectedSocket(Socket); } ClearSelectedSockets = false; } else if (HitProxy->IsA(HSMECollisionProxy::StaticGetType()) && StaticMesh->BodySetup) { HSMECollisionProxy* CollisionProxy = (HSMECollisionProxy*)HitProxy; if (StaticMeshEditorPtr.Pin()->IsSelectedPrim(CollisionProxy->PrimData)) { if (!bCtrlDown) { StaticMeshEditorPtr.Pin()->AddSelectedPrim(CollisionProxy->PrimData, true); } else { StaticMeshEditorPtr.Pin()->RemoveSelectedPrim(CollisionProxy->PrimData); } } else { StaticMeshEditorPtr.Pin()->AddSelectedPrim(CollisionProxy->PrimData, !bCtrlDown); } // Force the widget to translate, if not already set if (WidgetMode == FWidget::WM_None) { WidgetMode = FWidget::WM_Translate; } ClearSelectedPrims = false; } } else { const bool bShiftDown = Viewport->KeyState(EKeys::LeftShift) || Viewport->KeyState(EKeys::RightShift); if(!bCtrlDown && !bShiftDown) { SelectedEdgeIndices.Empty(); } // Check to see if we clicked on a mesh edge if( StaticMeshComponent != NULL && Viewport->GetSizeXY().X > 0 && Viewport->GetSizeXY().Y > 0 ) { FSceneViewFamilyContext ViewFamily( FSceneViewFamily::ConstructionValues( Viewport, GetScene(), EngineShowFlags )); FSceneView* View = CalcSceneView(&ViewFamily); FViewportClick ViewportClick(View, this, Key, Event, HitX, HitY); const FVector ClickLineStart( ViewportClick.GetOrigin() ); const FVector ClickLineEnd( ViewportClick.GetOrigin() + ViewportClick.GetDirection() * HALF_WORLD_MAX ); // Don't bother doing a line check as there is only one mesh in the SME and it makes fuzzy selection difficult // FHitResult CheckResult( 1.0f ); // if( StaticMeshComponent->LineCheck( // CheckResult, // In/Out: Result // ClickLineEnd, // Target // ClickLineStart, // Source // FVector::ZeroVector, // Extend // TRACE_ComplexCollision ) ) // Trace flags { // @todo: Should be in screen space ideally const float WorldSpaceMinClickDistance = 100.0f; float ClosestEdgeDistance = FLT_MAX; TArray< int32 > ClosestEdgeIndices; FVector ClosestEdgeVertices[ 2 ]; const uint32 LODLevel = FMath::Clamp( StaticMeshComponent->ForcedLodModel - 1, 0, StaticMeshComponent->StaticMesh->GetNumLODs() - 1 ); FRawMesh RawMesh; StaticMeshComponent->StaticMesh->SourceModels[LODLevel].RawMeshBulkData->LoadRawMesh(RawMesh); const int32 RawEdgeCount = RawMesh.WedgeIndices.Num() - 1; const int32 NumFaces = RawMesh.WedgeIndices.Num() / 3; int32 NumBackFacingTriangles = 0; for(int32 FaceIndex = 0; FaceIndex < NumFaces; ++FaceIndex) { // We disable edge selection where all adjoining triangles are back face culled and the // material is not two-sided. This prevents edges that are back-face culled from being selected. bool bIsBackFacing = false; bool bIsTwoSided = false; UMaterialInterface* Material = StaticMeshComponent->GetMaterial(RawMesh.FaceMaterialIndices[FaceIndex]); if (Material && Material->GetMaterial()) { bIsTwoSided = Material->IsTwoSided(); } if(!bIsTwoSided) { // Check whether triangle if back facing const FVector A = RawMesh.GetWedgePosition( FaceIndex * 3); const FVector B = RawMesh.GetWedgePosition( FaceIndex * 3 + 1); const FVector C = RawMesh.GetWedgePosition( FaceIndex * 3 + 2); // Compute the per-triangle normal const FVector BA = A - B; const FVector CA = A - C; const FVector TriangleNormal = (CA ^ BA).SafeNormal(); // Transform the view position from world to component space const FVector ComponentSpaceViewOrigin = StaticMeshComponent->ComponentToWorld.InverseTransformPosition( View->ViewMatrices.ViewOrigin); // Determine which side of the triangle's plane that the view position lies on. bIsBackFacing = (FVector::PointPlaneDist( ComponentSpaceViewOrigin, A, TriangleNormal) < 0.0f); } for( int32 VertIndex = 0; VertIndex < 3; ++VertIndex ) { const int32 EdgeIndex = FaceIndex * 3 + VertIndex; const int32 EdgeIndex2 = FaceIndex * 3 + ((VertIndex + 1) % 3); FVector EdgeVertices[ 2 ]; EdgeVertices[0] = RawMesh.GetWedgePosition(EdgeIndex); EdgeVertices[1] = RawMesh.GetWedgePosition(EdgeIndex2); // First check to see if this edge is already in our "closest to click" list. // Most edges are shared by two faces in our raw triangle data set, so we want // to select (or deselect) both of these edges that the user clicks on (what // appears to be) a single edge if( ClosestEdgeIndices.Num() > 0 && ( ( EdgeVertices[ 0 ].Equals( ClosestEdgeVertices[ 0 ] ) && EdgeVertices[ 1 ].Equals( ClosestEdgeVertices[ 1 ] ) ) || ( EdgeVertices[ 0 ].Equals( ClosestEdgeVertices[ 1 ] ) && EdgeVertices[ 1 ].Equals( ClosestEdgeVertices[ 0 ] ) ) ) ) { // Edge overlaps the closest edge we have so far, so just add it to the list ClosestEdgeIndices.Add( EdgeIndex ); // Increment the number of back facing triangles if the adjoining triangle // is back facing and isn't two-sided if(bIsBackFacing && !bIsTwoSided) { ++NumBackFacingTriangles; } } else { FVector WorldSpaceEdgeStart( StaticMeshComponent->ComponentToWorld.TransformPosition( EdgeVertices[ 0 ] ) ); FVector WorldSpaceEdgeEnd( StaticMeshComponent->ComponentToWorld.TransformPosition( EdgeVertices[ 1 ] ) ); // Determine the mesh edge that's closest to the ray cast through the eye towards the click location FVector ClosestPointToEdgeOnClickLine; FVector ClosestPointToClickLineOnEdge; FMath::SegmentDistToSegment( ClickLineStart, ClickLineEnd, WorldSpaceEdgeStart, WorldSpaceEdgeEnd, ClosestPointToEdgeOnClickLine, ClosestPointToClickLineOnEdge ); // Compute the minimum distance (squared) const float MinDistanceToEdgeSquared = ( ClosestPointToClickLineOnEdge - ClosestPointToEdgeOnClickLine ).SizeSquared(); if( MinDistanceToEdgeSquared <= WorldSpaceMinClickDistance ) { if( MinDistanceToEdgeSquared <= ClosestEdgeDistance ) { // This is the closest edge to the click line that we've found so far! ClosestEdgeDistance = MinDistanceToEdgeSquared; ClosestEdgeVertices[ 0 ] = EdgeVertices[ 0 ]; ClosestEdgeVertices[ 1 ] = EdgeVertices[ 1 ]; ClosestEdgeIndices.Reset(); ClosestEdgeIndices.Add( EdgeIndex ); // Reset the number of back facing triangles. NumBackFacingTriangles = (bIsBackFacing && !bIsTwoSided) ? 1 : 0; } } } } } // Did the user click on an edge? Edges must also have at least one adjoining triangle // which isn't back face culled (for one-sided materials) if( ClosestEdgeIndices.Num() > 0 && ClosestEdgeIndices.Num() > NumBackFacingTriangles) { for( int32 CurIndex = 0; CurIndex < ClosestEdgeIndices.Num(); ++CurIndex ) { const int32 CurEdgeIndex = ClosestEdgeIndices[ CurIndex ]; if( bCtrlDown ) { // Toggle selection if( SelectedEdgeIndices.Contains( CurEdgeIndex ) ) { SelectedEdgeIndices.Remove( CurEdgeIndex ); } else { SelectedEdgeIndices.Add( CurEdgeIndex ); } } else { // Append to selection SelectedEdgeIndices.Add( CurEdgeIndex ); } } // Reset cached vertices and uv coordinates. SelectedEdgeVertices.Reset(); for(int32 TexCoordIndex = 0; TexCoordIndex < MAX_STATIC_TEXCOORDS; ++TexCoordIndex) { SelectedEdgeTexCoords[TexCoordIndex].Reset(); } for(FSelectedEdgeSet::TIterator SelectionIt( SelectedEdgeIndices ); SelectionIt; ++SelectionIt) { const uint32 EdgeIndex = *SelectionIt; const uint32 FaceIndex = EdgeIndex / 3; const uint32 WedgeIndex = FaceIndex * 3 + (EdgeIndex % 3); const uint32 WedgeIndex2 = FaceIndex * 3 + ((EdgeIndex + 1) % 3); // Cache edge vertices in local space. FVector EdgeVertices[ 2 ]; EdgeVertices[ 0 ] = RawMesh.GetWedgePosition(WedgeIndex); EdgeVertices[ 1 ] = RawMesh.GetWedgePosition(WedgeIndex2); SelectedEdgeVertices.Add(EdgeVertices[0]); SelectedEdgeVertices.Add(EdgeVertices[1]); // Cache UV for(int32 TexCoordIndex = 0; TexCoordIndex < MAX_STATIC_TEXCOORDS; ++TexCoordIndex) { if( RawMesh.WedgeTexCoords[TexCoordIndex].Num() > 0) { FVector2D UVIndex1, UVIndex2; UVIndex1 = RawMesh.WedgeTexCoords[TexCoordIndex][WedgeIndex]; UVIndex2 = RawMesh.WedgeTexCoords[TexCoordIndex][WedgeIndex2]; SelectedEdgeTexCoords[TexCoordIndex].Add(UVIndex1); SelectedEdgeTexCoords[TexCoordIndex].Add(UVIndex2); } } } ClearSelectedEdges = false; } } } } if (ClearSelectedSockets && StaticMeshEditorPtr.Pin()->GetSelectedSocket()) { StaticMeshEditorPtr.Pin()->SetSelectedSocket(NULL); } if (ClearSelectedPrims) { StaticMeshEditorPtr.Pin()->ClearSelectedPrims(); } if (ClearSelectedEdges) { SelectedEdgeIndices.Empty(); } Invalidate(); }