Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 22 additions & 17 deletions MMManaged/MeshUtil.fs
Original file line number Diff line number Diff line change
Expand Up @@ -502,26 +502,31 @@ map_Kd $$filename

int hElement.Offset + sizeBytes

let getSizeFromFormat (f:SharpDX.DXGI.Format) =
match f with
| SharpDX.DXGI.Format.R32_Float -> 4
| SharpDX.DXGI.Format.R32G32_Float -> 8
| SharpDX.DXGI.Format.R32G32B32_Float -> 12
| SharpDX.DXGI.Format.R32G32B32A32_Float -> 16
| SharpDX.DXGI.Format.R16G16_Float -> 4
| SharpDX.DXGI.Format.R16G16_SNorm -> 4
| SharpDX.DXGI.Format.R16G16B16A16_Float -> 8
| SharpDX.DXGI.Format.R8G8B8A8_UInt -> 4
| SharpDX.DXGI.Format.B8G8R8A8_UNorm -> 4
| SharpDX.DXGI.Format.R16G16B16A16_SInt -> 8
| SharpDX.DXGI.Format.R16G16B16A16_SNorm -> 8
| _ -> failwithf "Some lazy person didn't fill in the size of format type %A" f

/// Returns the size (in bytes) of a single vertex element.
let getSizeFromElType (elType:VertexTypes.MMVertexElementType) =
match elType with
| VertexTypes.MMVertexElementType.DeclType(dt) -> getSizeFromDeclType dt
| VertexTypes.MMVertexElementType.Format(f) -> getSizeFromFormat f

let getVertSizeFromEls(elements:VertexTypes.MMVertexElement []) =
let hElement =
elements |> Array.maxBy (fun el -> el.Offset)
let sizeBytes =
match hElement.Type with
| VertexTypes.MMVertexElementType.DeclType(dt) -> getSizeFromDeclType dt
| VertexTypes.MMVertexElementType.Format(f) ->
match f with
| SharpDX.DXGI.Format.R32_Float -> 4
| SharpDX.DXGI.Format.R32G32_Float -> 8
| SharpDX.DXGI.Format.R32G32B32_Float -> 12
| SharpDX.DXGI.Format.R32G32B32A32_Float -> 16
| SharpDX.DXGI.Format.R16G16_Float -> 4
| SharpDX.DXGI.Format.R16G16_SNorm -> 4
| SharpDX.DXGI.Format.R16G16B16A16_Float -> 8
| SharpDX.DXGI.Format.R8G8B8A8_UInt -> 4
| SharpDX.DXGI.Format.B8G8R8A8_UNorm -> 4
| SharpDX.DXGI.Format.R16G16B16A16_SInt -> 8
| SharpDX.DXGI.Format.R16G16B16A16_SNorm -> 8
| _ -> failwithf "Some lazy person didn't fill in the size of format type %A" f
let sizeBytes = getSizeFromElType hElement.Type
int hElement.Offset + sizeBytes

/// Returns true if the declaration list contains blend data, false otherwise.
Expand Down
83 changes: 64 additions & 19 deletions MMManaged/ModDBInterop.fs
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,59 @@ module ModDBInterop =
WriteD3D9Decl of (BinaryWriter * int)
| ReadD3D11Layout of (MMVertexElement [])

/// We don't track source data for texture coordinate semantics above index 0, but something must
/// still be written for them: if the element is skipped the vertex ends up short, which trips the
/// size check in the vertex writer and prevents the mod from loading at all. So write stub data
/// of the correct size instead. The UV components get a mid-range value (0.5,0.5) and any extra
/// components are zeroed.
let private makeTexCoordStub (elType:MMET): byte[] =
let half (f:float32) = System.BitConverter.GetBytes(MonoGameHelpers.floatToHalfUint16 f)
let flt (f:float32) = System.BitConverter.GetBytes(f)
let snorm (f:float32) = System.BitConverter.GetBytes(int16(f * 32767.f))
match elType with
| MMET.DeclType(dt) when dt = SDXVT.Unused -> [||]
| MMET.DeclType(dt) when dt = SDXVT.UByte4N ->
// this stub predates the general handling here; keep it as is since it is
// known to work in at least one game
[| byte 16; byte 128; byte 128; byte 128 |]
| MMET.DeclType(dt) when dt = SDXVT.Float2 -> Array.concat [ flt 0.5f; flt 0.5f ]
| MMET.DeclType(dt) when dt = SDXVT.HalfTwo -> Array.concat [ half 0.5f; half 0.5f ]
| MMET.DeclType(dt) when dt = SDXVT.HalfFour ->
Array.concat [ half 0.5f; half 0.5f; half 0.f; half 0.f ]
| MMET.Format(f) when f = SDXF.R32G32_Float -> Array.concat [ flt 0.5f; flt 0.5f ]
| MMET.Format(f) when f = SDXF.R16G16_Float ->
let h (x:float32) = System.BitConverter.GetBytes(SharpDX.Half(x).RawValue)
Array.concat [ h 0.5f; h 0.5f ]
| MMET.Format(f) when f = SDXF.R16G16B16A16_SNorm ->
Array.concat [ snorm 0.5f; snorm 0.5f; snorm 0.f; snorm 0.f ]
| other ->
// unknown type: just zero fill it, which at least keeps the vertex size correct.
// if even the size is unknown, write nothing, as older code did; the vert size
// check may then fail, but that's no worse than throwing here.
try
Array.zeroCreate (MeshUtil.getSizeFromElType other)
with e ->
log.Warn "can't determine size of texture coordinate element type %A, no stub data will be written: %s"
other e.Message
[||]

/// Build the stub data (keyed on semantic index) for all texture coordinate elements above
/// semantic index 0, warning about each one. Callers should do this on every mod load, even
/// when the fill itself is skipped (VBData cache hit), so that it is always apparent from the
/// log that part of the vertex data is fake and rendering may therefore be somewhat incorrect.
let private buildTexCoordStubs (modName:string) (elements:MMVertexElement []) =
let stubs = new System.Collections.Generic.Dictionary<int,byte[]>()
elements
|> Array.filter (fun el ->
el.Semantic = MMVertexElemSemantic.TextureCoordinate && el.SemanticIndex > 0)
|> Array.iter (fun el ->
let stub = makeTexCoordStub el.Type
if stub.Length > 0 then
log.Warn "mod %A: no source data available for texture coordinate semantic index %d (type %A); using %d bytes of stub data (%A), rendering may be somewhat incorrect"
modName el.SemanticIndex el.Type stub.Length stub
stubs.[el.SemanticIndex] <- stub)
stubs

/// Fill the render buffers associated with the specified mod.
// Note: there is a lot of symmetry between this and the snapshot module (essentially they are the same
// process in two different directions), but they have totally separate implementations right now. Might be worth
Expand Down Expand Up @@ -823,6 +876,12 @@ module ModDBInterop =
let elStr = vertElsToString elements
"d3d11", [||], elements, VBDataDiskCache.hashString elStr, 0

// Build stub data for any texture coordinate semantic we have no source data for.
// Done here, before the VBData cache check below, so that the warnings it logs appear on
// every load of the mod: a cache hit skips the fill entirely, and the stub data baked
// into the cached bytes would otherwise be invisible in the log.
let texCoordStubs = buildTexCoordStubs meshrel.DBMod.Name declElements

// copy index data...someday
if (destIbSize > 0) then
failwith "Filling index data is not yet supported"
Expand Down Expand Up @@ -1047,11 +1106,6 @@ module ModDBInterop =
| Some bvd -> bvd
RawBinaryWriters.rbBinormalTangent binDataLookup vertRels

// lazy variants: these fire from the per-vertex hot loop, so
// the message thunk avoids sprintf/%A cost on every call
let tex1SemUnused = Logging.logOnce(0)
let tex1UnormSub = Logging.logOnce(0)

// Write part of a vertex. The input element controls which
// part is written.
let writeElement (v:PTNIndex) (el:VertexTypes.MMVertexElement) =
Expand Down Expand Up @@ -1117,20 +1171,11 @@ module ModDBInterop =
// remaining two shorts unused for now but maybe not in general case
()
| _ -> failwithf "Unsupported type for texture coordinate: %A" el.Type
else
match el.Type with
| MMET.DeclType(dt) when dt = SDXVT.UByte4N ->
// I don't track this data currently but write some stub bytes so the mod at least loads; if we don't
// write anything the vert size check will complain about an insufficient number of bytes
let stubBytes = [| byte 16; byte 128; byte 128; byte 128 |]
tex1UnormSub (fun () ->
sprintf "warning: writing stub bytes %A for tex coord semantic index %d (format %A); no source data available" stubBytes el.SemanticIndex el.Type)
bw.Write(stubBytes)
| _ ->
tex1SemUnused (fun () ->
sprintf "warning: texture coord semantic index > 0 is ignored: index: %A; format: %A"
el.SemanticIndex el.Type)

else
// no source data for this semantic; write the stub built (and
// warned about) at the top of this function
bw.Write(texCoordStubs.[el.SemanticIndex])

| MMVertexElemSemantic.Normal -> normalWriter modNrmIndex modVertIndex el bw
| MMVertexElemSemantic.Binormal
| MMVertexElemSemantic.Tangent -> binormalTangentWriter modNrmIndex modVertIndex el bw
Expand Down
Loading