corrade-vassal – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | vero | 1 | /* Copyright (c) 2008 Robert Adams |
2 | * Redistribution and use in source and binary forms, with or without |
||
3 | * modification, are permitted provided that the following conditions are met: |
||
4 | * * Redistributions of source code must retain the above copyright |
||
5 | * notice, this list of conditions and the following disclaimer. |
||
6 | * * Redistributions in binary form must reproduce the above copyright |
||
7 | * notice, this list of conditions and the following disclaimer in the |
||
8 | * documentation and/or other materials provided with the distribution. |
||
9 | * * The name of the copyright holder may not be used to endorse or promote products |
||
10 | * derived from this software without specific prior written permission. |
||
11 | * |
||
12 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY |
||
13 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||
14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||
15 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY |
||
16 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||
17 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||
18 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||
19 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
21 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
22 | */ |
||
23 | /* |
||
24 | * Portions of this code are: |
||
25 | * Copyright (c) Contributors, http://idealistviewer.org |
||
26 | * The basic logic of the extrusion code is based on the Idealist viewer code. |
||
27 | * The Idealist viewer is licensed under the three clause BSD license. |
||
28 | */ |
||
29 | /* |
||
30 | * MeshmerizerR class implments OpenMetaverse.Rendering.IRendering interface |
||
31 | * using PrimMesher (http://forge.opensimulator.org/projects/primmesher). |
||
32 | */ |
||
33 | |||
34 | using System; |
||
35 | using System.Collections.Generic; |
||
36 | using System.Drawing; |
||
37 | using System.Text; |
||
38 | using OMV = OpenMetaverse; |
||
39 | using OMVR = OpenMetaverse.Rendering; |
||
40 | |||
41 | namespace OpenMetaverse.Rendering |
||
42 | { |
||
43 | /// <summary> |
||
44 | /// Meshing code based on the Idealist Viewer (20081213). |
||
45 | /// </summary> |
||
46 | [RendererName("MeshmerizerR")] |
||
47 | public class MeshmerizerR : OMVR.IRendering |
||
48 | { |
||
49 | /// <summary> |
||
50 | /// Generates a basic mesh structure from a primitive |
||
51 | /// </summary> |
||
52 | /// <param name="prim">Primitive to generate the mesh from</param> |
||
53 | /// <param name="lod">Level of detail to generate the mesh at</param> |
||
54 | /// <returns>The generated mesh or null on failure</returns> |
||
55 | public OMVR.SimpleMesh GenerateSimpleMesh(OMV.Primitive prim, OMVR.DetailLevel lod) |
||
56 | { |
||
57 | PrimMesher.PrimMesh newPrim = GeneratePrimMesh(prim, lod, false); |
||
58 | if (newPrim == null) |
||
59 | return null; |
||
60 | |||
61 | SimpleMesh mesh = new SimpleMesh(); |
||
62 | mesh.Path = new Path(); |
||
63 | mesh.Prim = prim; |
||
64 | mesh.Profile = new Profile(); |
||
65 | mesh.Vertices = new List<Vertex>(newPrim.coords.Count); |
||
66 | for (int i = 0; i < newPrim.coords.Count; i++) |
||
67 | { |
||
68 | PrimMesher.Coord c = newPrim.coords[i]; |
||
69 | mesh.Vertices.Add(new Vertex { Position = new Vector3(c.X, c.Y, c.Z) }); |
||
70 | } |
||
71 | |||
72 | mesh.Indices = new List<ushort>(newPrim.faces.Count * 3); |
||
73 | for (int i = 0; i < newPrim.faces.Count; i++) |
||
74 | { |
||
75 | PrimMesher.Face face = newPrim.faces[i]; |
||
76 | mesh.Indices.Add((ushort)face.v1); |
||
77 | mesh.Indices.Add((ushort)face.v2); |
||
78 | mesh.Indices.Add((ushort)face.v3); |
||
79 | } |
||
80 | |||
81 | return mesh; |
||
82 | } |
||
83 | |||
84 | /// <summary> |
||
85 | /// Generates a basic mesh structure from a sculpted primitive |
||
86 | /// </summary> |
||
87 | /// <param name="prim">Sculpted primitive to generate the mesh from</param> |
||
88 | /// <param name="sculptTexture">Sculpt texture</param> |
||
89 | /// <param name="lod">Level of detail to generate the mesh at</param> |
||
90 | /// <returns>The generated mesh or null on failure</returns> |
||
91 | public OMVR.SimpleMesh GenerateSimpleSculptMesh(OMV.Primitive prim, System.Drawing.Bitmap sculptTexture, OMVR.DetailLevel lod) |
||
92 | { |
||
93 | OMVR.FacetedMesh faceted = GenerateFacetedSculptMesh(prim, sculptTexture, lod); |
||
94 | |||
95 | if (faceted != null && faceted.Faces.Count == 1) |
||
96 | { |
||
97 | Face face = faceted.Faces[0]; |
||
98 | |||
99 | SimpleMesh mesh = new SimpleMesh(); |
||
100 | mesh.Indices = face.Indices; |
||
101 | mesh.Vertices = face.Vertices; |
||
102 | mesh.Path = faceted.Path; |
||
103 | mesh.Prim = prim; |
||
104 | mesh.Profile = faceted.Profile; |
||
105 | mesh.Vertices = face.Vertices; |
||
106 | |||
107 | return mesh; |
||
108 | } |
||
109 | |||
110 | return null; |
||
111 | } |
||
112 | |||
113 | /// <summary> |
||
114 | /// Generates a a series of faces, each face containing a mesh and |
||
115 | /// metadata |
||
116 | /// </summary> |
||
117 | /// <param name="prim">Primitive to generate the mesh from</param> |
||
118 | /// <param name="lod">Level of detail to generate the mesh at</param> |
||
119 | /// <returns>The generated mesh</returns > |
||
120 | public OMVR.FacetedMesh GenerateFacetedMesh(OMV.Primitive prim, OMVR.DetailLevel lod) |
||
121 | { |
||
122 | bool isSphere = ((OMV.ProfileCurve)(prim.PrimData.profileCurve & 0x07) == OMV.ProfileCurve.HalfCircle); |
||
123 | PrimMesher.PrimMesh newPrim = GeneratePrimMesh(prim, lod, true); |
||
124 | if (newPrim == null) |
||
125 | return null; |
||
126 | |||
127 | // copy the vertex information into OMVR.IRendering structures |
||
128 | OMVR.FacetedMesh omvrmesh = new OMVR.FacetedMesh(); |
||
129 | omvrmesh.Faces = new List<OMVR.Face>(); |
||
130 | omvrmesh.Prim = prim; |
||
131 | omvrmesh.Profile = new OMVR.Profile(); |
||
132 | omvrmesh.Profile.Faces = new List<OMVR.ProfileFace>(); |
||
133 | omvrmesh.Profile.Positions = new List<OMV.Vector3>(); |
||
134 | omvrmesh.Path = new OMVR.Path(); |
||
135 | omvrmesh.Path.Points = new List<OMVR.PathPoint>(); |
||
136 | var indexer = newPrim.GetVertexIndexer(); |
||
137 | |||
138 | for (int i = 0; i < indexer.numPrimFaces; i++) |
||
139 | { |
||
140 | OMVR.Face oface = new OMVR.Face(); |
||
141 | oface.Vertices = new List<OMVR.Vertex>(); |
||
142 | oface.Indices = new List<ushort>(); |
||
143 | oface.TextureFace = prim.Textures.GetFace((uint)i); |
||
144 | |||
145 | for (int j = 0; j < indexer.viewerVertices[i].Count; j++) |
||
146 | { |
||
147 | var vert = new OMVR.Vertex(); |
||
148 | var m = indexer.viewerVertices[i][j]; |
||
149 | vert.Position = new Vector3(m.v.X, m.v.Y, m.v.Z); |
||
150 | vert.Normal = new Vector3(m.n.X, m.n.Y, m.n.Z); |
||
151 | vert.TexCoord = new OMV.Vector2(m.uv.U, 1.0f - m.uv.V); |
||
152 | oface.Vertices.Add(vert); |
||
153 | } |
||
154 | |||
155 | for (int j = 0; j < indexer.viewerPolygons[i].Count; j++) |
||
156 | { |
||
157 | var p = indexer.viewerPolygons[i][j]; |
||
158 | // Skip "degenerate faces" where the same vertex appears twice in the same tri |
||
159 | if (p.v1 == p.v2 || p.v1 == p.v2 || p.v2 == p.v3) continue; |
||
160 | oface.Indices.Add((ushort)p.v1); |
||
161 | oface.Indices.Add((ushort)p.v2); |
||
162 | oface.Indices.Add((ushort)p.v3); |
||
163 | } |
||
164 | |||
165 | omvrmesh.Faces.Add(oface); |
||
166 | } |
||
167 | |||
168 | return omvrmesh; |
||
169 | } |
||
170 | |||
171 | /// <summary> |
||
172 | /// Create a sculpty faceted mesh. The actual scuplt texture is fetched and passed to this |
||
173 | /// routine since all the context for finding teh texture is elsewhere. |
||
174 | /// </summary> |
||
175 | /// <returns>The faceted mesh or null if can't do it</returns> |
||
176 | public OMVR.FacetedMesh GenerateFacetedSculptMesh(OMV.Primitive prim, System.Drawing.Bitmap scupltTexture, OMVR.DetailLevel lod) |
||
177 | { |
||
178 | PrimMesher.SculptMesh.SculptType smSculptType; |
||
179 | switch (prim.Sculpt.Type) |
||
180 | { |
||
181 | case OpenMetaverse.SculptType.Cylinder: |
||
182 | smSculptType = PrimMesher.SculptMesh.SculptType.cylinder; |
||
183 | break; |
||
184 | case OpenMetaverse.SculptType.Plane: |
||
185 | smSculptType = PrimMesher.SculptMesh.SculptType.plane; |
||
186 | break; |
||
187 | case OpenMetaverse.SculptType.Sphere: |
||
188 | smSculptType = PrimMesher.SculptMesh.SculptType.sphere; |
||
189 | break; |
||
190 | case OpenMetaverse.SculptType.Torus: |
||
191 | smSculptType = PrimMesher.SculptMesh.SculptType.torus; |
||
192 | break; |
||
193 | default: |
||
194 | smSculptType = PrimMesher.SculptMesh.SculptType.plane; |
||
195 | break; |
||
196 | } |
||
197 | // The lod for sculpties is the resolution of the texture passed. |
||
198 | // The first guess is 1:1 then lower resolutions after that |
||
199 | // int mesherLod = (int)Math.Sqrt(scupltTexture.Width * scupltTexture.Height); |
||
200 | int mesherLod = 32; // number used in Idealist viewer |
||
201 | switch (lod) |
||
202 | { |
||
203 | case OMVR.DetailLevel.Highest: |
||
204 | break; |
||
205 | case OMVR.DetailLevel.High: |
||
206 | break; |
||
207 | case OMVR.DetailLevel.Medium: |
||
208 | mesherLod /= 2; |
||
209 | break; |
||
210 | case OMVR.DetailLevel.Low: |
||
211 | mesherLod /= 4; |
||
212 | break; |
||
213 | } |
||
214 | PrimMesher.SculptMesh newMesh = |
||
215 | new PrimMesher.SculptMesh(scupltTexture, smSculptType, mesherLod, true, prim.Sculpt.Mirror, prim.Sculpt.Invert); |
||
216 | |||
217 | int numPrimFaces = 1; // a scuplty has only one face |
||
218 | |||
219 | // copy the vertex information into OMVR.IRendering structures |
||
220 | OMVR.FacetedMesh omvrmesh = new OMVR.FacetedMesh(); |
||
221 | omvrmesh.Faces = new List<OMVR.Face>(); |
||
222 | omvrmesh.Prim = prim; |
||
223 | omvrmesh.Profile = new OMVR.Profile(); |
||
224 | omvrmesh.Profile.Faces = new List<OMVR.ProfileFace>(); |
||
225 | omvrmesh.Profile.Positions = new List<OMV.Vector3>(); |
||
226 | omvrmesh.Path = new OMVR.Path(); |
||
227 | omvrmesh.Path.Points = new List<OMVR.PathPoint>(); |
||
228 | |||
229 | Dictionary<OMVR.Vertex, int> vertexAccount = new Dictionary<OMVR.Vertex, int>(); |
||
230 | |||
231 | |||
232 | for (int ii = 0; ii < numPrimFaces; ii++) |
||
233 | { |
||
234 | vertexAccount.Clear(); |
||
235 | OMVR.Face oface = new OMVR.Face(); |
||
236 | oface.Vertices = new List<OMVR.Vertex>(); |
||
237 | oface.Indices = new List<ushort>(); |
||
238 | oface.TextureFace = prim.Textures.GetFace((uint)ii); |
||
239 | int faceVertices = newMesh.coords.Count; |
||
240 | OMVR.Vertex vert; |
||
241 | |||
242 | for (int j = 0; j < faceVertices; j++) |
||
243 | { |
||
244 | vert = new OMVR.Vertex(); |
||
245 | vert.Position = new Vector3(newMesh.coords[j].X, newMesh.coords[j].Y, newMesh.coords[j].Z); |
||
246 | vert.Normal = new Vector3(newMesh.normals[j].X, newMesh.normals[j].Y, newMesh.normals[j].Z); |
||
247 | vert.TexCoord = new Vector2(newMesh.uvs[j].U, newMesh.uvs[j].V); |
||
248 | oface.Vertices.Add(vert); |
||
249 | } |
||
250 | |||
251 | for (int j = 0; j < newMesh.faces.Count; j++) |
||
252 | { |
||
253 | oface.Indices.Add((ushort)newMesh.faces[j].v1); |
||
254 | oface.Indices.Add((ushort)newMesh.faces[j].v2); |
||
255 | oface.Indices.Add((ushort)newMesh.faces[j].v3); |
||
256 | } |
||
257 | |||
258 | if (faceVertices > 0) |
||
259 | { |
||
260 | oface.TextureFace = prim.Textures.FaceTextures[ii]; |
||
261 | if (oface.TextureFace == null) |
||
262 | { |
||
263 | oface.TextureFace = prim.Textures.DefaultTexture; |
||
264 | } |
||
265 | oface.ID = ii; |
||
266 | omvrmesh.Faces.Add(oface); |
||
267 | } |
||
268 | } |
||
269 | |||
270 | return omvrmesh; |
||
271 | } |
||
272 | |||
273 | /// <summary> |
||
274 | /// Apply texture coordinate modifications from a |
||
275 | /// <seealso cref="TextureEntryFace"/> to a list of vertices |
||
276 | /// </summary> |
||
277 | /// <param name="vertices">Vertex list to modify texture coordinates for</param> |
||
278 | /// <param name="center">Center-point of the face</param> |
||
279 | /// <param name="teFace">Face texture parameters</param> |
||
280 | public void TransformTexCoords(List<OMVR.Vertex> vertices, OMV.Vector3 center, OMV.Primitive.TextureEntryFace teFace, Vector3 primScale) |
||
281 | { |
||
282 | // compute trig stuff up front |
||
283 | float cosineAngle = (float)Math.Cos(teFace.Rotation); |
||
284 | float sinAngle = (float)Math.Sin(teFace.Rotation); |
||
285 | |||
286 | for (int ii = 0; ii < vertices.Count; ii++) |
||
287 | { |
||
288 | // tex coord comes to us as a number between zero and one |
||
289 | // transform about the center of the texture |
||
290 | OMVR.Vertex vert = vertices[ii]; |
||
291 | |||
292 | // aply planar tranforms to the UV first if applicable |
||
293 | if (teFace.TexMapType == MappingType.Planar) |
||
294 | { |
||
295 | Vector3 binormal; |
||
296 | float d = Vector3.Dot(vert.Normal, Vector3.UnitX); |
||
297 | if (d >= 0.5f || d <= -0.5f) |
||
298 | { |
||
299 | binormal = Vector3.UnitY; |
||
300 | if (vert.Normal.X < 0f) binormal *= -1; |
||
301 | } |
||
302 | else |
||
303 | { |
||
304 | binormal = Vector3.UnitX; |
||
305 | if (vert.Normal.Y > 0f) binormal *= -1; |
||
306 | } |
||
307 | Vector3 tangent = binormal % vert.Normal; |
||
308 | Vector3 scaledPos = vert.Position * primScale; |
||
309 | vert.TexCoord.X = 1f + (Vector3.Dot(binormal, scaledPos) * 2f - 0.5f); |
||
310 | vert.TexCoord.Y = -(Vector3.Dot(tangent, scaledPos) * 2f - 0.5f); |
||
311 | } |
||
312 | |||
313 | float repeatU = teFace.RepeatU; |
||
314 | float repeatV = teFace.RepeatV; |
||
315 | float tX = vert.TexCoord.X - 0.5f; |
||
316 | float tY = vert.TexCoord.Y - 0.5f; |
||
317 | |||
318 | vert.TexCoord.X = (tX * cosineAngle + tY * sinAngle) * repeatU + teFace.OffsetU + 0.5f; |
||
319 | vert.TexCoord.Y = (-tX * sinAngle + tY * cosineAngle) * repeatV + teFace.OffsetV + 0.5f; |
||
320 | vertices[ii] = vert; |
||
321 | } |
||
322 | return; |
||
323 | } |
||
324 | |||
325 | private PrimMesher.PrimMesh GeneratePrimMesh(Primitive prim, DetailLevel lod, bool viewerMode) |
||
326 | { |
||
327 | OMV.Primitive.ConstructionData primData = prim.PrimData; |
||
328 | int sides = 4; |
||
329 | int hollowsides = 4; |
||
330 | |||
331 | float profileBegin = primData.ProfileBegin; |
||
332 | float profileEnd = primData.ProfileEnd; |
||
333 | |||
334 | bool isSphere = false; |
||
335 | |||
336 | if ((OMV.ProfileCurve)(primData.profileCurve & 0x07) == OMV.ProfileCurve.Circle) |
||
337 | { |
||
338 | switch (lod) |
||
339 | { |
||
340 | case OMVR.DetailLevel.Low: |
||
341 | sides = 6; |
||
342 | break; |
||
343 | case OMVR.DetailLevel.Medium: |
||
344 | sides = 12; |
||
345 | break; |
||
346 | default: |
||
347 | sides = 24; |
||
348 | break; |
||
349 | } |
||
350 | } |
||
351 | else if ((OMV.ProfileCurve)(primData.profileCurve & 0x07) == OMV.ProfileCurve.EqualTriangle) |
||
352 | sides = 3; |
||
353 | else if ((OMV.ProfileCurve)(primData.profileCurve & 0x07) == OMV.ProfileCurve.HalfCircle) |
||
354 | { |
||
355 | // half circle, prim is a sphere |
||
356 | isSphere = true; |
||
357 | switch (lod) |
||
358 | { |
||
359 | case OMVR.DetailLevel.Low: |
||
360 | sides = 6; |
||
361 | break; |
||
362 | case OMVR.DetailLevel.Medium: |
||
363 | sides = 12; |
||
364 | break; |
||
365 | default: |
||
366 | sides = 24; |
||
367 | break; |
||
368 | } |
||
369 | profileBegin = 0.5f * profileBegin + 0.5f; |
||
370 | profileEnd = 0.5f * profileEnd + 0.5f; |
||
371 | } |
||
372 | |||
373 | if ((OMV.HoleType)primData.ProfileHole == OMV.HoleType.Same) |
||
374 | hollowsides = sides; |
||
375 | else if ((OMV.HoleType)primData.ProfileHole == OMV.HoleType.Circle) |
||
376 | { |
||
377 | switch (lod) |
||
378 | { |
||
379 | case OMVR.DetailLevel.Low: |
||
380 | hollowsides = 6; |
||
381 | break; |
||
382 | case OMVR.DetailLevel.Medium: |
||
383 | hollowsides = 12; |
||
384 | break; |
||
385 | default: |
||
386 | hollowsides = 24; |
||
387 | break; |
||
388 | } |
||
389 | } |
||
390 | else if ((OMV.HoleType)primData.ProfileHole == OMV.HoleType.Triangle) |
||
391 | hollowsides = 3; |
||
392 | |||
393 | PrimMesher.PrimMesh newPrim = new PrimMesher.PrimMesh(sides, profileBegin, profileEnd, (float)primData.ProfileHollow, hollowsides); |
||
394 | newPrim.viewerMode = viewerMode; |
||
395 | newPrim.sphereMode = isSphere; |
||
396 | newPrim.holeSizeX = primData.PathScaleX; |
||
397 | newPrim.holeSizeY = primData.PathScaleY; |
||
398 | newPrim.pathCutBegin = primData.PathBegin; |
||
399 | newPrim.pathCutEnd = primData.PathEnd; |
||
400 | newPrim.topShearX = primData.PathShearX; |
||
401 | newPrim.topShearY = primData.PathShearY; |
||
402 | newPrim.radius = primData.PathRadiusOffset; |
||
403 | newPrim.revolutions = primData.PathRevolutions; |
||
404 | newPrim.skew = primData.PathSkew; |
||
405 | switch (lod) |
||
406 | { |
||
407 | case OMVR.DetailLevel.Low: |
||
408 | newPrim.stepsPerRevolution = 6; |
||
409 | break; |
||
410 | case OMVR.DetailLevel.Medium: |
||
411 | newPrim.stepsPerRevolution = 12; |
||
412 | break; |
||
413 | default: |
||
414 | newPrim.stepsPerRevolution = 24; |
||
415 | break; |
||
416 | } |
||
417 | |||
418 | if ((primData.PathCurve == OMV.PathCurve.Line) || (primData.PathCurve == OMV.PathCurve.Flexible)) |
||
419 | { |
||
420 | newPrim.taperX = 1.0f - primData.PathScaleX; |
||
421 | newPrim.taperY = 1.0f - primData.PathScaleY; |
||
422 | newPrim.twistBegin = (int)(180 * primData.PathTwistBegin); |
||
423 | newPrim.twistEnd = (int)(180 * primData.PathTwist); |
||
424 | newPrim.ExtrudeLinear(); |
||
425 | } |
||
426 | else |
||
427 | { |
||
428 | newPrim.taperX = primData.PathTaperX; |
||
429 | newPrim.taperY = primData.PathTaperY; |
||
430 | newPrim.twistBegin = (int)(360 * primData.PathTwistBegin); |
||
431 | newPrim.twistEnd = (int)(360 * primData.PathTwist); |
||
432 | newPrim.ExtrudeCircular(); |
||
433 | } |
||
434 | |||
435 | return newPrim; |
||
436 | } |
||
437 | |||
438 | /// <summary> |
||
439 | /// Method for generating mesh Face from a heightmap |
||
440 | /// </summary> |
||
441 | /// <param name="zMap">Two dimension array of floats containing height information</param> |
||
442 | /// <param name="xBegin">Starting value for X</param> |
||
443 | /// <param name="xEnd">Max value for X</param> |
||
444 | /// <param name="yBegin">Starting value for Y</param> |
||
445 | /// <param name="yEnd">Max value of Y</param> |
||
446 | /// <returns></returns> |
||
447 | public OMVR.Face TerrainMesh(float[,] zMap, float xBegin, float xEnd, float yBegin, float yEnd) |
||
448 | { |
||
449 | PrimMesher.SculptMesh newMesh = new PrimMesher.SculptMesh(zMap, xBegin, xEnd, yBegin, yEnd, true); |
||
450 | OMVR.Face terrain = new OMVR.Face(); |
||
451 | int faceVertices = newMesh.coords.Count; |
||
452 | terrain.Vertices = new List<Vertex>(faceVertices); |
||
453 | terrain.Indices = new List<ushort>(newMesh.faces.Count * 3); |
||
454 | |||
455 | for (int j = 0; j < faceVertices; j++) |
||
456 | { |
||
457 | var vert = new OMVR.Vertex(); |
||
458 | vert.Position = new Vector3(newMesh.coords[j].X, newMesh.coords[j].Y, newMesh.coords[j].Z); |
||
459 | vert.Normal = new Vector3(newMesh.normals[j].X, newMesh.normals[j].Y, newMesh.normals[j].Z); |
||
460 | vert.TexCoord = new Vector2(newMesh.uvs[j].U, newMesh.uvs[j].V); |
||
461 | terrain.Vertices.Add(vert); |
||
462 | } |
||
463 | |||
464 | for (int j = 0; j < newMesh.faces.Count; j++) |
||
465 | { |
||
466 | terrain.Indices.Add((ushort)newMesh.faces[j].v1); |
||
467 | terrain.Indices.Add((ushort)newMesh.faces[j].v2); |
||
468 | terrain.Indices.Add((ushort)newMesh.faces[j].v3); |
||
469 | } |
||
470 | |||
471 | return terrain; |
||
472 | } |
||
473 | } |
||
474 | } |