/* * Copyright (c) Contributors * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSim Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Drawing; using System.Drawing.Imaging; namespace PrimMesher { public class SculptMesh { public List coords; public List faces; public List viewerFaces; public List normals; public List uvs; public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 }; private const float pixScale = 0.00390625f; // 1.0 / 256 private Bitmap ScaleImage(Bitmap srcImage, float scale) { int sourceWidth = srcImage.Width; int sourceHeight = srcImage.Height; int sourceX = 0; int sourceY = 0; int destX = 0; int destY = 0; int destWidth = (int)(srcImage.Width * scale); int destHeight = (int)(srcImage.Height * scale); if (srcImage.PixelFormat == PixelFormat.Format32bppArgb) for (int y = 0; y < srcImage.Height; y++) for (int x = 0; x < srcImage.Width; x++) { Color c = srcImage.GetPixel(x, y); srcImage.SetPixel(x, y, Color.FromArgb(255, c.R, c.G, c.B)); } Bitmap scaledImage = new Bitmap(destWidth, destHeight, PixelFormat.Format24bppRgb); scaledImage.SetResolution(srcImage.HorizontalResolution, srcImage.VerticalResolution); Graphics grPhoto = Graphics.FromImage(scaledImage); grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear; grPhoto.DrawImage(srcImage, new Rectangle(destX, destY, destWidth, destHeight), new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight), GraphicsUnit.Pixel); grPhoto.Dispose(); return scaledImage; } public SculptMesh SculptMeshFromFile(string fileName, SculptType sculptType, int lod, bool viewerMode) { Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); SculptMesh sculptMesh = new SculptMesh(bitmap, sculptType, lod, viewerMode); bitmap.Dispose(); return sculptMesh; } /// /// ** Experimental ** May disappear from future versions ** not recommeneded for use in applications /// Construct a sculpt mesh from a 2D array of floats /// /// /// /// /// /// /// public SculptMesh(float[,] zMap, float xBegin, float xEnd, float yBegin, float yEnd, bool viewerMode) { float xStep, yStep; float uStep, vStep; int numYElements = zMap.GetLength(0); int numXElements = zMap.GetLength(1); try { xStep = (xEnd - xBegin) / (float)(numXElements - 1); yStep = (yEnd - yBegin) / (float)(numYElements - 1); uStep = 1.0f / (numXElements - 1); vStep = 1.0f / (numYElements - 1); } catch (DivideByZeroException) { return; } coords = new List(); faces = new List(); normals = new List(); uvs = new List(); viewerFaces = new List(); int p1, p2, p3, p4; int x, y; int xStart = 0, yStart = 0; for (y = yStart; y < numYElements; y++) { int rowOffset = y * numXElements; for (x = xStart; x < numXElements; x++) { /* * p1-----p2 * | \ f2 | * | \ | * | f1 \| * p3-----p4 */ p4 = rowOffset + x; p3 = p4 - 1; p2 = p4 - numXElements; p1 = p3 - numXElements; Coord c = new Coord(xBegin + x * xStep, yBegin + y * yStep, zMap[y, x]); this.coords.Add(c); if (viewerMode) { this.normals.Add(new Coord()); this.uvs.Add(new UVCoord(uStep * x, 1.0f - vStep * y)); } if (y > 0 && x > 0) { Face f1, f2; //if (viewerMode) //{ // f1 = new Face(p1, p3, p4, p1, p3, p4); // f1.uv1 = p1; // f1.uv2 = p3; // f1.uv3 = p4; // f2 = new Face(p1, p4, p2, p1, p4, p2); // f2.uv1 = p1; // f2.uv2 = p4; // f2.uv3 = p2; //} //else //{ // f1 = new Face(p1, p3, p4); // f2 = new Face(p1, p4, p2); //} if (viewerMode) { f1 = new Face(p1, p4, p3, p1, p4, p3); f1.uv1 = p1; f1.uv2 = p4; f1.uv3 = p3; f2 = new Face(p1, p2, p4, p1, p2, p4); f2.uv1 = p1; f2.uv2 = p2; f2.uv3 = p4; } else { f1 = new Face(p1, p4, p3); f2 = new Face(p1, p2, p4); } this.faces.Add(f1); this.faces.Add(f2); } } } if (viewerMode) calcVertexNormals(SculptType.plane, numXElements, numYElements); } public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode) { coords = new List(); faces = new List(); normals = new List(); uvs = new List(); //float sourceScaleFactor = (float)lod / (float)Math.Max(sculptBitmap.Width, sculptBitmap.Height); float sourceScaleFactor = (float)(lod) / (float)Math.Sqrt(sculptBitmap.Width * sculptBitmap.Height); bool scaleSourceImage = sourceScaleFactor < 1.0f ? true : false; Bitmap bitmap; if (scaleSourceImage) bitmap = ScaleImage(sculptBitmap, sourceScaleFactor); else bitmap = sculptBitmap; viewerFaces = new List(); int width = bitmap.Width; int height = bitmap.Height; float widthUnit = 1.0f / width; float heightUnit = 1.0f / (height - 1); int p1, p2, p3, p4; Color color; float x, y, z; int imageX, imageY; if (sculptType == SculptType.sphere) { // average the top and bottom row pixel values so the resulting vertices appear to converge int lastRow = height - 1; int r1 = 0, g1 = 0, b1 = 0; int r2 = 0, g2 = 0, b2 = 0; for (imageX = 0; imageX < width; imageX++) { Color c1 = bitmap.GetPixel(imageX, 0); Color c2 = bitmap.GetPixel(imageX, lastRow); r1 += c1.R; g1 += c1.G; b1 += c1.B; r2 += c2.R; g2 += c2.G; b2 += c2.B; } Color newC1 = Color.FromArgb(r1 / width, g1 / width, b1 / width); Color newC2 = Color.FromArgb(r2 / width, g2 / width, b2 / width); for (imageX = 0; imageX < width; imageX++) { bitmap.SetPixel(imageX, 0, newC1); bitmap.SetPixel(imageX, lastRow, newC2); } } int pixelsAcross = sculptType == SculptType.plane ? width : width + 1; int pixelsDown = sculptType == SculptType.sphere || sculptType == SculptType.cylinder ? height + 1 : height; for (imageY = 0; imageY < pixelsDown; imageY++) { int rowOffset = imageY * width; for (imageX = 0; imageX < pixelsAcross; imageX++) { /* * p1-----p2 * | \ f2 | * | \ | * | f1 \| * p3-----p4 */ if (imageX < width) { p4 = rowOffset + imageX; p3 = p4 - 1; } else { p4 = rowOffset; // wrap around to beginning p3 = rowOffset + imageX - 1; } p2 = p4 - width; p1 = p3 - width; color = bitmap.GetPixel(imageX == width ? 0 : imageX, imageY == height ? height - 1 : imageY); x = (color.R - 128) * pixScale; y = (color.G - 128) * pixScale; z = (color.B - 128) * pixScale; Coord c = new Coord(x, y, z); this.coords.Add(c); if (viewerMode) { this.normals.Add(new Coord()); this.uvs.Add(new UVCoord(widthUnit * imageX, heightUnit * imageY)); } if (imageY > 0 && imageX > 0) { Face f1, f2; if (viewerMode) { f1 = new Face(p1, p3, p4, p1, p3, p4); f1.uv1 = p1; f1.uv2 = p3; f1.uv3 = p4; f2 = new Face(p1, p4, p2, p1, p4, p2); f2.uv1 = p1; f2.uv2 = p4; f2.uv3 = p2; } else { f1 = new Face(p1, p3, p4); f2 = new Face(p1, p4, p2); } this.faces.Add(f1); this.faces.Add(f2); } } } if (scaleSourceImage) bitmap.Dispose(); if (viewerMode) calcVertexNormals(sculptType, width, height); } private void calcVertexNormals(SculptType sculptType, int xSize, int ySize) { // compute vertex normals by summing all the surface normals of all the triangles sharing // each vertex and then normalizing int numFaces = this.faces.Count; for (int i = 0; i < numFaces; i++) { Face face = this.faces[i]; Coord surfaceNormal = face.SurfaceNormal(this.coords); this.normals[face.v1] += surfaceNormal; this.normals[face.v2] += surfaceNormal; this.normals[face.v3] += surfaceNormal; } int numCoords = this.coords.Count; for (int i = 0; i < numCoords; i++) this.coords[i].Normalize(); if (sculptType != SculptType.plane) { // blend the vertex normals at the cylinder seam int pixelsAcross = xSize + 1; for (int y = 0; y < ySize; y++) { int rowOffset = y * pixelsAcross; this.normals[rowOffset] = this.normals[rowOffset + xSize - 1] = (this.normals[rowOffset] + this.normals[rowOffset + xSize - 1]).Normalize(); } } foreach (Face face in this.faces) { ViewerFace vf = new ViewerFace(0); vf.v1 = this.coords[face.v1]; vf.v2 = this.coords[face.v2]; vf.v3 = this.coords[face.v3]; vf.n1 = this.normals[face.n1]; vf.n2 = this.normals[face.n2]; vf.n3 = this.normals[face.n3]; vf.uv1 = this.uvs[face.uv1]; vf.uv2 = this.uvs[face.uv2]; vf.uv3 = this.uvs[face.uv3]; this.viewerFaces.Add(vf); } } public void AddRot(Quat q) { int i; int numVerts = this.coords.Count; for (i = 0; i < numVerts; i++) this.coords[i] *= q; if (this.viewerFaces != null) { int numViewerFaces = this.viewerFaces.Count; for (i = 0; i < numViewerFaces; i++) { ViewerFace v = this.viewerFaces[i]; v.v1 *= q; v.v2 *= q; v.v3 *= q; v.n1 *= q; v.n2 *= q; v.n3 *= q; this.viewerFaces[i] = v; } } } public void Scale(float x, float y, float z) { int i; int numVerts = this.coords.Count; //Coord vert; Coord m = new Coord(x, y, z); for (i = 0; i < numVerts; i++) this.coords[i] *= m; if (this.viewerFaces != null) { int numViewerFaces = this.viewerFaces.Count; for (i = 0; i < numViewerFaces; i++) { ViewerFace v = this.viewerFaces[i]; v.v1 *= m; v.v2 *= m; v.v3 *= m; this.viewerFaces[i] = v; } } } public void DumpRaw(String path, String name, String title) { if (path == null) return; String fileName = name + "_" + title + ".raw"; String completePath = Path.Combine(path, fileName); StreamWriter sw = new StreamWriter(completePath); for (int i = 0; i < this.faces.Count; i++) { string s = this.coords[this.faces[i].v1].ToString(); s += " " + this.coords[this.faces[i].v2].ToString(); s += " " + this.coords[this.faces[i].v3].ToString(); sw.WriteLine(s); } sw.Close(); } } }