/// <summary> /// Calculate the shader selector value based on the original shader ID, the render batch and general /// information from the model. /// </summary> /// <param name="baseShaderID">The original shader ID. Typically 0.</param> /// <param name="renderBatch">The render batch to which the ID belongs.</param> /// <param name="model">The model to which the batch belongs.</param> /// <returns>A new shader selector value.</returns> public static ushort GetRuntimeShaderID(ushort baseShaderID, MDXRenderBatch renderBatch, Warcraft.MDX.MDX model) { // The shader ID is already "modernized", so there's nothing to do. if ((baseShaderID & 0x8000) > 0) { return(baseShaderID); } var operationCount = renderBatch.TextureCount; var material = model.Materials ![renderBatch.MaterialIndex];
/// <summary> /// Calculate the shader selector value based on the original shader ID, the render batch and general /// information from the model. /// </summary> /// <param name="baseShaderID">The original shader ID. Typically 0.</param> /// <param name="renderBatch">The render batch to which the ID belongs.</param> /// <param name="model">The model to which the batch belongs.</param> /// <returns>A new shader selector value.</returns> public static ushort GetRuntimeShaderID(ushort baseShaderID, MDXRenderBatch renderBatch, Warcraft.MDX.MDX model) { // The shader ID is already "modernized", so there's nothing to do. if ((baseShaderID & 0x8000) > 0) { return(baseShaderID); } var operationCount = renderBatch.TextureCount; var material = model.Materials[renderBatch.MaterialIndex]; ushort newShaderID = 0; if (!model.GlobalModelFlags.HasFlag(ModelObjectFlags.HasBlendModeOverrides)) { var textureMapping = model.TextureMappingLookupTable[renderBatch.TextureMappingLookupTableIndex]; bool isEnvMapped = textureMapping == EMDXTextureMappingType.Environment; bool nonOpaqueBlendingMode = material.BlendMode != BlendingMode.Opaque; if (nonOpaqueBlendingMode) { newShaderID = 1; if (isEnvMapped) { newShaderID |= 8; } } newShaderID *= 16; if (textureMapping == EMDXTextureMappingType.T2) { newShaderID |= 0x4000; } return(newShaderID); } if (operationCount == 0) { return(baseShaderID); } var v19 = new short[] { 0, 0 }; for (int opIndex = 0; opIndex < operationCount; ++opIndex) { int blendingOverrideIndex = baseShaderID + opIndex; var blendingOverride = model.BlendMapOverrides[blendingOverrideIndex]; if (opIndex == 0 && material.BlendMode == BlendingMode.Opaque) { blendingOverride = BlendingMode.Opaque; } int textureMappingOverrideIndex = renderBatch.TextureMappingLookupTableIndex + opIndex; EMDXTextureMappingType textureSlotOverride = model.TextureMappingLookupTable[textureMappingOverrideIndex]; bool isEnvMapped = textureSlotOverride == EMDXTextureMappingType.Environment; if (isEnvMapped) { v19[opIndex] = (short)((short)blendingOverride | 8); } else { v19[opIndex] = (short)blendingOverride; } if (textureSlotOverride == EMDXTextureMappingType.Environment && (opIndex + 1) == operationCount) { newShaderID |= 0x4000; } } newShaderID |= (ushort)(v19[1] | (v19[0] * 16)); return(newShaderID); }
/// <summary> /// Reads an <see cref="MDXRenderBatch"/> from the data stream. /// </summary> /// <param name="binaryReader">The reader.</param> /// <returns>The value.</returns> public static MDXRenderBatch ReadMDXRenderBatch(this BinaryReader binaryReader) { return(new MDXRenderBatch(binaryReader.ReadBytes(MDXRenderBatch.GetSize()))); }
/// <summary> /// Prepares the OpenGL state for rendering the specified batch. /// </summary> /// <param name="renderBatch">The batch to prepare for rendering.</param> /// <exception cref="ArgumentOutOfRangeException">Thrown if the batch has more than four textures.</exception> private void PrepareBatchForRender(MDXRenderBatch renderBatch) { var fragmentShader = MDXShaderHelper.GetFragmentShaderType(renderBatch.TextureCount, renderBatch.ShaderID); var vertexShader = MDXShaderHelper.GetVertexShaderType(renderBatch.TextureCount, renderBatch.ShaderID); var batchMaterial = this.Model.Materials[renderBatch.MaterialIndex]; this.Shader.SetVertexShaderType(vertexShader); this.Shader.SetFragmentShaderType(fragmentShader); this.Shader.SetMaterial(batchMaterial); var baseColour = Color4.White; if (renderBatch.ColorIndex >= 0) { var colorAnimation = this.Model.ColourAnimations[renderBatch.ColorIndex]; // TODO: Sample based on animated values RGB rgb; float alpha; if (colorAnimation.ColourTrack.IsComposite) { rgb = colorAnimation.ColourTrack.CompositeTimelineValues.First(); alpha = colorAnimation.OpacityTrack.CompositeTimelineValues.First() / 0x7fff; } else { rgb = colorAnimation.ColourTrack.Values.First().First(); alpha = colorAnimation.OpacityTrack.Values.First().First() / 0x7fff; } baseColour = new Color4 ( MathHelper.Clamp(rgb.R, 0.0f, 1.0f), MathHelper.Clamp(rgb.G, 0.0f, 1.0f), MathHelper.Clamp(rgb.B, 0.0f, 1.0f), MathHelper.Clamp(alpha, 0.0f, 1.0f) ); } if ((short)renderBatch.TransparencyLookupTableIndex >= 0) { var transparencyAnimationIndex = this.Model.TransparencyLookupTable[renderBatch.TransparencyLookupTableIndex]; var transparencyAnimation = this.Model.TransparencyAnimations[transparencyAnimationIndex]; float alphaWeight; if (transparencyAnimation.Weight.IsComposite) { alphaWeight = transparencyAnimation.Weight.CompositeTimelineValues.First() / 0x7fff; } else { alphaWeight = transparencyAnimation.Weight.Values.First().First() / 0x7fff; } baseColour.A *= alphaWeight; } this.Shader.SetBaseInputColour(baseColour); var textureIndexes = this.Model.TextureLookupTable.Skip(renderBatch.TextureLookupTableIndex) .Take(renderBatch.TextureCount); var textures = this.Model.Textures.Where((t, i) => textureIndexes.Contains((short)i)).ToList(); for (int i = 0; i < textures.Count; ++i) { var texture = textures[i]; string textureName; switch (texture.TextureType) { case MDXTextureType.Regular: { textureName = texture.Filename; break; } case MDXTextureType.MonsterSkin1: { textureName = GetDisplayInfoTexturePath(this.CurrentDisplayInfo?.TextureVariation1.Value); break; } case MDXTextureType.MonsterSkin2: { textureName = GetDisplayInfoTexturePath(this.CurrentDisplayInfo?.TextureVariation2.Value); break; } case MDXTextureType.MonsterSkin3: { textureName = GetDisplayInfoTexturePath(this.CurrentDisplayInfo?.TextureVariation3.Value); break; } default: { // Use the fallback texture if we don't know how to load the texture type textureName = string.Empty; break; } } var textureObject = this.TextureLookup[textureName]; switch (i) { case 0: { this.Shader.BindTexture0(textureObject); break; } case 1: { this.Shader.BindTexture1(textureObject); break; } case 2: { this.Shader.BindTexture2(textureObject); break; } case 3: { this.Shader.BindTexture3(textureObject); break; } default: { throw new ArgumentOutOfRangeException(); } } } }
/// <summary> /// Prepares the OpenGL state for rendering the specified batch. /// </summary> /// <param name="renderBatch">The batch to prepare for rendering.</param> /// <exception cref="ArgumentOutOfRangeException">Thrown if the batch has more than four textures.</exception> private void PrepareBatchForRender(MDXRenderBatch renderBatch) { var fragmentShader = MDXShaderHelper.GetFragmentShaderType(renderBatch.TextureCount, renderBatch.ShaderID); var vertexShader = MDXShaderHelper.GetVertexShaderType(renderBatch.TextureCount, renderBatch.ShaderID); var batchMaterial = this.Model.Materials[renderBatch.MaterialIndex]; this.Shader.SetVertexShaderType(vertexShader); this.Shader.SetFragmentShaderType(fragmentShader); this.Shader.SetMaterial(batchMaterial); var textureIndexes = this.Model.TextureLookupTable.Skip(renderBatch.TextureLookupTableIndex) .Take(renderBatch.TextureCount); var textures = this.Model.Textures.Where((t, i) => textureIndexes.Contains((short)i)).ToList(); for (int i = 0; i < textures.Count; ++i) { var texture = textures[i]; string textureName; switch (texture.TextureType) { case EMDXTextureType.Regular: { textureName = texture.Filename; break; } case EMDXTextureType.MonsterSkin1: { textureName = GetDisplayInfoTexturePath(this.CurrentDisplayInfo?.TextureVariation1.Value); break; } case EMDXTextureType.MonsterSkin2: { textureName = GetDisplayInfoTexturePath(this.CurrentDisplayInfo?.TextureVariation2.Value); break; } case EMDXTextureType.MonsterSkin3: { textureName = GetDisplayInfoTexturePath(this.CurrentDisplayInfo?.TextureVariation3.Value); break; } default: { // Use the fallback texture if we don't know how to load the texture type textureName = string.Empty; break; } } var textureObject = this.TextureLookup[textureName]; switch (i) { case 0: { this.Shader.BindTexture0(textureObject); break; } case 1: { this.Shader.BindTexture1(textureObject); break; } case 2: { this.Shader.BindTexture2(textureObject); break; } case 3: { this.Shader.BindTexture3(textureObject); break; } default: { throw new ArgumentOutOfRangeException(); } } } }