public static void RecalculateNormal(ReadOnlySpan <Vector3> positions, ReadOnlySpan <int> indices, Span <Vector3> normals) { if (indices.Length % 3 != 0) { ThrowArgumentIndicesLengthInvalid(); } // [NOTE] // Sharp edge is not supported. normals.Clear(); using var countsBuf = new ValueTypeRentMemory <int>(positions.Length, true); var counts = countsBuf.AsSpan(); var faces = indices.MarshalCast <int, Face>(); foreach (var f in faces) { var n = Vector3.Cross(positions[f.I1] - positions[f.I0], positions[f.I2] - positions[f.I0]).Normalized(); normals[f.I0] += n; normals[f.I1] += n; normals[f.I2] += n; counts[f.I0] += 1; counts[f.I1] += 1; counts[f.I2] += 1; } for (int i = 0; i < positions.Length; i++) { normals[i] /= counts[i]; } }
public static void RecalculateNormal(Span <Vertex> vertices, ReadOnlySpan <int> indices) // TODO: something wrong { if (indices.Length % 3 != 0) { ThrowArgumentIndicesLengthInvalid(); } for (int i = 0; i < vertices.Length; i++) { vertices[i].Normal = default; } using var counts = new ValueTypeRentMemory <int>(vertices.Length, true); var faces = indices.MarshalCast <int, Face>(); foreach (var f in faces) { var n = Vector3.Cross(vertices[f.I1].Position - vertices[f.I0].Position, vertices[f.I2].Position - vertices[f.I0].Position).Normalized(); vertices[f.I0].Normal += n; vertices[f.I1].Normal += n; vertices[f.I2].Normal += n; counts[f.I0] += 1; counts[f.I1] += 1; counts[f.I2] += 1; } for (int i = 0; i < vertices.Length; i++) { vertices[i].Normal /= counts[i]; } }
public void LargeRent(int length) { using (var memory = new ValueTypeRentMemory <int>(length, true)) { var span = memory.AsSpan(); Assert.Equal(length, span.Length); } }
public void RentEmpty() { using (var memory = new ValueTypeRentMemory <int>(0, true)) { Assert.True(memory.AsSpan().IsEmpty); Assert.Equal(0, memory.Length); } }
public void RentMultiParallel() { Parallel.For(0, 100, i => { using var mem = new ValueTypeRentMemory <int>(1000, false); Assert.Equal(1000, mem.Length); Assert.Equal(1000, mem.AsSpan().Length); }); }
public void Rent(int length) { using (var memory = new ValueTypeRentMemory <byte>(length, true)) { var span = memory.AsSpan(); Assert.Equal(length, span.Length); foreach (var item in span) { Assert.Equal(0, item); } } }
internal FbxSemanticsUnsafe([MaybeNull] ref FbxObject fbx, ref UnsafeRawArray <int> indices, ref UnsafeRawArray <TVertex> vertices, ref ValueTypeRentMemory <RawString> texture, ref SkeletonDataList skeletons) { (_fbx, fbx) = (fbx, default); (_textures, texture) = (texture, default); (_indices, indices) = (indices, default); (_vertices, vertices) = (vertices, default); (_skeletons, skeletons) = (skeletons, default); }
public ModelList(FbxNode objectsNode) { var meshDic = new BufferPooledDictionary <long, MeshModel>(); var limbDic = new BufferPooledDictionary <long, LimbNode>(); var nullDic = new BufferPooledDictionary <long, NullModel>(); try { using var indexBuf = new ValueTypeRentMemory <int>(objectsNode.Children.Count, false); var modelCount = objectsNode.FindChildIndexAll(FbxConstStrings.Model(), indexBuf.AsSpan()); foreach (var i in indexBuf.AsSpan(0, modelCount)) { var modelNode = objectsNode.Children[i]; var modelType = modelNode.Properties[2].AsString().ToModelType(); switch (modelType) { case ModelType.LimbNode: { var limb = new LimbNode(modelNode); limbDic.Add(limb.ID, limb); break; } case ModelType.Mesh: { var meshModel = new MeshModel(modelNode); meshDic.Add(meshModel.ID, meshModel); break; } case ModelType.Null: { var nullModel = new NullModel(modelNode); nullDic.Add(nullModel.ID, nullModel); break; } case ModelType.Unknown: default: break; } } _meshDic = meshDic; _limbDic = limbDic; _nullDic = nullDic; } catch { meshDic.Dispose(); limbDic.Dispose(); nullDic.Dispose(); throw; } }
public void Load(ReadOnlySpan <T> data, bool asPowerOfTwoTexture = true) { ThrowIfNotMultipleOfFour(data.GetByteLength()); if (asPowerOfTwoTexture) { _textureCore.LoadAsPOT(data.MarshalCast <T, Color4>()); } else { _textureCore.Load(data.MarshalCast <T, Color4>()); } _memory = new ValueTypeRentMemory <T>(data.Length, false); data.CopyTo(_memory.AsSpan()); }
public void RentMulti() { const int Count = 100; using var list = new Disposables <ValueTypeRentMemory <int> >(); for (int i = 0; i < Count; i++) { var mem = new ValueTypeRentMemory <int>(1000, false); list.Add(mem); } for (int i = 0; i < Count; i++) { Assert.Equal(1000, list[i].Length); Assert.Equal(1000, list[i].AsSpan().Length); } }
private static async UniTask Build(StateObject state, Model3D model, Model3DLoadMeshDelegate load) { //var (resourceLoader, name, token) = obj; var(file, token) = state; var screen = model.GetValidScreen(); var timingPoints = screen.TimingPoints; token.ThrowIfCancellationRequested(); await UniTask.SwitchToThreadPool(); // ↓ thread pool -------------------------------------- token.ThrowIfCancellationRequested(); // Parse fbx file using var fbx = FbxSemanticParser <SkinnedVertex> .ParseUnsafe(file.GetStream(), false, token); await timingPoints.Update.Next(token); // ↓ main thread -------------------------------------- await CreateTexture(file, fbx, model); // Create a skeleton component if (fbx.Skeletons.IsEmpty == false) { var skeletonIndex = 0; var skeleton = new HumanoidSkeleton(); model.AddComponent(skeleton); using var bones = new ValueTypeRentMemory <Bone>(fbx.Skeletons[skeletonIndex].BoneCount, false); fbx.Skeletons[skeletonIndex].CreateBones(bones.AsSpan()); await skeleton.LoadAsync(bones, timingPoints, cancellationToken : token); } load.Invoke(fbx.Vertices, fbx.Indices); await UniTask.SwitchToThreadPool(); // ↓ thread pool -------------------------------------- // 'using' scope ends here. Dispose resources on a thread pool. // Nobody knows the thread if exceptions are thrown in this method, // but I don't care about that. }
internal static FbxSemanticsUnsafe <TVertex> ParseUnsafe(Stream stream, bool leaveStreamOpen = true, CancellationToken cancellationToken = default) { ValueTypeRentMemory <RawString> textures = default; FbxObject?fbx = null; try { var vertexCreator = SkinnedVertexCreator <TVertex> .Build(); fbx = FbxParser.Parse(stream); using var resolver = new SemanticResolver(fbx); var objectsNode = resolver.ObjectsNode; var(meshes, skeletons) = ParseMeshAndSkeleton(resolver, vertexCreator, cancellationToken); try { using (meshes) { textures = ParseTexture(objectsNode, cancellationToken); //ParseMaterial(objectsNode); var(vertices, indices) = meshes.CreateCombined(); return(new FbxSemanticsUnsafe <TVertex>(ref fbx, ref indices, ref vertices, ref textures, ref skeletons)); } } catch { skeletons.Dispose(); throw; } } catch { fbx?.Dispose(); textures.Dispose(); throw; } finally { if (leaveStreamOpen == false) { stream?.Dispose(); } } }
internal ValueTypeRentMemoryDebuggerTypeProxy(ValueTypeRentMemory <T> entity) { _entity = entity; }
static async UniTask BackToMainThread(ArrayTexture arrayTexture, Vector3i size, ValueTypeRentMemory <ColorByte> buffer, FrameTimingPoint timingPoint, CancellationToken ct) { try { await timingPoint.Next(ct); arrayTexture.Load(new(size.X, size.Y), size.Z, buffer.AsSpan()); } finally { buffer.Dispose(); } }
private static UniTask BuildTexture(PMXObject pmx, ResourceFile pmxFile, Model3D model, UnsafeRawArray <SkinnedVertex> vertices, CancellationToken ct) { var arrayTexture = new ArrayTexture(TextureConfig.BilinearRepeat); model.AddComponent(arrayTexture); var dir = ResourcePath.GetDirectoryName(pmxFile.Name); var textureNames = pmx.TextureList.AsSpan(); var resourcePackage = pmxFile.Package; var materials = pmx.MaterialList.AsSpan(); using var matTexMem = new ValueTypeRentMemory <int>(materials.Length, true); var matTex = matTexMem.AsSpan(); for (int i = 0; i < materials.Length; i++) { matTex[i] = materials[i].Texture; } matTex.Sort(); var texCount = 0; if (matTex.Length > 0) { texCount = 1; for (int i = 1; i < matTex.Length; i++) { if (matTex[i - 1] != matTex[i]) { texCount++; } } } using var textureUsed = new ValueTypeRentMemory <bool>(textureNames.Length, true); var size = new Vector2i(1024, 1024); // TODO: var count = 0; var buffer = new ValueTypeRentMemory <ColorByte>(size.X * size.Y * texCount, false); try { for (int i = 0; i < textureNames.Length; i++) { if (matTex.Contains(i) == false) { continue; } using var _ = GetTexturePath(dir, textureNames[i], out var texturePath, out var ext); var path = texturePath.ToString(); // Some pmx have the texture paths that don't exist. (Nobody references them.) // So skip them. if (resourcePackage.TryGetStream(path, out var stream) == false) { continue; } textureUsed[i] = true; var dest = buffer.AsSpan(count * size.X * size.Y); try { using var image = Image.LoadToImageSource(stream, Image.GetTypeFromExt(ext)); if (image.Width != size.X || image.Height != size.Y) { using var resized = Image.Resized(image, size); resized.GetPixels().CopyTo(dest); } else { image.GetPixels().CopyTo(dest); } count++; } finally { stream.Dispose(); } } // Calculate texture index of each vertex { var indices = pmx.SurfaceList.AsSpan().MarshalCast <Surface, int>(); int i = 0; foreach (var mat in materials) { for (int j = 0; j < mat.VertexCount; j++) { var unusedCount = 0; foreach (var isUsed in textureUsed.AsSpan(0, mat.Texture)) { if (isUsed == false) { unusedCount++; } } vertices[indices[i++]].TextureIndex = mat.Texture - unusedCount; } } } } catch { buffer.Dispose(); throw; } return(BackToMainThread(arrayTexture, new Vector3i(size.X, size.Y, count), buffer, model.Screen !.TimingPoints.Update, ct));