Example #1
0
        private static UnsafeRawArray <Components.Bone> GetBones(PMXObject pmx)
        {
            var pmxBones = pmx.BoneList.AsSpan();
            var bones    = new UnsafeRawArray <Components.Bone>(pmxBones.Length, false);

            try {
                for (int i = 0; i < pmxBones.Length; i++)
                {
                    var     parentBone = pmxBones[i].ParentBone >= 0 ? pmxBones[i].ParentBone : (int?)null;
                    Vector3 boneVec;
                    var     c = pmxBones[i].ConnectedBone;
                    if (c >= 0)
                    {
                        boneVec = pmxBones[c].Position.ToVector3() - pmxBones[i].Position.ToVector3();
                    }
                    else
                    {
                        boneVec = pmxBones[i].PositionOffset.ToVector3();
                    }

                    bones[i] = new Components.Bone();
                }
            }
            catch {
                bones.Dispose();
                throw;
            }
            return(bones);
        }
Example #2
0
        private static UnsafeRawArray <SkinnedVertex> GetVertices(PMXObject pmx)
        {
            // build vertices
            var pmxVertices = pmx.VertexList.AsSpan();
            var vertices    = new UnsafeRawArray <SkinnedVertex>(pmxVertices.Length, false);

            try {
                for (int i = 0; i < pmxVertices.Length; i++)
                {
                    vertices[i] = pmxVertices[i].ToRigVertex();
                }
            }
            catch {
                vertices.Dispose();
                throw;
            }
            return(vertices);
        }
Example #3
0
        private static async UniTask LoadToModel(PMXObject pmx, Model3D model, Model3DLoadMeshDelegate load, ModelState obj)
        {
            // ------------------------------
            //      ↓ thread pool
            Debug.Assert(Engine.IsThreadMain == false);
            var screen = model.GetValidScreen();

            // Don't make the followings parallel like UniTask.WhenAll.
            // ModelData will be disposed when an exception is throw, but another parallel code could not know that.
            using var vertices = GetVertices(pmx);
            using var bones    = GetBones(pmx);
            await BuildTexture(pmx, obj.File, model, vertices, obj.CancellationToken);

            await UniTask.SwitchToThreadPool();

            var skeleton = new HumanoidSkeleton();

            model.AddComponent(skeleton);
            await skeleton.LoadAsync(bones.AsSpanLike(), screen.TimingPoints, obj.CancellationToken);

            //      ↑ thread pool
            // ------------------------------
            await screen.TimingPoints.Update.NextOrNow(obj.CancellationToken);

            // ------------------------------
            //      ↓ main thread
            Debug.Assert(Engine.CurrentContext == screen);
            Debug.Assert(model.LifeState == LifeState.Activating);

            model.Shader ??= new PmxModelShader();

            // load vertices and indices
            load.Invoke(vertices.AsSpan().AsReadOnly(), pmx.SurfaceList.AsSpan().MarshalCast <Surface, int>());

            //      ↑ main thread
            // ------------------------------
        }
Example #4
0
        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));