Add PrimMesher. A fork from Dahlia's

This commit is contained in:
UbitUmarov
2025-06-15 14:10:03 +01:00
parent bd512f3c69
commit 15b309fca2
13 changed files with 3717 additions and 0 deletions
@@ -0,0 +1,9 @@
The primary developer of PrimMesher is Dahlia Trimble.
Additional contributors (in no particular order):
Morgaine Dinova
Latif Kalifa (lkalif)
Some portions of PrimMesher are from the following projects:
* OpenSimulator (original extrusion concept)
* LibOpenMetaverse (quaternion multiplication routine)
+54
View File
@@ -0,0 +1,54 @@
#
# Makefile for PrimMesher
# See http://forge.opensimulator.org/gf/project/primmesher
#
# Release and Debug files are not placed in {obj,bin}/{Release,Debug}
# by default to avoid clashing with the output from other build tools.
# Use "make merge_all" to merge output with {obj,bin} if desired.
#
MCS = gmcs
SYS_LIB = \
-r:System.Drawing.dll \
-r:System.Drawing.Design \
-r:mscorlib.dll
RELEASE_DLL = \
PrimMesher.dll \
Properties/AssemblyInfo.dll
DEBUG_DLL = \
Debug/PrimMesher.dll \
Debug/AssemblyInfo.dll
all: Debug $(RELEASE_DLL) $(DEBUG_DLL)
Debug:
mkdir -p Debug
PrimMesher.dll: PrimMesher.cs SculptMesh.cs
$(MCS) $(SYS_LIB) PrimMesher.cs SculptMesh.cs -t:library
Debug/PrimMesher.dll: PrimMesher.cs SculptMesh.cs
$(MCS) $(SYS_LIB) PrimMesher.cs SculptMesh.cs -t:library -debug -out:$@
Properties/AssemblyInfo.dll: Properties/AssemblyInfo.cs
$(MCS) $< -t:library
Debug/AssemblyInfo.dll: Properties/AssemblyInfo.cs
$(MCS) $< -t:library -debug -out:$@
merge_release: all
cp -p $(RELEASE_DLL) obj/Release/
cp -p $(RELEASE_DLL) bin/Release/
merge_debug: all
cp -p Debug/* obj/Debug/
cp -p Debug/* bin/Debug/
merge_all: merge_release merge_debug
clean:
rm -f $(RELEASE_DLL)
rm -f $(DEBUG_DLL) Debug/*.dll.mdb
+20
View File
@@ -0,0 +1,20 @@
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual C# Express 2010
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PrimMesher", "PrimMesher\PrimMesher.csproj", "{2E2B643F-F18B-4791-BA4B-6E82D0E794B6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
Binary file not shown.
@@ -0,0 +1,222 @@
/*
* 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 OpenSimulator 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;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace PrimMesher
{
public class ObjMesh
{
List<Coord> coords = new List<Coord>();
List<Coord> normals = new List<Coord>();
List<UVCoord> uvs = new List<UVCoord>();
public string meshName = string.Empty;
public List<List<ViewerVertex>> viewerVertices = new List<List<ViewerVertex>>();
public List<List<ViewerPolygon>> viewerPolygons = new List<List<ViewerPolygon>>();
List<ViewerVertex> faceVertices = new List<ViewerVertex>();
List<ViewerPolygon> facePolygons = new List<ViewerPolygon>();
public int numPrimFaces;
Dictionary<int, int> viewerVertexLookup = new Dictionary<int, int>();
public ObjMesh(string path)
{
ProcessStream(new StreamReader(path));
}
public ObjMesh(StreamReader sr)
{
ProcessStream(sr);
}
private void ProcessStream(StreamReader s)
{
numPrimFaces = 0;
while (!s.EndOfStream)
{
string line = s.ReadLine().Trim();
string[] tokens = Regex.Split(line, @"\s+");
// Skip blank lines and comments
if (tokens.Length > 0 && tokens[0] != String.Empty && !tokens[0].StartsWith("#"))
ProcessTokens(tokens);
}
MakePrimFace();
}
public VertexIndexer GetVertexIndexer()
{
VertexIndexer vi = new VertexIndexer();
vi.numPrimFaces = this.numPrimFaces;
vi.viewerPolygons = this.viewerPolygons;
vi.viewerVertices = this.viewerVertices;
return vi;
}
private void ProcessTokens(string[] tokens)
{
switch (tokens[0].ToLower())
{
case "o":
meshName = tokens[1];
break;
case "v":
coords.Add(ParseCoord(tokens));
break;
case "vt":
{
uvs.Add(ParseUVCoord(tokens));
break;
}
case "vn":
normals.Add(ParseCoord(tokens));
break;
case "g":
MakePrimFace();
break;
case "s":
break;
case "f":
int[] vertIndices = new int[3];
for (int vertexIndex = 1; vertexIndex <= 3; vertexIndex++)
{
string[] indices = tokens[vertexIndex].Split('/');
int positionIndex = int.Parse(indices[0],
CultureInfo.InvariantCulture) - 1;
int texCoordIndex = -1;
int normalIndex = -1;
if (indices.Length > 1)
{
if (int.TryParse(indices[1], System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out texCoordIndex))
texCoordIndex--;
else texCoordIndex = -1;
}
if (indices.Length > 2)
{
if (int.TryParse(indices[1], System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out normalIndex))
normalIndex--;
else normalIndex = -1;
}
int hash = hashInts(positionIndex, texCoordIndex, normalIndex);
if (viewerVertexLookup.ContainsKey(hash))
vertIndices[vertexIndex - 1] = viewerVertexLookup[hash];
else
{
ViewerVertex vv = new ViewerVertex();
vv.v = coords[positionIndex];
if (normalIndex > -1)
vv.n = normals[normalIndex];
if (texCoordIndex > -1)
vv.uv = uvs[texCoordIndex];
faceVertices.Add(vv);
vertIndices[vertexIndex - 1] = viewerVertexLookup[hash] = faceVertices.Count - 1;
}
}
facePolygons.Add(new ViewerPolygon(vertIndices[0], vertIndices[1], vertIndices[2]));
break;
case "mtllib":
break;
case "usemtl":
break;
default:
break;
}
}
private void MakePrimFace()
{
if (faceVertices.Count > 0 && facePolygons.Count > 0)
{
viewerVertices.Add(faceVertices);
faceVertices = new List<ViewerVertex>();
viewerPolygons.Add(facePolygons);
facePolygons = new List<ViewerPolygon>();
viewerVertexLookup = new Dictionary<int, int>();
numPrimFaces++;
}
}
private UVCoord ParseUVCoord(string[] tokens)
{
return new UVCoord(
float.Parse(tokens[1], CultureInfo.InvariantCulture),
float.Parse(tokens[1], CultureInfo.InvariantCulture));
}
private Coord ParseCoord(string[] tokens)
{
return new Coord(
float.Parse(tokens[1], CultureInfo.InvariantCulture),
float.Parse(tokens[2], CultureInfo.InvariantCulture),
float.Parse(tokens[3], CultureInfo.InvariantCulture));
}
private int hashInts(int i1, int i2, int i3)
{
return (i1.ToString() + " " + i2.ToString() + " " + i3.ToString()).GetHashCode();
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<OutputType>Library</OutputType>
<PublishUrl>publish\</PublishUrl>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<GenerateAssemblyInfo>True</GenerateAssemblyInfo>
<AssemblyTitle>PrimMesher</AssemblyTitle>
<Product>PrimMesher</Product>
<Copyright>Copyright © 2023</Copyright>
<AssemblyVersion>1.0.0.1</AssemblyVersion>
<FileVersion>1.0.0.1</FileVersion>
<IsPublishable>False</IsPublishable>
<ProduceReferenceAssembly>False</ProduceReferenceAssembly>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineConstants>TRACE;DEBUG;VERTEX_INDEXER</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DefineConstants>TRACE;VERTEX_INDEXER</DefineConstants>
</PropertyGroup>
<ItemGroup>
<None Remove="System.Drawing.Common.dll" />
<None Remove="license.txt" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Drawing.Common">
<SpecificVersion>False</SpecificVersion>
<HintPath>System.Drawing.Common.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
@@ -0,0 +1,13 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("ef69232f-7e8c-4efa-9ce6-d4e9743aac66")]
@@ -0,0 +1,193 @@
/*
* 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 OpenSimulator 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.
*/
// to build without references to System.Drawing, comment this out
#define SYSTEM_DRAWING
using System;
using System.Collections.Generic;
using System.Text;
#if SYSTEM_DRAWING
using System.Drawing;
using System.Drawing.Imaging;
namespace PrimMesher
{
public class SculptMap
{
public int width;
public int height;
public byte[] redBytes;
public byte[] greenBytes;
public byte[] blueBytes;
public SculptMap()
{
}
public SculptMap(Bitmap bm, int lod)
{
int bmW = bm.Width;
int bmH = bm.Height;
if (bmW == 0 || bmH == 0)
throw new Exception("SculptMap: bitmap has no data");
int numLodPixels = lod * 2 * lod * 2; // (32 * 2)^2 = 64^2 pixels for default sculpt map image
bool needsScaling = false;
bool smallMap = bmW * bmH <= lod * lod;
width = bmW;
height = bmH;
while (width * height > numLodPixels)
{
width >>= 1;
height >>= 1;
needsScaling = true;
}
try
{
if (needsScaling)
bm = ScaleImage(bm, width, height,
System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor);
}
catch (Exception e)
{
throw new Exception("Exception in ScaleImage(): e: " + e.ToString());
}
if (width * height > lod * lod)
{
width >>= 1;
height >>= 1;
}
int numBytes = smallMap ? width * height : (width + 1) * (height + 1);
redBytes = new byte[numBytes];
greenBytes = new byte[numBytes];
blueBytes = new byte[numBytes];
int byteNdx = 0;
try
{
if (smallMap)
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
Color c = bm.GetPixel(x, y);
redBytes[byteNdx] = c.R;
greenBytes[byteNdx] = c.G;
blueBytes[byteNdx] = c.B;
++byteNdx;
}
}
else
for (int y = 0; y <= height; y++)
{
for (int x = 0; x <= width; x++)
{
Color c = bm.GetPixel(x < width ? x * 2 : x * 2 - 1,
y < height ? y * 2 : y * 2 - 1);
redBytes[byteNdx] = c.R;
greenBytes[byteNdx] = c.G;
blueBytes[byteNdx] = c.B;
++byteNdx;
}
}
}
catch (Exception e)
{
throw new Exception("Caught exception processing byte arrays in SculptMap(): e: " + e.ToString());
}
if (!smallMap)
{
width++;
height++;
}
}
public List<List<Coord>> ToRows(bool mirror)
{
int numRows = height;
int numCols = width;
List<List<Coord>> rows = new List<List<Coord>>(numRows);
float pixScale = 1.0f / 255;
int rowNdx, colNdx;
int smNdx = 0;
for (rowNdx = 0; rowNdx < numRows; rowNdx++)
{
List<Coord> row = new List<Coord>(numCols);
for (colNdx = 0; colNdx < numCols; colNdx++)
{
if (mirror)
row.Add(new Coord(-(redBytes[smNdx] * pixScale - 0.5f), (greenBytes[smNdx] * pixScale - 0.5f), blueBytes[smNdx] * pixScale - 0.5f));
else
row.Add(new Coord(redBytes[smNdx] * pixScale - 0.5f, greenBytes[smNdx] * pixScale - 0.5f, blueBytes[smNdx] * pixScale - 0.5f));
++smNdx;
}
rows.Add(row);
}
return rows;
}
private Bitmap ScaleImage(Bitmap srcImage, int destWidth, int destHeight,
System.Drawing.Drawing2D.InterpolationMode interpMode)
{
Bitmap scaledImage = new Bitmap(srcImage, destWidth, destHeight);
scaledImage.SetResolution(96.0f, 96.0f);
Graphics grPhoto = Graphics.FromImage(scaledImage);
grPhoto.InterpolationMode = interpMode;
grPhoto.DrawImage(srcImage,
new Rectangle(0, 0, destWidth, destHeight),
new Rectangle(0, 0, srcImage.Width, srcImage.Height),
GraphicsUnit.Pixel);
grPhoto.Dispose();
return scaledImage;
}
}
}
#endif
@@ -0,0 +1,646 @@
/*
* 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 OpenSimulator 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.
*/
// to build without references to System.Drawing, comment this out
#define SYSTEM_DRAWING
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
#if SYSTEM_DRAWING
using System.Drawing;
using System.Drawing.Imaging;
#endif
namespace PrimMesher
{
public class SculptMesh
{
public List<Coord> coords;
public List<Face> faces;
public List<ViewerFace> viewerFaces;
public List<Coord> normals;
public List<UVCoord> uvs;
public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 };
#if SYSTEM_DRAWING
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;
}
public SculptMesh(string fileName, int sculptType, int lod, int viewerMode, int mirror, int invert)
{
Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName);
_SculptMesh(bitmap, (SculptType)sculptType, lod, viewerMode != 0, mirror != 0, invert != 0);
bitmap.Dispose();
}
#endif
/// <summary>
/// ** Experimental ** May disappear from future versions ** not recommeneded for use in applications
/// Construct a sculpt mesh from a 2D array of floats
/// </summary>
/// <param name="zMap"></param>
/// <param name="xBegin"></param>
/// <param name="xEnd"></param>
/// <param name="yBegin"></param>
/// <param name="yEnd"></param>
/// <param name="viewerMode"></param>
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<Coord>();
faces = new List<Face>();
normals = new List<Coord>();
uvs = new List<UVCoord>();
viewerFaces = new List<ViewerFace>();
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, 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);
}
#if SYSTEM_DRAWING
public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode)
{
_SculptMesh(sculptBitmap, sculptType, lod, viewerMode, false, false);
}
public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert)
{
_SculptMesh(sculptBitmap, sculptType, lod, viewerMode, mirror, invert);
}
#endif
public SculptMesh(List<List<Coord>> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert)
{
_SculptMesh(rows, sculptType, viewerMode, mirror, invert);
}
#if SYSTEM_DRAWING
/// <summary>
/// converts a bitmap to a list of lists of coords, while scaling the image.
/// the scaling is done in floating point so as to allow for reduced vertex position
/// quantization as the position will be averaged between pixel values. this routine will
/// likely fail if the bitmap width and height are not powers of 2.
/// </summary>
/// <param name="bitmap"></param>
/// <param name="scale"></param>
/// <param name="mirror"></param>
/// <returns></returns>
private List<List<Coord>> bitmap2Coords(Bitmap bitmap, int scale, bool mirror)
{
int numRows = bitmap.Height / scale;
int numCols = bitmap.Width / scale;
List<List<Coord>> rows = new List<List<Coord>>(numRows);
float pixScale = 1.0f / (scale * scale);
pixScale /= 255;
int imageX, imageY = 0;
int rowNdx, colNdx;
for (rowNdx = 0; rowNdx < numRows; rowNdx++)
{
List<Coord> row = new List<Coord>(numCols);
for (colNdx = 0; colNdx < numCols; colNdx++)
{
imageX = colNdx * scale;
int imageYStart = rowNdx * scale;
int imageYEnd = imageYStart + scale;
int imageXEnd = imageX + scale;
float rSum = 0.0f;
float gSum = 0.0f;
float bSum = 0.0f;
for (; imageX < imageXEnd; imageX++)
{
for (imageY = imageYStart; imageY < imageYEnd; imageY++)
{
Color c = bitmap.GetPixel(imageX, imageY);
if (c.A != 255)
{
bitmap.SetPixel(imageX, imageY, Color.FromArgb(255, c.R, c.G, c.B));
c = bitmap.GetPixel(imageX, imageY);
}
rSum += c.R;
gSum += c.G;
bSum += c.B;
}
}
if (mirror)
row.Add(new Coord(-(rSum * pixScale - 0.5f), gSum * pixScale - 0.5f, bSum * pixScale - 0.5f));
else
row.Add(new Coord(rSum * pixScale - 0.5f, gSum * pixScale - 0.5f, bSum * pixScale - 0.5f));
}
rows.Add(row);
}
return rows;
}
private List<List<Coord>> bitmap2CoordsSampled(Bitmap bitmap, int scale, bool mirror)
{
int numRows = bitmap.Height / scale;
int numCols = bitmap.Width / scale;
List<List<Coord>> rows = new List<List<Coord>>(numRows);
float pixScale = 1.0f / 256.0f;
int imageX, imageY = 0;
int rowNdx, colNdx;
for (rowNdx = 0; rowNdx <= numRows; rowNdx++)
{
List<Coord> row = new List<Coord>(numCols);
imageY = rowNdx * scale;
if (rowNdx == numRows) imageY--;
for (colNdx = 0; colNdx <= numCols; colNdx++)
{
imageX = colNdx * scale;
if (colNdx == numCols) imageX--;
Color c = bitmap.GetPixel(imageX, imageY);
if (c.A != 255)
{
bitmap.SetPixel(imageX, imageY, Color.FromArgb(255, c.R, c.G, c.B));
c = bitmap.GetPixel(imageX, imageY);
}
if (mirror)
row.Add(new Coord(-(c.R * pixScale - 0.5f), c.G * pixScale - 0.5f, c.B * pixScale - 0.5f));
else
row.Add(new Coord(c.R * pixScale - 0.5f, c.G * pixScale - 0.5f, c.B * pixScale - 0.5f));
}
rows.Add(row);
}
return rows;
}
void _SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert)
{
_SculptMesh(new SculptMap(sculptBitmap, lod).ToRows(mirror), sculptType, viewerMode, mirror, invert);
}
#endif
void _SculptMesh(List<List<Coord>> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert)
{
coords = new List<Coord>();
faces = new List<Face>();
normals = new List<Coord>();
uvs = new List<UVCoord>();
sculptType = (SculptType)(((int)sculptType) & 0x07);
if (mirror)
invert = !invert;
viewerFaces = new List<ViewerFace>();
int width = rows[0].Count;
int p1, p2, p3, p4;
int imageX, imageY;
if (sculptType != SculptType.plane)
{
if (rows.Count % 2 == 0)
{
for (int rowNdx = 0; rowNdx < rows.Count; rowNdx++)
rows[rowNdx].Add(rows[rowNdx][0]);
}
else
{
int lastIndex = rows[0].Count - 1;
for (int i = 0; i < rows.Count; i++)
rows[i][0] = rows[i][lastIndex];
}
}
Coord topPole = rows[0][width / 2];
Coord bottomPole = rows[rows.Count - 1][width / 2];
if (sculptType == SculptType.sphere)
{
if (rows.Count % 2 == 0)
{
int count = rows[0].Count;
List<Coord> topPoleRow = new List<Coord>(count);
List<Coord> bottomPoleRow = new List<Coord>(count);
for (int i = 0; i < count; i++)
{
topPoleRow.Add(topPole);
bottomPoleRow.Add(bottomPole);
}
rows.Insert(0, topPoleRow);
rows.Add(bottomPoleRow);
}
else
{
int count = rows[0].Count;
List<Coord> topPoleRow = rows[0];
List<Coord> bottomPoleRow = rows[rows.Count - 1];
for (int i = 0; i < count; i++)
{
topPoleRow[i] = topPole;
bottomPoleRow[i] = bottomPole;
}
}
}
if (sculptType == SculptType.torus)
rows.Add(rows[0]);
int coordsDown = rows.Count;
int coordsAcross = rows[0].Count;
int lastColumn = coordsAcross - 1;
float widthUnit = 1.0f / (coordsAcross - 1);
float heightUnit = 1.0f / (coordsDown - 1);
for (imageY = 0; imageY < coordsDown; imageY++)
{
int rowOffset = imageY * coordsAcross;
for (imageX = 0; imageX < coordsAcross; imageX++)
{
/*
* p1-----p2
* | \ f2 |
* | \ |
* | f1 \|
* p3-----p4
*/
p4 = rowOffset + imageX;
p3 = p4 - 1;
p2 = p4 - coordsAcross;
p1 = p3 - coordsAcross;
this.coords.Add(rows[imageY][imageX]);
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)
{
if (invert)
{
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, 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
{
if (invert)
{
f1 = new Face(p1, p4, p3);
f2 = new Face(p1, p2, p4);
}
else
{
f1 = new Face(p1, p3, p4);
f2 = new Face(p1, p4, p2);
}
}
this.faces.Add(f1);
this.faces.Add(f2);
}
}
}
if (viewerMode)
calcVertexNormals(sculptType, coordsAcross, coordsDown);
}
/// <summary>
/// Duplicates a SculptMesh object. All object properties are copied by value, including lists.
/// </summary>
/// <returns></returns>
public SculptMesh Copy()
{
return new SculptMesh(this);
}
public SculptMesh(SculptMesh sm)
{
coords = new List<Coord>(sm.coords);
faces = new List<Face>(sm.faces);
viewerFaces = new List<ViewerFace>(sm.viewerFaces);
normals = new List<Coord>(sm.normals);
uvs = new List<UVCoord>(sm.uvs);
}
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.n1] += surfaceNormal;
this.normals[face.n2] += surfaceNormal;
this.normals[face.n3] += surfaceNormal;
}
int numNormals = this.normals.Count;
for (int i = 0; i < numNormals; i++)
this.normals[i] = this.normals[i].Normalize();
if (sculptType != SculptType.plane)
{ // blend the vertex normals at the cylinder seam
for (int y = 0; y < ySize; y++)
{
int rowOffset = y * xSize;
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.coordIndex1 = face.v1;
vf.coordIndex2 = face.v2;
vf.coordIndex3 = 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);
}
}
/// <summary>
/// Adds a value to each XYZ vertex coordinate in the mesh
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="z"></param>
public void AddPos(float x, float y, float z)
{
int i;
int numVerts = this.coords.Count;
Coord vert;
for (i = 0; i < numVerts; i++)
{
vert = this.coords[i];
vert.X += x;
vert.Y += y;
vert.Z += z;
this.coords[i] = vert;
}
if (this.viewerFaces != null)
{
int numViewerFaces = this.viewerFaces.Count;
for (i = 0; i < numViewerFaces; i++)
{
ViewerFace v = this.viewerFaces[i];
v.AddPos(x, y, z);
this.viewerFaces[i] = v;
}
}
}
/// <summary>
/// Rotates the mesh
/// </summary>
/// <param name="q"></param>
public void AddRot(Quat q)
{
int i;
int numVerts = this.coords.Count;
for (i = 0; i < numVerts; i++)
this.coords[i] *= q;
int numNormals = this.normals.Count;
for (i = 0; i < numNormals; i++)
this.normals[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 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 = System.IO.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();
}
}
}
@@ -0,0 +1,185 @@
/*
* 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 OpenSimulator 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;
namespace PrimMesher
{
public struct ViewerVertex
{
public Coord v;
public Coord n;
public UVCoord uv;
public ViewerVertex(Coord coord, Coord normal, UVCoord uv)
{
this.v = coord;
this.n = normal;
this.uv = uv;
}
}
public struct ViewerPolygon
{
public int v1;
public int v2;
public int v3;
public ViewerPolygon(int v1, int v2, int v3)
{
this.v1 = v1;
this.v2 = v2;
this.v3 = v3;
}
}
public class VertexIndexer
{
public List<List<ViewerVertex>> viewerVertices;
public List<List<ViewerPolygon>> viewerPolygons;
public int numPrimFaces;
private int[][] viewerVertIndices;
public VertexIndexer()
{
}
bool IsValidFace(ViewerFace vf)
{
if (Math.Abs(vf.n1.X) + Math.Abs(vf.n1.Y) + Math.Abs(vf.n1.Z) < 0.2f)
return false;
if (Math.Abs(vf.n2.X) + Math.Abs(vf.n2.Y) + Math.Abs(vf.n2.Z) < 0.2f)
return false;
if (Math.Abs(vf.n3.X) + Math.Abs(vf.n3.Y) + Math.Abs(vf.n3.Z) < 0.2f)
return false;
return true;
}
public VertexIndexer(PrimMesh primMesh)
{
int maxPrimFaceNumber = 0;
int[] validFaces = new int[9], invalidFaces = new int[9];
for (int i = 0; i < 9; i++)
{
validFaces[i] = -1;
invalidFaces[i] = 0;
}
int numValidFaces = 0;
foreach (ViewerFace vf in primMesh.viewerFaces)
{
if (maxPrimFaceNumber < vf.primFaceNumber)
maxPrimFaceNumber = vf.primFaceNumber;
if (!IsValidFace(vf))
invalidFaces[vf.primFaceNumber]++;
}
this.numPrimFaces = maxPrimFaceNumber + 1;
for (int i = 0; i < this.numPrimFaces; i++)
if (invalidFaces[i] == 0)
validFaces[i] = numValidFaces++;
int[] numViewerVerts = new int[numPrimFaces];
int[] numVertsPerPrimFace = new int[numPrimFaces];
for (int i = 0; i < numPrimFaces; i++)
{
numViewerVerts[i] = 0;
numVertsPerPrimFace[i] = 0;
}
foreach (ViewerFace vf in primMesh.viewerFaces)
numVertsPerPrimFace[vf.primFaceNumber] += 3;
this.viewerVertices = new List<List<ViewerVertex>>(numPrimFaces);
this.viewerPolygons = new List<List<ViewerPolygon>>(numPrimFaces);
this.viewerVertIndices = new int[numPrimFaces][];
// create index lists
for (int primFaceNumber = 0; primFaceNumber < numPrimFaces; primFaceNumber++)
{
//set all indices to -1 to indicate an invalid index
int[] vertIndices = new int[primMesh.coords.Count];
for (int i = 0; i < primMesh.coords.Count; i++)
vertIndices[i] = -1;
viewerVertIndices[primFaceNumber] = vertIndices;
viewerVertices.Add(new List<ViewerVertex>(numVertsPerPrimFace[primFaceNumber]));
viewerPolygons.Add(new List<ViewerPolygon>());
}
this.numPrimFaces = numValidFaces;
// populate the index lists
foreach (ViewerFace vf in primMesh.viewerFaces)
{
if (invalidFaces[vf.primFaceNumber] != 0)
continue;
int v1, v2, v3;
int[] vertIndices = viewerVertIndices[validFaces[vf.primFaceNumber]];
List<ViewerVertex> viewerVerts = viewerVertices[validFaces[vf.primFaceNumber]];
// add the vertices
if (vertIndices[vf.coordIndex1] < 0)
{
viewerVerts.Add(new ViewerVertex(vf.v1, vf.n1, vf.uv1));
v1 = viewerVerts.Count - 1;
vertIndices[vf.coordIndex1] = v1;
}
else v1 = vertIndices[vf.coordIndex1];
if (vertIndices[vf.coordIndex2] < 0)
{
viewerVerts.Add(new ViewerVertex(vf.v2, vf.n2, vf.uv2));
v2 = viewerVerts.Count - 1;
vertIndices[vf.coordIndex2] = v2;
}
else v2 = vertIndices[vf.coordIndex2];
if (vertIndices[vf.coordIndex3] < 0)
{
viewerVerts.Add(new ViewerVertex(vf.v3, vf.n3, vf.uv3));
v3 = viewerVerts.Count - 1;
vertIndices[vf.coordIndex3] = v3;
}
else v3 = vertIndices[vf.coordIndex3];
if (v1 != v2 && v1 != v3 && v2 != v3)
viewerPolygons[validFaces[vf.primFaceNumber]].Add(new ViewerPolygon(v1, v2, v3));
}
}
}
}
+13
View File
@@ -0,0 +1,13 @@
# PrimMesher
==================
This a fork from Dahlia's https://github.com/dahliaT/PrimMesher for opensimulator use only
Original comments:
This library makes procedural mesh objects, or "Prims", as they are known in several on-line shared virtual worlds. It was first developed as part of OpenSimulator to make collision meshes for the physics engine. Later it was spun off and rendering features (vertex normals, UV coortinates, etc.) were added to make it suitable for use in viewer applications. The primary repository for the spin-off was forge.opensimulator.org but that site disappeared a few years ago and doesn't look like it's coming back any time soon. I had some local copies but since subversion does not store version history in checked-out copies, I had no version history. Fortunately, prior to his untimely passing, lkalif had made a github mirror of the original subversion repo which included history and I started this repository by forking his Github repository. As such I'll consider https://github.com/dahliaT/PrimMesher as my master repository for any future changes.
This project contains the source for the original C# library PrimMesher.dll. This C# version should be considered the primary development version from which ports to other languages are based.