diff --git a/UmodelTool/Main.cpp b/UmodelTool/Main.cpp index 6fb87d66..941ca5ed 100644 --- a/UmodelTool/Main.cpp +++ b/UmodelTool/Main.cpp @@ -73,6 +73,9 @@ BEGIN_CLASS_TABLE #if UNREAL3 REGISTER_MATERIAL_CLASSES_U3 //!! needed for Bioshock 2 too #endif +#if VANGUARD + REGISTER_MATERIAL_CLASSES_VANGUARD +#endif #if DECLARE_VIEWER_PROPS REGISTER_SKELMESH_VCLASSES diff --git a/Unreal/UnRenderer.cpp b/Unreal/UnRenderer.cpp index 49e640cb..f2cbd074 100644 --- a/Unreal/UnRenderer.cpp +++ b/Unreal/UnRenderer.cpp @@ -1558,6 +1558,15 @@ void UShader::GetParams(CMaterialParams &Params) const NormalMap->GetParams(Params2); Params.Normal = Params2.Diffuse; } +#endif +#if VANGUARD + if (Normal) + { + //Some models look funky with this on, others look good. Disabling for now + //CMaterialParams Params2; + //Normal->GetParams(Params2); + //Params.Normal = Params2.Diffuse; + } #endif if (SpecularityMask) { @@ -1583,6 +1592,143 @@ bool UShader::IsTranslucent() const return (OutputBlending != OB_Normal); } +#if VANGUARD +void UNormalBitmapMaterial::GetParams(CMaterialParams ¶ms) const +{ + if (normalTex) params.Diffuse = normalTex; + else if (BumpMap) params.Diffuse = BumpMap; +} + +void UNormalBitmapMaterial::PostLoad() +{ + if (BumpMap) + { + UBits = BumpMap->UBits; + VBits = BumpMap->VBits; + USize = BumpMap->USize; + VSize = BumpMap->VSize; + UClamp = BumpMap->UClamp; + VClamp = BumpMap->VClamp; + UClampMode = BumpMap->UClampMode; + VClampMode = BumpMap->VClampMode; + + if (BumpMap->Format == TEXF_DXT3) return; + + assert(BumpMap->Format == TEXF_P8); + + normalTex = new UTexture; + normalTex->UBits = UBits; + normalTex->VBits = VBits; + normalTex->USize = USize; + normalTex->VSize = VSize; + normalTex->UClamp = UClamp; + normalTex->VClamp = VClamp; + normalTex->UClampMode = UClampMode; + normalTex->VClampMode = VClampMode; + + normalTex->Mips.SetNum(1); + //Norm mip + FMipmap &nMip = normalTex->Mips[0]; + nMip.UBits = UBits; + nMip.VBits = VBits; + nMip.USize = USize; + nMip.VSize = VSize; + + //Bump Mip + const FMipmap &bMip = BumpMap->Mips[0]; + + /* BGRA8 CODE - In client but image doesn't look like other normal maps ive seen + { + float scale = BumpScale / USize * 0.0039215689; + + nMip.DataArray.AddUninitialized(USize * VSize * 4); + + const byte *rawU = &bMip.DataArray[0]; + const byte *rawV = &bMip.DataArray[USize]; + + //Decode into BGRA8 format + byte *dest = &nMip.DataArray[0]; + for (int v = 0; v < VSize; v++) + { + const byte *rawU2 = rawU + 1; + for (int u = 0; u < USize; u++) + { + float fu1 = *rawU++ * scale; + float fv = *rawV++ * scale; + float fu2 = fu1 - *rawU2++ * scale; + fu1 -= fv; + + float t = (fu1 * fu1) + (fu2 * fu2) + 1.0f; + float r, g, b; + if (t >= 0.00000001) + { + b = 1.0f / sqrtf(t); + g = fu1 * r; + r = fu2 * r; + } + else r = 0.0f, g = 0.0f, b = 0.0f; + + b = (r + 1.0f) * 127.5f; + g = (g + 1.0f) * 127.5f; + r = (r + 1.0f) * 127.5f; + + uint32 bb = (byte)b, gb = (byte)g, rb = (byte)r; + uint32 pack = bb | (gb << 8) | (rb << 16); + *reinterpret_cast(dest) = pack; + dest += 4; + + //Safety check to stay in-bounds + if (u == USize - 2) rawU2 -= USize; + } + } + + normalTex->Format = TEXF_RGBA8; + Format = TEXF_RGBA8; + } + else */ + { + float scale = BumpScale / 0.00390625f; + + nMip.DataArray.AddUninitialized(USize * VSize * 2); + byte *dest = &nMip.DataArray[0]; + + const byte *rawU = &bMip.DataArray[0]; + const byte *rawV = &bMip.DataArray[USize]; + for (int v = 0; v < VSize; v++) + { + const byte *rawU2 = rawU + 1; + for (int u = 0; u < USize; u++) + { + float fu1 = *rawU++ * scale; + float fv = *rawV++ * scale; + float fu2 = fu1 - *rawU2++ * scale; + fu1 -= fv; + + float t = (fu1 * fu1) + (fu2 * fu2) + 1.0f; + + float tv, tu; + if (t >= 0.00000001) + { + float n = 1.0f / sqrt(t); + tv = n * fu2; + tu = n * fu1; + } + else tv = tu = 0.0f; + + *dest++ = appRound(tv * 128.0f); + *dest++ = appRound(tu * 128.0f); + + //Safety check to stay in-bounds + if (u == USize - 2) rawU2 -= USize; + } + } + + normalTex->Format = TEXF_V8U8; + Format = TEXF_V8U8; + } + } +} +#endif // VANGUARD #if BIOSHOCK diff --git a/Unreal/UnrealMaterial/UnMaterial2.h b/Unreal/UnrealMaterial/UnMaterial2.h index 5864b60b..cfc653a8 100644 --- a/Unreal/UnrealMaterial/UnMaterial2.h +++ b/Unreal/UnrealMaterial/UnMaterial2.h @@ -36,6 +36,10 @@ UE2 MATERIALS TREE: ShadowBitmapMaterial - Texture Cubemap +#if VANGUARD + NormalBitmapMaterial + SpecularBitmapMaterial +#endif - ConstantMaterial - ConstantColor FadeColor @@ -296,6 +300,7 @@ enum ETextureFormat TEXF_CxV8U8, TEXF_DXT5N, // Note: in Bioshock this value has name 3DC, but really DXT5N is used TEXF_3DC, // BC5 compression + TEXF_V8U8 }; _ENUM(ETextureFormat) @@ -316,6 +321,7 @@ _ENUM(ETextureFormat) _E(TEXF_CxV8U8), _E(TEXF_DXT5N), _E(TEXF_3DC), + _E(TEXF_V8U8) }; enum ETexClampMode @@ -363,6 +369,63 @@ class UBitmapMaterial : public URenderedMaterial // abstract #endif // RENDERING }; +#if VANGUARD +class UNormalBitmapMaterial : public UBitmapMaterial +{ + DECLARE_CLASS(UNormalBitmapMaterial, UBitmapMaterial); +public: + + UNormalBitmapMaterial() : BumpScale(8.0f) {} + + ~UNormalBitmapMaterial() + { + if (normalTex) delete normalTex; + } + class UTexture* BumpMap; + float BumpScale; + //Looks wrong but correct casing + float BumpCulldistance; + + BEGIN_PROP_TABLE + PROP_OBJ(BumpMap) + PROP_FLOAT(BumpScale) + PROP_FLOAT(BumpCulldistance) + END_PROP_TABLE + + //CUSTOM + UTexture* normalTex; + void PostLoad() override; + void GetParams(CMaterialParams &Params) const override; +}; + +//No functionality yet +class USpecularBitmapMaterial : public UBitmapMaterial +{ + DECLARE_CLASS(USpecularBitmapMaterial, UBitmapMaterial); +public: + class UMaterial* ExponentMap; + float DiffuseStrength, SpecularStrength, EnvStrength; + FColor SpecularColor; + float SpecularPower; + float DiffuseClarity, SpecularClarity, EnvClarity; + float DiffuseBumpiness, SpecularBumpiness, EnvBumpiness; + + BEGIN_PROP_TABLE + PROP_OBJ(ExponentMap) + PROP_FLOAT(DiffuseStrength) + PROP_FLOAT(SpecularStrength) + PROP_FLOAT(EnvStrength) + PROP_COLOR(SpecularColor) + PROP_FLOAT(SpecularPower) + PROP_FLOAT(DiffuseClarity) + PROP_FLOAT(SpecularClarity) + PROP_FLOAT(EnvClarity) + PROP_FLOAT(DiffuseBumpiness) + PROP_FLOAT(SpecularBumpiness) + PROP_FLOAT(EnvBumpiness) + END_PROP_TABLE +}; +#endif //VANGUARD class UPalette : public UObject { @@ -597,6 +660,9 @@ class UShader : public URenderedMaterial UMaterial *Diffuse; #if BIOSHOCK UMaterial *NormalMap; +#endif +#if VANGUARD + UMaterial *Normal; #endif UMaterial *Opacity; UMaterial *Specular; @@ -658,6 +724,9 @@ class UShader : public URenderedMaterial PROP_BOOL(AlphaTest) PROP_BYTE(AlphaRef) #endif // LINEAGE2 +#if VANGUARD + PROP_OBJ(Normal) +#endif//VANGUARD #if BIOSHOCK PROP_STRUC(Opacity_Bio, FMaskMaterial) PROP_STRUC(HeightMap, FMaskMaterial) @@ -1356,6 +1425,10 @@ class USCX_basic_material : public URenderedMaterial REGISTER_CLASS(FMaskMaterial) \ REGISTER_CLASS(UFacingShader) +#define REGISTER_MATERIAL_CLASSES_VANGUARD \ + REGISTER_CLASS(UNormalBitmapMaterial) \ + REGISTER_CLASS(USpecularBitmapMaterial) + #define REGISTER_MATERIAL_CLASSES_SCELL \ REGISTER_CLASS(UUnreal3Material) \ REGISTER_CLASS(USCX_basic_material) diff --git a/Unreal/UnrealMaterial/UnTexture2.cpp b/Unreal/UnrealMaterial/UnTexture2.cpp index 34edffae..eeb10e31 100644 --- a/Unreal/UnrealMaterial/UnTexture2.cpp +++ b/Unreal/UnrealMaterial/UnTexture2.cpp @@ -519,6 +519,9 @@ ETexturePixelFormat UTexture::GetTexturePixelFormat() const case TEXF_3DC: intFormat = TPF_BC5; break; + case TEXF_V8U8: + intFormat = TPF_V8U8; + break; default: appNotify("Unknown UE2 texture format: %s (%d)", EnumToName(Format), Format); } diff --git a/Unreal/UnrealMesh/UnMesh2.cpp b/Unreal/UnrealMesh/UnMesh2.cpp index 277d16ca..b9e3c89c 100644 --- a/Unreal/UnrealMesh/UnMesh2.cpp +++ b/Unreal/UnrealMesh/UnMesh2.cpp @@ -1444,42 +1444,34 @@ void FStaticLODModel::RestoreLineageMesh() UStaticMesh class -----------------------------------------------------------------------------*/ -struct FStaticMeshTriangleUnk -{ - float unk1[2]; - float unk2[2]; - float unk3[2]; -}; - // complex FStaticMeshTriangle structure struct FStaticMeshTriangle { - FVector f0; - FVector fC; - FVector f18; - FStaticMeshTriangleUnk f24[8]; - byte fE4[12]; - int fF0; - int fF4; - int fF8; + FVector Verts[3]; + FMeshUVFloat UVs[8][3]; + FColor Colors[3]; + int MaterialIndex; + uint32 UnkMask; + int UVCount; friend FArchive& operator<<(FArchive &Ar, FStaticMeshTriangle &T) { guard(FStaticMeshTriangle<<); - int i; + int i, j; assert(Ar.ArVer >= 112); - Ar << T.f0 << T.fC << T.f18; - Ar << T.fF8; - assert(T.fF8 <= ARRAY_COUNT(T.f24)); - for (i = 0; i < T.fF8; i++) + for (i = 0; i < 3; i++) + Ar << T.Verts[i]; + Ar << T.UVCount; + assert(T.UVCount <= 8); + for (i = 0; i < T.UVCount; i++) { - FStaticMeshTriangleUnk &V = T.f24[i]; - Ar << V.unk1[0] << V.unk1[1] << V.unk2[0] << V.unk2[1] << V.unk3[0] << V.unk3[1]; + for (j = 0; j < 3; j++) + Ar << T.UVs[i][j]; } - for (i = 0; i < 12; i++) - Ar << T.fE4[i]; // UT2 has strange order of field serialization: [2,1,0,3] x 3 times - Ar << T.fF0 << T.fF4; + for (i = 0; i < 3; i++) + Ar << T.Colors[i]; // UT2 has strange order of field serialization: [2,1,0,3] x 3 times + Ar << T.MaterialIndex << T.UnkMask; // extra fields for older version (<= 111) return Ar; @@ -1808,24 +1800,28 @@ void UStaticMesh::SerializeVanguardMesh(FArchive &Ar) Ar << InternalVersion; GUseNewVanguardStaticMesh = (InternalVersion >= 13); - int unk1CC, unk134; - UObject *unk198, *unk1DC; - float unk194, unk19C, unk1A0; - byte unk1A4; + int AuthKey, DefaultSkin; + UObject *CollisionModel, *Impostor, *unk1DC; + float ImpostorDistance, CullDistance, CullDistanceScalar; + byte MeshDetailLevel; TArray BasisStream; + //These 2 int arrays are related to enabling collision in the mesh sections TArray unk144, unk150; TArray unk200; - Ar << unk1CC << unk134 << f108 << unk198 << unk194 << unk19C; + Ar << AuthKey << DefaultSkin << CollisionModel << Impostor << ImpostorDistance << CullDistance; + f108 = CollisionModel; + #if DEBUG_STATICMESH appPrintf("Version: %d\n", InternalVersion); #endif if (InternalVersion > 11) - Ar << unk1A0; - Ar << unk1A4; + Ar << CullDistanceScalar; + Ar << MeshDetailLevel; Ar << unk1DC; Ar << BoundingBox; + #if DEBUG_STATICMESH appPrintf("Bounds: %g %g %g - %g %g %g (%d)\n", VECTOR_ARG(BoundingBox.Min), VECTOR_ARG(BoundingBox.Max), BoundingBox.IsValid); #endif @@ -1836,7 +1832,7 @@ void UStaticMesh::SerializeVanguardMesh(FArchive &Ar) Ar << Skins; if (Skins.Num() && !Materials.Num()) { - const FVanguardSkin &S = Skins[0]; + const FVanguardSkin &S = Skins[(DefaultSkin < Skins.Num() && DefaultSkin >= 0) ? DefaultSkin : 0]; Materials.AddZeroed(S.Textures.Num()); for (int i = 0; i < S.Textures.Num(); i++) Materials[i].Material = S.Textures[i]; @@ -1845,7 +1841,21 @@ void UStaticMesh::SerializeVanguardMesh(FArchive &Ar) Ar << Faces << UVStream << BasisStream; Ar << unk144 << unk150 << unk200; - Ar << VertexStream << ColorStream << AlphaStream << IndexStream1 << IndexStream2; + TArray VangVerts; + int VertexStreamRev; + + Ar << VangVerts << VertexStreamRev << ColorStream << AlphaStream << IndexStream1 << IndexStream2; + + CopyArray(VertexStream.Vert, VangVerts); + + //For vanguard uv set 0 is in the vertex stream, set 1 would be the first UVStream if serialized + if (GUseNewVanguardStaticMesh && !UVStream.Num()) + { + UVStream.AddDefaulted(1); + UVStream[0].Data.SetNum(VangVerts.Num()); + for (int i = 0; i < VangVerts.Num(); i++) + UVStream[0].Data[i] = VangVerts[i].UV; + } // skip the remaining data Ar.Seek(Ar.GetStopper()); diff --git a/Unreal/UnrealMesh/UnMesh2.h b/Unreal/UnrealMesh/UnMesh2.h index ea41c350..0922752d 100644 --- a/Unreal/UnrealMesh/UnMesh2.h +++ b/Unreal/UnrealMesh/UnMesh2.h @@ -1544,12 +1544,14 @@ struct FStaticMeshVertexVanguard { FVector Pos; FVector Normal; - float unk[8]; + FVector Binormal2; + FVector Tangent3; + FMeshUVFloat UV; friend FArchive& operator<<(FArchive &Ar, FStaticMeshVertexVanguard &V) { Ar << V.Pos << V.Normal; - for (int i = 0; i < 8; i++) Ar << V.unk[i]; + if (GUseNewVanguardStaticMesh) Ar << V.Binormal2 << V.Tangent3 << V.UV; return Ar; } diff --git a/umodel.exe b/umodel.exe index 9f027d21..4b2185af 100644 Binary files a/umodel.exe and b/umodel.exe differ