internal int GetStride(ShaderAttributeIds attribute) { switch (attribute) { case ShaderAttributeIds.Position: case ShaderAttributeIds.Normal: return(4 * 3); case ShaderAttributeIds.Color0: case ShaderAttributeIds.Color1: return(4 * 4); case ShaderAttributeIds.Tex0: case ShaderAttributeIds.Tex1: case ShaderAttributeIds.Tex2: case ShaderAttributeIds.Tex3: case ShaderAttributeIds.Tex4: case ShaderAttributeIds.Tex5: case ShaderAttributeIds.Tex6: case ShaderAttributeIds.Tex7: return(4 * 2); default: WLog.Warning(LogCategory.Rendering, this, "Unsupported ShaderAttributeId: {0}", attribute); return(0); } }
public static Shader GenerateShader(Material fromMat) { Shader shader = new Shader(fromMat.Name); bool success = GenerateVertexShader(shader, fromMat); if (success) { success = GenerateFragmentShader(shader, fromMat); } if (!success) { WLog.Warning(LogCategory.ShaderCompiler, shader, "Failed to generate shader for material {0}", fromMat.Name); shader.Dispose(); // ToDo: Generate stub-shader here that expects Pos/UV and single texture. shader = new Shader(fromMat.Name); shader.CompileSource(File.ReadAllText("RenderSystem/Shaders/frag.glsl"), ShaderType.FragmentShader); shader.CompileSource(File.ReadAllText("RenderSystem/Shaders/vert.glsl"), ShaderType.VertexShader); shader.LinkShader(); return(shader); } if (!shader.LinkShader()) { shader.Dispose(); shader = null; } return(shader); }
public T GetProperty <T>(string propertyName) { Property prop = null; for (int i = 0; i < Properties.Count; i++) { if (string.Compare(propertyName, Properties[i].Name, StringComparison.InvariantCultureIgnoreCase) == 0) { prop = Properties[i]; break; } } if (prop == null) { WLog.Warning(LogCategory.EditorCore, this, "Requested Property {0} on object {1}, but no property found!", propertyName, this); return(default(T)); } if (prop.Value == null) { return(default(T)); } return((T)prop.Value); }
private static string GetAlphaInString(GXCombineAlphaInput inputType, GXKonstAlphaSel konst, TevOrder texMapping) { switch (inputType) { case GXCombineAlphaInput.AlphaPrev: return(m_tevOutputRegs[0] + ".a"); case GXCombineAlphaInput.A0: return(m_tevOutputRegs[1] + ".a"); case GXCombineAlphaInput.A1: return(m_tevOutputRegs[2] + ".a"); case GXCombineAlphaInput.A2: return(m_tevOutputRegs[3] + ".a"); case GXCombineAlphaInput.TexAlpha: return(GetTexTapString(texMapping) + ".a"); case GXCombineAlphaInput.RasAlpha: return(GetVertColorString(texMapping) + ".a"); case GXCombineAlphaInput.Konst: return(GetKonstAlphaString(konst)); case GXCombineAlphaInput.Zero: return("0.0f"); default: WLog.Warning(LogCategory.TEVShaderGenerator, null, "Unknown Alpha Input type: {0}", inputType); return("0.0f"); } }
public static DepthFunction GetOpenGLDepthFunc(GXCompareType gxCompare) { switch (gxCompare) { case GXCompareType.Never: return(DepthFunction.Never); case GXCompareType.Less: return(DepthFunction.Less); case GXCompareType.Equal: return(DepthFunction.Equal); case GXCompareType.LEqual: return(DepthFunction.Lequal); case GXCompareType.Greater: return(DepthFunction.Greater); case GXCompareType.NEqual: return(DepthFunction.Notequal); case GXCompareType.GEqual: return(DepthFunction.Gequal); case GXCompareType.Always: return(DepthFunction.Always); default: WLog.Warning(LogCategory.Rendering, null, "Unsupported GXCompareType: \"{0}\" in GetOpenGLDepthFunc!", gxCompare); return(DepthFunction.Less); } }
public static BlendingFactorDest GetOpenGLBlendDest(GXBlendModeControl gxMode) { switch (gxMode) { case GXBlendModeControl.Zero: return(BlendingFactorDest.Zero); case GXBlendModeControl.One: return(BlendingFactorDest.One); case GXBlendModeControl.SrcColor: return(BlendingFactorDest.SrcColor); case GXBlendModeControl.InverseSrcColor: return(BlendingFactorDest.OneMinusSrcColor); case GXBlendModeControl.SrcAlpha: return(BlendingFactorDest.SrcAlpha); case GXBlendModeControl.InverseSrcAlpha: return(BlendingFactorDest.OneMinusSrcAlpha); case GXBlendModeControl.DstAlpha: return(BlendingFactorDest.DstAlpha); case GXBlendModeControl.InverseDstAlpha: return(BlendingFactorDest.OneMinusDstAlpha); default: WLog.Warning(LogCategory.Rendering, null, "Unsupported GXBlendModeControl: \"{0}\" in GetOpenGLBlendDest!", gxMode); return(BlendingFactorDest.OneMinusSrcAlpha); } }
private static string GetVertColorString(TevOrder orderInfo) { switch (orderInfo.ChannelId) { case GXColorChannelId.Color0: return("Color0.rgb"); case GXColorChannelId.Color1: return("Color1.rgb"); case GXColorChannelId.Alpha0: return("Color0.aaaa"); case GXColorChannelId.Alpha1: return("Color1.aaaa"); case GXColorChannelId.Color0A0: return("Color0.rgba"); case GXColorChannelId.Color1A1: return("Color1.rgba"); case GXColorChannelId.ColorZero: return("0.rrrr"); case GXColorChannelId.AlphaBump: case GXColorChannelId.AlphaBumpN: case GXColorChannelId.ColorNull: default: WLog.Warning(LogCategory.TEVShaderGenerator, null, "Unsupported ChannelId: {0}", orderInfo.ChannelId); return("vec4(0.0, 1.0, 0.0, 1.0)"); } }
private static string GetKonstAlphaString(GXKonstAlphaSel konst) { switch (konst) { case GXKonstAlphaSel.KASel_1: return("1.0"); case GXKonstAlphaSel.KASel_7_8: return("0.875"); case GXKonstAlphaSel.KASel_3_4: return("0.75"); case GXKonstAlphaSel.KASel_5_8: return("0.625"); case GXKonstAlphaSel.KASel_1_2: return("0.5"); case GXKonstAlphaSel.KASel_3_8: return("0.375"); case GXKonstAlphaSel.KASel_1_4: return("0.25"); case GXKonstAlphaSel.KASel_1_8: return("0.125"); case GXKonstAlphaSel.KASel_K0_R: return("konst0.r"); case GXKonstAlphaSel.KASel_K1_R: return("konst1.r"); case GXKonstAlphaSel.KASel_K2_R: return("konst2.r"); case GXKonstAlphaSel.KASel_K3_R: return("konst3.r"); case GXKonstAlphaSel.KASel_K0_G: return("konst0.g"); case GXKonstAlphaSel.KASel_K1_G: return("konst1.g"); case GXKonstAlphaSel.KASel_K2_G: return("konst2.g"); case GXKonstAlphaSel.KASel_K3_G: return("konst3.g"); case GXKonstAlphaSel.KASel_K0_B: return("konst0.b"); case GXKonstAlphaSel.KASel_K1_B: return("konst1.b"); case GXKonstAlphaSel.KASel_K2_B: return("konst2.b"); case GXKonstAlphaSel.KASel_K3_B: return("konst3.b"); case GXKonstAlphaSel.KASel_K0_A: return("konst0.a"); case GXKonstAlphaSel.KASel_K1_A: return("konst1.a"); case GXKonstAlphaSel.KASel_K2_A: return("konst2.a"); case GXKonstAlphaSel.KASel_K3_A: return("konst3.a"); default: WLog.Warning(LogCategory.TEVShaderGenerator, null, "Unsupported GXKonstAlphaSel: {0}, returning 1.0", konst); return("1.0"); } }
public void RemoveProperty(string propertyName) { for (int i = 0; i < Properties.Count; i++) { if (string.Compare(propertyName, Properties[i].Name, StringComparison.InvariantCultureIgnoreCase) == 0) { Properties.RemoveAt(i); return; } } WLog.Warning(LogCategory.EditorCore, this, "Tried to remove Property {0} on object {1}, but no property found!", propertyName, this); }
public void PostProcessEntities() { foreach (var kvp in m_entityData) { foreach (var entity in kvp.Value) { MapEntityDataDescriptor origTemplate = m_editorCore.Templates.MapEntityDataDescriptors.Find(x => string.Compare(x.FourCC, entity.FourCC, StringComparison.InvariantCultureIgnoreCase) == 0); if (origTemplate == null) { WLog.Warning(LogCategory.EntityLoading, null, "Failed to find template for entity {0}, not attempting to post-process.", entity); continue; } foreach (Property property in entity.Fields.Properties) { var origTemplateProperty = origTemplate.Fields.Find(x => x.FieldName == property.Name); if (origTemplateProperty == null) { WLog.Warning(LogCategory.EntityLoading, null, "Failed to find property {0} on template {1} for entity {2}, not attempting to post-process.", property.Name, origTemplate.FourCC, entity); continue; } // We cheated earlier and stored the various reference-type ones as their index values. That means the type of the object doesn't actually // reflect the Type field. Thus, we now need to go back, patch up the references, and set them to be their proper type. Yeah! switch (origTemplateProperty.FieldType) { case PropertyType.ObjectReference: case PropertyType.ObjectReferenceShort: { int objIndex = (int)property.Value; property.Value = ResolveEntityReference(entity.FourCC, origTemplateProperty, objIndex, kvp.Key); } break; case PropertyType.ObjectReferenceArray: { BindingList <object> indexes = (BindingList <object>)property.Value; BindingList <object> resolvedRefs = new BindingList <object>(); for (int i = 0; i < indexes.Count; i++) { var obj = ResolveEntityReference(entity.FourCC, origTemplateProperty, (int)indexes[i], kvp.Key); resolvedRefs.Add(obj); } property.Value = resolvedRefs; } break; } } } } }
private object ResolveEntityReference(string askingChunkFourCC, DataDescriptorField templateProperty, int index, Scene scene) { switch (templateProperty.ReferenceType) { case ReferenceTypes.Room: // Some things will specify a Room index of 255 for "This isn't Used", so we're going to special-case handle that. if (index == 0xFF) { return(null); } if (index < m_map.Rooms.Count) { return(m_map.Rooms[index]); } else { WLog.Warning(LogCategory.EntityLoading, null, "Chunk {0} requested reference for room but index is out of range. (Property Name: {1}, Index: {2})", askingChunkFourCC, templateProperty.FieldName, index); } return(null); case ReferenceTypes.FourCC: // Get an (ordered) list of all chunks of that type. List <RawMapEntity> potentialRefs = new List <RawMapEntity>(); foreach (var entity in m_entityData[scene]) { if (entity.FourCC == templateProperty.ReferenceFourCCType) { potentialRefs.Add(entity); } } // There's an edge-case here where some maps omit an entity (such as Fairy01 not having a Virt chunk) but use index 0 (Fairy01's Pale chunk) // and so it was finding no potentialRefs if (index < potentialRefs.Count) { return(potentialRefs[index]); } else { WLog.Warning(LogCategory.EntityLoading, null, "Chunk {0} requested reference for property {1} but index ({2}) is out of range.", askingChunkFourCC, templateProperty.FieldName, index); } return(null); } return(null); }
/// <summary> /// This function is used to convert from a flat-file of J3DFileResource.InfoNodes into a treeview-type version of SceneNode. /// </summary> /// <param name="parent"></param> /// <param name="allNodes"></param> /// <param name="currentListIndex"></param> /// <returns></returns> private static int ConvertInfoHiearchyToSceneGraph(ref SceneNode parent, List <InfoNode> allNodes, int currentListIndex) { for (int i = currentListIndex; i < allNodes.Count; i++) { InfoNode curNode = allNodes[i]; SceneNode newNode = new SceneNode(); switch (curNode.Type) { case HierarchyDataTypes.NewNode: // Increase the depth of the hierarchy by creating a new node and then processing all of the next nodes as its children. // This function is recursive and will return the integer value of how many nodes it processed, this allows us to skip // the list forward that many now that they've been handled. newNode.Type = HierarchyDataTypes.NewNode; newNode.Value = curNode.Value; i += ConvertInfoHiearchyToSceneGraph(ref newNode, allNodes, i + 1); parent.Children.Add(newNode); break; case HierarchyDataTypes.EndNode: // Alternatively, if it's a EndNode, that's our signal to go up a level. We return the number of nodes that were processed between the last NewNode // and this EndNode at this depth in the hierarchy. return(i - currentListIndex + 1); case HierarchyDataTypes.Material: case HierarchyDataTypes.Joint: case HierarchyDataTypes.Batch: case HierarchyDataTypes.Finish: // If it's any of the above we simply create a node for them. We create and pull from a different InfoNode because // Hitting a NewNode can modify the value of i so curNode is now no longer valid. InfoNode thisNode = allNodes[i]; newNode.Type = thisNode.Type; newNode.Value = thisNode.Value; parent.Children.Add(newNode); break; default: WLog.Warning(LogCategory.ModelLoading, null, "Unsupported HierarchyDataType \"{0}\" in model!", curNode.Type); break; } } return(0); }
public bool CompileSource(string code, ShaderType type) { // Generate a new shader and clean up the old shader with a warning if they forgot to link before // trying to compile again. int shaderAddress = -1; switch (type) { case ShaderType.FragmentShader: if (m_fragmentAddress >= 0) { WLog.Warning(LogCategory.ShaderCompiler, this, "Shader \"{0}\" called CompileSource for ShaderType: {1} twice before linking! Disposing old shader.", Name, type); GL.DeleteShader(m_fragmentAddress); } m_fragmentAddress = GL.CreateShader(type); shaderAddress = m_fragmentAddress; break; case ShaderType.VertexShader: if (m_vertexAddress >= 0) { WLog.Warning(LogCategory.ShaderCompiler, this, "Shader \"{0}\" called CompileSource for ShaderType: {1} twice before linking! Disposing old shader.", Name, type); GL.DeleteShader(m_fragmentAddress); } m_vertexAddress = GL.CreateShader(type); shaderAddress = m_vertexAddress; break; } GL.ShaderSource(shaderAddress, code); GL.CompileShader(shaderAddress); //GL.AttachShader(program, address); int compileStatus; GL.GetShader(shaderAddress, ShaderParameter.CompileStatus, out compileStatus); if (compileStatus != 1) { WLog.Warning(LogCategory.ShaderCompiler, this, "Failed to compile shader {0}. Log:\n{1}", Name, GL.GetShaderInfoLog(shaderAddress)); return(false); } return(true); }
private static string GetAlphaOpString(GXTevOp op, GXTevBias bias, GXTevScale scale, bool clamp, byte outputRegIndex, string[] alphaInputs) { string channelSelect = ".a"; string dest = m_tevOutputRegs[outputRegIndex] + channelSelect; StringBuilder sb = new StringBuilder(); switch (op) { case GXTevOp.Add: case GXTevOp.Sub: { // out_color = (d + lerp(a, b, c)); - Add // out_color = (d - lerp(a, b, c)); - Sub string compareOp = (op == GXTevOp.Add) ? "+" : "-"; sb.AppendLine(string.Format("{0} = ({1} {5} mix({2}, {3}, {4}));", dest, alphaInputs[3], alphaInputs[0], alphaInputs[1], alphaInputs[2], compareOp)); sb.AppendLine(GetModString(outputRegIndex, bias, scale, clamp, true)); } break; case GXTevOp.Comp_A8_EQ: case GXTevOp.Comp_A8_GT: { // out_color = (d + ((a.a > b.a) ? c : 0)) string compareOp = (op == GXTevOp.Comp_R8_GT) ? ">" : "=="; sb.AppendLine(string.Format("{0} = ({1} + (({2} {5} {3}) ? {4} : 0))", dest, alphaInputs[3], alphaInputs[0], alphaInputs[1], alphaInputs[2], compareOp)); } break; default: WLog.Warning(LogCategory.TEVShaderGenerator, null, "Unsupported op in GetAlphaOpString: {0}", op); sb.AppendLine("// Invalid Alpha op for TEV broke here."); break; } if (op == GXTevOp.Comp_A8_GT || op == GXTevOp.Comp_A8_EQ) { // if(bias != 3 || scale != 1 || clamp != 1) // warn unexpected bias/scale/etc } return(sb.ToString()); }
private static string GetCompareString(GXCompareType compare, string a, byte refVal) { string outStr = ""; float fRef = refVal / 255f; if (compare != GXCompareType.Always) { WLog.Warning(LogCategory.TEVShaderGenerator, null, "Untested alpha-test functionality: {0}", compare); } switch (compare) { case GXCompareType.Never: outStr = "false"; break; case GXCompareType.Less: outStr = "<"; break; case GXCompareType.Equal: outStr = "=="; break; case GXCompareType.LEqual: outStr = "<="; break; case GXCompareType.Greater: outStr = ">"; break; case GXCompareType.NEqual: outStr = "!="; break; case GXCompareType.GEqual: outStr = ">="; break; case GXCompareType.Always: outStr = "true"; break; default: WLog.Warning(LogCategory.TEVShaderGenerator, null, "Invalid comparison function, defaulting to always."); outStr = "true"; break; } if (outStr == "false" || outStr == "true") { return(outStr); } return(string.Format("{0} {1} {2}", a, outStr, fRef)); }
private static string GetColorInString(GXCombineColorInput inputType, GXKonstColorSel konst, TevOrder texMapping) { switch (inputType) { case GXCombineColorInput.ColorPrev: return(m_tevOutputRegs[0] + ".rgb"); case GXCombineColorInput.AlphaPrev: return(m_tevOutputRegs[0] + ".aaa"); case GXCombineColorInput.C0: return(m_tevOutputRegs[1] + ".rgb"); case GXCombineColorInput.A0: return(m_tevOutputRegs[1] + ".aaa"); case GXCombineColorInput.C1: return(m_tevOutputRegs[2] + ".rgb"); case GXCombineColorInput.A1: return(m_tevOutputRegs[2] + ".aaa"); case GXCombineColorInput.C2: return(m_tevOutputRegs[3] + ".rgb"); case GXCombineColorInput.A2: return(m_tevOutputRegs[3] + ".aaa"); case GXCombineColorInput.TexColor: return(GetTexTapString(texMapping) + ".rgb"); case GXCombineColorInput.TexAlpha: return(GetTexTapString(texMapping) + ".aaa"); case GXCombineColorInput.RasColor: return(GetVertColorString(texMapping) + ".rgb"); case GXCombineColorInput.RasAlpha: return(GetVertColorString(texMapping) + ".aaa"); case GXCombineColorInput.One: return("1.0f.rrr"); case GXCombineColorInput.Half: return("0.5f.rrr"); case GXCombineColorInput.Konst: return(GetKonstColorString(konst) + ".rgb"); case GXCombineColorInput.Zero: return("0.0f.rrr"); default: WLog.Warning(LogCategory.TEVShaderGenerator, null, "Unknown Color Input type: {0}", inputType); return("0.0f.rrr"); } }
public void SetProperty(string propertyName, object value) { Property prop = null; for (int i = 0; i < Properties.Count; i++) { if (string.Compare(propertyName, Properties[i].Name, StringComparison.InvariantCultureIgnoreCase) == 0) { prop = Properties[i]; break; } } if (prop != null) { prop.Value = value; return; } WLog.Warning(LogCategory.EditorCore, this, "Tried to set Property {0} on object {1}, but no property found!", propertyName, this); }
internal VertexAttribPointerType GetAttributePointerType(ShaderAttributeIds attribute) { switch (attribute) { case ShaderAttributeIds.Position: case ShaderAttributeIds.Normal: case ShaderAttributeIds.Color0: case ShaderAttributeIds.Color1: case ShaderAttributeIds.Tex0: case ShaderAttributeIds.Tex1: case ShaderAttributeIds.Tex2: case ShaderAttributeIds.Tex3: case ShaderAttributeIds.Tex4: case ShaderAttributeIds.Tex5: case ShaderAttributeIds.Tex6: case ShaderAttributeIds.Tex7: return(VertexAttribPointerType.Float); default: WLog.Warning(LogCategory.Rendering, this, "Unsupported ShaderAttributeId: {0}", attribute); return(VertexAttribPointerType.Float); } }
public Map CreateFromDirectory(WWorld world, EditorCore editorCore, string folderPath) { if (world == null) { throw new ArgumentNullException("world", "No world to load map into specified."); } if (string.IsNullOrEmpty(folderPath)) { throw new ArgumentException("folderPath is null or empty!"); } if (!System.IO.Directory.Exists(folderPath)) { throw new System.IO.DirectoryNotFoundException("folderPath not found, ensure the directory exists first!"); } // Calculate the Map Name from the folderPath - it should be the last segment of the folder path.s System.IO.DirectoryInfo rootFolderInfo = new System.IO.DirectoryInfo(folderPath); string mapName = rootFolderInfo.Name; // Sort the directories in rootFolderInfo into natural order, instead of alphabetical order which solves issues // where room indexes were getting remapped to the wrong one. IEnumerable <System.IO.DirectoryInfo> subFolders = rootFolderInfo.GetDirectories().OrderByNatural(x => x.Name); IEnumerable <System.IO.FileInfo> subFiles = rootFolderInfo.GetFiles().OrderByNatural(x => x.Name); // Maps are stored in two distinct parts. A Stage which encompasses global data for all rooms, and then // one or more rooms. We're going to load both the room and stage into ZArchives and then load the data // stored in them into different data. var archiveFolderMap = new Dictionary <string, VirtualFilesystemDirectory>(); foreach (var dirInfo in subFolders) { VirtualFilesystemDirectory archive = null; string folderName = dirInfo.Name; if (folderName.ToLower().StartsWith("stage")) { archive = new VirtualFilesystemDirectory(folderName); if (archiveFolderMap.ContainsKey("stage")) { WLog.Warning(LogCategory.EditorCore, null, "{0} contains more than one stage archive, ignoring second...", folderPath); continue; } } else if (folderName.ToLower().StartsWith("room")) { archive = new VirtualFilesystemDirectory(folderName); } // sea has LOD folders which don't have the right sub-folder setup, boo. This skips them for now, // maybe later we can add an ArchiveType.LOD. if (archive == null) { continue; } // Fill the archives with their contents. archive.ImportFromDisk(dirInfo.FullName); archiveFolderMap[folderName.ToLower()] = archive; } // We're also going to try and process the files inside the folder to see if they're archives. foreach (var fileInfo in subFiles) { VirtualFilesystemDirectory archive = WArchiveTools.ArcUtilities.LoadArchive(fileInfo.FullName); // File wasn't a valid RARC archive. if (archive == null) { continue; } if (archive.Name.ToLower().StartsWith("stage")) { if (archiveFolderMap.ContainsKey("stage")) { WLog.Warning(LogCategory.EditorCore, null, "{0} contains more than one stage archive, ignoring second...", folderPath); continue; } } string arcName = System.IO.Path.GetFileNameWithoutExtension(fileInfo.FullName).ToLower(); archiveFolderMap[arcName] = archive; } Map newMap = new Map(); newMap.Name = mapName; newMap.ProjectFilePath = System.IO.Path.GetDirectoryName(folderPath); var sceneMap = CreateScenesFromArchives(newMap, archiveFolderMap); LoadEntities(newMap, editorCore, sceneMap, world); LoadModels(sceneMap); return(newMap); }
private static void LoadSHP1SectionFromFile(MeshVertexAttributeHolder vertexData, Mesh j3dMesh, EndianBinaryReader reader, long chunkStart) { short batchCount = reader.ReadInt16(); short padding = reader.ReadInt16(); int batchOffset = reader.ReadInt32(); int unknownTableOffset = reader.ReadInt32(); // Another one of those 0->(n-1) counters. I think all sections have it? Might be part of the way they used inheritance to write files. int alwaysZero = reader.ReadInt32(); Trace.Assert(alwaysZero == 0); int attributeOffset = reader.ReadInt32(); int matrixTableOffset = reader.ReadInt32(); int primitiveDataOffset = reader.ReadInt32(); int matrixDataOffset = reader.ReadInt32(); int packetLocationOffset = reader.ReadInt32(); // Batches can have different attributes (ie: some have pos, some have normal, some have texcoords, etc.) they're split by batches, // where everything in the batch uses the same set of vertex attributes. Each batch then has several packets, which are a collection // of primitives. for (int b = 0; b < batchCount; b++) { MeshBatch meshBatch = new MeshBatch(); j3dMesh.SubMeshes.Add(meshBatch); int overallVertexCount = 0; meshBatch.PrimitveType = OpenTK.Graphics.OpenGL.PrimitiveType.TriangleStrip; // HackHack, this varies per primitive. // We need to look on each primitive and convert them to trianglestrips, most are TS some are TF's. // We re-use the list struct here to dynamically add paired pos/col/tex as we load them // then we convert them into arrays for the MeshBatch afterwards. MeshVertexAttributeHolder meshVertexData = new MeshVertexAttributeHolder(); // chunkStart + batchOffset gets you the position where the batches are listed // 0x28 * b gives you the right batch - a batch is 0x28 in length reader.BaseStream.Position = chunkStart + batchOffset + (0x28 * b); long batchStart = reader.BaseStream.Position; byte matrixType = reader.ReadByte(); Trace.Assert(reader.ReadByte() == 0xFF); // Padding ushort packetCount = reader.ReadUInt16(); ushort batchAttributeOffset = reader.ReadUInt16(); ushort firstMatrixIndex = reader.ReadUInt16(); ushort firstPacketIndex = reader.ReadUInt16(); ushort unknownpadding = reader.ReadUInt16(); Trace.Assert(unknownpadding == 0xFFFF); float boundingSphereDiameter = reader.ReadSingle(); Vector3 boundingBoxMin = new Vector3(); boundingBoxMin.X = reader.ReadSingle(); boundingBoxMin.Y = reader.ReadSingle(); boundingBoxMin.Z = reader.ReadSingle(); Vector3 boundingBoxMax = new Vector3(); boundingBoxMax.X = reader.ReadSingle(); boundingBoxMax.Y = reader.ReadSingle(); boundingBoxMax.Z = reader.ReadSingle(); // We need to figure out how many primitive attributes there are in the SHP1 section. This can differ from the number of // attributes in the VTX1 section, as the SHP1 can also include things like PositionMatrixIndex, so the count can be different. // This also varies *per batch* as not all batches will have the things like PositionMatrixIndex. reader.BaseStream.Position = chunkStart + attributeOffset + batchAttributeOffset; var batchAttributes = new List <ShapeAttribute>(); do { ShapeAttribute attribute = new ShapeAttribute(); attribute.ArrayType = (VertexArrayType)reader.ReadInt32(); attribute.DataType = (VertexDataType)reader.ReadInt32(); if (attribute.ArrayType == VertexArrayType.NullAttr) { break; } batchAttributes.Add(attribute); } while (true); for (ushort p = 0; p < packetCount; p++) { // Packet Location reader.BaseStream.Position = chunkStart + packetLocationOffset; reader.BaseStream.Position += (firstPacketIndex + p) * 0x8; // A Packet Location is 0x8 long, so we skip ahead to the right one. int packetSize = reader.ReadInt32(); int packetOffset = reader.ReadInt32(); // Read the matrix data for this packet reader.BaseStream.Position = chunkStart + matrixDataOffset + (firstMatrixIndex + p) * 0x08; ushort matrixUnknown0 = reader.ReadUInt16(); ushort matrixCount = reader.ReadUInt16(); uint matrixFirstIndex = reader.ReadUInt32(); // Skip ahead to the actual data. reader.BaseStream.Position = chunkStart + matrixTableOffset + (matrixFirstIndex * 0x2); List <ushort> matrixTable = new List <ushort>(); for (int m = 0; m < matrixCount; m++) { matrixTable.Add(reader.ReadUInt16()); } // Jump the read head to the location of the primitives for this packet. reader.BaseStream.Position = chunkStart + primitiveDataOffset + packetOffset; int numVertexesAtPacketStart = meshVertexData.PositionMatrixIndexes.Count; uint numPrimitiveBytesRead = 0; while (numPrimitiveBytesRead < packetSize) { // Jump to the primitives // Primitives GXPrimitiveType type = (GXPrimitiveType)reader.ReadByte(); // Game pads the chunks out with zeros, so this is the signal for an early break; if (type == 0 || numPrimitiveBytesRead >= packetSize) { break; } ushort vertexCount = reader.ReadUInt16(); meshBatch.PrimitveType = type == GXPrimitiveType.TriangleStrip ? OpenTK.Graphics.OpenGL.PrimitiveType.TriangleStrip : OpenTK.Graphics.OpenGL.PrimitiveType.TriangleFan; //if (type != GXPrimitiveType.TriangleStrip) //{ // WLog.Warning(LogCategory.ModelLoading, null, "Unsupported GXPrimitiveType {0}", type); //} numPrimitiveBytesRead += 0x3; // Advance us by 3 for the Primitive header. for (int v = 0; v < vertexCount; v++) { meshVertexData.Indexes.Add(overallVertexCount); overallVertexCount++; // Iterate through the attribute types. I think the actual vertices are stored in interleaved format, // ie: there's say 13 vertexes but those 13 vertexes will have a pos/color/tex index listed after it // depending on the overall attributes of the file. for (int attrib = 0; attrib < batchAttributes.Count; attrib++) { // Jump to primitive location //reader.BaseStream.Position = chunkStart + primitiveDataOffset + numPrimitiveBytesRead + packetOffset; // Now that we know how big the vertex type is stored in (either a Signed8 or a Signed16) we can read that much data // and then we can use that index and index into int val = 0; uint numBytesRead = 0; switch (batchAttributes[attrib].DataType) { case VertexDataType.Signed8: val = reader.ReadByte(); numBytesRead = 1; break; case VertexDataType.Signed16: val = reader.ReadInt16(); numBytesRead = 2; break; default: WLog.Warning(LogCategory.ModelLoading, null, "Unknown Batch Index Type: {0}", batchAttributes[attrib].DataType); break; } // Now that we know what the index is, we can retrieve it from the appropriate array // and stick it into our vertex. The J3D format removes all duplicate vertex attributes // so we need to re-duplicate them here so that we can feed them to a PC GPU in a normal fashion. switch (batchAttributes[attrib].ArrayType) { case VertexArrayType.Position: meshVertexData.Position.Add(vertexData.Position[val]); break; case VertexArrayType.PositionMatrixIndex: meshVertexData.PositionMatrixIndexes.Add(val); break; case VertexArrayType.Normal: meshVertexData.Normal.Add(vertexData.Normal[val]); break; case VertexArrayType.Color0: meshVertexData.Color0.Add(vertexData.Color0[val]); break; case VertexArrayType.Color1: meshVertexData.Color1.Add(vertexData.Color1[val]); break; case VertexArrayType.Tex0: meshVertexData.Tex0.Add(vertexData.Tex0[val]); break; case VertexArrayType.Tex1: meshVertexData.Tex1.Add(vertexData.Tex1[val]); break; case VertexArrayType.Tex2: meshVertexData.Tex2.Add(vertexData.Tex2[val]); break; case VertexArrayType.Tex3: meshVertexData.Tex3.Add(vertexData.Tex3[val]); break; case VertexArrayType.Tex4: meshVertexData.Tex4.Add(vertexData.Tex4[val]); break; case VertexArrayType.Tex5: meshVertexData.Tex5.Add(vertexData.Tex5[val]); break; case VertexArrayType.Tex6: meshVertexData.Tex6.Add(vertexData.Tex6[val]); break; case VertexArrayType.Tex7: meshVertexData.Tex7.Add(vertexData.Tex7[val]); break; default: WLog.Warning(LogCategory.ModelLoading, null, "Unsupported attribType {0}", batchAttributes[attrib].ArrayType); break; } numPrimitiveBytesRead += numBytesRead; } // Gonna try a weird hack, where if the mesh doesn't have PMI values, we're going to use just use the packet index into the matrixtable // so that all meshes always have PMI values, to abstract out the ones that don't seem to (but still have matrixtable) junk. It's a guess // here. if (batchAttributes.Find(x => x.ArrayType == VertexArrayType.PositionMatrixIndex) == null) { meshVertexData.PositionMatrixIndexes.Add(p); } } // After we write a primitive, write a special null-terminator which signifies the GPU to do a primitive restart for the next tri-strip. meshVertexData.Indexes.Add(0xFFFF); } // The Matrix Table is per-packet, so we need to reach into the the matrix table after processing each packet // and transform the indexes. Yuck. Yay. for (int j = numVertexesAtPacketStart; j < meshVertexData.PositionMatrixIndexes.Count; j++) { // Yes you divide this by 3. No, no one knows why. $20 to the person who figures out why. meshBatch.drawIndexes.Add(matrixTable[meshVertexData.PositionMatrixIndexes[j] / 3]); } } meshBatch.Vertices = meshVertexData.Position.ToArray(); meshBatch.Color0 = meshVertexData.Color0.ToArray(); meshBatch.Color1 = meshVertexData.Color1.ToArray(); meshBatch.TexCoord0 = meshVertexData.Tex0.ToArray(); meshBatch.TexCoord1 = meshVertexData.Tex0.ToArray(); meshBatch.TexCoord2 = meshVertexData.Tex0.ToArray(); meshBatch.TexCoord3 = meshVertexData.Tex0.ToArray(); meshBatch.TexCoord4 = meshVertexData.Tex0.ToArray(); meshBatch.TexCoord5 = meshVertexData.Tex0.ToArray(); meshBatch.TexCoord6 = meshVertexData.Tex0.ToArray(); meshBatch.TexCoord7 = meshVertexData.Tex0.ToArray(); meshBatch.Indexes = meshVertexData.Indexes.ToArray(); meshBatch.PositionMatrixIndexs = meshVertexData.PositionMatrixIndexes; // This should be obsolete as they should be transformed already. } }
public Mesh LoadFromStream(EndianBinaryReader reader) { MeshVertexAttributeHolder vertexData = null; SceneNode rootNode = new SceneNode(); List <Texture2D> textureList = new List <Texture2D>(); List <WEditor.Common.Nintendo.J3D.Material> materialList = null; List <SkeletonBone> joints = new List <SkeletonBone>(); DrawInfo drawInfo = null; Envelopes envelopes = null; Mesh j3dMesh = new Mesh(); // Read the Header int magic = reader.ReadInt32(); // J3D1, J3D2, etc if (magic != 1244873778) { WLog.Warning(LogCategory.ModelLoading, null, "Attempted to load model with invalid magic, ignoring!"); return(null); } int j3dType = reader.ReadInt32(); // BMD3 (models) BDL4 (models), jpa1 (particles), bck1 (animations), etc. int totalFileSize = reader.ReadInt32(); int chunkCount = reader.ReadInt32(); // Skip over an unused tag (consistent in all files) and some padding. reader.ReadBytes(16); for (int i = 0; i < chunkCount; i++) { long chunkStart = reader.BaseStream.Position; string tagName = reader.ReadString(4); int chunkSize = reader.ReadInt32(); switch (tagName) { // INFO - Vertex Count, Scene Hierarchy case "INF1": rootNode = LoadINF1FromFile(rootNode, reader, chunkStart); break; // VERTEX - Stores vertex arrays for pos/normal/color0/tex0 etc. Contains VertexAttributes which describe // how this data is stored/laid out. case "VTX1": vertexData = LoadVTX1FromFile(reader, chunkStart, chunkSize); break; // ENVELOPES - Defines vertex weights for skinning. case "EVP1": envelopes = LoadEVP1FromStream(reader, chunkStart); break; // DRAW (Skeletal Animation Data) - Stores which matrices are weighted, and which are used directly. case "DRW1": drawInfo = LoadDRW1FromStream(reader, chunkStart); break; // JOINTS - Stores the skeletal joints (position, rotation, scale, etc.) case "JNT1": joints = LoadJNT1SectionFromStream(reader, chunkStart); break; // SHAPE - Face/Triangle information for model. case "SHP1": LoadSHP1SectionFromFile(vertexData, j3dMesh, reader, chunkStart); break; // MATERIAL - Stores materials (which describes how textures, etc. are drawn) case "MAT3": materialList = LoadMAT3SectionFromStream(reader, chunkStart, chunkSize); break; // TEXTURES - Stores binary texture images. case "TEX1": textureList = LoadTEX1FromFile(reader, chunkStart); break; // MODEL - Seems to be bypass commands for Materials and invokes GX registers directly. case "MDL3": break; } reader.BaseStream.Position = chunkStart + chunkSize; } // Resolve the texture indexes into actual textures now that we've loaded the TEX1 section. foreach (Material mat in materialList) { for (int i = 0; i < mat.TextureIndexes.Length; i++) { short index = mat.TextureIndexes[i]; if (index < 0) { continue; } mat.Textures[i] = textureList[index]; } } // loltests for (int i = 0; i < materialList.Count; i++) { materialList[i].VtxDesc = j3dMesh.SubMeshes[0].GetVertexDescription(); Shader shader = TEVShaderGenerator.GenerateShader(materialList[i]); materialList[i].Shader = shader; } // We're going to do something a little crazy - we're going to read the scene view and apply textures to meshes (for now) Material curMat = null; AssignMaterialsToMeshRecursive(rootNode, j3dMesh, ref curMat, materialList); List <SkeletonBone> skeleton = new List <SkeletonBone>(); BuildSkeletonRecursive(rootNode, skeleton, joints, 0); j3dMesh.Skeleton = skeleton; j3dMesh.BindPoses = envelopes.inverseBindPose; // Let's do some ugly post-processing here to see if we can't resolve all of the cross-references and turn it into // a normal computer-readable format that we can digest in our RenderSytem. { for (int i = 0; i < j3dMesh.SubMeshes.Count; i++) { MeshBatch batch = j3dMesh.SubMeshes[i]; batch.BoneWeights = new BoneWeight[batch.Vertices.Length]; for (int j = 0; j < batch.PositionMatrixIndexs.Count; j++) { // Okay so this is where it gets more complicated. The PMI gives us an index into the MatrixTable for the packet, which we // resolve and call "drawIndexes" - however we have to divide the number they give us by three for some reason, so that is // already done and now our drawIndexes array should be one-index-for-every-vertex-in-batch and it should be the index into // the draw section we need. ushort drw1Index = batch.drawIndexes[j]; // The drw1Index can be set as 0xFFFF - if so, this means that you need to use the dr1Index of the previous one. // until it is no longer 0xFFFF. int counter = 0; while (drw1Index == 0xFFFF) { drw1Index = batch.drawIndexes[j - counter]; counter++; } bool isWeighted = drawInfo.IsWeighted[drw1Index]; BoneWeight weight = new BoneWeight(); if (isWeighted) { // Something on this doesn't work for models that actually specify a PositionMatrixIndex. // So... some math is off somewhere and I don't know where for the moment. ushort numBonesAffecting = envelopes.numBonesAffecting[drw1Index]; weight.BoneIndexes = new ushort[numBonesAffecting]; weight.BoneWeights = new float[numBonesAffecting]; // "Much WTFs" ushort offset = 0; for (ushort e = 0; e < envelopes.indexRemap[drw1Index]; e++) { offset += envelopes.numBonesAffecting[e]; } offset *= 2; Matrix4 finalTransform = Matrix4.Identity; for (ushort k = 0; k < numBonesAffecting; k++) { ushort boneIndex = envelopes.indexRemap[offset + (k * 0x2)]; float boneWeight = envelopes.weights[(offset / 2) + k]; weight.BoneIndexes[k] = boneIndex; weight.BoneWeights[k] = boneWeight; // This was apaprently a partial thought I never finished or got working in the old one? :S } } else { // If the vertex isn't weighted, we just use the position from the bone matrix. SkeletonBone joint = skeleton[drawInfo.Indexes[drw1Index]]; Matrix4 translation = Matrix4.CreateTranslation(joint.Translation); Matrix4 rotation = Matrix4.CreateFromQuaternion(joint.Rotation); Matrix4 finalMatrix = rotation * translation; // Move the mesh by transforming the position by this much. // I think we can just assign full weight to the first bone index and call it good. weight.BoneIndexes = new[] { drawInfo.Indexes[drw1Index] }; weight.BoneWeights = new[] { 1f }; } batch.BoneWeights[j] = weight; } } } return(j3dMesh); }
private static List <T> LoadVertexAttribute <T>(EndianBinaryReader reader, int totalAttributeDataLength, byte decimalPoint, VertexArrayType arrayType, VertexDataType dataType, VertexColorType colorType) where T : new() { int componentCount = 0; switch (arrayType) { case VertexArrayType.Position: case VertexArrayType.Normal: componentCount = 3; break; case VertexArrayType.Color0: case VertexArrayType.Color1: componentCount = 4; break; case VertexArrayType.Tex0: case VertexArrayType.Tex1: case VertexArrayType.Tex2: case VertexArrayType.Tex3: case VertexArrayType.Tex4: case VertexArrayType.Tex5: case VertexArrayType.Tex6: case VertexArrayType.Tex7: componentCount = 2; break; default: WLog.Warning(LogCategory.ModelLoading, null, "Unsupported ArrayType \"{0}\" found while loading VTX1!", arrayType); break; } // We need to know the length of each 'vertex' (which can vary based on how many attributes and what types there are) int vertexSize = 0; switch (dataType) { case VertexDataType.Float32: vertexSize = componentCount * 4; break; case VertexDataType.Unsigned16: case VertexDataType.Signed16: vertexSize = componentCount * 2; break; case VertexDataType.Signed8: case VertexDataType.Unsigned8: vertexSize = componentCount * 1; break; case VertexDataType.None: break; default: WLog.Warning(LogCategory.ModelLoading, null, "Unsupported DataType \"{0}\" found while loading VTX1!", dataType); break; } switch (colorType) { case VertexColorType.RGB8: vertexSize = 3; break; case VertexColorType.RGBX8: case VertexColorType.RGBA8: vertexSize = 4; break; case VertexColorType.None: break; case VertexColorType.RGB565: case VertexColorType.RGBA4: case VertexColorType.RGBA6: default: WLog.Warning(LogCategory.ModelLoading, null, "Unsupported Color Data Type: {0}!", colorType); break; } int sectionSize = totalAttributeDataLength / vertexSize; List <T> values = new List <T>(sectionSize); float scaleFactor = (float)Math.Pow(0.5, decimalPoint); for (int v = 0; v < sectionSize; v++) { // Create a default version of the object and then fill it up depending on our component count and its data type... dynamic value = new T(); for (int i = 0; i < componentCount; i++) { switch (dataType) { case VertexDataType.Float32: value[i] = reader.ReadSingle() * scaleFactor; break; case VertexDataType.Unsigned16: value[i] = (float)reader.ReadUInt16() * scaleFactor; break; case VertexDataType.Signed16: value[i] = (float)reader.ReadInt16() * scaleFactor; break; case VertexDataType.Unsigned8: value[i] = (float)reader.ReadByte() * scaleFactor; break; case VertexDataType.Signed8: value[i] = (float)reader.ReadSByte() * scaleFactor; break; case VertexDataType.None: // Let the next switch statement get it. break; default: WLog.Warning(LogCategory.ModelLoading, null, "Unsupported Data Type: {0}!", dataType); break; } switch (colorType) { case VertexColorType.RGBX8: case VertexColorType.RGB8: case VertexColorType.RGBA8: value[i] = reader.ReadByte() / 255f; break; case VertexColorType.None: break; case VertexColorType.RGB565: case VertexColorType.RGBA4: case VertexColorType.RGBA6: default: WLog.Warning(LogCategory.ModelLoading, null, "Unsupported Color Data Type: {0}!", colorType); break; } } values.Add(value); } return(values); }
public bool LinkShader() { if (m_programAddress >= 0) { WLog.Warning(LogCategory.ShaderCompiler, this, "Shader \"{0}\" called LinkShader for already linked shader! Disposing old program.", Name); GL.DeleteProgram(m_programAddress); } if (m_fragmentAddress < 0 || m_vertexAddress < 0) { throw new Exception("Shader does not have both a Vertex and Fragment shader!"); } // Initialize a program and link the already compiled shaders m_programAddress = GL.CreateProgram(); GL.AttachShader(m_programAddress, m_vertexAddress); GL.AttachShader(m_programAddress, m_fragmentAddress); // Bind our Attribute locations before we link the program. GL.BindAttribLocation(m_programAddress, (int)ShaderAttributeIds.Position, "RawPosition"); GL.BindAttribLocation(m_programAddress, (int)ShaderAttributeIds.Normal, "RawNormal"); GL.BindAttribLocation(m_programAddress, (int)ShaderAttributeIds.Color0, "RawColor0"); GL.BindAttribLocation(m_programAddress, (int)ShaderAttributeIds.Color1, "RawColor1"); GL.BindAttribLocation(m_programAddress, (int)ShaderAttributeIds.Tex0, "RawTex0"); GL.BindAttribLocation(m_programAddress, (int)ShaderAttributeIds.Tex1, "RawTex1"); GL.BindAttribLocation(m_programAddress, (int)ShaderAttributeIds.Tex2, "RawTex2"); GL.BindAttribLocation(m_programAddress, (int)ShaderAttributeIds.Tex3, "RawTex3"); GL.BindAttribLocation(m_programAddress, (int)ShaderAttributeIds.Tex4, "RawTex4"); GL.BindAttribLocation(m_programAddress, (int)ShaderAttributeIds.Tex5, "RawTex5"); GL.BindAttribLocation(m_programAddress, (int)ShaderAttributeIds.Tex6, "RawTex6"); GL.BindAttribLocation(m_programAddress, (int)ShaderAttributeIds.Tex7, "RawTex7"); GL.LinkProgram(m_programAddress); int linkStatus; GL.GetProgram(m_programAddress, GetProgramParameterName.LinkStatus, out linkStatus); if (linkStatus != 1) { WLog.Warning(LogCategory.ShaderCompiler, this, "Error linking shader. Result: {0}", GL.GetProgramInfoLog(m_programAddress)); return(false); } // Now that the program is linked, bind to our uniform locations. UniformModelMtx = GL.GetUniformLocation(m_programAddress, "ModelMtx"); UniformViewMtx = GL.GetUniformLocation(m_programAddress, "ViewMtx"); UniformProjMtx = GL.GetUniformLocation(m_programAddress, "ProjMtx"); UniformTexMtx = GL.GetUniformLocation(m_programAddress, "TexMtx"); UniformPostMtx = GL.GetUniformLocation(m_programAddress, "PostMtx"); UniformColor0Amb = GL.GetUniformLocation(m_programAddress, "COLOR0_Amb"); UniformColor0Mat = GL.GetUniformLocation(m_programAddress, "COLOR0_Mat"); UniformColor1Amb = GL.GetUniformLocation(m_programAddress, "COLOR1_Amb"); UniformColor1Mat = GL.GetUniformLocation(m_programAddress, "COLOR1_Mat"); UniformLightBlock = GL.GetUniformLocation(m_programAddress, "Lights"); UniformNumLights = GL.GetUniformLocation(m_programAddress, "NumLights"); // Now that we've (presumably) set both a vertex and a fragment shader and linked them to the program, // we're going to clean up the reference to the shaders as the Program now keeps its own reference. GL.DeleteShader(m_vertexAddress); GL.DeleteShader(m_fragmentAddress); m_vertexAddress = -1; m_fragmentAddress = -1; return(true); }
public static bool GenerateVertexShader(Shader shader, Material mat) { StringBuilder stream = new StringBuilder(); // Shader Header stream.AppendLine("#version 330 core"); stream.AppendLine(); // Input Format stream.AppendLine("// Input"); if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Position)) { stream.AppendLine("in vec3 RawPosition;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Normal)) { stream.AppendLine("in vec3 RawNormal;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Color0)) { stream.AppendLine("in vec4 RawColor0;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Color1)) { stream.AppendLine("in vec4 RawColor1;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex0)) { stream.AppendLine("in vec2 RawTex0;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex1)) { stream.AppendLine("in vec2 RawTex1;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex2)) { stream.AppendLine("in vec2 RawTex2;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex3)) { stream.AppendLine("in vec2 RawTex3;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex4)) { stream.AppendLine("in vec2 RawTex4;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex5)) { stream.AppendLine("in vec2 RawTex5;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex6)) { stream.AppendLine("in vec2 RawTex6;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Tex7)) { stream.AppendLine("in vec2 RawTex7;"); } stream.AppendLine(); // Output Format stream.AppendLine("// Output"); if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Normal)) { stream.AppendLine("out vec3 Normal;"); } for (int i = 0; i < mat.NumChannelControls; i++) { stream.AppendLine(string.Format("out vec4 Color{0};", i)); } for (int texGen = 0; texGen < mat.NumTexGens; texGen++) { if (mat.TexGenInfos[texGen] != null) { stream.AppendLine(string.Format("out vec3 Tex{0};", texGen)); } } // Uniforms stream.AppendLine(); stream.AppendLine("// Uniforms"); stream.AppendLine( " uniform mat4 ModelMtx;\n" + " uniform mat4 ViewMtx;\n" + " uniform mat4 ProjMtx;\n" + "\n" + " uniform mat4 TexMtx[10];\n" + " uniform mat4 PostMtx[20];\n" + " uniform vec4 COLOR0_Amb;\n" + " uniform vec4 COLOR0_Mat;\n" + " uniform vec4 COLOR1_Amb;\n" + " uniform vec4 COLOR1_Mat;\n" + "\n" + "struct GXLight\n" + "{\n" + " vec4 Position;\n" + " vec4 Direction;\n" + " vec4 Color;\n" + " vec4 DistAtten;\n" + " vec4 AngleAtten;\n" + "};\n" + "\n" + " GXLight Lights[8];\n" + "\n" + "uniform int NumLights;\n" + "uniform vec4 ambLightColor;\n"); // Main Shader Code stream.AppendLine("// Main"); stream.AppendLine("void main()"); stream.AppendLine("{"); stream.AppendLine(" mat4 MVP = ProjMtx * ViewMtx * ModelMtx;"); stream.AppendLine(" mat4 MV = ViewMtx * ModelMtx;"); if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Position)) { stream.AppendLine(" gl_Position = MVP * vec4(RawPosition, 1);"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Normal)) { stream.AppendLine(" Normal = normalize(RawNormal.xyz * inverse(transpose(mat3(MV))));"); } //if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Color0)) // stream.AppendLine(" Color0 = RawColor0;"); //if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Color1)) //stream.AppendLine(" Color1 = RawColor1;"); stream.AppendLine(); stream.AppendLine(" // Ambient Colors & Material Colors"); // Add the Ambient Colors for the Material for (int a = 0; a < mat.AmbientColors.Length; a++) { stream.AppendLine(string.Format(" vec4 ambColor{0} = vec4({1}, {2}, {3}, {4});", a, mat.AmbientColors[a].R, mat.AmbientColors[a].G, mat.AmbientColors[a].B, mat.AmbientColors[a].A)); } // Add in the Material Colors for (int m = 0; m < mat.MaterialColors.Length; m++) { stream.AppendLine(string.Format(" vec4 matColor{0} = vec4({1}, {2}, {3}, {4});", m, mat.MaterialColors[m].R, mat.MaterialColors[m].G, mat.MaterialColors[m].B, mat.MaterialColors[m].A)); } stream.AppendLine(); stream.AppendLine(string.Format(" // ChanCtrl's - {0} count", mat.NumChannelControls)); // Channel Controllers // A vertex can have up to two color channels (RGBA each) which gives us four possible channels: // color0, color1, alpha0, alpha1 // Each channel has an associated ambient color/alpha and a material color/alpha. These can come // from vertex colors or existing amb/mat registers. for (int chanSel = 0; chanSel < mat.NumChannelControls; chanSel++) { ChanCtrl chanInfo = mat.ChannelControls[chanSel]; string chanTarget, ambColor, matColor, ambLight, diffLight; string swizzle, chan; bool alpha; // Todo: Is this really a fixed order? switch (chanSel) { case /* Color0 */ 0: chan = "0"; swizzle = ".rgb"; alpha = false; break; case /* Alpha0 */ 1: chan = "0"; swizzle = ".a"; alpha = true; break; case /* Color1 */ 2: chan = "1"; swizzle = ".rgb"; alpha = false; break; case /* Alpha1 */ 3: chan = "1"; swizzle = ".a"; alpha = true; break; default: WLog.Warning(LogCategory.TEVShaderGenerator, shader, "Unknown vertex output color channel {0}, skipping.", chanSel); continue; } chanTarget = string.Format("Color{0}{1}", chan, swizzle); ambColor = (chanInfo.AmbientSrc == GXColorSrc.Vertex ? "RawColor" : "ambColor") + chan + swizzle; matColor = (chanInfo.MaterialSrc == GXColorSrc.Vertex ? "RawColor" : "matColor") + chan + swizzle; ambLight = "ambLightColor" + swizzle; diffLight = GetLightCalcString(chanInfo, alpha); //Color{0}.rgb = ambient * ambLightColor * light stream.AppendLine(string.Format(" Color{0} = vec4(1, 1, 1, 1);", chan)); if (chanInfo.Enable) { stream.AppendLine(string.Format(" {0} = {1} * {2} + {3} * {4};", chanTarget, ambColor, ambLight, matColor, diffLight)); } else { stream.AppendLine(string.Format(" {0} = {1};", chanTarget, matColor)); } stream.AppendLine(); stream.AppendLine(); } // Texture Coordinate Generation stream.AppendLine(string.Format(" // TexGen - {0} count", mat.NumTexGens)); for (int i = 0; i < mat.NumTexGens; i++) { if (mat.TexGenInfos[i] == null) { continue; } TexCoordGen texGen = mat.TexGenInfos[i]; string texGenSrc; switch (texGen.Source) { case GXTexGenSrc.Position: texGenSrc = "RawPosition"; break; case GXTexGenSrc.Normal: texGenSrc = "RawNormal"; break; case GXTexGenSrc.Color0: texGenSrc = "Color0"; break; case GXTexGenSrc.Color1: texGenSrc = "Color1"; break; case GXTexGenSrc.Tex0: texGenSrc = "RawTex0"; break; // Should Tex0 be TEXTURE 0? Or is it TEX0 = Input TEX0, while TEXCOORD0 = Output TEX0? case GXTexGenSrc.Tex1: texGenSrc = "RawTex1"; break; case GXTexGenSrc.Tex2: texGenSrc = "RawTex2"; break; case GXTexGenSrc.Tex3: texGenSrc = "RawTex3"; break; case GXTexGenSrc.Tex4: texGenSrc = "RawTex4"; break; case GXTexGenSrc.Tex5: texGenSrc = "RawTex5"; break; case GXTexGenSrc.Tex6: texGenSrc = "RawTex6"; break; case GXTexGenSrc.Tex7: texGenSrc = "RawTex7"; break; case GXTexGenSrc.TexCoord0: texGenSrc = "Tex0"; break; case GXTexGenSrc.TexCoord1: texGenSrc = "Tex1"; break; case GXTexGenSrc.TexCoord2: texGenSrc = "Tex2"; break; case GXTexGenSrc.TexCoord3: texGenSrc = "Tex3"; break; case GXTexGenSrc.TexCoord4: texGenSrc = "Tex4"; break; case GXTexGenSrc.TexCoord5: texGenSrc = "Tex5"; break; case GXTexGenSrc.TexCoord6: texGenSrc = "Tex6"; break; case GXTexGenSrc.Tangent: case GXTexGenSrc.Binormal: default: WLog.Warning(LogCategory.TEVShaderGenerator, shader, "Unsupported TexGenSrc: {0}, defaulting to TEXCOORD0.", texGen.Source); texGenSrc = "Tex0"; break; } if (texGen.TexMatrixSource == GXTexMatrix.Identity) { switch (texGen.Type) { case GXTexGenType.Matrix2x4: stream.AppendLine(string.Format(" Tex{0} = vec3({1}.xy, 0);", i, texGenSrc)); break; case GXTexGenType.Matrix3x4: stream.AppendLine(string.Format(" float3 uvw = {0}.xyz;", texGenSrc)); stream.AppendLine(string.Format(" Tex{0} = vec3((uvw / uvw.z).xy,0);", i)); break; case GXTexGenType.SRTG: stream.AppendLine(string.Format(" Tex{0} = vec3({1}.rg, 0);", i, texGenSrc)); break; case GXTexGenType.Bump0: case GXTexGenType.Bump1: case GXTexGenType.Bump2: case GXTexGenType.Bump3: case GXTexGenType.Bump4: case GXTexGenType.Bump5: case GXTexGenType.Bump6: case GXTexGenType.Bump7: default: WLog.Warning(LogCategory.TEVShaderGenerator, shader, "Unsupported TexMatrixSource: {0}, Defaulting to Matrix2x4", texGen.TexMatrixSource); stream.AppendLine(string.Format(" Tex{0} = vec3({1}.xy, 0);", i, texGenSrc)); break; } } else { // Convert to TexMtx0 to TexMtx9 int matIndex = ((int)texGen.TexMatrixSource - 30) / 3; switch (texGen.Type) { default: WLog.Warning(LogCategory.TEVShaderGenerator, shader, "Unsupported TexMatrixSource"); break; } } } stream.AppendLine("}"); stream.AppendLine(); // Compile the Vertex Shader and return whether it compiled sucesfully or not. Directory.CreateDirectory("ShaderDump"); System.IO.File.WriteAllText("ShaderDump/" + mat.Name + "_vert_output", stream.ToString()); return(shader.CompileSource(stream.ToString(), OpenTK.Graphics.OpenGL.ShaderType.VertexShader)); }
private static bool GenerateFragmentShader(Shader shader, Material mat) { StringBuilder stream = new StringBuilder(); // Shader Header stream.AppendLine("#version 330 core"); stream.AppendLine(); // Configure inputs to match our outputs from VS if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Position)) { stream.AppendLine("in vec3 Position;"); } if (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Normal)) { stream.AppendLine("in vec3 Normal;"); } for (int i = 0; i < mat.NumChannelControls; i++) { stream.AppendLine(string.Format("in vec4 Color{0};", i)); } for (int texGen = 0; texGen < mat.NumTexGens; texGen++) { stream.AppendLine(string.Format("in vec3 Tex{0};", texGen)); } stream.AppendLine(); // Final Output stream.AppendLine("// Final Output"); stream.AppendLine("out vec4 PixelColor;"); // Texture Inputs for (int i = 0; i < 8; i++) { if (mat.Textures[i] == null) { continue; } stream.AppendLine(string.Format("uniform sampler2D Texture{0};", i)); } // Main Function stream.AppendLine("void main()"); stream.AppendLine("{"); // Default initial values of the TEV registers. // ToDo: Does this need swizzling? themikelester has it marked as mat.registerColor[i==0?3:i-1]] stream.AppendLine(" // Initial TEV Register Values"); for (int i = 0; i < 4; i++) { stream.AppendLine(string.Format(" vec4 {0} = vec4({1}, {2}, {3}, {4});", m_tevOutputRegs[i], mat.TevColor[i].R, mat.TevColor[i].G, mat.TevColor[i].B, mat.TevColor[i].A)); } stream.AppendLine(); // Constant Color Registers stream.AppendLine(" // Konst TEV Colors"); for (int i = 0; i < 4; i++) { stream.AppendLine(string.Format(" vec4 konst{0} = vec4({1}, {2}, {3}, {4});", i, mat.TevKonstColors[i].R, mat.TevKonstColors[i].G, mat.TevKonstColors[i].B, mat.TevKonstColors[i].A)); } stream.AppendLine(); // Texture Samples bool[] oldCombos = new bool[256]; for (int i = 0; i < mat.NumTevStages; i++) { TevOrder order = mat.TevOrderInfos[i]; int tex = order.TexMap; GXTexCoordSlot coord = order.TexCoordId; // This TEV probably doesn't use textures. if (tex == 0xFF || coord == GXTexCoordSlot.Null) { continue; } if (IsNewTexCombo(tex, (int)coord, oldCombos)) { string swizzle = ""; // Uhh I don't know if we need to swizzle since everyone's been converted into ARGB stream.AppendLine(string.Format(" vec4 texCol{0} = texture(Texture{0}, Tex{1}.xy){2};", tex, (int)coord, swizzle)); } } stream.AppendLine(); // ToDo: Implement indirect texturing. stream.AppendLine(" // TEV Stages"); stream.AppendLine(); stream.AppendLine(); for (int i = 0; i < mat.NumTevStages; i++) { stream.AppendLine(string.Format(" // TEV Stage {0}", i)); TevOrder order = mat.TevOrderInfos[i]; TevStage stage = mat.TevStageInfos[i]; TevSwapMode swap = mat.TevSwapModes[i]; TevSwapModeTable rasTable = mat.TevSwapModeTables[swap.RasSel]; TevSwapModeTable texTable = mat.TevSwapModeTables[swap.TexSel]; // There's swapping involved in the ras table. stream.AppendLine(string.Format(" // Rasterization Swap Table: {0}", rasTable)); if (!(rasTable.R == 0 && rasTable.G == 1 && rasTable.B == 2 && rasTable.A == 3)) { stream.AppendLine(string.Format(" {0} = {1}{2};", GetVertColorString(order), GetVertColorString(order), GetSwapModeSwizzleString(rasTable))); } stream.AppendLine(); // There's swapping involved in the texture table. stream.AppendLine(string.Format(" // Texture Swap Table: {0}", texTable)); if (!(texTable.R == 0 && texTable.G == 1 && texTable.B == 2 && texTable.A == 3)) { stream.AppendLine(string.Format(" {0} = {1}{2};", GetTexTapString(order), GetTexTapString(order), GetSwapModeSwizzleString(rasTable))); } stream.AppendLine(); string[] colorInputs = new string[4]; colorInputs[0] = GetColorInString(stage.ColorIn[0], mat.KonstColorSels[i], order); colorInputs[1] = GetColorInString(stage.ColorIn[1], mat.KonstColorSels[i], order); colorInputs[2] = GetColorInString(stage.ColorIn[2], mat.KonstColorSels[i], order); colorInputs[3] = GetColorInString(stage.ColorIn[3], mat.KonstColorSels[i], order); stream.AppendLine(" // Color and Alpha Operations"); stream.AppendLine(string.Format(" {0}", GetColorOpString(stage.ColorOp, stage.ColorBias, stage.ColorScale, stage.ColorClamp, stage.ColorRegId, colorInputs))); string[] alphaInputs = new string[4]; alphaInputs[0] = GetAlphaInString(stage.AlphaIn[0], mat.KonstAlphaSels[i], order); alphaInputs[1] = GetAlphaInString(stage.AlphaIn[1], mat.KonstAlphaSels[i], order); alphaInputs[2] = GetAlphaInString(stage.AlphaIn[2], mat.KonstAlphaSels[i], order); alphaInputs[3] = GetAlphaInString(stage.AlphaIn[3], mat.KonstAlphaSels[i], order); stream.AppendLine(string.Format(" {0}", GetAlphaOpString(stage.AlphaOp, stage.AlphaBias, stage.AlphaScale, stage.AlphaClamp, stage.AlphaRegId, alphaInputs))); stream.AppendLine(); } stream.AppendLine(); // Alpha Compare stream.AppendLine(" // Alpha Compare Test"); AlphaCompare alphaCompare = mat.AlphaCompare; string alphaOp; switch (alphaCompare.Operation) { case GXAlphaOp.And: alphaOp = "&&"; break; case GXAlphaOp.Or: alphaOp = "||"; break; case GXAlphaOp.XOR: alphaOp = "^"; break; // Not really tested, unsupported in some examples but I don't see why. case GXAlphaOp.XNOR: alphaOp = "=="; break; // Not really tested. ^ default: WLog.Warning(LogCategory.TEVShaderGenerator, null, "Unsupported alpha compare operation: {0}", alphaCompare.Operation); alphaOp = "||"; break; } // clip(result.a < 0.5 && result a > 0.2 ? -1 : 1) string ifContents = string.Format("(!({0} {1} {2}))", GetCompareString(alphaCompare.Comp0, m_tevOutputRegs[0] + ".a", alphaCompare.Reference0), alphaOp, GetCompareString(alphaCompare.Comp1, m_tevOutputRegs[0] + ".a", alphaCompare.Reference1)); // clip equivelent stream.AppendLine(" // Alpha Compare (Clip)"); stream.AppendLine(string.Format(" if{0}\n\t\tdiscard;", ifContents)); //string output = "PixelColor = texCol0" + (mat.VtxDesc.AttributeIsEnabled(ShaderAttributeIds.Color0) ? " * Color0;" : ";"); //stream.AppendLine(output); stream.AppendLine(string.Format(" PixelColor = {0};", m_tevOutputRegs[0])); stream.AppendLine("}"); stream.AppendLine(); // Compile the Fragment Shader and return whether it compiled sucesfully or not. Directory.CreateDirectory("ShaderDump"); System.IO.File.WriteAllText("ShaderDump/" + mat.Name + "_frag_output", stream.ToString()); return(shader.CompileSource(stream.ToString(), OpenTK.Graphics.OpenGL.ShaderType.FragmentShader)); }
private static string GetColorOpString(GXTevOp op, GXTevBias bias, GXTevScale scale, bool clamp, byte outputRegIndex, string[] colorInputs) { string channelSelect = ".rgb"; string dest = m_tevOutputRegs[outputRegIndex] + channelSelect; StringBuilder sb = new StringBuilder(); switch (op) { case GXTevOp.Add: case GXTevOp.Sub: { // out_color = (d + lerp(a, b, c)); - Add // out_color = (d - lerp(a, b, c)); - Sub string compareOp = (op == GXTevOp.Add) ? "+" : "-"; sb.AppendLine(string.Format("{0} = ({1} {5} mix({2}, {3}, {4}));", dest, colorInputs[3], colorInputs[0], colorInputs[2], colorInputs[1], compareOp)); sb.AppendLine(GetModString(outputRegIndex, bias, scale, clamp, false)); } break; case GXTevOp.Comp_R8_GT: case GXTevOp.Comp_R8_EQ: { // out_color = (d + ((a.r > b.r) ? c : 0)); string compareOp = (op == GXTevOp.Comp_R8_GT) ? ">" : "=="; sb.AppendLine(string.Format("{0} = ({1} + (({2}.r {5} {3}.r) ? {4} : 0))", dest, colorInputs[3], colorInputs[0], colorInputs[1], colorInputs[2], compareOp)); } break; case GXTevOp.Comp_GR16_GT: case GXTevOp.Comp_GR16_EQ: { // out_color = (d + (dot(a.gr, rgTo16Bit) > dot(b.gr, rgTo16Bit) ? c : 0)); string compareOp = (op == GXTevOp.Comp_GR16_GT) ? ">" : "=="; string rgTo16Bit = "vec2(255.0/65535.6, 255.0 * 256.0/65535.0)"; sb.AppendLine(string.Format("{0} = ({1} + (dot({2}.gr, {3}) {4} dot({5}.gr, {3}) ? {6} : 0));", dest, colorInputs[3], colorInputs[0], rgTo16Bit, compareOp, colorInputs[1], colorInputs[2])); } break; case GXTevOp.Comp_BGR24_GT: case GXTevOp.Comp_BGR24_EQ: { // out_color = (d + (dot(a.bgr, bgrTo24Bit) > dot(b.bgr, bgrTo24Bit) ? c : 0)); string compareOp = (op == GXTevOp.Comp_BGR24_GT) ? ">" : "=="; string bgrTo24Bit = "vec3(255.0/16777215.0, 255.0 * 256.0/16777215.0, 255.0*65536.0/16777215.0)"; sb.AppendLine(string.Format("{0} = ({1} + (dot({2}.bgr, {5}) {6} dot({3}.bgr, {5}) ? {4} : 0));", dest, colorInputs[3], colorInputs[0], colorInputs[1], colorInputs[2], bgrTo24Bit, compareOp)); } break; case GXTevOp.Comp_RGB8_GT: case GXTevOp.Comp_RGB8_EQ: { // out_color.r = d.r + ((a.r > b.r) ? c.r : 0); // out_color.g = d.g + ((a.g > b.g) ? c.g : 0); // out_color.b = d.b + ((a.b > b.b) ? c.b : 0); string compareOp = (op == GXTevOp.Comp_RGB8_GT) ? ">" : "=="; string format = "{0}.{6} = {1}.{6} + (({2}.{6} {5} {3}.{6}) ? {4}.{6} : 0);"; sb.AppendLine(string.Format(format, dest, colorInputs[3], colorInputs[0], colorInputs[1], colorInputs[2], compareOp, "r")); sb.AppendLine(string.Format(format, dest, colorInputs[3], colorInputs[0], colorInputs[1], colorInputs[2], compareOp, "g")); sb.AppendLine(string.Format(format, dest, colorInputs[3], colorInputs[0], colorInputs[1], colorInputs[2], compareOp, "b")); } break; default: WLog.Warning(LogCategory.TEVShaderGenerator, null, "Unsupported Color Op: {0}!", op); sb.AppendLine("// Invalid Color op for TEV broke here."); break; } if (op > GXTevOp.Sub) { //if(bias != 3 || scale != 0 || clamp != 1) // warn(unexpected bias, scale, clamp)...? } return(sb.ToString()); }