/// <inheritdoc /> public void Initialize() { ThrowIfDisposed(); if (this.IsInitialized) { return; } this.Shader = this.Cache.GetShader(EverlookShader.GameModel) as GameModelShader; if (this.Shader == null) { throw new ShaderNullException(typeof(GameModelShader)); } this.VertexBuffer = new Buffer <byte>(BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw) { Data = this.Model.Vertices.Select(v => v.PackForOpenGL()).SelectMany(b => b).ToArray() }; var attributePointers = new[] { // Position new VertexAttributePointer(0, 3, VertexAttribPointerType.Float, MDXVertex.GetSize(), 0), // Bone weights new VertexAttributePointer(1, 4, VertexAttribPointerType.UnsignedByte, MDXVertex.GetSize(), 12), // Bone indexes new VertexAttributePointer(2, 4, VertexAttribPointerType.UnsignedByte, MDXVertex.GetSize(), 16), // Normal new VertexAttributePointer(3, 3, VertexAttribPointerType.Float, MDXVertex.GetSize(), 20), // UV1 new VertexAttributePointer(4, 2, VertexAttribPointerType.Float, MDXVertex.GetSize(), 32), // UV2 new VertexAttributePointer(5, 2, VertexAttribPointerType.Float, MDXVertex.GetSize(), 40) }; this.VertexBuffer.AttachAttributePointers(attributePointers); this.BoundingBox = new RenderableBoundingBox(this.Model.BoundingBox, this.ActorTransform); this.BoundingBox.Initialize(); foreach (MDXTexture texture in this.Model.Textures) { if (!this.TextureLookup.ContainsKey(texture.Filename)) { this.TextureLookup.Add ( texture.Filename, this.Cache.GetTexture(texture, this.GameContext) ); } } foreach (MDXSkin skin in this.Model.Skins) { ushort[] absoluteTriangleVertexIndexes = skin.Triangles.Select(relativeIndex => skin.VertexIndices[relativeIndex]).ToArray(); var skinIndexBuffer = new Buffer <ushort>(BufferTarget.ElementArrayBuffer, BufferUsageHint.StaticDraw) { Data = absoluteTriangleVertexIndexes }; this.SkinIndexArrayBuffers.Add(skin, skinIndexBuffer); if (this.Model.Version <= WarcraftVersion.Wrath) { // In models earlier than Cata, we need to calculate the shader selector value at runtime. foreach (var renderBatch in skin.RenderBatches) { ushort shaderSelector = MDXShaderHelper.GetRuntimeShaderID(renderBatch.ShaderID, renderBatch, this.Model); renderBatch.ShaderID = shaderSelector; } } } // Cache the default display info if (this.CurrentDisplayInfo != null) { CacheDisplayInfo(this.CurrentDisplayInfo); } this.IsInitialized = true; }
/// <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(); } } } }