virtual int32 Recompress(FName Format, const TArray<uint8>& SrcBuffer, FSoundQualityInfo& QualityInfo, TArray<uint8>& OutBuffer) const { check(Format == NAME_OGG); FVorbisAudioInfo OggInfo; int32 CompressedSize = -1; // Cannot quality preview multichannel sounds if( QualityInfo.NumChannels > 2 ) { return 0; } TArray<uint8> CompressedDataStore; if( !Cook( Format, SrcBuffer, QualityInfo, CompressedDataStore ) ) { return 0; } // Parse the ogg vorbis header for the relevant information if( !OggInfo.ReadCompressedInfo( CompressedDataStore.GetTypedData(), CompressedDataStore.Num(), &QualityInfo ) ) { return 0; } // Decompress all the sample data OutBuffer.Empty(QualityInfo.SampleDataSize); OutBuffer.AddZeroed(QualityInfo.SampleDataSize); OggInfo.ExpandFile( OutBuffer.GetTypedData(), &QualityInfo ); return CompressedDataStore.Num(); }
void FHttpResponseWinInet::ProcessResponseHeaders() { ::DWORD HeaderSize = 0; TArray<FString> Result; if (!HttpQueryInfo(Request.RequestHandle, HTTP_QUERY_RAW_HEADERS_CRLF, NULL, &HeaderSize, NULL)) { uint32 ErrorCode = GetLastError(); if (ErrorCode != ERROR_INSUFFICIENT_BUFFER) { UE_LOG(LogHttp, Warning, TEXT("HttpQueryInfo to get header length for all headers failed: %s. %p"), *InternetTranslateError(GetLastError()), &Request); } if (HeaderSize == 0) { UE_LOG(LogHttp, Warning, TEXT("HttpQueryInfo for all headers returned zero header size. %p"), this); } TArray<TCHAR> HeaderBuffer; HeaderBuffer.AddUninitialized(HeaderSize/sizeof(TCHAR)); if (!HttpQueryInfo(Request.RequestHandle, HTTP_QUERY_RAW_HEADERS_CRLF, HeaderBuffer.GetTypedData(), &HeaderSize, NULL)) { UE_LOG(LogHttp, Warning, TEXT("HttpQueryInfo for all headers failed: %s. %p"), *InternetTranslateError(GetLastError()), &Request); } // parse all the key/value pairs const TCHAR* HeaderPtr = HeaderBuffer.GetTypedData(); // don't count the terminating NULL character as one to search. const TCHAR* EndPtr = HeaderPtr + HeaderBuffer.Num()-1; while (HeaderPtr < EndPtr) { const TCHAR* DelimiterPtr = FCString::Strstr(HeaderPtr, TEXT("\r\n")); if (DelimiterPtr == NULL) { DelimiterPtr = EndPtr; } FString HeaderLine(DelimiterPtr-HeaderPtr, HeaderPtr); FString HeaderKey,HeaderValue; if (HeaderLine.Split(TEXT(":"), &HeaderKey, &HeaderValue)) { if (!HeaderKey.IsEmpty()) { ResponseHeaders.Add(HeaderKey, HeaderValue.Trim()); } } HeaderPtr = DelimiterPtr + 2; } } else { UE_LOG(LogHttp, Warning, TEXT("HttpQueryInfo for all headers failed when trying to determine the size for the header buffer. %p"), &Request); } }
/** * Dump allocation information. */ void FBestFitAllocator::DumpAllocs( FOutputDevice& Ar/*=*GLog*/ ) { // Memory usage stats. INT UsedSize = 0; INT FreeSize = 0; INT NumUsedChunks = 0; INT NumFreeChunks = 0; // Fragmentation and allocation size visualization. INT NumBlocks = MemorySize / AllocationAlignment; INT Dimension = 1 + NumBlocks / appTrunc(appSqrt(NumBlocks)); TArray<FColor> AllocationVisualization; AllocationVisualization.AddZeroed( Dimension * Dimension ); INT VisIndex = 0; // Traverse linked list and gather allocation information. FMemoryChunk* CurrentChunk = FirstChunk; while( CurrentChunk ) { FColor VisColor; // Free chunk. if( CurrentChunk->bIsAvailable ) { NumFreeChunks++; FreeSize += CurrentChunk->Size; VisColor = FColor(0,255,0); } // Allocated chunk. else { NumUsedChunks++; UsedSize += CurrentChunk->Size; // Slightly alternate coloration to also visualize allocation sizes. if( NumUsedChunks % 2 == 0 ) { VisColor = FColor(255,0,0); } else { VisColor = FColor(192,0,0); } } for( INT i=0; i<(CurrentChunk->Size/AllocationAlignment); i++ ) { AllocationVisualization(VisIndex++) = VisColor; } CurrentChunk = CurrentChunk->NextChunk; } check(UsedSize == AllocatedMemorySize); check(FreeSize == AvailableMemorySize); // Write out bitmap for visualization of fragmentation and allocation patterns. appCreateBitmap( TEXT("..\\..\\Binaries\\TextureMemory"), Dimension, Dimension, AllocationVisualization.GetTypedData() ); Ar.Logf( TEXT("BestFitAllocator: Allocated %i KByte in %i chunks, leaving %i KByte in %i chunks."), UsedSize / 1024, NumUsedChunks, FreeSize / 1024, NumFreeChunks ); Ar.Logf( TEXT("BestFitAllocator: %5.2f ms in allocator"), TimeSpentInAllocator * 1000 ); }
FVertexShaderRHIRef FD3D11DynamicRHI::RHICreateVertexShader(const TArray<uint8>& Code) { check(Code.Num()); TRefCountPtr<ID3D11VertexShader> D3DShader; VERIFYD3D11RESULT(Direct3DDevice->CreateVertexShader((void*)Code.GetTypedData(),Code.Num() - 1,NULL,D3DShader.GetInitReference())); return new FD3D11VertexShader(D3DShader,Code); }
void UDestructibleComponent::SetCollisionResponseForActor(const FCollisionResponse& ColResponse, PxRigidDynamic* Actor, int32 ChunkIdx) { // Get collision channel and response PxFilterData PQueryFilterData, PSimFilterData; uint8 MoveChannel = GetCollisionObjectType(); if(IsCollisionEnabled()) { AActor* Owner = GetOwner(); CreateShapeFilterData(MoveChannel, (Owner ? Owner->GetUniqueID() : 0), ColResponse.GetResponseContainer(), 0, ChunkIdxToBoneIdx(ChunkIdx), PQueryFilterData, PSimFilterData, BodyInstance.bUseCCD, BodyInstance.bNotifyRigidBodyCollision, false); PQueryFilterData.word3 |= EPDF_SimpleCollision | EPDF_ComplexCollision; SCOPED_SCENE_WRITE_LOCK(Actor->getScene()); TArray<PxShape*> Shapes; Shapes.AddUninitialized(Actor->getNbShapes()); int ShapeCount = Actor->getShapes(Shapes.GetTypedData(), Shapes.Num()); for (int32 i=0; i < ShapeCount; ++i) { PxShape* Shape = Shapes[i]; Shape->setQueryFilterData(PQueryFilterData); Shape->setSimulationFilterData(PSimFilterData); Shape->setFlag(PxShapeFlag::eSCENE_QUERY_SHAPE, true); Shape->setFlag(PxShapeFlag::eSIMULATION_SHAPE, true); Shape->setFlag(PxShapeFlag::eVISUALIZATION, true); } } }
FComputeShaderRHIRef FD3D11DynamicRHI::RHICreateComputeShader(const TArray<uint8>& Code) { check(Code.Num()); TRefCountPtr<ID3D11ComputeShader> D3DShader; // bGlobalUniformBufferUsed is in the last byte, see CompileD3D11Shader VERIFYD3D11RESULT(Direct3DDevice->CreateComputeShader((void*)Code.GetTypedData(),Code.Num() - 1,NULL,(ID3D11ComputeShader**)D3DShader.GetInitReference())); return new FD3D11ComputeShader(D3DShader, Code); }
virtual void InitRHI() { VertexBufferRHI = RHICreateVertexBuffer(Vertices.Num() * sizeof(FDynamicMeshVertex), NULL, BUF_Static); // Copy the vertex data into the vertex buffer. void* VertexBufferData = RHILockVertexBuffer(VertexBufferRHI, 0, Vertices.Num() * sizeof(FDynamicMeshVertex), RLM_WriteOnly); FMemory::Memcpy(VertexBufferData, Vertices.GetTypedData(), Vertices.Num() * sizeof(FDynamicMeshVertex)); RHIUnlockVertexBuffer(VertexBufferRHI); }
virtual void InitRHI() { IndexBufferRHI = RHICreateIndexBuffer(sizeof(int32), Indices.Num() * sizeof(int32), NULL, BUF_Static); // Write the indices to the index buffer. void* Buffer = RHILockIndexBuffer(IndexBufferRHI, 0, Indices.Num() * sizeof(int32), RLM_WriteOnly); FMemory::Memcpy(Buffer, Indices.GetTypedData(), Indices.Num() * sizeof(int32)); RHIUnlockIndexBuffer(IndexBufferRHI); }
virtual void InitRHI() override { FRHIResourceCreateInfo CreateInfo; IndexBufferRHI = RHICreateIndexBuffer(sizeof(int32), Indices.Num() * sizeof(int32), BUF_Static, CreateInfo); // Write the indices to the index buffer. void* Buffer = RHILockIndexBuffer(IndexBufferRHI, 0, Indices.Num() * sizeof(int32), RLM_WriteOnly); FMemory::Memcpy(Buffer, Indices.GetTypedData(), Indices.Num() * sizeof(int32)); RHIUnlockIndexBuffer(IndexBufferRHI); }
/** * Assembles an accelerator key table for the browser by calling down to AddAcceleratorTableEntries. */ void WxBrowser::SetBrowserAcceleratorTable(wxFrame* Frame) { TArray<wxAcceleratorEntry> Entries; // Allow derived classes an opportunity to register accelerator keys. AddAcceleratorTableEntries( Entries ); // Create the new table with these. Frame->SetAcceleratorTable( wxAcceleratorTable(Entries.Num(),Entries.GetTypedData()) ); }
/** * Dumps capture stack trace summary to the passed in log. */ void FScriptStackTracker::DumpStackTraces( INT StackThreshold, FOutputDevice& Ar ) { // Avoid distorting results while we log them. check( !bAvoidCapturing ); bAvoidCapturing = TRUE; // Make a copy as sorting causes index mismatch with TMap otherwise. TArray<FCallStack> SortedCallStacks = CallStacks; // Sort callstacks in descending order by stack count. Sort<USE_COMPARE_CONSTREF(FCallStack,StackTracker)>( SortedCallStacks.GetTypedData(), SortedCallStacks.Num() ); // Iterate over each callstack to get total stack count. QWORD TotalStackCount = 0; for( INT CallStackIndex=0; CallStackIndex<SortedCallStacks.Num(); CallStackIndex++ ) { const FCallStack& CallStack = SortedCallStacks(CallStackIndex); TotalStackCount += CallStack.StackCount; } // Calculate the number of frames we captured. INT FramesCaptured = 0; if( bIsEnabled ) { FramesCaptured = GFrameCounter - StartFrameCounter; } else { FramesCaptured = StopFrameCounter - StartFrameCounter; } // Log quick summary as we don't log each individual so totals in CSV won't represent real totals. Ar.Logf(TEXT("Captured %i unique callstacks totalling %i function calls over %i frames, averaging %5.2f calls/frame"), SortedCallStacks.Num(), (int)TotalStackCount, FramesCaptured, (FLOAT) TotalStackCount / FramesCaptured); // Iterate over each callstack and write out info in human readable form in CSV format for( INT CallStackIndex=0; CallStackIndex<SortedCallStacks.Num(); CallStackIndex++ ) { const FCallStack& CallStack = SortedCallStacks(CallStackIndex); // Avoid log spam by only logging above threshold. if( CallStack.StackCount > StackThreshold ) { // First row is stack count. FString CallStackString = appItoa(CallStack.StackCount); CallStackString += LINE_TERMINATOR; CallStackString += CallStack.StackTrace; // Finally log with ',' prefix so "Log:" can easily be discarded as row in Excel. Ar.Logf(TEXT(",%s"),*CallStackString); } } // Done logging. bAvoidCapturing = FALSE; }
virtual void InitRHI() { if (Vertices.Num() > 0) { FRHIResourceCreateInfo CreateInfo; VertexBufferRHI = RHICreateVertexBuffer(Vertices.Num() * sizeof(FBrickVertex), BUF_Dynamic, CreateInfo); // Copy the vertex data into the vertex buffer. void* VertexBufferData = RHILockVertexBuffer(VertexBufferRHI, 0, Vertices.Num() * sizeof(FBrickVertex), RLM_WriteOnly); FMemory::Memcpy(VertexBufferData, Vertices.GetTypedData(), Vertices.Num() * sizeof(FBrickVertex)); RHIUnlockVertexBuffer(VertexBufferRHI); } }
void FDirectoryWatcherWindows::Tick( float DeltaSeconds ) { TArray<HANDLE> DirectoryHandles; TMap<FString, FDirectoryWatchRequestWindows*> InvalidRequestsToDelete; // Find all handles to listen to and invalid requests to delete for (TMap<FString, FDirectoryWatchRequestWindows*>::TConstIterator RequestIt(RequestMap); RequestIt; ++RequestIt) { if ( RequestIt.Value()->IsPendingDelete() ) { InvalidRequestsToDelete.Add(RequestIt.Key(), RequestIt.Value()); } else { DirectoryHandles.Add(RequestIt.Value()->GetDirectoryHandle()); } } // Remove all invalid requests from the request map and add them to the pending delete list so they will be deleted below for (TMap<FString, FDirectoryWatchRequestWindows*>::TConstIterator RequestIt(InvalidRequestsToDelete); RequestIt; ++RequestIt) { RequestMap.Remove(RequestIt.Key()); RequestsPendingDelete.AddUnique(RequestIt.Value()); } // Trigger any file changed delegates that are queued up if ( DirectoryHandles.Num() > 0 ) { MsgWaitForMultipleObjectsEx(DirectoryHandles.Num(), DirectoryHandles.GetTypedData(), 0, QS_ALLEVENTS, MWMO_ALERTABLE); } // Delete any stale or invalid requests for ( int32 RequestIdx = RequestsPendingDelete.Num() - 1; RequestIdx >= 0; --RequestIdx ) { FDirectoryWatchRequestWindows* Request = RequestsPendingDelete[RequestIdx]; if ( Request->IsPendingDelete() ) { // This request is safe to delete. Delete and remove it from the list delete Request; NumRequests--; RequestsPendingDelete.RemoveAt(RequestIdx); } } // Finally, trigger any file change notification delegates for (TMap<FString, FDirectoryWatchRequestWindows*>::TConstIterator RequestIt(RequestMap); RequestIt; ++RequestIt) { RequestIt.Value()->ProcessPendingNotifications(); } }
/** * Translates an error returned from GetLastError returned from a WinInet API call. * * @param GetLastErrorResult - error code to translate * @return string for the error code */ FString InternetTranslateError(::DWORD GetLastErrorResult) { FString ErrorStr = FString::Printf(TEXT("ErrorCode: %08X. "), (uint32)GetLastErrorResult); HANDLE ProcessHeap = GetProcessHeap(); if (ProcessHeap == NULL) { ErrorStr += TEXT("Call to GetProcessHeap() failed, cannot translate error... "); return ErrorStr; } TCHAR FormatBuffer[1024]; uint32 BaseLength = FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE, GetModuleHandle(TEXT("wininet.dll")), GetLastErrorResult, 0, FormatBuffer, ARRAYSIZE(FormatBuffer), NULL); if (!BaseLength) { ErrorStr += FString::Printf(TEXT("Call to FormatMessage() failed: %08X. "), (uint32)GetLastError()); return ErrorStr; } ErrorStr += FString::Printf(TEXT("Desc: %s. "), FormatBuffer); if (GetLastErrorResult == ERROR_INTERNET_EXTENDED_ERROR) { ::DWORD InetError; ::DWORD ExtLength = 0; InternetGetLastResponseInfo(&InetError, NULL, &ExtLength); ExtLength = ExtLength+1; TArray<TCHAR> ExtErrMsg; ExtErrMsg.AddUninitialized(ExtLength); if (!InternetGetLastResponseInfo(&InetError, ExtErrMsg.GetTypedData(), &ExtLength)) { ErrorStr += FString::Printf(TEXT("Call to InternetGetLastResponseInfo() failed: %08X. "), (uint32) GetLastError()); return ErrorStr; } } return ErrorStr; }
//------------------------------------------------------------------------- // //------------------------------------------------------------------------- void UnFbx::FFbxImporter::FixupMaterial( FbxSurfaceMaterial& FbxMaterial, UMaterial* UnrealMaterial ) { // add a basic diffuse color if no texture is linked to diffuse if (UnrealMaterial->BaseColor.Expression == NULL) { FbxDouble3 DiffuseColor; UMaterialExpressionVectorParameter* MyColorExpression = ConstructObject<UMaterialExpressionVectorParameter>( UMaterialExpressionVectorParameter::StaticClass(), UnrealMaterial ); UnrealMaterial->Expressions.Add( MyColorExpression ); UnrealMaterial->BaseColor.Expression = MyColorExpression; bool bFoundDiffuseColor = true; if( FbxMaterial.GetClassId().Is(FbxSurfacePhong::ClassId) ) { DiffuseColor = ((FbxSurfacePhong&)(FbxMaterial)).Diffuse.Get(); } else if( FbxMaterial.GetClassId().Is(FbxSurfaceLambert::ClassId) ) { DiffuseColor = ((FbxSurfaceLambert&)(FbxMaterial)).Diffuse.Get(); } else { bFoundDiffuseColor = false; } if( bFoundDiffuseColor ) { MyColorExpression->DefaultValue.R = (float)(DiffuseColor[0]); MyColorExpression->DefaultValue.G = (float)(DiffuseColor[1]); MyColorExpression->DefaultValue.B = (float)(DiffuseColor[2]); } else { // use random color because there may be multiple materials, then they can be different MyColorExpression->DefaultValue.R = 0.5f+(0.5f*FMath::Rand())/RAND_MAX; MyColorExpression->DefaultValue.G = 0.5f+(0.5f*FMath::Rand())/RAND_MAX; MyColorExpression->DefaultValue.B = 0.5f+(0.5f*FMath::Rand())/RAND_MAX; } TArray<FExpressionOutput> Outputs = UnrealMaterial->BaseColor.Expression->GetOutputs(); FExpressionOutput* Output = Outputs.GetTypedData(); UnrealMaterial->BaseColor.Mask = Output->Mask; UnrealMaterial->BaseColor.MaskR = Output->MaskR; UnrealMaterial->BaseColor.MaskG = Output->MaskG; UnrealMaterial->BaseColor.MaskB = Output->MaskB; UnrealMaterial->BaseColor.MaskA = Output->MaskA; } }
FGeometryShaderRHIRef FD3D11DynamicRHI::RHICreateGeometryShaderWithStreamOutput(const TArray<uint8>& Code, const FStreamOutElementList& ElementList, uint32 NumStrides, const uint32* Strides, int32 RasterizedStream) { check(Code.Num()); uint32 D3DRasterizedStream = RasterizedStream; if (RasterizedStream == -1) { D3DRasterizedStream = D3D11_SO_NO_RASTERIZED_STREAM; } D3D11_SO_DECLARATION_ENTRY StreamOutEntries[D3D11_SO_STREAM_COUNT * D3D11_SO_OUTPUT_COMPONENT_COUNT]; for (int32 EntryIndex = 0; EntryIndex < ElementList.Num(); EntryIndex++) { StreamOutEntries[EntryIndex].Stream = ElementList[EntryIndex].Stream; StreamOutEntries[EntryIndex].SemanticName = ElementList[EntryIndex].SemanticName; StreamOutEntries[EntryIndex].SemanticIndex = ElementList[EntryIndex].SemanticIndex; StreamOutEntries[EntryIndex].StartComponent = ElementList[EntryIndex].StartComponent; StreamOutEntries[EntryIndex].ComponentCount = ElementList[EntryIndex].ComponentCount; StreamOutEntries[EntryIndex].OutputSlot = ElementList[EntryIndex].OutputSlot; } TRefCountPtr<ID3D11GeometryShader> D3DShader; VERIFYD3D11RESULT(Direct3DDevice->CreateGeometryShaderWithStreamOutput( (const void*)Code.GetTypedData(), // bGlobalUniformBufferUsed is in the last byte, see CompileD3D11Shader Code.Num() - 1, StreamOutEntries, ElementList.Num(), Strides, NumStrides, D3DRasterizedStream, NULL, (ID3D11GeometryShader**)D3DShader.GetInitReference())); return new FD3D11GeometryShader(D3DShader, Code); }
/** * 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; }
/** * Dumps capture stack trace summary to the passed in log. */ void FStackTracker::DumpStackTraces( INT StackThreshold, FOutputDevice& Ar ) { // Avoid distorting results while we log them. check( !bAvoidCapturing ); bAvoidCapturing = TRUE; // Make a copy as sorting causes index mismatch with TMap otherwise. TArray<FCallStack> SortedCallStacks = CallStacks; // Sort callstacks in descending order by stack count. Sort<USE_COMPARE_CONSTREF(FCallStack,StackTracker)>( SortedCallStacks.GetTypedData(), SortedCallStacks.Num() ); // Iterate over each callstack to get total stack count. QWORD TotalStackCount = 0; for( INT CallStackIndex=0; CallStackIndex<SortedCallStacks.Num(); CallStackIndex++ ) { const FCallStack& CallStack = SortedCallStacks(CallStackIndex); TotalStackCount += CallStack.StackCount; } // Calculate the number of frames we captured. INT FramesCaptured = 0; if( bIsEnabled ) { FramesCaptured = GFrameCounter - StartFrameCounter; } else { FramesCaptured = StopFrameCounter - StartFrameCounter; } // Log quick summary as we don't log each individual so totals in CSV won't represent real totals. Ar.Logf(TEXT("Captured %i unique callstacks totalling %i function calls over %i frames, averaging %5.2f calls/frame, Avg Per Frame"), SortedCallStacks.Num(), (int)TotalStackCount, FramesCaptured, (FLOAT) TotalStackCount / FramesCaptured); // Iterate over each callstack and write out info in human readable form in CSV format for( INT CallStackIndex=0; CallStackIndex<SortedCallStacks.Num(); CallStackIndex++ ) { const FCallStack& CallStack = SortedCallStacks(CallStackIndex); // Avoid log spam by only logging above threshold. if( CallStack.StackCount > StackThreshold ) { // First row is stack count. FString CallStackString = appItoa(CallStack.StackCount); CallStackString += FString::Printf( TEXT(",%5.2f"), static_cast<FLOAT>(CallStack.StackCount)/static_cast<FLOAT>(FramesCaptured) ); // Iterate over all addresses in the callstack to look up symbol name. for( INT AddressIndex=0; AddressIndex<ARRAY_COUNT(CallStack.Addresses) && CallStack.Addresses[AddressIndex]; AddressIndex++ ) { ANSICHAR AddressInformation[512]; AddressInformation[0] = 0; appProgramCounterToHumanReadableString( CallStack.Addresses[AddressIndex], AddressInformation, ARRAY_COUNT(AddressInformation)-1, VF_DISPLAY_FILENAME ); CallStackString = CallStackString + LINE_TERMINATOR TEXT(",,,") + FString(AddressInformation); } // Finally log with ',' prefix so "Log:" can easily be discarded as row in Excel. Ar.Logf(TEXT(",%s"),*CallStackString); //Append any user information before moving on to the next callstack if (ReportFn) { ReportFn(CallStack, Ar); } } } // Done logging. bAvoidCapturing = FALSE; }
/** * Fill an FSkeletalMeshImportData with data from an APEX Destructible Asset. * * @param ImportData - SkeletalMesh import data into which we are extracting information * @param ApexDestructibleAsset - the Apex Destructible Asset * @param bHaveAllNormals - if the function is successful, this value is true iff every submesh has a normal channel * @param bHaveAllTangents - if the function is successful, this value is true iff every submesh has a tangent channel * * @return Boolean true iff the operation is successful */ static bool FillSkelMeshImporterFromApexDestructibleAsset(FSkeletalMeshImportData& ImportData, const NxDestructibleAsset& ApexDestructibleAsset, bool& bHaveAllNormals, bool& bHaveAllTangents) { // The APEX Destructible Asset contains an APEX Render Mesh Asset, get a pointer to this const physx::NxRenderMeshAsset* ApexRenderMesh = ApexDestructibleAsset.getRenderMeshAsset(); if (ApexRenderMesh == NULL) { return false; } if (ApexDestructibleAsset.getChunkCount() != ApexRenderMesh->getPartCount()) { UE_LOG(LogApexDestructibleAssetImport, Warning,TEXT("Chunk count does not match part count. APEX Destructible Asset with chunk instancing not yet supported.")); return false; } // Apex Render Mesh uses triangle lists only, currently. No need to triangulate. // Assume there are no vertex colors ImportData.bHasVertexColors = false; // Different submeshes can have different UV counts. Get the max. uint32 UniqueUVCount = 0; // Count vertices and triangles uint32 VertexCount = 0; uint32 TriangleCount = 0; for (uint32 SubmeshIndex = 0; SubmeshIndex < ApexRenderMesh->getSubmeshCount(); ++SubmeshIndex) { const NxRenderSubmesh& Submesh = ApexRenderMesh->getSubmesh(SubmeshIndex); const NxVertexBuffer& VB = Submesh.getVertexBuffer(); const NxVertexFormat& VBFormat = VB.getFormat(); // Count UV channels in this VB uint32 UVNum; for (UVNum = 0; UVNum < NxVertexFormat::MAX_UV_COUNT; ++UVNum) { const NxVertexFormat::BufferID BufferID = VBFormat.getSemanticID((NxRenderVertexSemantic::Enum)(NxRenderVertexSemantic::TEXCOORD0 + UVNum)); if (VBFormat.getBufferIndexFromID(BufferID) < 0) { break; } } UniqueUVCount = FMath::Max<uint32>( UniqueUVCount, UVNum ); // See if this VB has a color channel const NxVertexFormat::BufferID BufferID = VBFormat.getSemanticID(NxRenderVertexSemantic::COLOR); if (VBFormat.getBufferIndexFromID(BufferID) >= 0) { ImportData.bHasVertexColors = true; } // Count vertices VertexCount += VB.getVertexCount(); // Count triangles uint32 IndexCount = 0; for (uint32 PartIndex = 0; PartIndex < ApexRenderMesh->getPartCount(); ++PartIndex) { IndexCount += Submesh.getIndexCount(PartIndex); } check(IndexCount%3 == 0); TriangleCount += IndexCount/3; } // One UV set is required but only import up to MAX_TEXCOORDS number of uv layers ImportData.NumTexCoords = FMath::Clamp<uint32>(UniqueUVCount, 1, MAX_TEXCOORDS); // Expand buffers in ImportData: ImportData.Points.AddUninitialized(VertexCount); ImportData.Influences.AddUninitialized(VertexCount); ImportData.Wedges.AddUninitialized(3*TriangleCount); uint32 WedgeIndex = 0; ImportData.Faces.AddUninitialized(TriangleCount); uint32 TriangleIndex = 0; uint32 VertexIndexBase = 0; // True until proven otherwise bHaveAllNormals = true; bHaveAllTangents = true; TArray<VMaterial> UniqueMaterials; for (int32 CurMatIdx=0; CurMatIdx < ImportData.Materials.Num(); ++CurMatIdx) { bool bHasMaterial = false; for (int32 CurUniqueMatIdx=0; CurUniqueMatIdx < UniqueMaterials.Num(); ++CurUniqueMatIdx) { if (ImportData.Materials[CurMatIdx].MaterialName == UniqueMaterials[CurUniqueMatIdx].MaterialName) { bHasMaterial = true; break; } } if (!bHasMaterial) { UniqueMaterials.Add(ImportData.Materials[CurMatIdx]); } } // APEX render meshes are organized by submesh (render elements) // Looping through submeshes first, can be done either way for (uint32 SubmeshIndex = 0; SubmeshIndex < ApexRenderMesh->getSubmeshCount(); ++SubmeshIndex) { // Submesh data const NxRenderSubmesh& Submesh = ApexRenderMesh->getSubmesh(SubmeshIndex); const NxVertexBuffer& VB = Submesh.getVertexBuffer(); const NxVertexFormat& VBFormat = VB.getFormat(); const physx::PxU32 SubmeshVertexCount = VB.getVertexCount(); // Get VB data semantic indices: // Positions const PxI32 PositionBufferIndex = VBFormat.getBufferIndexFromID(VBFormat.getSemanticID(NxRenderVertexSemantic::POSITION)); if (!VB.getBufferData(&ImportData.Points[VertexIndexBase], physx::NxRenderDataFormat::FLOAT3, sizeof(FVector), PositionBufferIndex, 0, SubmeshVertexCount)) { return false; // Need a position buffer! } #if INVERT_Y_AND_V for (uint32 VertexNum = 0; VertexNum < SubmeshVertexCount; ++VertexNum) { ImportData.Points[VertexIndexBase + VertexNum].Y *= -1.0f; } #endif // Normals const PxI32 NormalBufferIndex = VBFormat.getBufferIndexFromID(VBFormat.getSemanticID(NxRenderVertexSemantic::NORMAL)); TArray<FVector> Normals; Normals.AddUninitialized(SubmeshVertexCount); const bool bHaveNormals = VB.getBufferData(Normals.GetTypedData(), physx::NxRenderDataFormat::FLOAT3, sizeof(FVector), NormalBufferIndex, 0, SubmeshVertexCount); if (!bHaveNormals) { FMemory::Memset(Normals.GetTypedData(), 0, SubmeshVertexCount*sizeof(FVector)); // Fill with zeros } // Tangents const PxI32 TangentBufferIndex = VBFormat.getBufferIndexFromID(VBFormat.getSemanticID(NxRenderVertexSemantic::TANGENT)); TArray<FVector> Tangents; Tangents.AddUninitialized(SubmeshVertexCount); const bool bHaveTangents = VB.getBufferData(Tangents.GetTypedData(), physx::NxRenderDataFormat::FLOAT3, sizeof(FVector), TangentBufferIndex, 0, SubmeshVertexCount); if (!bHaveTangents) { FMemory::Memset(Tangents.GetTypedData(), 0, SubmeshVertexCount*sizeof(FVector)); // Fill with zeros } // Update bHaveAllNormals and bHaveAllTangents bHaveAllNormals = bHaveAllNormals && bHaveNormals; bHaveAllTangents = bHaveAllTangents && bHaveTangents; // Binromals const PxI32 BinormalBufferIndex = VBFormat.getBufferIndexFromID(VBFormat.getSemanticID(NxRenderVertexSemantic::BINORMAL)); TArray<FVector> Binormals; Binormals.AddUninitialized(SubmeshVertexCount); bool bHaveBinormals = VB.getBufferData(Binormals.GetTypedData(), physx::NxRenderDataFormat::FLOAT3, sizeof(FVector), BinormalBufferIndex, 0, SubmeshVertexCount); if (!bHaveBinormals) { bHaveBinormals = bHaveNormals && bHaveTangents; for (uint32 i = 0; i < SubmeshVertexCount; ++i) { Binormals[i] = Normals[i]^Tangents[i]; // Build from normals and tangents. If one of these doesn't exist we'll get (0,0,0)'s } } // Colors const PxI32 ColorBufferIndex = VBFormat.getBufferIndexFromID(VBFormat.getSemanticID(NxRenderVertexSemantic::COLOR)); TArray<FColor> Colors; Colors.AddUninitialized(SubmeshVertexCount); const bool bHaveColors = VB.getBufferData(Colors.GetTypedData(), physx::NxRenderDataFormat::B8G8R8A8, sizeof(FColor), ColorBufferIndex, 0, SubmeshVertexCount); if (!bHaveColors) { FMemory::Memset(Colors.GetTypedData(), 0xFF, SubmeshVertexCount*sizeof(FColor)); // Fill with 0xFF } // UVs TArray<FVector2D> UVs[NxVertexFormat::MAX_UV_COUNT]; for (uint32 UVNum = 0; UVNum < ImportData.NumTexCoords; ++UVNum) { const PxI32 UVBufferIndex = VBFormat.getBufferIndexFromID(VBFormat.getSemanticID((NxRenderVertexSemantic::Enum)(NxRenderVertexSemantic::TEXCOORD0 + UVNum))); UVs[UVNum].AddUninitialized(SubmeshVertexCount); if (!VB.getBufferData(&UVs[UVNum][0].X, physx::NxRenderDataFormat::FLOAT2, sizeof(FVector2D), UVBufferIndex, 0, SubmeshVertexCount)) { FMemory::Memset(&UVs[UVNum][0].X, 0, SubmeshVertexCount*sizeof(FVector2D)); // Fill with zeros } } // Bone indices will not be imported - they're implicitly the PartIndex // Each submesh is partitioned into parts. Currently we're assuming a 1-1 correspondence between chunks and parts, // which means that instanced chunks are not supported. However, we will not assume that the chunk and part ordering is the same. // Therefore, instead of looping through parts, we loop through chunks here, and get the part index. for (uint32 ChunkIndex = 0; ChunkIndex < ApexDestructibleAsset.getChunkCount(); ++ChunkIndex) { const physx::PxU32 PartIndex = ApexDestructibleAsset.getPartIndex(ChunkIndex); const physx::PxU32* PartIndexBuffer = Submesh.getIndexBuffer(PartIndex); const physx::PxU32* PartIndexBufferStop = PartIndexBuffer + Submesh.getIndexCount(PartIndex); while (PartIndexBuffer < PartIndexBufferStop) { physx::PxU32 SubmeshVertexIndex[3]; #if !INVERT_Y_AND_V SubmeshVertexIndex[2] = *PartIndexBuffer++; SubmeshVertexIndex[1] = *PartIndexBuffer++; SubmeshVertexIndex[0] = *PartIndexBuffer++; #else SubmeshVertexIndex[0] = *PartIndexBuffer++; SubmeshVertexIndex[1] = *PartIndexBuffer++; SubmeshVertexIndex[2] = *PartIndexBuffer++; #endif // Fill triangle VTriangle& Triangle = ImportData.Faces[TriangleIndex++]; // set the face smoothing by default. It could be any number, but not zero Triangle.SmoothingGroups = 255; // Material index int32 MatIdx = 0; check(ImportData.Materials.Num() > 0); if (SubmeshIndex < (uint32)ImportData.Materials.Num()) { const FString& MaterialName = ImportData.Materials[SubmeshIndex].MaterialName; for (int32 UniqueMaterialIdx = 0; UniqueMaterialIdx < UniqueMaterials.Num(); ++UniqueMaterialIdx) { if (UniqueMaterials[UniqueMaterialIdx].MaterialName == MaterialName) { MatIdx = UniqueMaterialIdx; break; } } } Triangle.MatIndex = MatIdx; Triangle.AuxMatIndex = 0; // Per-vertex for (uint32 V = 0; V < 3; ++V) { // Tangent basis Triangle.TangentX[V] = Binormals[SubmeshVertexIndex[V]]; Triangle.TangentY[V] = Tangents[SubmeshVertexIndex[V]]; Triangle.TangentZ[V] = Normals[SubmeshVertexIndex[V]]; #if INVERT_Y_AND_V Triangle.TangentX[V].Y *= -1.0f; Triangle.TangentY[V].Y *= -1.0f; Triangle.TangentZ[V].Y *= -1.0f; #endif // Wedges Triangle.WedgeIndex[V] = WedgeIndex; VVertex& Wedge = ImportData.Wedges[WedgeIndex++]; Wedge.VertexIndex = VertexIndexBase + SubmeshVertexIndex[V]; Wedge.MatIndex = Triangle.MatIndex; Wedge.Color = Colors[SubmeshVertexIndex[V]]; Wedge.Reserved = 0; for (uint32 UVNum = 0; UVNum < ImportData.NumTexCoords; ++UVNum) { const FVector2D& UV = UVs[UVNum][SubmeshVertexIndex[V]]; #if !INVERT_Y_AND_V Wedge.UVs[UVNum] = UV; #else Wedge.UVs[UVNum] = FVector2D(UV.X, 1.0f-UV.Y); #endif } } } // Bone influences const physx::PxU32 PartVertexStart = Submesh.getFirstVertexIndex(PartIndex); const physx::PxU32 PartVertexStop = PartVertexStart + Submesh.getVertexCount(PartIndex); for (uint32 PartVertexIndex = PartVertexStart; PartVertexIndex < PartVertexStop; ++PartVertexIndex) { const physx::PxU32 VertexIndex = VertexIndexBase + PartVertexIndex; // Note, by using ChunkIndex instead of PartInedx we are effectively setting PartIndex = ChunkIndex, which is OK since we won't be supporting instancing with the SkeletalMesh. ImportData.Influences[VertexIndex].BoneIndex = ChunkIndex + 1; // Adding 1, since the 0 bone will have no geometry from the Apex Destructible Asset. ImportData.Influences[VertexIndex].Weight = 1.0; ImportData.Influences[VertexIndex].VertexIndex = VertexIndex; } } VertexIndexBase += SubmeshVertexCount; } // Switch material arrays so we have a list with unique materials ImportData.Materials = UniqueMaterials; // Create mapping from import to raw- @TODO trivial at the moment, do we need this info for destructibles? ImportData.PointToRawMap.AddUninitialized(ImportData.Points.Num()); for(int32 PointIdx=0; PointIdx<ImportData.PointToRawMap.Num(); PointIdx++) { ImportData.PointToRawMap[PointIdx] = PointIdx; } return true; }
virtual bool Cook(FName Format, const TArray<uint8>& SrcBuffer, FSoundQualityInfo& QualityInfo, TArray<uint8>& CompressedDataStore) const { check(Format == NAME_OGG); #if WITH_OGGVORBIS { short ReadBuffer[SAMPLES_TO_READ * SAMPLE_SIZE * 2]; ogg_stream_state os; // take physical pages, weld into a logical stream of packets ogg_page og; // one ogg bitstream page. Vorbis packets are inside ogg_packet op; // one raw packet of data for decode vorbis_info vi; // struct that stores all the static vorbis bitstream settings vorbis_comment vc; // struct that stores all the user comments vorbis_dsp_state vd; // central working state for the packet->PCM decoder vorbis_block vb; // local working space for packet->PCM decode uint32 i; bool eos; // Create a buffer to store compressed data CompressedDataStore.Empty(); FMemoryWriter CompressedData( CompressedDataStore ); uint32 BufferOffset = 0; float CompressionQuality = ( float )( QualityInfo.Quality + VORBIS_QUALITY_MODIFIER ) / 100.0f; CompressionQuality = FMath::Clamp( CompressionQuality, -0.1f, 1.0f ); vorbis_info_init( &vi ); if( vorbis_encode_init_vbr( &vi, QualityInfo.NumChannels, QualityInfo.SampleRate, CompressionQuality ) ) { return false; } // add a comment vorbis_comment_init( &vc ); vorbis_comment_add_tag( &vc, "ENCODER", "UnrealEngine4" ); // set up the analysis state and auxiliary encoding storage vorbis_analysis_init( &vd, &vi ); vorbis_block_init( &vd, &vb ); // set up our packet->stream encoder ogg_stream_init( &os, 0 ); ogg_packet header; ogg_packet header_comm; ogg_packet header_code; vorbis_analysis_headerout( &vd, &vc, &header, &header_comm, &header_code); ogg_stream_packetin( &os, &header ); ogg_stream_packetin( &os, &header_comm ); ogg_stream_packetin( &os, &header_code ); // This ensures the actual audio data will start on a new page, as per spec while( true ) { int result = ogg_stream_flush( &os, &og ); if( result == 0 ) { break; } CompressedData.Serialize( og.header, og.header_len ); CompressedData.Serialize( og.body, og.body_len ); } eos = false; while( !eos ) { // Read samples uint32 BytesToRead = FMath::Min( SAMPLES_TO_READ * QualityInfo.NumChannels * SAMPLE_SIZE, QualityInfo.SampleDataSize - BufferOffset ); FMemory::Memcpy( ReadBuffer, SrcBuffer.GetTypedData() + BufferOffset, BytesToRead ); BufferOffset += BytesToRead; if( BytesToRead == 0) { // end of file vorbis_analysis_wrote( &vd, 0 ); } else { // expose the buffer to submit data float **buffer = vorbis_analysis_buffer( &vd, SAMPLES_TO_READ ); if( QualityInfo.NumChannels == 1 ) { for( i = 0; i < BytesToRead / SAMPLE_SIZE; i++ ) { buffer[0][i] = ( ReadBuffer[i] ) / 32768.0f; } } else { for( i = 0; i < BytesToRead / ( SAMPLE_SIZE * 2 ); i++ ) { buffer[0][i] = ( ReadBuffer[i * 2] ) / 32768.0f; buffer[1][i] = ( ReadBuffer[i * 2 + 1] ) / 32768.0f; } } // tell the library how many samples we actually submitted vorbis_analysis_wrote( &vd, i ); } // vorbis does some data preanalysis, then divvies up blocks for more involved (potentially parallel) processing. while( vorbis_analysis_blockout( &vd, &vb ) == 1 ) { // analysis, assume we want to use bitrate management vorbis_analysis( &vb, NULL ); vorbis_bitrate_addblock( &vb ); while( vorbis_bitrate_flushpacket( &vd, &op ) ) { // weld the packet into the bitstream ogg_stream_packetin( &os, &op ); // write out pages (if any) while( !eos ) { int result = ogg_stream_pageout( &os, &og ); if( result == 0 ) { break; } CompressedData.Serialize( og.header, og.header_len ); CompressedData.Serialize( og.body, og.body_len ); // this could be set above, but for illustrative purposes, I do it here (to show that vorbis does know where the stream ends) if( ogg_page_eos( &og ) ) { eos = true; } } } } } // clean up and exit. vorbis_info_clear() must be called last ogg_stream_clear( &os ); vorbis_block_clear( &vb ); vorbis_dsp_clear( &vd ); vorbis_comment_clear( &vc ); vorbis_info_clear( &vi ); // ogg_page and ogg_packet structs always point to storage in libvorbis. They're never freed or manipulated directly } return CompressedDataStore.Num() > 0; #else return false; #endif // WITH_OGGVOBVIS }
static bool CompressSliceToASTC( const void* SourceData, int32 SizeX, int32 SizeY, FString CompressionParameters, TArray<uint8>& OutCompressedData ) { // Always Y-invert the image prior to compression for proper orientation post-compression uint8 LineBuffer[16384 * 4]; uint32 LineSize = SizeX * 4; for (int32 LineIndex = 0; LineIndex < (SizeY / 2); LineIndex++) { uint8* LineData0 = ((uint8*)SourceData) + (LineSize * LineIndex); uint8* LineData1 = ((uint8*)SourceData) + (LineSize * (SizeY - LineIndex - 1)); FMemory::Memcpy(LineBuffer, LineData0, LineSize); FMemory::Memcpy(LineData0, LineData1, LineSize); FMemory::Memcpy(LineData1, LineBuffer, LineSize); } // Compress and retrieve the PNG data to write out to disk IImageWrapperPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG); ImageWrapper->SetRaw(SourceData, SizeX * SizeY * 4, SizeX, SizeY); const TArray<uint8>& FileData = ImageWrapper->GetCompressed(); int32 FileDataSize = FileData.Num(); FGuid Guid; FPlatformMisc::CreateGuid(Guid); FString InputFilePath = FString::Printf(TEXT("Cache/%08x-%08x-%08x-%08x-RGBToASTCIn.png"), Guid.A, Guid.B, Guid.C, Guid.D); FString OutputFilePath = FString::Printf(TEXT("Cache/%08x-%08x-%08x-%08x-RGBToASTCOut.astc"), Guid.A, Guid.B, Guid.C, Guid.D); InputFilePath = FPaths::GameIntermediateDir() + InputFilePath; OutputFilePath = FPaths::GameIntermediateDir() + OutputFilePath; FArchive* PNGFile = NULL; while (!PNGFile) { PNGFile = GFileManager->CreateFileWriter(*InputFilePath); // Occasionally returns NULL due to error code ERROR_SHARING_VIOLATION FPlatformProcess::Sleep(0.01f); // ... no choice but to wait for the file to become free to access } PNGFile->Serialize((void*)&FileData[0], FileDataSize); delete PNGFile; // Compress PNG file to ASTC (using the reference astcenc.exe from ARM) FString Params = FString::Printf(TEXT("-c %s %s %s -thorough"), *InputFilePath, *OutputFilePath, *CompressionParameters ); // Give a debug message about the process if (GIsUCC) { //UE_LOG(LogTextureFormatASTC, Display, TEXT("Compressing to ASTC_%s..."), *CompressionRate); } // Start Compressor FString compressorPath(FPaths::EngineDir() + TEXT("Binaries/ThirdParty/ARM/Win32/astcenc.exe")); void* Proc = FPlatformProcess::CreateProc(compressorPath.GetCharArray().GetData(), *Params, true, false, false, NULL, -1, NULL, NULL); // Failed to start the compressor process if (Proc == NULL) { UE_LOG(LogTextureFormatASTC, Error, TEXT("Failed to start astcenc.exe for compressing images (%s)"), compressorPath.GetCharArray().GetData()); return false; } // Wait for the process to complete int ReturnCode; while (!FPlatformProcess::GetProcReturnCode(Proc, &ReturnCode)) { FPlatformProcess::Sleep(0.01f); } // Did it work? bool bConversionWasSuccessful = (ReturnCode == 0); // Open compressed file and put the data in OutCompressedImage if (bConversionWasSuccessful) { // Get raw file data TArray<uint8> ASTCData; FFileHelper::LoadFileToArray(ASTCData, *OutputFilePath); // Process it FASTCHeader* Header = (FASTCHeader*)ASTCData.GetData(); // Fiddle with the texel count data to get the right value uint32 TexelCountX = (Header->TexelCountX[0] << 0) + (Header->TexelCountX[1] << 8) + (Header->TexelCountX[2] << 16); uint32 TexelCountY = (Header->TexelCountY[0] << 0) + (Header->TexelCountY[1] << 8) + (Header->TexelCountY[2] << 16); uint32 TexelCountZ = (Header->TexelCountZ[0] << 0) + (Header->TexelCountZ[1] << 8) + (Header->TexelCountZ[2] << 16); //UE_LOG(LogTextureFormatASTC, Display, TEXT(" Compressed Texture Header:")); //UE_LOG(LogTextureFormatASTC, Display, TEXT(" Magic: %x"), Header->Magic); //UE_LOG(LogTextureFormatASTC, Display, TEXT(" BlockSizeX: %u"), Header->BlockSizeX); //UE_LOG(LogTextureFormatASTC, Display, TEXT(" BlockSizeY: %u"), Header->BlockSizeY); //UE_LOG(LogTextureFormatASTC, Display, TEXT(" BlockSizeZ: %u"), Header->BlockSizeZ); //UE_LOG(LogTextureFormatASTC, Display, TEXT(" TexelCountX: %u"), TexelCountX); //UE_LOG(LogTextureFormatASTC, Display, TEXT(" TexelCountY: %u"), TexelCountY); //UE_LOG(LogTextureFormatASTC, Display, TEXT(" TexelCountZ: %u"), TexelCountZ); // Calculate size of this mip in blocks uint32 MipSizeX = (TexelCountX + Header->BlockSizeX - 1) / Header->BlockSizeX; uint32 MipSizeY = (TexelCountY + Header->BlockSizeY - 1) / Header->BlockSizeY; // A block is always 16 bytes uint32 MipSize = MipSizeX * MipSizeY * 16; // Copy the compressed data OutCompressedData.Empty(MipSize); OutCompressedData.AddUninitialized(MipSize); void* MipData = OutCompressedData.GetTypedData(); // Calculate the offset to get to the mip data check(sizeof(FASTCHeader) == 16); check(ASTCData.Num() == (sizeof(FASTCHeader) + MipSize)); FMemory::Memcpy(MipData, ASTCData.GetData() + sizeof(FASTCHeader), MipSize); } else { UE_LOG(LogTextureFormatASTC, Error, TEXT("ASTC encoder failed with return code %d, mip size (%d, %d)"), ReturnCode, SizeX, SizeY); GFileManager->Delete(*InputFilePath); GFileManager->Delete(*OutputFilePath); return false; } // Delete intermediate files GFileManager->Delete(*InputFilePath); GFileManager->Delete(*OutputFilePath); return true; }
bool UDestructibleComponent::DoCustomNavigableGeometryExport(FNavigableGeometryExport* GeomExport) const { #if WITH_APEX if (ApexDestructibleActor == NULL) { return false; } NxDestructibleActor* DestrActor = const_cast<NxDestructibleActor*>(ApexDestructibleActor); const FTransform ComponentToWorldNoScale(ComponentToWorld.GetRotation(), ComponentToWorld.GetTranslation(), FVector(1.f)); TArray<PxShape*> Shapes; Shapes.AddUninitialized(8); PxRigidDynamic** PActorBuffer = NULL; PxU32 PActorCount = 0; if (DestrActor->acquirePhysXActorBuffer(PActorBuffer, PActorCount , NxDestructiblePhysXActorQueryFlags::Static | NxDestructiblePhysXActorQueryFlags::Dormant | NxDestructiblePhysXActorQueryFlags::Dynamic)) { uint32 ShapesExportedCount = 0; while (PActorCount--) { const PxRigidDynamic* PActor = *PActorBuffer++; if (PActor != NULL) { const FTransform PActorGlobalPose = P2UTransform(PActor->getGlobalPose()); const PxU32 ShapesCount = PActor->getNbShapes(); if (ShapesCount > PxU32(Shapes.Num())) { Shapes.AddUninitialized(ShapesCount - Shapes.Num()); } const PxU32 RetrievedShapesCount = PActor->getShapes(Shapes.GetTypedData(), Shapes.Num()); PxShape* const* ShapePtr = Shapes.GetTypedData(); for (PxU32 ShapeIndex = 0; ShapeIndex < RetrievedShapesCount; ++ShapeIndex, ++ShapePtr) { if (*ShapePtr != NULL) { const PxTransform LocalPose = (*ShapePtr)->getLocalPose(); FTransform LocalToWorld = P2UTransform(LocalPose); LocalToWorld.Accumulate(PActorGlobalPose); switch((*ShapePtr)->getGeometryType()) { case PxGeometryType::eCONVEXMESH: { PxConvexMeshGeometry Geometry; if ((*ShapePtr)->getConvexMeshGeometry(Geometry)) { ++ShapesExportedCount; // @todo address Geometry.scale not being used here GeomExport->ExportPxConvexMesh(Geometry.convexMesh, LocalToWorld); } } break; case PxGeometryType::eTRIANGLEMESH: { // @todo address Geometry.scale not being used here PxTriangleMeshGeometry Geometry; if ((*ShapePtr)->getTriangleMeshGeometry(Geometry)) { ++ShapesExportedCount; if ((Geometry.triangleMesh->getTriangleMeshFlags()) & PxTriangleMeshFlag::eHAS_16BIT_TRIANGLE_INDICES) { GeomExport->ExportPxTriMesh16Bit(Geometry.triangleMesh, LocalToWorld); } else { GeomExport->ExportPxTriMesh32Bit(Geometry.triangleMesh, LocalToWorld); } } } default: { UE_LOG(LogPhysics, Log, TEXT("UDestructibleComponent::DoCustomNavigableGeometryExport(): unhandled PxGeometryType, %d.") , int32((*ShapePtr)->getGeometryType())); } break; } } } } } ApexDestructibleActor->releasePhysXActorBuffer(); INC_DWORD_STAT_BY(STAT_Navigation_DestructiblesShapesExported, ShapesExportedCount); } #endif // WITH_APEX // we don't want a regular geometry export return false; }
FReply FLandscapeEditorDetailCustomization_CopyPaste::OnGizmoImportButtonClicked() { FEdModeLandscape* LandscapeEdMode = GetEditorMode(); if (LandscapeEdMode != NULL) { ALandscapeGizmoActiveActor* Gizmo = LandscapeEdMode->CurrentGizmoActor.Get(); if (Gizmo) { TArray<uint8> Data; FFileHelper::LoadFileToArray(Data, *LandscapeEdMode->UISettings->GizmoHeightmapFilenameString); if (Data.Num() <= 0 || Data.Num() != (LandscapeEdMode->UISettings->GizmoImportSize.X * LandscapeEdMode->UISettings->GizmoImportSize.Y * 2)) { FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "LandscapeImport_BadHeightmapSize", "File size does not match")); return FReply::Handled(); } TArray<ULandscapeLayerInfoObject*> LayerInfos; TArray<TArray<uint8> > LayerDataArrays; TArray<uint8*> LayerDataPtrs; for (int32 LayerIndex = 0; LayerIndex < LandscapeEdMode->UISettings->GizmoImportLayers.Num(); LayerIndex++) { const FGizmoImportLayer& Layer = LandscapeEdMode->UISettings->GizmoImportLayers[LayerIndex]; FString LayerName = Layer.LayerName.Replace(TEXT(" "), TEXT("")); if (LayerName == TEXT("")) { FMessageDialog::Open(EAppMsgType::Ok, FText::Format( NSLOCTEXT("UnrealEd", "LandscapeImport_BadLayerName", "You must enter a name for the layer being imported from {0}."), FText::FromString(Layer.LayerFilename) )); return FReply::Handled(); } if (Layer.LayerFilename != TEXT("") && !Layer.bNoImport) { TArray<uint8>* LayerData = new(LayerDataArrays)(TArray<uint8>); FFileHelper::LoadFileToArray(*LayerData, *Layer.LayerFilename); if (LayerData->Num() != (LandscapeEdMode->UISettings->GizmoImportSize.X * LandscapeEdMode->UISettings->GizmoImportSize.Y)) { FMessageDialog::Open(EAppMsgType::Ok, FText::Format( NSLOCTEXT("UnrealEd", "LandscapeImport_BadLayerSize", "Layer {0} file size does not match the heightmap resolution."), FText::FromString(Layer.LayerFilename) )); return FReply::Handled(); } LayerInfos.Add(LandscapeEdMode->CurrentToolTarget.LandscapeInfo->GetLayerInfoByName(FName(*LayerName))); LayerDataPtrs.Add(&(*LayerData)[0]); } } Gizmo->Import(LandscapeEdMode->UISettings->GizmoImportSize.X, LandscapeEdMode->UISettings->GizmoImportSize.Y, (uint16*)Data.GetTypedData(), LayerInfos, LayerDataPtrs.Num() ? LayerDataPtrs.GetTypedData() : NULL); } } return FReply::Handled(); }
bool FTextureSource::GetMipData(TArray<uint8>& OutMipData, int32 MipIndex) { bool bSuccess = false; if (MipIndex < NumMips && BulkData.GetBulkDataSize() > 0) { void* RawSourceData = BulkData.Lock(LOCK_READ_ONLY); if (bPNGCompressed) { bool bCanPngCompressFormat = (Format == TSF_G8 || Format == TSF_RGBA8 || Format == TSF_BGRA8 || Format == TSF_RGBA16); if (MipIndex == 0 && NumSlices == 1 && bCanPngCompressFormat) { IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>( FName("ImageWrapper") ); IImageWrapperPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper( EImageFormat::PNG ); if ( ImageWrapper.IsValid() && ImageWrapper->SetCompressed( RawSourceData, BulkData.GetBulkDataSize() ) ) { if (ImageWrapper->GetWidth() == SizeX && ImageWrapper->GetHeight() == SizeY) { const TArray<uint8>* RawData = NULL; // TODO: TSF_BGRA8 is stored as RGBA, so the R and B channels are swapped in the internal png. Should we fix this? ERGBFormat::Type RawFormat = (Format == TSF_G8) ? ERGBFormat::Gray : ERGBFormat::RGBA; if (ImageWrapper->GetRaw( RawFormat, Format == TSF_RGBA16 ? 16 : 8, RawData )) { OutMipData = *RawData; bSuccess = true; } else { UE_LOG(LogTexture, Warning, TEXT("PNG decompression of source art failed")); OutMipData.Empty(); } } else { UE_LOG(LogTexture, Warning, TEXT("PNG decompression of source art failed. ") TEXT("Source image should be %dx%d but is %dx%d"), SizeX, SizeY, ImageWrapper->GetWidth(), ImageWrapper->GetHeight() ); } } else { UE_LOG(LogTexture, Log, TEXT("Only pngs are supported")); } } } else { int32 MipOffset = CalcMipOffset(MipIndex); int32 MipSize = CalcMipSize(MipIndex); if (BulkData.GetBulkDataSize() >= MipOffset + MipSize) { OutMipData.Empty(MipSize); OutMipData.AddUninitialized(MipSize); FMemory::Memcpy( OutMipData.GetTypedData(), (uint8*)RawSourceData + MipOffset, MipSize ); } bSuccess = true; } BulkData.Unlock(); } return bSuccess; }
void FVisualizeTexture::GenerateContent(const FSceneRenderTargetItem& RenderTargetItem, const FPooledRenderTargetDesc& Desc) { // otherwise StartFrame() wasn't called check(ViewRect != FIntRect(0, 0, 0, 0)) FTexture2DRHIRef VisTexture = (FTexture2DRHIRef&)RenderTargetItem.ShaderResourceTexture; if(!IsValidRef(VisTexture) || !Desc.IsValid()) { // todo: improve return; } FIntRect VisualizeTextureRect = ComputeVisualizeTextureRect(Desc.Extent); FIntPoint Size = VisualizeTextureRect.Size(); // clamp to reasonable value to prevent crash Size.X = FMath::Max(Size.X, 1); Size.Y = FMath::Max(Size.Y, 1); FPooledRenderTargetDesc OutputDesc(FPooledRenderTargetDesc::Create2DDesc(Size, PF_B8G8R8A8, TexCreate_None, TexCreate_RenderTargetable | TexCreate_ShaderResource, false)); GRenderTargetPool.FindFreeElement(OutputDesc, VisualizeTextureContent, TEXT("VisualizeTexture")); if(!VisualizeTextureContent) { return; } const FSceneRenderTargetItem& DestRenderTarget = VisualizeTextureContent->GetRenderTargetItem(); RHISetRenderTarget(DestRenderTarget.TargetableTexture, FTextureRHIRef()); RHIClear(true, FLinearColor(1,1,0,1), false, 0.0f, false, 0, FIntRect()); RHISetBlendState(TStaticBlendState<>::GetRHI()); RHISetRasterizerState(TStaticRasterizerState<>::GetRHI()); RHISetDepthStencilState(TStaticDepthStencilState<false,CF_Always>::GetRHI()); FIntPoint RTExtent = GSceneRenderTargets.GetBufferSizeXY(); FVector2D Tex00 = FVector2D(0, 0); FVector2D Tex11 = FVector2D(1, 1); uint32 LocalVisualizeTextureInputMapping = UVInputMapping; if(!Desc.Is2DTexture()) { LocalVisualizeTextureInputMapping = 1; } // set UV switch(LocalVisualizeTextureInputMapping) { // UV in left top case 0: Tex11 = FVector2D((float)ViewRect.Width() / RTExtent.X, (float)ViewRect.Height() / RTExtent.Y); break; // whole texture default: break; } bool bIsDefault = StencilSRVSrc == GBlackTexture->TextureRHI; bool bDepthStencil = Desc.Is2DTexture() && Desc.Format == PF_DepthStencil; //clear if this is a new different Stencil buffer, or it's not a stencil buffer and we haven't switched to the default yet. bool bNeedsClear = bDepthStencil && (StencilSRVSrc != RenderTargetItem.TargetableTexture); bNeedsClear |= !bDepthStencil && !bIsDefault; if (bNeedsClear) { StencilSRVSrc = nullptr; StencilSRV.SafeRelease(); } //always set something into the StencilSRV slot for platforms that require a full resource binding, even if //dynamic branching will cause them not to be used. if(bDepthStencil && !StencilSRVSrc) { StencilSRVSrc = RenderTargetItem.TargetableTexture; StencilSRV = RHICreateShaderResourceView((FTexture2DRHIRef&) RenderTargetItem.TargetableTexture, 0, 1, PF_X24_G8); } else if(!StencilSRVSrc) { StencilSRVSrc = GBlackTexture->TextureRHI; StencilSRV = RHICreateShaderResourceView((FTexture2DRHIRef&) GBlackTexture->TextureRHI, 0, 1, PF_B8G8R8A8); } FVisualizeTextureData VisualizeTextureData(RenderTargetItem, Desc); bool bDepthTexture = (Desc.TargetableFlags & TexCreate_DepthStencilTargetable) != 0; VisualizeTextureData.RGBMul = RGBMul; VisualizeTextureData.AMul = AMul; VisualizeTextureData.Tex00 = Tex00; VisualizeTextureData.Tex11 = Tex11; VisualizeTextureData.bSaturateInsteadOfFrac = (Flags & 1) != 0; VisualizeTextureData.InputValueMapping = bDepthTexture ? 1 : 0; VisualizeTextureData.ArrayIndex = ArrayIndex; VisualizeTextureData.CustomMip = CustomMip; VisualizeTextureData.StencilSRV = StencilSRV; { SCOPED_DRAW_EVENT(VisualizeTexture, DEC_SCENE_ITEMS); // continue rendering to HDR if necessary RenderVisualizeTexture(VisualizeTextureData); } RHICopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams()); VisualizeTextureDesc = Desc; // save to disk if(bSaveBitmap) { bSaveBitmap = false; uint32 MipAdjustedExtentX = FMath::Clamp(Desc.Extent.X >> CustomMip, 0, Desc.Extent.X); uint32 MipAdjustedExtentY = FMath::Clamp(Desc.Extent.Y >> CustomMip, 0, Desc.Extent.Y); FIntPoint Extent(MipAdjustedExtentX, MipAdjustedExtentY); FReadSurfaceDataFlags ReadDataFlags; ReadDataFlags.SetLinearToGamma(false); ReadDataFlags.SetOutputStencil(bOutputStencil); ReadDataFlags.SetMip(CustomMip); FTextureRHIRef Texture = RenderTargetItem.TargetableTexture ? RenderTargetItem.TargetableTexture : RenderTargetItem.ShaderResourceTexture; check(Texture); TArray<FColor> Bitmap; RHIReadSurfaceData(Texture, FIntRect(0, 0, Extent.X, Extent.Y), Bitmap, ReadDataFlags); // if the format and texture type is supported if(Bitmap.Num()) { // Create screenshot folder if not already present. IFileManager::Get().MakeDirectory(*FPaths::ScreenShotDir(), true); const FString ScreenFileName(FPaths::ScreenShotDir() / TEXT("VisualizeTexture")); uint32 ExtendXWithMSAA = Bitmap.Num() / Extent.Y; // Save the contents of the array to a bitmap file. (24bit only so alpha channel is dropped) FFileHelper::CreateBitmap(*ScreenFileName, ExtendXWithMSAA, Extent.Y, Bitmap.GetTypedData()); UE_LOG(LogConsoleResponse, Display, TEXT("Content was saved to \"%s\""), *FPaths::ScreenShotDir()); } else { UE_LOG(LogConsoleResponse, Error, TEXT("Failed to save BMP for VisualizeTexture, format or texture type is not supported")); } } }
//------------------------------------------------------------------------- // //------------------------------------------------------------------------- bool UnFbx::FFbxImporter::CreateAndLinkExpressionForMaterialProperty( FbxSurfaceMaterial& FbxMaterial, UMaterial* UnrealMaterial, const char* MaterialProperty , FExpressionInput& MaterialInput, bool bSetupAsNormalMap, TArray<FString>& UVSet ) { bool bCreated = false; FbxProperty FbxProperty = FbxMaterial.FindProperty( MaterialProperty ); if( FbxProperty.IsValid() ) { int32 LayeredTextureCount = FbxProperty.GetSrcObjectCount(FbxLayeredTexture::ClassId); if (LayeredTextureCount>0) { UE_LOG(LogFbxMaterialImport, Warning,TEXT("Layered TEXTures are not supported (material %s)"),ANSI_TO_TCHAR(FbxMaterial.GetName())); } else { int32 TextureCount = FbxProperty.GetSrcObjectCount(FbxTexture::ClassId); if (TextureCount>0) { for(int32 TextureIndex =0; TextureIndex<TextureCount; ++TextureIndex) { FbxFileTexture* FbxTexture = FbxProperty.GetSrcObject(FBX_TYPE(FbxFileTexture), TextureIndex); // create an unreal texture asset UTexture* UnrealTexture = ImportTexture(FbxTexture, bSetupAsNormalMap); if (UnrealTexture) { // and link it to the material UMaterialExpressionTextureSample* UnrealTextureExpression = ConstructObject<UMaterialExpressionTextureSample>( UMaterialExpressionTextureSample::StaticClass(), UnrealMaterial ); UnrealMaterial->Expressions.Add( UnrealTextureExpression ); MaterialInput.Expression = UnrealTextureExpression; UnrealTextureExpression->Texture = UnrealTexture; UnrealTextureExpression->SamplerType = bSetupAsNormalMap ? SAMPLERTYPE_Normal : SAMPLERTYPE_Color; // add/find UVSet and set it to the texture FbxString UVSetName = FbxTexture->UVSet.Get(); FString LocalUVSetName = ANSI_TO_TCHAR(UVSetName.Buffer()); int32 SetIndex = UVSet.Find(LocalUVSetName); UMaterialExpressionTextureCoordinate* MyCoordExpression = ConstructObject<UMaterialExpressionTextureCoordinate>( UMaterialExpressionTextureCoordinate::StaticClass(), UnrealMaterial ); UnrealMaterial->Expressions.Add( MyCoordExpression ); MyCoordExpression->CoordinateIndex = (SetIndex >= 0)? SetIndex: 0; UnrealTextureExpression->Coordinates.Expression = MyCoordExpression; if ( !bSetupAsNormalMap ) { UnrealMaterial->BaseColor.Expression = UnrealTextureExpression; } else { UnrealMaterial->Normal.Expression = UnrealTextureExpression; } bCreated = true; } } } if (MaterialInput.Expression) { TArray<FExpressionOutput> Outputs = MaterialInput.Expression->GetOutputs(); FExpressionOutput* Output = Outputs.GetTypedData(); MaterialInput.Mask = Output->Mask; MaterialInput.MaskR = Output->MaskR; MaterialInput.MaskG = Output->MaskG; MaterialInput.MaskB = Output->MaskB; MaterialInput.MaskA = Output->MaskA; } } } return bCreated; }
bool FD3D9MeshUtilities::LayoutUVs( struct FRawMesh& RawMesh, uint32 TextureResolution, uint32 TexCoordIndex, FText& OutError ) { OutError = FText(); if(!IsValid() || !RawMesh.IsValid()) { OutError = LOCTEXT("LayoutUVs_FailedInvalid", "LayoutUVs failed, mesh was invalid."); return false; } int32 NumTexCoords = 0; for (int32 i = 0; i < MAX_MESH_TEXTURE_COORDS; ++i) { if (RawMesh.WedgeTexCoords[i].Num() != RawMesh.WedgeIndices.Num()) { break; } NumTexCoords++; } if (TexCoordIndex > (uint32)NumTexCoords) { OutError = LOCTEXT("LayoutUVs_FailedUVs", "LayoutUVs failed, incorrect number of texcoords."); return false; } // Sort the mesh's triangles by whether they need to be charted, or just to be packed into the atlas. FRawMesh MeshToAtlas = RawMesh; if (TexCoordIndex > 0) { MeshToAtlas.WedgeTexCoords[TexCoordIndex] = MeshToAtlas.WedgeTexCoords[0]; } TRefCountPtr<ID3DXMesh> ChartMesh; TArray<uint32> AtlasAndChartAdjacency; TArray<int32> AtlasAndChartTriangleCharts; TRefCountPtr<ID3DXMesh> MergedMesh; TArray<uint32> MergedAdjacency; TArray<int32> MergedTriangleCharts; TRefCountPtr<ID3DXMesh> AtlasOnlyMesh; TArray<uint32> AtlasOnlyAdjacency; TArray<int32> AtlasOnlyTriangleCharts; { // Create a D3DXMesh for the triangles that only need to be atlassed. const bool bRemoveDegenerateTriangles = true; if (!ConvertRawMeshToD3DXMesh(Device,MeshToAtlas,bRemoveDegenerateTriangles,AtlasOnlyMesh)) { OutError = LOCTEXT("LayoutUVs_FailedConvert", "LayoutUVs failed, couldn't convert to a D3DXMesh."); return false; } // generate mapping orientations info FLayoutUVWindingInfo WindingInfo(AtlasOnlyMesh, TexCoordIndex); // Generate adjacency for the pre-charted triangles based on their input charts. GenerateAdjacency(AtlasOnlyMesh,AtlasOnlyAdjacency,FUVChartAdjacencyFilter(TexCoordIndex), &WindingInfo); ////clean the mesh TRefCountPtr<ID3DXMesh> TempMesh; TArray<uint32> CleanedAdjacency; CleanedAdjacency.AddUninitialized(AtlasOnlyMesh->GetNumFaces() * 3); if( FAILED(D3DXCleanMesh( D3DXCLEAN_SIMPLIFICATION, AtlasOnlyMesh, (::DWORD *)AtlasOnlyAdjacency.GetTypedData(), TempMesh.GetInitReference(), (::DWORD *)CleanedAdjacency.GetTypedData(), NULL ) ) ) { OutError = LOCTEXT("LayoutUVs_FailedClean", "LayoutUVs failed, couldn't clean mesh."); return false; } // Group the pre-charted triangles into indexed charts based on their adjacency in the chart. AssignMinimalAdjacencyGroups(CleanedAdjacency,AtlasOnlyTriangleCharts); MergedMesh = TempMesh; MergedAdjacency = CleanedAdjacency; MergedTriangleCharts = AtlasOnlyTriangleCharts; } if(MergedMesh) { // Create a buffer to hold the triangle chart data. TRefCountPtr<ID3DXBuffer> MergedTriangleChartsBuffer; VERIFYD3D9RESULT(D3DXCreateBuffer( MergedTriangleCharts.Num() * sizeof(int32), MergedTriangleChartsBuffer.GetInitReference() )); uint32* MergedTriangleChartsBufferPointer = (uint32*)MergedTriangleChartsBuffer->GetBufferPointer(); for(int32 TriangleIndex = 0;TriangleIndex < MergedTriangleCharts.Num();TriangleIndex++) { *MergedTriangleChartsBufferPointer++ = MergedTriangleCharts[TriangleIndex]; } const float GutterSize = 2.0f; // Pack the charts into a unified atlas. HRESULT Result = D3DXUVAtlasPack( MergedMesh, TextureResolution, TextureResolution, GutterSize, TexCoordIndex, (::DWORD *)MergedAdjacency.GetTypedData(), NULL, 0, NULL, 0, MergedTriangleChartsBuffer ); if (FAILED(Result)) { UE_LOG(LogD3D9MeshUtils, Warning, TEXT("D3DXUVAtlasPack() returned %u."), Result ); OutError = LOCTEXT("LayoutUVs_FailedPack", "LayoutUVs failed, D3DXUVAtlasPack failed."); return false; } int32 NewNumTexCoords = FMath::Max<int32>(NumTexCoords, TexCoordIndex + 1); FRawMesh FinalMesh; if (!ConvertD3DXMeshToRawMesh(MergedMesh, FinalMesh, NewNumTexCoords)) { OutError = LOCTEXT("LayoutUVs_FailedSimple", "LayoutUVs failed, couldn't convert the simplified D3DXMesh back to a UStaticMesh."); return false; } RawMesh = FinalMesh; } return true; }
UTexture* UnFbx::FFbxImporter::ImportTexture( FbxFileTexture* FbxTexture, bool bSetupAsNormalMap ) { // create an unreal texture asset UTexture* UnrealTexture = NULL; FString Filename1 = ANSI_TO_TCHAR(FbxTexture->GetFileName()); FString Extension = FPaths::GetExtension(Filename1).ToLower(); // name the texture with file name FString TextureName = FPaths::GetBaseFilename(Filename1); TextureName = ObjectTools::SanitizeObjectName(TextureName); // set where to place the textures FString NewPackageName = FPackageName::GetLongPackagePath(Parent->GetOutermost()->GetName()) + TEXT("/") + TextureName; NewPackageName = PackageTools::SanitizePackageName(NewPackageName); UPackage* Package = CreatePackage(NULL, *NewPackageName); bool AlreadyExistTexture = (FindObject<UTexture>(Package,*TextureName/*textureName.GetName()*/) != NULL); // try opening from absolute path FString Filename = Filename1; TArray<uint8> DataBinary; if ( ! FFileHelper::LoadFileToArray( DataBinary, *Filename )) { // try fbx file base path + relative path FString Filename2 = FileBasePath / ANSI_TO_TCHAR(FbxTexture->GetRelativeFileName()); Filename = Filename2; if ( ! FFileHelper::LoadFileToArray( DataBinary, *Filename )) { // try fbx file base path + texture file name (no path) FString Filename3 = ANSI_TO_TCHAR(FbxTexture->GetRelativeFileName()); FString FileOnly = FPaths::GetCleanFilename(Filename3); Filename3 = FileBasePath / FileOnly; Filename = Filename3; if ( ! FFileHelper::LoadFileToArray( DataBinary, *Filename )) { UE_LOG(LogFbxMaterialImport, Warning,TEXT("Unable to find TEXTure file %s. Tried:\n - %s\n - %s\n - %s"),*FileOnly,*Filename1,*Filename2,*Filename3); } } } if (DataBinary.Num()>0) { UE_LOG(LogFbxMaterialImport, Warning, TEXT("Loading texture file %s"),*Filename); const uint8* PtrTexture = DataBinary.GetTypedData(); UTextureFactory* TextureFact = new UTextureFactory(FPostConstructInitializeProperties()); // save texture settings if texture exist TextureFact->SuppressImportOverwriteDialog(); const TCHAR* TextureType = *Extension; // Unless the normal map setting is used during import, // the user has to manually hit "reimport" then "recompress now" button if ( bSetupAsNormalMap ) { if (!AlreadyExistTexture) { TextureFact->LODGroup = TEXTUREGROUP_WorldNormalMap; TextureFact->CompressionSettings = TC_Normalmap; } else { UE_LOG(LogFbxMaterialImport, Warning, TEXT("Manual texture reimport and recompression may be needed for %s"), *TextureName); } } UnrealTexture = (UTexture*)TextureFact->FactoryCreateBinary( UTexture2D::StaticClass(), Package, *TextureName, RF_Standalone|RF_Public, NULL, TextureType, PtrTexture, PtrTexture+DataBinary.Num(), GWarn ); if ( UnrealTexture != NULL ) { // Notify the asset registry FAssetRegistryModule::AssetCreated(UnrealTexture); // Set the dirty flag so this package will get saved later Package->SetDirtyFlag(true); } } return UnrealTexture; }
bool FD3D9MeshUtilities::GenerateUVs( struct FRawMesh& RawMesh, uint32 TexCoordIndex, float MinChartSpacingPercent, float BorderSpacingPercent, bool bUseMaxStretch, const TArray< int32 >* InFalseEdgeIndices, uint32& MaxCharts, float& MaxDesiredStretch, FText& OutError ) { OutError = FText(); if(!IsValid()) { OutError = LOCTEXT("GenerateUVs_FailedInvalid", "GenerateUVs failed, mesh was invalid."); return false; } int32 NumTexCoords = 0; for (int32 i = 0; i < MAX_MESH_TEXTURE_COORDS; ++i) { if (RawMesh.WedgeTexCoords[i].Num() != RawMesh.WedgeIndices.Num()) { break; } NumTexCoords++; } if (TexCoordIndex > (uint32)NumTexCoords) { OutError = LOCTEXT("GenerateUVs_FailedUVs", "GenerateUVs failed, incorrect number of texcoords."); return false; } TRefCountPtr<ID3DXMesh> ChartMesh; TArray<uint32> AtlasAndChartAdjacency; TArray<int32> AtlasAndChartTriangleCharts; { const bool bUseFalseEdges = InFalseEdgeIndices != NULL; // When using false edges we don't remove degenerates as we want our incoming selected edge list to map // correctly to the D3DXMesh. const bool bRemoveDegenerateTriangles = !bUseFalseEdges; // Create a D3DXMesh for the triangles being charted. TRefCountPtr<ID3DXMesh> SourceMesh; if (!ConvertRawMeshToD3DXMesh(Device, RawMesh,bRemoveDegenerateTriangles,SourceMesh)) { OutError = LOCTEXT("GenerateUVs_FailedConvert", "GenerateUVs failed, couldn't convert to a D3DXMesh."); return false; } //generate adjacency info for the mesh, which is needed later TArray<uint32> Adjacency; GenerateAdjacency(SourceMesh,Adjacency,FFragmentedAdjacencyFilter()); // We don't clean the mesh as this can collapse vertices or delete degenerate triangles, and // we want our incoming selected edge list to map correctly to the D3DXMesh. if( !bUseFalseEdges ) { //clean the mesh TRefCountPtr<ID3DXMesh> TempMesh; TArray<uint32> CleanedAdjacency; CleanedAdjacency.AddUninitialized(SourceMesh->GetNumFaces() * 3); if( FAILED(D3DXCleanMesh( D3DXCLEAN_SIMPLIFICATION, SourceMesh, (::DWORD *)Adjacency.GetTypedData(), TempMesh.GetInitReference(), (::DWORD *)CleanedAdjacency.GetTypedData(), NULL ) ) ) { OutError = LOCTEXT("GenerateUVs_FailedClean", "GenerateUVs failed, couldn't clean mesh."); return false; } SourceMesh = TempMesh; Adjacency = CleanedAdjacency; } // Setup the D3DX "false edge" array. This is three DWORDS per face that define properties of the // face's edges. Values of -1 indicates that the edge may be used as a UV seam in a the chart. Any // other value indicates that the edge should never be a UV seam. This essentially allows us to // provide a precise list of edges to be used as UV seams in the new charts. uint32* FalseEdgeArray = NULL; TArray<uint32> FalseEdges; if( bUseFalseEdges ) { // -1 means "always use this edge as a chart UV seam" to D3DX FalseEdges.AddUninitialized( SourceMesh->GetNumFaces() * 3 ); for( int32 CurFalseEdgeIndex = 0; CurFalseEdgeIndex < (int32)SourceMesh->GetNumFaces() * 3; ++CurFalseEdgeIndex ) { FalseEdges[ CurFalseEdgeIndex ] = -1; } // For each tagged edge for( int32 CurTaggedEdgeIndex = 0; CurTaggedEdgeIndex < InFalseEdgeIndices->Num(); ++CurTaggedEdgeIndex ) { const int32 EdgeIndex = ( *InFalseEdgeIndices )[ CurTaggedEdgeIndex ]; // Mark this as a false edge by setting it to a value other than negative one FalseEdges[ EdgeIndex ] = Adjacency[ CurTaggedEdgeIndex ]; } FalseEdgeArray = (uint32*)FalseEdges.GetTypedData(); } // Partition the mesh's triangles into charts. TRefCountPtr<ID3DXBuffer> PartitionResultAdjacencyBuffer; TRefCountPtr<ID3DXBuffer> FacePartitionBuffer; HRESULT Result = D3DXUVAtlasPartition( SourceMesh, bUseMaxStretch ? 0 : MaxCharts, // Max charts (0 = use max stretch instead) MaxDesiredStretch, TexCoordIndex, (::DWORD *)Adjacency.GetTypedData(), (::DWORD *)FalseEdgeArray, // False edges NULL, // IMT data &GenerateUVsStatusCallback, 0.01f, // Callback frequency NULL, // Callback user data D3DXUVATLAS_GEODESIC_QUALITY, ChartMesh.GetInitReference(), FacePartitionBuffer.GetInitReference(), NULL, PartitionResultAdjacencyBuffer.GetInitReference(), &MaxDesiredStretch, &MaxCharts ); if (FAILED(Result)) { UE_LOG(LogD3D9MeshUtils, Warning, TEXT("D3DXUVAtlasPartition() returned %u with MaxDesiredStretch=%.2f, TexCoordIndex=%u."), Result, MaxDesiredStretch, TexCoordIndex ); OutError = LOCTEXT("GenerateUVs_FailedPartition", "GenerateUVs failed, D3DXUVAtlasPartition failed."); return false; } // Extract the chart adjacency data from the D3DX buffer into an array. for(uint32 TriangleIndex = 0;TriangleIndex < ChartMesh->GetNumFaces();TriangleIndex++) { for(int32 EdgeIndex = 0;EdgeIndex < 3;EdgeIndex++) { AtlasAndChartAdjacency.Add(*((uint32*)PartitionResultAdjacencyBuffer->GetBufferPointer()+TriangleIndex*3+EdgeIndex)); } } // Extract the triangle chart data from the D3DX buffer into an array. uint32* FacePartitionBufferPointer = (uint32*)FacePartitionBuffer->GetBufferPointer(); for(uint32 TriangleIndex = 0;TriangleIndex < ChartMesh->GetNumFaces();TriangleIndex++) { AtlasAndChartTriangleCharts.Add(*FacePartitionBufferPointer++); } // Scale the partitioned UVs down. FUtilVertex* LockedVertices; ChartMesh->LockVertexBuffer(0,(LPVOID*)&LockedVertices); for(uint32 VertexIndex = 0;VertexIndex < ChartMesh->GetNumVertices();VertexIndex++) { LockedVertices[VertexIndex].UVs[TexCoordIndex] /= 2048.0f; } ChartMesh->UnlockVertexBuffer(); } if(ChartMesh) { // Create a buffer to hold the triangle chart data. TRefCountPtr<ID3DXBuffer> MergedTriangleChartsBuffer; VERIFYD3D9RESULT(D3DXCreateBuffer( AtlasAndChartTriangleCharts.Num() * sizeof(int32), MergedTriangleChartsBuffer.GetInitReference() )); uint32* MergedTriangleChartsBufferPointer = (uint32*)MergedTriangleChartsBuffer->GetBufferPointer(); for(int32 TriangleIndex = 0;TriangleIndex < AtlasAndChartTriangleCharts.Num();TriangleIndex++) { *MergedTriangleChartsBufferPointer++ = AtlasAndChartTriangleCharts[TriangleIndex]; } const uint32 FakeTexSize = 1024; const float GutterSize = ( float )FakeTexSize * MinChartSpacingPercent * 0.01f; // Pack the charts into a unified atlas. HRESULT Result = D3DXUVAtlasPack( ChartMesh, FakeTexSize, FakeTexSize, GutterSize, TexCoordIndex, (::DWORD *)AtlasAndChartAdjacency.GetTypedData(), &GenerateUVsStatusCallback, 0.01f, // Callback frequency NULL, 0, MergedTriangleChartsBuffer ); if (FAILED(Result)) { UE_LOG(LogD3D9MeshUtils, Warning, TEXT("D3DXUVAtlasPack() returned %u."), Result ); OutError = LOCTEXT("GenerateUVs_FailedPack", "GenerateUVs failed, D3DXUVAtlasPack failed."); return false; } int32 NewNumTexCoords = FMath::Max<int32>(NumTexCoords, TexCoordIndex + 1); FRawMesh FinalMesh; if (!ConvertD3DXMeshToRawMesh(ChartMesh, FinalMesh, NewNumTexCoords)) { OutError = LOCTEXT("GenerateUVs_FailedSimple", "GenerateUVs failed, couldn't convert the simplified D3DXMesh back to a UStaticMesh."); return false; } // Scale/offset the UVs appropriately to ensure there is empty space around the border { const float BorderSize = BorderSpacingPercent * 0.01f; const float ScaleAmount = 1.0f - BorderSize * 2.0f; for( int32 CurUVIndex = 0; CurUVIndex < MAX_MESH_TEXTURE_COORDS; ++CurUVIndex ) { int32 NumWedges = FinalMesh.WedgeTexCoords[CurUVIndex].Num(); for( int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex ) { FVector2D& UV = FinalMesh.WedgeTexCoords[CurUVIndex][WedgeIndex]; UV.X = BorderSize + UV.X * ScaleAmount; UV.Y = BorderSize + UV.Y * ScaleAmount; } } } RawMesh = FinalMesh; } return true; }