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();
	}
Example #2
0
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);
	}
Example #10
0
/**
 * 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()) );
}
Example #11
0
/**
* 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();
	}
}
Example #14
0
/** 
 * 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;
	}
}
Example #16
0
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;
}
Example #18
0
/**
 * 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();
}
Example #24
0
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; 
}