Ejemplo n.º 1
0
bool UTexture::GetTextureData(CTextureData &TexData) const
{
	guard(UTexture::GetTextureData);

	TexData.Platform           = PLATFORM_PC;
	TexData.OriginalFormatEnum = Format;
	TexData.OriginalFormatName = EnumToName("ETextureFormat", Format);
	TexData.Obj                = this;
	TexData.Palette            = Palette;

	const FArchive* PackageAr = GetPackageArchive();

	// process external sources for some games
#if BIOSHOCK
	if (PackageAr && PackageAr->Game == GAME_Bioshock && CachedBulkDataSize) //?? check bStripped or Baked ?
	{
		byte* CompressedData = FindBioTexture(this);	// may be NULL
		if (CompressedData)
		{
			CMipMap* DstMip = new (TexData.Mips) CMipMap;
			DstMip->CompressedData = CompressedData;
			DstMip->ShouldFreeData = true;
			DstMip->USize = USize;
			DstMip->VSize = VSize;
			DstMip->DataSize = (int)CachedBulkDataSize;
		}
		TexData.Platform = PackageAr->Platform;
	}
#endif // BIOSHOCK
#if UC2
	if (PackageAr && PackageAr->Engine() == GAME_UE2X)
	{
		// try to find texture inside XBox xpr files
		int DataSize;
		byte* CompressedData = FindXprData(Name, &DataSize);	// may be NULL
		if (CompressedData)
		{
			CMipMap* DstMip = new (TexData.Mips) CMipMap;
			DstMip->CompressedData = CompressedData;
			DstMip->ShouldFreeData = true;
			DstMip->USize = USize;
			DstMip->VSize = VSize;
			DstMip->DataSize = DataSize;
		}
	}
#endif // UC2

	if (TexData.Mips.Num() == 0)
	{
		// texture was not taken from external source
		for (int n = 0; n < Mips.Num(); n++)
		{
			// find 1st mipmap with non-null data array
			// reference: DemoPlayerSkins.utx/DemoSkeleton have null-sized 1st 2 mips
			const FMipmap &Mip = Mips[n];
			if (!Mip.DataArray.Num())
				continue;
			CMipMap* DstMip = new (TexData.Mips) CMipMap;
			DstMip->CompressedData = &Mip.DataArray[0];
			DstMip->ShouldFreeData = false;
			DstMip->USize = Mip.USize;
			DstMip->VSize = Mip.VSize;
			DstMip->DataSize = Mip.DataArray.Num();
			break;
		}
	}

	ETexturePixelFormat intFormat;

	//?? return old code back - UE1 and UE2 differs in codes 6 and 7 only
	if (Package && (PackageAr->Engine() == GAME_UE1))
	{
		// UE1 has different ETextureFormat layout
		switch (Format)
		{
		case 0:
			intFormat = TPF_P8;
			break;
//		case 1:
//			intFormat = TPF_RGB32; // in script source code: TEXF_RGB32, but TEXF_RGBA7 in .h
//			break;
//		case 2:
//			intFormat = TPF_RGB64; // in script source code: TEXF_RGB64, but TEXF_RGB16 in .h
//			break;
		case 3:
			intFormat = TPF_DXT1;
			break;
		case 4:
			intFormat = TPF_RGB8;
			break;
		case 5:
			intFormat = TPF_BGRA8;
			break;
		// newer UE1 versions has DXT3 and DXT5
		case 6:
			intFormat = TPF_DXT3;
			break;
		case 7:
			intFormat = TPF_DXT5;
			break;
		default:
			appNotify("Unknown UE1 texture format: %d", Format);
			return false;
		}
	}
	else
	{
		// UE2
		switch (Format)
		{
		case TEXF_P8:
			intFormat = TPF_P8;
			break;
		case TEXF_DXT1:
			intFormat = TPF_DXT1;
			break;
		case TEXF_RGB8:
			intFormat = TPF_RGB8;
			break;
		case TEXF_RGBA8:
			intFormat = TPF_BGRA8;
			break;
		case TEXF_DXT3:
			intFormat = TPF_DXT3;
			break;
		case TEXF_DXT5:
			intFormat = TPF_DXT5;
			break;
		case TEXF_L8:
			intFormat = TPF_G8;
			break;
		case TEXF_CxV8U8:
			intFormat = TPF_V8U8_2;
			break;
		case TEXF_DXT5N:
			intFormat = TPF_DXT5N;
			break;
		case TEXF_3DC:
			intFormat = TPF_BC5;
			break;
		default:
			appNotify("Unknown UE2 texture format: %s (%d)", TexData.OriginalFormatName, Format);
			return false;
		}
	}

	TexData.Format = intFormat;

#if BIOSHOCK && SUPPORT_XBOX360
	if (TexData.Mips.Num() && TexData.Platform == PLATFORM_XBOX360)
	{
		for (int MipLevel = 0; MipLevel < TexData.Mips.Num(); MipLevel++)
			TexData.DecodeXBox360(MipLevel);
	}
#endif
#if BIOSHOCK
	if (Package && PackageAr->Game == GAME_Bioshock)
	{
		// This game has DataSize stored for all mipmaps, we should compute side of 1st mipmap
		// in order to accept this value when uploading texture to video card (some vendors rejects
		// large values)
		//?? Place code to CTextureData method?
		const CPixelFormatInfo &Info = PixelFormatInfo[intFormat];
		int numBlocks = TexData.Mips[0].USize * TexData.Mips[0].VSize / (Info.BlockSizeX * Info.BlockSizeY);	// used for validation only
		int requiredDataSize = numBlocks * Info.BytesPerBlock;
		if (requiredDataSize > TexData.Mips[0].DataSize)
		{
			appNotify("Bioshock texture %s: data too small; %dx%d, requires %X bytes, got %X\n",
				Name, TexData.Mips[0].USize, TexData.Mips[0].VSize, requiredDataSize, TexData.Mips[0].DataSize);
		}
		else if (requiredDataSize < TexData.Mips[0].DataSize)
		{
//			appPrintf("Bioshock texture %s: stripping data size from %X to %X\n", Name, TexData.Mips[0].DataSize, requiredDataSize);
			TexData.Mips[0].DataSize = requiredDataSize;
		}
	}
#endif // BIOSHOCK

	return (TexData.Mips.Num() > 0);

	unguardf("%s", Name);
}
Ejemplo n.º 2
0
void USkeleton::ConvertAnims(UAnimSequence4* Seq)
{
	guard(USkeleton::ConvertAnims);

	CAnimSet* AnimSet = ConvertedAnim;

	if (!AnimSet)
	{
		AnimSet = new CAnimSet(this);
		ConvertedAnim = AnimSet;

		// Copy bone names
		AnimSet->TrackBoneNames.Empty(ReferenceSkeleton.RefBoneInfo.Num());
		for (int i = 0; i < ReferenceSkeleton.RefBoneInfo.Num(); i++)
		{
			AnimSet->TrackBoneNames.Add(ReferenceSkeleton.RefBoneInfo[i].Name);
		}

		//TODO: verify if UE4 has AnimRotationOnly stuff
		AnimSet->AnimRotationOnly = false;
	}

	if (!Seq) return; // allow calling ConvertAnims(NULL) to create empty AnimSet

//	DBG("----------- Skeleton %s: %d seq, %d bones -----------\n", Name, Anims.Num(), ReferenceSkeleton.RefBoneInfo.Num());

	int NumTracks = Seq->GetNumTracks();

#if DEBUG_DECOMPRESS
	appPrintf("Sequence %s: %d bones, %d offsets (%g per bone), %d frames, %d compressed data\n"
		   "          trans %s, rot %s, scale %s, key %s\n",
		Seq->Name, NumTracks, Seq->CompressedTrackOffsets.Num(), Seq->CompressedTrackOffsets.Num() / (float)NumTracks,
		Seq->NumFrames, Seq->CompressedByteStream.Num(),
		EnumToName(Seq->TranslationCompressionFormat),
		EnumToName(Seq->RotationCompressionFormat),
		EnumToName(Seq->ScaleCompressionFormat),
		EnumToName(Seq->KeyEncodingFormat)
	);
	for (int i2 = 0; i2 < Seq->CompressedTrackOffsets.Num(); /*empty*/)
	{
		if (Seq->KeyEncodingFormat != AKF_PerTrackCompression)
		{
			FName BoneName = ReferenceSkeleton.RefBoneInfo[Seq->GetTrackBoneIndex(i2/4)].Name;
			int TransOffset = Seq->CompressedTrackOffsets[i2  ];
			int TransKeys   = Seq->CompressedTrackOffsets[i2+1];
			int RotOffset   = Seq->CompressedTrackOffsets[i2+2];
			int RotKeys     = Seq->CompressedTrackOffsets[i2+3];
			appPrintf("    [%d] = trans %d[%d] rot %d[%d] - %s\n", i2/4, TransOffset, TransKeys, RotOffset, RotKeys, *BoneName);
			i2 += 4;
		}
		else
		{
			FName BoneName = ReferenceSkeleton.RefBoneInfo[Seq->GetTrackBoneIndex(i2/2)].Name;
			int TransOffset = Seq->CompressedTrackOffsets[i2  ];
			int RotOffset   = Seq->CompressedTrackOffsets[i2+1];
			appPrintf("    [%d] = trans %d rot %d - %s\n", i2/2, TransOffset, RotOffset, *BoneName);
			i2 += 2;
		}
	}
#endif // DEBUG_DECOMPRESS

	// some checks
	int offsetsPerBone = 4;
	if (Seq->KeyEncodingFormat == AKF_PerTrackCompression)
		offsetsPerBone = 2;

	if (Seq->CompressedTrackOffsets.Num() != NumTracks * offsetsPerBone && !Seq->RawAnimationData.Num())
	{
		appNotify("AnimSequence %s has wrong CompressedTrackOffsets size (has %d, expected %d), removing track",
			Seq->Name, Seq->CompressedTrackOffsets.Num(), NumTracks * offsetsPerBone);
		return;
	}

	// create CAnimSequence
	CAnimSequence *Dst = new CAnimSequence;
	AnimSet->Sequences.Add(Dst);
	Dst->Name      = Seq->Name;
	Dst->NumFrames = Seq->NumFrames;
	Dst->Rate      = Seq->NumFrames / Seq->SequenceLength * Seq->RateScale;

	// bone tracks ...
	Dst->Tracks.Empty(NumTracks);

	FMemReader Reader(Seq->CompressedByteStream.GetData(), Seq->CompressedByteStream.Num());
	Reader.SetupFrom(*Package);

	bool HasTimeTracks = (Seq->KeyEncodingFormat == AKF_VariableKeyLerp);

	for (int BoneIndex = 0; BoneIndex < ReferenceSkeleton.RefBoneInfo.Num(); BoneIndex++)
	{
		CAnimTrack *A = new (Dst->Tracks) CAnimTrack;
		int TrackIndex = Seq->FindTrackForBoneIndex(BoneIndex);

		if (TrackIndex < 0)
		{
			// this track has no animation, use static pose from ReferenceSkeleton
			const FTransform& RefPose = ReferenceSkeleton.RefBonePose[BoneIndex];
			A->KeyPos.Add(CVT(RefPose.Translation));
			A->KeyQuat.Add(CVT(RefPose.Rotation));
			//!! RefPose.Scale3D
			continue;
		}

		int k;

		if (!Seq->CompressedTrackOffsets.Num())	//?? or if RawAnimData.Num() != 0
		{
			// using RawAnimData array
			assert(Seq->RawAnimationData.Num() == NumTracks);
			CopyArray(A->KeyPos,  CVT(Seq->RawAnimationData[TrackIndex].PosKeys));
			CopyArray(A->KeyQuat, CVT(Seq->RawAnimationData[TrackIndex].RotKeys));
			CopyArray(A->KeyTime, Seq->RawAnimationData[TrackIndex].KeyTimes);	// may be empty
			for (int k = 0; k < A->KeyTime.Num(); k++)
				A->KeyTime[k] *= Dst->Rate;
			continue;
		}

		FVector Mins, Ranges;	// common ...
		static const CVec3 nullVec  = { 0, 0, 0 };
		static const CQuat nullQuat = { 0, 0, 0, 1 };

		int offsetIndex = TrackIndex * offsetsPerBone;

		// PARAGON has invalid data inside some animation tracks. Not sure if game engine ignores them
		// or trying to process (this game has holes in data due to wrong pointers in CompressedTrackOffsets).
		// This causes garbage data to appear instead of real animation track header, with wrong compression
		// method etc. We're going to skip such tracks with displaying a warning message.
		if (0) // this is just a placeholder for error handler - it should be located somewhere
		{
		track_error:
			AnimSet->Sequences.RemoveSingle(Dst);
			delete Dst;
			return;
		}

		//----------------------------------------------
		// decode AKF_PerTrackCompression data
		//----------------------------------------------
		if (Seq->KeyEncodingFormat == AKF_PerTrackCompression)
		{
			// this format uses different key storage
			guard(PerTrackCompression);
			assert(Seq->TranslationCompressionFormat == ACF_Identity);
			assert(Seq->RotationCompressionFormat == ACF_Identity);

			int TransOffset = Seq->CompressedTrackOffsets[offsetIndex  ];
			int RotOffset   = Seq->CompressedTrackOffsets[offsetIndex+1];

			uint32 PackedInfo;
			AnimationCompressionFormat KeyFormat;
			int ComponentMask;
			int NumKeys;

#define DECODE_PER_TRACK_INFO(info)									\
			KeyFormat = (AnimationCompressionFormat)(info >> 28);	\
			ComponentMask = (info >> 24) & 0xF;						\
			NumKeys       = info & 0xFFFFFF;						\
			HasTimeTracks = (ComponentMask & 8) != 0;

			guard(TransKeys);
			// read translation keys
			if (TransOffset == -1)
			{
				A->KeyPos.Add(nullVec);
				DBG("    [%d] no translation data\n", TrackIndex);
			}
			else
			{
				Reader.Seek(TransOffset);
				Reader << PackedInfo;
				DECODE_PER_TRACK_INFO(PackedInfo);
				A->KeyPos.Empty(NumKeys);
				DBG("    [%d] trans: fmt=%d (%s), %d keys, mask %d\n", TrackIndex,
					KeyFormat, EnumToName(KeyFormat), NumKeys, ComponentMask
				);
				if (KeyFormat == ACF_IntervalFixed32NoW)
				{
					// read mins/maxs
					Mins.Set(0, 0, 0);
					Ranges.Set(0, 0, 0);
					if (ComponentMask & 1) Reader << Mins.X << Ranges.X;
					if (ComponentMask & 2) Reader << Mins.Y << Ranges.Y;
					if (ComponentMask & 4) Reader << Mins.Z << Ranges.Z;
				}
				for (k = 0; k < NumKeys; k++)
				{
					switch (KeyFormat)
					{
//					case ACF_None:
					case ACF_Float96NoW:
						{
							FVector v;
							if (ComponentMask & 7)
							{
								v.Set(0, 0, 0);
								if (ComponentMask & 1) Reader << v.X;
								if (ComponentMask & 2) Reader << v.Y;
								if (ComponentMask & 4) Reader << v.Z;
							}
							else
							{
								// ACF_Float96NoW has a special case for ((ComponentMask & 7) == 0)
								Reader << v;
							}
							A->KeyPos.Add(CVT(v));
						}
						break;
					TPR(ACF_IntervalFixed32NoW, FVectorIntervalFixed32)
					case ACF_Fixed48NoW:
						{
							uint16 X, Y, Z;
							CVec3 v;
							v.Set(0, 0, 0);
							if (ComponentMask & 1)
							{
								Reader << X; v[0] = DecodeFixed48_PerTrackComponent<7>(X);
							}
							if (ComponentMask & 2)
							{
								Reader << Y; v[1] = DecodeFixed48_PerTrackComponent<7>(Y);
							}
							if (ComponentMask & 4)
							{
								Reader << Z; v[2] = DecodeFixed48_PerTrackComponent<7>(Z);
							}
							A->KeyPos.Add(v);
						}
						break;
					case ACF_Identity:
						A->KeyPos.Add(nullVec);
						break;
					default:
						{
							char buf[1024];
							Seq->GetFullName(buf, 1024);
							appNotify("%s: unknown translation compression method: %d (%s) - dropping track", buf, KeyFormat, EnumToName(KeyFormat));
							goto track_error;
						}
					}
				}
				// align to 4 bytes
				Reader.Seek(Align(Reader.Tell(), 4));
				if (HasTimeTracks)
					ReadTimeArray(Reader, NumKeys, A->KeyPosTime, Seq->NumFrames);
			}
			unguard;

			guard(RotKeys);
			// read rotation keys
			if (RotOffset == -1)
			{
				A->KeyQuat.Add(nullQuat);
				DBG("    [%d] no rotation data\n", TrackIndex);
			}
			else
			{
				Reader.Seek(RotOffset);
				Reader << PackedInfo;
				DECODE_PER_TRACK_INFO(PackedInfo);
				A->KeyQuat.Empty(NumKeys);
				DBG("    [%d] rot  : fmt=%d (%s), %d keys, mask %d\n", TrackIndex,
					KeyFormat, EnumToName(KeyFormat), NumKeys, ComponentMask
				);
				if (KeyFormat == ACF_IntervalFixed32NoW)
				{
					// read mins/maxs
					Mins.Set(0, 0, 0);
					Ranges.Set(0, 0, 0);
					if (ComponentMask & 1) Reader << Mins.X << Ranges.X;
					if (ComponentMask & 2) Reader << Mins.Y << Ranges.Y;
					if (ComponentMask & 4) Reader << Mins.Z << Ranges.Z;
				}
				for (k = 0; k < NumKeys; k++)
				{
					switch (KeyFormat)
					{
//					TR (ACF_None, FQuat)
					case ACF_Float96NoW:
						{
							FQuatFloat96NoW q;
							Reader << q;
							FQuat q2 = q;				// convert
							A->KeyQuat.Add(CVT(q2));
						}
						break;
					case ACF_Fixed48NoW:
						{
							FQuatFixed48NoW q;
							q.X = q.Y = q.Z = 32767;	// corresponds to 0
							if (ComponentMask & 1) Reader << q.X;
							if (ComponentMask & 2) Reader << q.Y;
							if (ComponentMask & 4) Reader << q.Z;
							FQuat q2 = q;				// convert
							A->KeyQuat.Add(CVT(q2));
						}
						break;
					TR (ACF_Fixed32NoW, FQuatFixed32NoW)
					TRR(ACF_IntervalFixed32NoW, FQuatIntervalFixed32NoW)
					TR (ACF_Float32NoW, FQuatFloat32NoW)
					case ACF_Identity:
						A->KeyQuat.Add(nullQuat);
						break;
					default:
						{
							char buf[1024];
							Seq->GetFullName(buf, 1024);
							appNotify("%s: unknown rotation compression method: %d (%s) - dropping track", buf, KeyFormat, EnumToName(KeyFormat));
							goto track_error;
						}
					}
				}
				// align to 4 bytes
				Reader.Seek(Align(Reader.Tell(), 4));
				if (HasTimeTracks)
					ReadTimeArray(Reader, NumKeys, A->KeyQuatTime, Seq->NumFrames);
			}
			unguard;

			unguard;
			continue;
			// end of AKF_PerTrackCompression block ...
		}

		//----------------------------------------------
		// end of AKF_PerTrackCompression decoder
		//----------------------------------------------

		// read animations
		int TransOffset = Seq->CompressedTrackOffsets[offsetIndex  ];
		int TransKeys   = Seq->CompressedTrackOffsets[offsetIndex+1];
		int RotOffset   = Seq->CompressedTrackOffsets[offsetIndex+2];
		int RotKeys     = Seq->CompressedTrackOffsets[offsetIndex+3];
//		appPrintf("[%d:%d:%d] :  %d[%d]  %d[%d]  %d[%d]\n", j, Seq->RotationCompressionFormat, Seq->TranslationCompressionFormat, TransOffset, TransKeys, RotOffset, RotKeys, ScaleOffset, ScaleKeys);

		A->KeyPos.Empty(TransKeys);
		A->KeyQuat.Empty(RotKeys);

		// read translation keys
		if (TransKeys)
		{
			Reader.Seek(TransOffset);
			AnimationCompressionFormat TranslationCompressionFormat = Seq->TranslationCompressionFormat;
			if (TransKeys == 1)
				TranslationCompressionFormat = ACF_None;	// single key is stored without compression
			// read mins/ranges
			if (TranslationCompressionFormat == ACF_IntervalFixed32NoW)
			{
				Reader << Mins << Ranges;
			}

			for (k = 0; k < TransKeys; k++)
			{
				switch (TranslationCompressionFormat)
				{
				TP (ACF_None,               FVector)
				TP (ACF_Float96NoW,         FVector)
				TPR(ACF_IntervalFixed32NoW, FVectorIntervalFixed32)
				TP (ACF_Fixed48NoW,         FVectorFixed48)
				case ACF_Identity:
					A->KeyPos.Add(nullVec);
					break;
				default:
					appError("Unknown translation compression method: %d (%s)", TranslationCompressionFormat, EnumToName(TranslationCompressionFormat));
				}
			}

			// align to 4 bytes
			Reader.Seek(Align(Reader.Tell(), 4));
			if (HasTimeTracks)
				ReadTimeArray(Reader, TransKeys, A->KeyPosTime, Seq->NumFrames);
		}
		else
		{
//			A->KeyPos.Add(nullVec);
//			appNotify("No translation keys!");
		}