/// <summary> /// Loads a ac file from the given uri /// </summary> private static ACFileInfo LoadFile(Stream inStream) { ACFileInfo result = null; StreamReader reader = null; try { reader = new StreamReader(inStream); //Check for correct header string header = reader.ReadLine(); if (!header.StartsWith("AC3D")) { throw new SeeingSharpGraphicsException("Header of AC3D file not found!"); } //Create file information object result = new ACFileInfo(); //Create a loaded objects stack Stack <ACObjectInfo> loadedObjects = new Stack <ACObjectInfo>(); Stack <ACObjectInfo> parentObjects = new Stack <ACObjectInfo>(); ACSurface currentSurface = null; //Read the file while (!reader.EndOfStream) { string actLine = reader.ReadLine().Trim(); string firstWord = string.Empty; int spaceIndex = actLine.IndexOf(' '); if (spaceIndex == -1) { firstWord = actLine; } else { firstWord = firstWord = actLine.Substring(0, spaceIndex); } switch (firstWord) { //New Material info case "MATERIAL": ACMaterialInfo materialInfo = new ACMaterialInfo(); { //Get the name of the material string[] materialData = actLine.Split(' '); if (materialData.Length > 1) { materialInfo.Name = materialData[1].Trim(' ', '"'); } //Parse components for (int loop = 0; loop < materialData.Length; loop++) { switch (materialData[loop]) { case "rgb": Color4 diffuseColor = materialInfo.Diffuse; diffuseColor.Alpha = 1f; diffuseColor.Red = Single.Parse(materialData[loop + 1], CultureInfo.InvariantCulture); diffuseColor.Green = Single.Parse(materialData[loop + 2], CultureInfo.InvariantCulture); diffuseColor.Blue = Single.Parse(materialData[loop + 3], CultureInfo.InvariantCulture); materialInfo.Diffuse = diffuseColor; break; case "amb": Color4 ambientColor = new Color4(); ambientColor.Red = Single.Parse(materialData[loop + 1], CultureInfo.InvariantCulture); ambientColor.Green = Single.Parse(materialData[loop + 2], CultureInfo.InvariantCulture); ambientColor.Blue = Single.Parse(materialData[loop + 3], CultureInfo.InvariantCulture); materialInfo.Ambient = ambientColor; break; case "emis": Color4 emissiveColor = new Color4(); emissiveColor.Red = Single.Parse(materialData[loop + 1], CultureInfo.InvariantCulture); emissiveColor.Green = Single.Parse(materialData[loop + 2], CultureInfo.InvariantCulture); emissiveColor.Blue = Single.Parse(materialData[loop + 3], CultureInfo.InvariantCulture); materialInfo.Emissive = emissiveColor; break; case "spec": Color4 specularColor = new Color4(); specularColor.Red = Single.Parse(materialData[loop + 1], CultureInfo.InvariantCulture); specularColor.Green = Single.Parse(materialData[loop + 2], CultureInfo.InvariantCulture); specularColor.Blue = Single.Parse(materialData[loop + 3], CultureInfo.InvariantCulture); materialInfo.Specular = specularColor; break; case "shi": materialInfo.Shininess = Single.Parse(materialData[loop + 1], CultureInfo.InvariantCulture); break; case "trans": diffuseColor = materialInfo.Diffuse; diffuseColor.Alpha = 1f - EngineMath.Clamp(Single.Parse(materialData[loop + 1], CultureInfo.InvariantCulture), 0f, 1f); materialInfo.Diffuse = diffuseColor; break; } } result.Materials.Add(materialInfo); } break; //New object starts here case "OBJECT": { ACObjectInfo newObject = new ACObjectInfo(); string[] lineData = actLine.Split(' '); if (lineData[1] == "poly") { newObject.Type = ACObjectType.Poly; } else if (lineData[1] == "group") { newObject.Type = ACObjectType.Group; } else if (lineData[1] == "world") { newObject.Type = ACObjectType.World; } loadedObjects.Push(newObject); } break; //End of an object, kids following case "kids": if (loadedObjects.Count == 0) { break; } { //Parse kid count int kidCount = 0; string[] lineData = actLine.Split(' '); if ((lineData != null) && (lineData.Length >= 1)) { Int32.TryParse(lineData[1], out kidCount); } ACObjectInfo currentObject = loadedObjects.Peek(); if (currentObject != null) { //Add object to parent object, if any related bool addedToParent = false; if (parentObjects.Count > 0) { ACObjectInfo currentParent = parentObjects.Peek(); if (currentParent.Childs.Count < currentParent.KidCount) { currentParent.Childs.Add(currentObject); addedToParent = true; } else { while (parentObjects.Count > 0) { parentObjects.Pop(); if (parentObjects.Count == 0) { break; } currentParent = parentObjects.Peek(); if (currentParent == null) { break; } if (currentParent.Childs.Count < currentParent.KidCount) { break; } } if ((currentParent != null) && (currentParent.Childs.Count < currentParent.KidCount)) { currentParent.Childs.Add(currentObject); addedToParent = true; } } } //Enable this object as parent object currentObject.KidCount = kidCount; if (currentObject.KidCount > 0) { parentObjects.Push(currentObject); } //Add to scene root if this object has no parent loadedObjects.Pop(); if (!addedToParent) { if (loadedObjects.Count == 0) { result.Objects.Add(currentObject); } else { loadedObjects.Peek().Childs.Add(currentObject); } } currentObject = null; } } break; //Current object's name case "name": if (loadedObjects.Count == 0) { break; } { ACObjectInfo currentObject = loadedObjects.Peek(); if (currentObject != null) { currentObject.Name = actLine.Replace("name ", "").Replace("\"", ""); } } break; case "data": break; case "texture": if (loadedObjects.Count == 0) { break; } { ACObjectInfo currentObject = loadedObjects.Peek(); if (currentObject != null) { string[] lineData = actLine.Split(' '); currentObject.Texture = lineData[1].Trim('"'); } } break; case "texrep": if (loadedObjects.Count == 0) { break; } { ACObjectInfo currentObject = loadedObjects.Peek(); if (currentObject != null) { string[] lineData = actLine.Split(' '); Vector2 repetition = new Vector2(); repetition.X = Single.Parse(lineData[1], CultureInfo.InvariantCulture); repetition.Y = Single.Parse(lineData[2], CultureInfo.InvariantCulture); currentObject.TextureRepeat = repetition; } } break; case "texoff": if (loadedObjects.Count == 0) { break; } { ACObjectInfo currentObject = loadedObjects.Peek(); if (currentObject != null) { string[] lineData = actLine.Split(' '); Vector2 offset = new Vector2(); offset.X = Single.Parse(lineData[1], CultureInfo.InvariantCulture); offset.Y = Single.Parse(lineData[2], CultureInfo.InvariantCulture); currentObject.TextureRepeat = offset; } } break; case "rot": if (loadedObjects.Count == 0) { break; } { ACObjectInfo currentObject = loadedObjects.Peek(); if (currentObject != null) { string[] lineData = actLine.Split(' '); Matrix4x4 rotation = Matrix4x4.Identity; rotation.M11 = !string.IsNullOrEmpty(lineData[1]) ? Single.Parse(lineData[1], CultureInfo.InvariantCulture) : 0f; rotation.M12 = !string.IsNullOrEmpty(lineData[2]) ? Single.Parse(lineData[2], CultureInfo.InvariantCulture) : 0f; rotation.M13 = !string.IsNullOrEmpty(lineData[3]) ? Single.Parse(lineData[3], CultureInfo.InvariantCulture) : 0f; rotation.M21 = !string.IsNullOrEmpty(lineData[4]) ? Single.Parse(lineData[4], CultureInfo.InvariantCulture) : 0f; rotation.M22 = !string.IsNullOrEmpty(lineData[5]) ? Single.Parse(lineData[5], CultureInfo.InvariantCulture) : 0f; rotation.M23 = !string.IsNullOrEmpty(lineData[6]) ? Single.Parse(lineData[6], CultureInfo.InvariantCulture) : 0f; rotation.M31 = !string.IsNullOrEmpty(lineData[7]) ? Single.Parse(lineData[7], CultureInfo.InvariantCulture) : 0f; rotation.M32 = !string.IsNullOrEmpty(lineData[8]) ? Single.Parse(lineData[8], CultureInfo.InvariantCulture) : 0f; rotation.M33 = !string.IsNullOrEmpty(lineData[9]) ? Single.Parse(lineData[9], CultureInfo.InvariantCulture) : 0f; currentObject.Rotation = rotation; } } break; case "url": if (loadedObjects.Count == 0) { break; } { ACObjectInfo currentObject = loadedObjects.Peek(); if (currentObject != null) { string[] lineData = actLine.Split(' '); currentObject.Url = lineData[1].Trim('"'); } } break; //Current object's location case "loc": if (loadedObjects.Count == 0) { break; } { ACObjectInfo currentObject = loadedObjects.Peek(); if (currentObject != null) { string[] lineData = actLine.Split(' '); Vector3 location = new Vector3(); location.X = Single.Parse(lineData[1], CultureInfo.InvariantCulture); location.Y = Single.Parse(lineData[2], CultureInfo.InvariantCulture); location.Z = Single.Parse(lineData[3], CultureInfo.InvariantCulture); currentObject.Translation = location; } } break; case "numvert": if (loadedObjects.Count == 0) { break; } { ACObjectInfo currentObject = loadedObjects.Peek(); if (currentObject != null) { string[] lineData = actLine.Split(' '); int numberOfVertices = Int32.Parse(lineData[1], CultureInfo.InvariantCulture); for (int loop = 0; loop < numberOfVertices; loop++) { string actInnerLine = reader.ReadLine().Trim(); string[] splittedVertex = actInnerLine.Split(' '); Vector3 position = new Vector3(); position.X = Single.Parse(splittedVertex[0], CultureInfo.InvariantCulture); position.Y = Single.Parse(splittedVertex[1], CultureInfo.InvariantCulture); position.Z = Single.Parse(splittedVertex[2], CultureInfo.InvariantCulture); currentObject.Vertices.Add(new ACVertex() { Position = position }); } } } break; //Start of a list of surfaces case "numsurf": break; //New surface starts here case "SURF": { if (currentSurface == null) { currentSurface = new ACSurface(); } string[] lineData = actLine.Split(' '); lineData[1] = lineData[1].Substring(2); currentSurface.Flags = Int32.Parse(lineData[1], NumberStyles.HexNumber); } break; //Current surface's material case "mat": { if (currentSurface == null) { currentSurface = new ACSurface(); } string[] lineData = actLine.Split(' '); currentSurface.Material = Int32.Parse(lineData[1], CultureInfo.InvariantCulture); } break; //Current surface's indices case "refs": if (loadedObjects.Count == 0) { break; } { if (currentSurface == null) { currentSurface = new ACSurface(); } string[] lineData = actLine.Split(' '); int numberOfRefs = Int32.Parse(lineData[1], CultureInfo.InvariantCulture); for (int loop = 0; loop < numberOfRefs; loop++) { string actInnerLine = reader.ReadLine().Trim(); string[] splittedRef = actInnerLine.Split(' '); Vector2 texCoord = new Vector2(); int vertexReference = UInt16.Parse(splittedRef[0], CultureInfo.InvariantCulture); texCoord.X = Single.Parse(splittedRef[1], CultureInfo.InvariantCulture); texCoord.Y = Single.Parse(splittedRef[2], CultureInfo.InvariantCulture); currentSurface.TextureCoordinates.Add(texCoord); currentSurface.VertexReferences.Add(vertexReference); } ACObjectInfo currentObject = loadedObjects.Peek(); if (currentObject != null) { currentObject.Surfaces.Add(currentSurface); } currentSurface = null; } break; default: break; } } } finally { if (reader != null) { reader.Dispose(); } } return(result); }
/// <summary> /// Fills the given vertex structure using information from the given AC-File-Objects. /// </summary> /// <param name="objInfo">The object information from the AC file.</param> /// <param name="acMaterials">A list containing all materials from the AC file.</param> /// <param name="structure">The VertexStructure to be filled.</param> /// <param name="transformStack">Current matrix stack (for stacked objects).</param> private static void FillVertexStructure(VertexStructure structure, List <ACMaterialInfo> acMaterials, ACObjectInfo objInfo, Matrix4Stack transformStack) { List <Tuple <int, int> > standardShadedVertices = new List <Tuple <int, int> >(); transformStack.Push(); try { // Perform local transformation for the current AC object transformStack.TransformLocal(objInfo.Rotation); transformStack.TranslateLocal(objInfo.Translation); // Build structures material by material for (int actMaterialIndex = 0; actMaterialIndex < acMaterials.Count; actMaterialIndex++) { ACMaterialInfo actMaterial = acMaterials[actMaterialIndex]; VertexStructureSurface actStructSurface = structure.CreateOrGetExistingSurface(actMaterial.CreateMaterialProperties()); bool isNewSurface = actStructSurface.CountTriangles == 0; // Create and configure vertex structure actStructSurface.Material = NamedOrGenericKey.Empty; actStructSurface.TextureKey = !string.IsNullOrEmpty(objInfo.Texture) ? new NamedOrGenericKey(objInfo.Texture) : NamedOrGenericKey.Empty; actStructSurface.MaterialProperties.DiffuseColor = actMaterial.Diffuse; actStructSurface.MaterialProperties.AmbientColor = actMaterial.Ambient; actStructSurface.MaterialProperties.EmissiveColor = actMaterial.Emissive; actStructSurface.MaterialProperties.Shininess = actMaterial.Shininess; actStructSurface.MaterialProperties.SpecularColor = actMaterial.Specular; // Initialize local index table (needed for vertex reuse) int oneSideVertexCount = objInfo.Vertices.Count; int[] localIndices = new int[oneSideVertexCount * 2]; for (int loop = 0; loop < localIndices.Length; loop++) { localIndices[loop] = int.MaxValue; } // Process all surfaces foreach (ACSurface actSurface in objInfo.Surfaces) { // Get the vertex index on which to start int startVertexIndex = structure.CountVertices; int startTriangleIndex = actStructSurface.CountTriangles; // Only handle surfaces of the current material if (actSurface.Material != actMaterialIndex) { continue; } // Sort out unsupported surfaces if (actSurface.VertexReferences.Count < 3) { continue; } if (actSurface.IsLine) { continue; } if (actSurface.IsClosedLine) { continue; } // Preprocess referenced vertices int oneSideSurfaceVertexCount = actSurface.VertexReferences.Count; int countSurfaceSides = actSurface.IsTwoSided ? 2 : 1; int[] onStructureReferencedVertices = new int[oneSideSurfaceVertexCount * countSurfaceSides]; List <int> surfaceVertexReferences = actSurface.VertexReferences; for (int loop = 0; loop < surfaceVertexReferences.Count; loop++) { Vector2 actTexCoord = actSurface.TextureCoordinates[loop]; if (!actSurface.IsFlatShaded) { // Try to reuse vertices on standard shading if (localIndices[surfaceVertexReferences[loop]] == int.MaxValue) { Vector3 position = Vector3.Transform( objInfo.Vertices[surfaceVertexReferences[loop]].Position, transformStack.Top); localIndices[surfaceVertexReferences[loop]] = structure.AddVertex(new Vertex( position, Color4.White, actTexCoord, Vector3.Zero)); if (actSurface.IsTwoSided) { localIndices[surfaceVertexReferences[loop] + oneSideVertexCount] = structure.AddVertex(new Vertex( position, Color4.White, actTexCoord, Vector3.Zero)); } } // Store vertex reference for this surface's index onStructureReferencedVertices[loop] = localIndices[surfaceVertexReferences[loop]]; if (actSurface.IsTwoSided) { onStructureReferencedVertices[loop + oneSideSurfaceVertexCount] = localIndices[surfaceVertexReferences[loop] + oneSideVertexCount]; } } else { // Create one vertex for one reference for flat shading Vector3 position = Vector3.Transform( objInfo.Vertices[surfaceVertexReferences[loop]].Position, transformStack.Top); onStructureReferencedVertices[loop] = structure.AddVertex(new Vertex( position, Color4.White, actTexCoord, Vector3.Zero)); if (actSurface.IsTwoSided) { onStructureReferencedVertices[loop + oneSideSurfaceVertexCount] = structure.AddVertex(new Vertex( position, Color4.White, actTexCoord, Vector3.Zero)); } } } // Build object geometry switch (actSurface.VertexReferences.Count) { case 3: // Front side actStructSurface.AddTriangle( onStructureReferencedVertices[0], onStructureReferencedVertices[1], onStructureReferencedVertices[2]); // Back side if (actSurface.IsTwoSided) { actStructSurface.AddTriangle( onStructureReferencedVertices[5], onStructureReferencedVertices[4], onStructureReferencedVertices[3]); } break; case 4: // Front side actStructSurface.AddTriangle( onStructureReferencedVertices[0], onStructureReferencedVertices[1], onStructureReferencedVertices[2]); actStructSurface.AddTriangle( onStructureReferencedVertices[2], onStructureReferencedVertices[3], onStructureReferencedVertices[0]); // Back side if (actSurface.IsTwoSided) { actStructSurface.AddTriangle( onStructureReferencedVertices[6], onStructureReferencedVertices[5], onStructureReferencedVertices[4]); actStructSurface.AddTriangle( onStructureReferencedVertices[4], onStructureReferencedVertices[7], onStructureReferencedVertices[6]); } break; default: if (!actSurface.IsTwoSided) { // Front side actStructSurface.AddPolygonByCuttingEars(onStructureReferencedVertices); } else { // Front and back side actStructSurface.AddPolygonByCuttingEars(onStructureReferencedVertices.Subset(0, oneSideSurfaceVertexCount)); actStructSurface.AddPolygonByCuttingEars(onStructureReferencedVertices.Subset(oneSideSurfaceVertexCount, oneSideSurfaceVertexCount)); } break; } // Perform shading if (actSurface.IsFlatShaded) { actStructSurface.CalculateNormalsFlat( startTriangleIndex, actStructSurface.CountTriangles - startTriangleIndex); } else { // Nothing to be done for now.. int vertexCount = structure.CountVertices - startVertexIndex; if (vertexCount > 0) { standardShadedVertices.Add( Tuple.Create((int)startVertexIndex, vertexCount)); } } } // Calculate default shading finally (if any) foreach (var actStandardShadedPair in standardShadedVertices) { structure.CalculateNormals( actStandardShadedPair.Item1, actStandardShadedPair.Item2); } standardShadedVertices.Clear(); // Append generated VertexStructure to the output collection if ((actStructSurface.CountTriangles <= 0) && (isNewSurface)) { structure.RemoveSurface(actStructSurface); } } //Fill in all child object data foreach (ACObjectInfo actObjInfo in objInfo.Childs) { FillVertexStructure(structure, acMaterials, actObjInfo, transformStack); } } finally { transformStack.Pop(); } }