private void LoadModels(Dictionary <Scene, VirtualFilesystemDirectory> archiveMap) { // We're going to search the archives for a specific list of meshes that the game supports and load those. List <string> supportedModelPaths = new List <string>(new string[] { "model", "model1", "model2", "model3" }); foreach (var kvp in archiveMap) { List <VirtualFilesystemFile> filesByExtension = kvp.Value.FindByExtension(".bmd", ".bdl"); foreach (var vfsFile in filesByExtension) { if (!supportedModelPaths.Contains(vfsFile.Name)) { continue; } using (EndianBinaryReader reader = new EndianBinaryReader(new System.IO.MemoryStream(vfsFile.File.GetData()), Endian.Big)) { WLog.Info(LogCategory.EntityLoading, null, "Loading {1} (3D Model) for {0}{1}...", vfsFile.Name, vfsFile.Extension); J3DLoader j3dLoader = new J3DLoader(); Mesh resultMesh = j3dLoader.LoadFromStream(reader); kvp.Key.MeshList.Add(resultMesh); WLog.Info(LogCategory.EntityLoading, null, "Finished loading {1} (3D Model) for {0}{1}.", vfsFile.Name, vfsFile.Extension); } } } }
public async Task <IActionResult> Edit(int id, [Bind("Id,WLNumber,Hours,DateTimeFrom,DateTimeTo,Subject,WLogStatusId,IncidentId,PersonId")] WLog wLog) { if (id != wLog.Id) { return(NotFound()); } if (ModelState.IsValid) { try { _context.Update(wLog); await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!WLogExists(wLog.Id)) { return(NotFound()); } else { throw; } } return(RedirectToAction(nameof(IndexSearch))); } ViewData["IncidentId"] = new SelectList(_context.Incident, "Id", "IncidentNumber", wLog.IncidentId); ViewData["PersonId"] = new SelectList(_context.Person, "Id", "FullName", wLog.PersonId); ViewData["WLogStatusId"] = new SelectList(_context.WLogStatus, "Id", "WLogStatusName", wLog.WLogStatusId); return(View(wLog)); }
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 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 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 async Task <IActionResult> Edit(int id, [Bind("Id,WLNumber,Hours,DateTimeFrom,DateTimeTo,Subject")] WLog wLog) { if (id != wLog.Id) { return(NotFound()); } if (ModelState.IsValid) { try { _context.Update(wLog); await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!WLogExists(wLog.Id)) { return(NotFound()); } else { throw; } } return(RedirectToAction(nameof(Index))); } return(View(wLog)); }
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); }
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); } }
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); } }
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); } }
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 async Task <IActionResult> Create([Bind("Id,WLNumber,Hours,DateTimeFrom,DateTimeTo,Subject")] WLog wLog) { if (ModelState.IsValid) { _context.Add(wLog); await _context.SaveChangesAsync(); return(RedirectToAction(nameof(Index))); } return(View(wLog)); }
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; } } } } }
public void LoadFromStream(Scene parentScene, EndianBinaryReader reader) { var mapEntities = new List <RawMapEntity>(); long fileOffsetStart = reader.BaseStream.Position; // File Header int chunkCount = reader.ReadInt32(); // Read the chunk headers List <ChunkHeader> chunks = new List <ChunkHeader>(); for (int i = 0; i < chunkCount; i++) { ChunkHeader chunk = new ChunkHeader(); chunk.FourCC = reader.ReadString(4); chunk.ElementCount = reader.ReadInt32(); chunk.ChunkOffset = reader.ReadInt32(); chunk.Layer = ResolveChunkFourCCToLayer(chunk.FourCC); chunk.FourCC = ResolveFourCCWithLayerToName(chunk.FourCC); chunks.Add(chunk); } // For each chunk, read all elements of that type of chunk. for (int i = 0; i < chunks.Count; i++) { ChunkHeader chunk = chunks[i]; // Find the appropriate JSON template that describes this chunk. MapEntityDataDescriptor template = m_editorCore.Templates.MapEntityDataDescriptors.Find(x => x.FourCC == chunk.FourCC); if (template == null) { WLog.Error(LogCategory.EntityLoading, null, "Unsupported entity FourCC: {0}. Map will save without this data!", chunk.FourCC); continue; } reader.BaseStream.Position = chunk.ChunkOffset; for (int k = 0; k < chunk.ElementCount; k++) { RawMapEntity entityInstance = LoadMapEntityFromStream(chunk.FourCC, reader, template); entityInstance.Layer = chunk.Layer; mapEntities.Add(entityInstance); } } m_entityData[parentScene] = mapEntities; }
private static void OnRecieveLoggerMessage(WLog.Entry message) { string messagePrefix = ""; if(message.Severity == LogSeverity.Warning) messagePrefix = "Warning: "; else if (message.Severity == LogSeverity.Error) messagePrefix = "Error: "; string categoryPrefix = ""; if (message.Category != LogCategory.None) categoryPrefix = string.Format("[{0}] - ", message.Category); Console.WriteLine("{0}{1}{2}", messagePrefix, categoryPrefix, message.Message); }
public async Task <IActionResult> Create([Bind("Id,WLNumber,Hours,DateTimeFrom,DateTimeTo,Subject,WLogStatusId,IncidentId,PersonId")] WLog wLog) { if (ModelState.IsValid) { _context.Add(wLog); await _context.SaveChangesAsync(); return(RedirectToAction(nameof(IndexSearch))); } ViewData["IncidentId"] = new SelectList(_context.Incident, "Id", "IncidentNumber", wLog.IncidentId); ViewData["PersonId"] = new SelectList(_context.Person, "Id", "FullName", wLog.PersonId); ViewData["WLogStatusId"] = new SelectList(_context.WLogStatus, "Id", "WLogStatusName", wLog.WLogStatusId); return(View(wLog)); }
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); }
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); }
/// <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); }
private void OnRecieveMessage(WLog.Entry message) { // Append Warning: or Error: to the messages if they have the right severity. string messagePrefix = ""; if (message.Severity == LogSeverity.Warning) messagePrefix = "Warning: "; else if (message.Severity == LogSeverity.Error) messagePrefix = "Error: "; string categoryPrefix = ""; if (message.Category != LogCategory.None) categoryPrefix = string.Format("[{0}] - ", message.Category); string finalMessage = string.Format("{0}{1}{2}", messagePrefix, categoryPrefix, message.Message); m_messageLog.AppendLine(finalMessage); OnPropertyChanged("Messages"); }
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); } }
private void LoadEntities(Map newMap, EditorCore core, Dictionary <Scene, VirtualFilesystemDirectory> archiveMap, WWorld world) { MapEntityLoader entityLoader = new MapEntityLoader(core, newMap); // For each room/scene, find the associated dzr/dzs file and load its // contents into the entityLoader. foreach (var kvp in archiveMap) { // Check to see if this Archive has stage/room entity data. var roomEntData = kvp.Value.FindByExtension(".dzr"); var stageEntData = kvp.Value.FindByExtension(".dzs"); VirtualFilesystemFile vfsFile = null; if (roomEntData.Count > 0) { vfsFile = roomEntData[0]; } else if (stageEntData.Count > 0) { vfsFile = stageEntData[0]; } else { continue; } using (EndianBinaryReader reader = new EndianBinaryReader(new System.IO.MemoryStream(vfsFile.File.GetData()), Endian.Big)) { WLog.Info(LogCategory.EntityLoading, null, "Loading .dzr/.dzs (Room/Stage Entity Dat) for {0}{1}...", vfsFile.Name, vfsFile.Extension); entityLoader.LoadFromStream(kvp.Key, reader); WLog.Info(LogCategory.EntityLoading, null, "Finished loading .dzr/.dzs (Room/Stage Entity Dat) for {0}{1}.", vfsFile.Name, vfsFile.Extension); } } // Once we've loaded all of the entities from the stream, we're going to // post-process them to resolve object references. entityLoader.PostProcessEntities(); // Finally, we can actually convert these into map objects foreach (var roomOrStageData in entityLoader.GetData()) { Stage stage = roomOrStageData.Key as Stage; Room room = roomOrStageData.Key as Room; if (stage != null) { PostProcessStage(stage, roomOrStageData.Value); foreach (var entity in stage.Entities) { entity.World = world; } } if (room != null) { PostProcessRoom(room, roomOrStageData.Value); foreach (var entity in room.Entities) { entity.World = world; } } } }
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)); }
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 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()); }
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 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. } }