private void ApplyBoneMatricesInternal(SkeletonAnimRigNode node, MeshEntity entity, Matrix4x4[] boneMatrices, float timeStamp, Matrix4x4 parentTransform) { string name = node.Name; Matrix4x4 nodeTransform = node.LocalTransform; SkeletonAnimChannel animChannel = GetMeshAnimBone(name); if (animChannel != null) { nodeTransform = animChannel.GetMatrixAtTimestamp(timeStamp); } Matrix4x4 globalTransform = nodeTransform * parentTransform; MeshBone bone = BoneIndexFromEntity(entity, name); if (bone != null) { boneMatrices[bone.BoneIndex] = bone.OffsetMatrix * globalTransform; } if (node.Children == null) { return; } for (var i = 0; i < node.Children.Length; i++) { SkeletonAnimRigNode child = node.Children[i]; ApplyBoneMatricesInternal(child, entity, boneMatrices, timeStamp, globalTransform); } }
private MeshBone BoneIndexFromEntity(MeshEntity entity, string boneName) { for (var i = 0; i < entity.Meshes.Length; i++) { Mesh mesh = entity.Meshes[i]; if (mesh.Bones == null) { continue; } for (var j = 0; j < mesh.Bones.Length; j++) { MeshBone bone = mesh.Bones[j]; if (bone.Name == boneName) { return(bone); } } } return(null); }
protected override void Run() { MeshEntity meshEntity = _input.Read(); double srcUMin = 9999, srcVMin = 9999; double srcUMax = -9999, srcVMax = -9999; // Determine the boundaries or set them to (0,0)..(1,1) and treat the UV coordinates as a subset within. if (_stretch.Value) { foreach (Face face in meshEntity) { foreach (HalfVertex hv in face.HalfVertices) { if (hv.UV0.X < srcUMin) { srcUMin = hv.UV0.X; } if (hv.UV0.X > srcUMax) { srcUMax = hv.UV0.X; } if (hv.UV0.Y < srcVMin) { srcVMin = hv.UV0.Y; } if (hv.UV0.Y > srcVMax) { srcVMax = hv.UV0.Y; } } } } else { srcUMin = 0; srcVMin = 0; srcUMax = 1; srcVMax = 1; } // Calculate the extent within the target texture if (srcUMax == srcUMin || srcVMax == srcVMin) { Logger.Log("Source UV coordinates do not expand themselves to a face, cannot proceed", Logging.LogType.Error); return; } double dstUStretch = (_UMax.Value - _UMin.Value) / (srcUMax - srcUMin); double dstVStretch = (_VMax.Value - _VMin.Value) / (srcVMax - srcVMin); // And remap all the UV coordinates. foreach (Face face in meshEntity) { foreach (HalfVertex hv in face.HalfVertices) { hv.UV0 = new Vector2D( (float)((hv.UV0.X - srcUMin) * dstUStretch + _UMin.Value), (float)((hv.UV0.Y - srcVMin) * dstVStretch + _VMin.Value) ); } } //finally, return the newly create pathentity _output.Write(meshEntity); }
protected override void RenderImGui() { if (ImGui.BeginMenuBar()) { ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0.3f, 0.3f, 0.3f, 1f)); if (ImGui.Button("Open OBJ")) { var explorer = new FileExplorer <ObjMeshAsset>(asset => { DisplayObject.Entity = asset.Entity; }); _toolsRoot.AddLegacyWindow(explorer); } if (ImGui.Button("Open EM3")) { var explorer = new FileExplorer <EmotionMeshAsset>(asset => { DisplayObject.Entity = asset.Entity; }); _toolsRoot.AddLegacyWindow(explorer); } if (ImGui.Button("Open Sprite Stack Texture")) { var explorer = new FileExplorer <SpriteStackTexture>(asset => { _toolsRoot.AddLegacyWindow(new Vec2Modal(size => { MeshEntity entity = asset.GetSpriteStackEntity(size); DisplayObject.Entity = entity; }, "Sprite Stack Settings", "Individual Frame Size", new Vector2(32, 32))); }); _toolsRoot.AddLegacyWindow(explorer); } ImGui.EndMenuBar(); } ImGui.Checkbox("Show Terrain", ref _showTerrain); Vector3 pos = DisplayObject.Position; if (ImGui.DragFloat3("Position", ref pos)) { DisplayObject.Position = pos; } float scale = DisplayObject.Scale; if (ImGui.DragFloat("Scale", ref scale)) { DisplayObject.Scale = scale; } Vector3 rot = DisplayObject.RotationDeg; if (ImGui.DragFloat3("Rotation", ref rot)) { DisplayObject.RotationDeg = rot; } if (DisplayObject.Entity != null && DisplayObject.Entity.Animations != null && ImGui.BeginCombo("Animation", DisplayObject.CurrentAnimation)) { if (ImGui.Button("None")) { DisplayObject.SetAnimation(null); } for (var i = 0; i < DisplayObject.Entity.Animations.Length; i++) { SkeletalAnimation anim = DisplayObject.Entity.Animations[i]; if (ImGui.Button($"{anim.Name}")) { DisplayObject.SetAnimation(anim.Name); } } ImGui.EndCombo(); } }
public MeshEntity GetSpriteStackEntity(Vector2 frameSize) { Vector2 size = Texture.Size; var frameWidth = (int)frameSize.X; var frameHeight = (int)frameSize.Y; var frameCount = (int)(size.X / frameWidth); var frames = new SpriteStackFrame[frameCount]; for (var i = 0; i < frameCount; i++) { var frame = new SpriteStackFrame(frameWidth, frameHeight); frames[i] = frame; } // Get the non 0 alpha pixels from the image. for (var i = 0; i < _textureData.Length; i += 4) { var c = new Color(_textureData[i], _textureData[i + 1], _textureData[i + 2], _textureData[i + 3], _textureDataFormat); if (c.A == 0) { continue; } int pixelIdx = i / 4; int x = pixelIdx % (int)size.X; int y = pixelIdx / (int)size.X; int frame = x / frameWidth; int frameStart = frame * frameWidth; int frameX = x - frameStart; frames[frame].SetPixel(frameX + y * frameWidth, c); } // Convert the pixels to voxel cubes. Vector3 center = new Vector3(frameWidth / 2, frameCount / 2, frameHeight / 2) * 2; for (var fIdx = 0; fIdx < frameCount; fIdx++) { SpriteStackFrame frame = frames[fIdx]; frame.Vertices = new VertexData[frame.FilledPixels * 8]; frame.Indices = new ushort[frame.FilledPixels * 6 * 6]; // 6 sides, one quad is 6 indices var pixelCount = 0; for (var pIdx = 0; pIdx < frame.Pixels.Length; pIdx++) { Color color = frame.Pixels[pIdx]; if (color.A == 0) { continue; } // Check if the pixel is not occluded by another. var pixelTwoDeeCoord = new Vector2(pIdx % frameWidth, pIdx / frameWidth); var anyTransp = false; foreach (Vector2 dir in Maths.CardinalDirections2D) { Vector2 otherPixel = pixelTwoDeeCoord + dir; if (otherPixel.X < 0 || otherPixel.Y < 0 || otherPixel.X > frameWidth || otherPixel.Y > frameHeight) { anyTransp = true; break; } var oneDeeCoord = (int)(otherPixel.Y * frameWidth + otherPixel.X); Color otherC = frame.Pixels[oneDeeCoord]; if (otherC.A < 255) { anyTransp = true; break; } } // Check between frames (layers). if (!anyTransp) { if (fIdx == 0 || fIdx == frameCount - 1) { anyTransp = true; } else { SpriteStackFrame prevFrame = frames[fIdx - 1]; SpriteStackFrame nextFrame = frames[fIdx + 1]; Color cBelow = prevFrame.Pixels[pIdx]; Color cAbove = nextFrame.Pixels[pIdx]; if (cBelow.A < 255 || cAbove.A < 255) { anyTransp = true; } } if (!anyTransp) { continue; } } Span <VertexData> thisPixel = new Span <VertexData>(frame.Vertices).Slice(pixelCount * 8, 8); Span <ushort> thisPixelIndices = new Span <ushort>(frame.Indices).Slice(pixelCount * 6 * 6, 6 * 6); // Darken the lower layers. //int halfway = (frames.Length - 1) / 2; //if (i < halfway) //{ // int layerFromTop = halfway - i; // int darkening = Emotion.Utility.Maths.Clamp(3 * layerFromTop, 0, 255); // int r = Emotion.Utility.Maths.Clamp(color.R - darkening, 0, 255); // int g = Emotion.Utility.Maths.Clamp(color.G - darkening, 0, 255); // int b = Emotion.Utility.Maths.Clamp(color.B - darkening, 0, 255); // color.R = (byte)r; // color.G = (byte)g; // color.B = (byte)b; //} uint c = color.ToUint(); for (var ic = 0; ic < thisPixel.Length; ic++) { thisPixel[ic].Color = c; } // Cube - 36 vertices, 12 triangles, 6 sides // Cube - 36 indices, 8 vertices, 6 quads thisPixel[0].Vertex = new Vector3(-1, -1, 1); thisPixel[1].Vertex = new Vector3(1, -1, 1); thisPixel[2].Vertex = new Vector3(1, 1, 1); thisPixel[3].Vertex = new Vector3(-1, 1, 1); thisPixel[4].Vertex = new Vector3(-1, -1, -1); thisPixel[5].Vertex = new Vector3(1, -1, -1); thisPixel[6].Vertex = new Vector3(1, 1, -1); thisPixel[7].Vertex = new Vector3(-1, 1, -1); // Front thisPixelIndices[00] = 0; thisPixelIndices[01] = 1; thisPixelIndices[02] = 2; thisPixelIndices[03] = 2; thisPixelIndices[04] = 3; thisPixelIndices[05] = 0; // Right thisPixelIndices[06] = 1; thisPixelIndices[07] = 5; thisPixelIndices[08] = 6; thisPixelIndices[09] = 6; thisPixelIndices[10] = 2; thisPixelIndices[11] = 1; // Back thisPixelIndices[12] = 7; thisPixelIndices[13] = 6; thisPixelIndices[14] = 5; thisPixelIndices[15] = 5; thisPixelIndices[16] = 4; thisPixelIndices[17] = 7; // Left thisPixelIndices[18] = 4; thisPixelIndices[19] = 0; thisPixelIndices[20] = 3; thisPixelIndices[21] = 3; thisPixelIndices[22] = 7; thisPixelIndices[23] = 4; // Bottom thisPixelIndices[24] = 4; thisPixelIndices[25] = 5; thisPixelIndices[26] = 1; thisPixelIndices[27] = 1; thisPixelIndices[28] = 0; thisPixelIndices[29] = 4; // Top thisPixelIndices[30] = 3; thisPixelIndices[31] = 2; thisPixelIndices[32] = 6; thisPixelIndices[33] = 6; thisPixelIndices[34] = 7; thisPixelIndices[35] = 3; // Add frame vertex index offset as the whole frame will be drawn together. int indexOffset = pixelCount * 8; for (var k = 0; k < thisPixelIndices.Length; k++) { thisPixelIndices[k] = (ushort)(thisPixelIndices[k] + indexOffset); } int x = pIdx % frameWidth; int y = pIdx / frameWidth; var pixelPositionMatrix = Matrix4x4.CreateTranslation(x * 2 - center.X, y * 2 - center.Y, fIdx * 2 + 1); for (var iv = 0; iv < thisPixel.Length; iv++) { thisPixel[iv].Vertex = Vector3.Transform(thisPixel[iv].Vertex, pixelPositionMatrix); } pixelCount++; } } var spriteStackEntity = new MeshEntity { Name = Name, Meshes = frames }; return(spriteStackEntity); }
/// <summary> /// Apply the transformations of this animation to the bone matrices of a particular entity. /// </summary> /// <param name="entity">The entity the bone matrices are to be filled for.</param> /// <param name="boneMatrices">Array of matrices. Indexed relative to bone indices in the entity.</param> /// <param name="timeStamp">The current animation time 0-Duration</param> public void ApplyBoneMatrices(MeshEntity entity, Matrix4x4[] boneMatrices, float timeStamp) { ApplyBoneMatricesInternal(entity.AnimationRig, entity, boneMatrices, timeStamp, Matrix4x4.Identity); }
public static MeshEntity EntityFromByteArray(ReadOnlyMemory <byte> byteArray) { var entity = new MeshEntity(); var memoryStream = new ReadOnlyMemoryStream(byteArray); var reader = new BinaryReader(memoryStream); char check = reader.ReadChar(); char check2 = reader.ReadChar(); char check3 = reader.ReadChar(); Debug.Assert(check == 'E' && check2 == 'M' && check3 == '3'); byte version = reader.ReadByte(); if (version != 1) { return(null); } entity.Name = reader.ReadString(); entity.Scale = reader.ReadSingle(); var materialMap = new Dictionary <string, MeshMaterial>(); var textureMap = new Dictionary <string, Texture>(); int meshesCount = reader.ReadInt32(); var meshArray = new Mesh[meshesCount]; entity.Meshes = meshArray; for (var i = 0; i < meshesCount; i++) { var newMesh = new Mesh { Name = reader.ReadString() }; string materialName = reader.ReadString(); if (materialMap.TryGetValue(materialName, out MeshMaterial mat)) { newMesh.Material = mat; } else { mat = new MeshMaterial { Name = materialName, DiffuseColor = new Color(reader.ReadUInt32()) }; bool hasDiffuseTexture = reader.ReadBoolean(); if (hasDiffuseTexture) { string textureName = reader.ReadString(); mat.DiffuseTextureName = textureName; if (textureMap.TryGetValue(textureName, out Texture diffuseTexture)) { mat.DiffuseTexture = diffuseTexture; } else { int textureByteLength = reader.ReadInt32(); float width = reader.ReadSingle(); float height = reader.ReadSingle(); int textureFormat = reader.ReadInt32(); Texture t = Texture.NonGLThreadInitialize(new Vector2(width, height)); mat.DiffuseTexture = t; byte[] data = reader.ReadBytes(textureByteLength); GLThread.ExecuteGLThreadAsync(() => { Texture.NonGLThreadInitializedCreatePointer(t); t.Upload(t.Size, data, (PixelFormat)textureFormat); }); textureMap.Add(textureName, t); } } newMesh.Material = mat; materialMap.Add(materialName, mat); } int vertexFormat = reader.ReadByte(); int length = reader.ReadInt32(); if (vertexFormat == 0) // Normal vertices { var vertices = new VertexData[length]; for (var j = 0; j < length; j++) { ref VertexData vert = ref vertices[j]; vert.Vertex = ReadVector3(reader); vert.UV = ReadVector2(reader); vert.Color = reader.ReadUInt32(); } newMesh.Vertices = vertices; }
protected override void DisposeInternal() { Entity = null; // todo: maybe clean up entity textures or w/e? }
protected override void CreateInternal(ReadOnlyMemory <byte> data) { Entity = EntityFromByteArray(data); }
protected override void CreateInternal(ReadOnlyMemory <byte> data) { var str = new ReadOnlyLinkedMemoryStream(); str.AddMemory(data); _assContext ??= new AssimpContext(); Scene scene = _assContext.ImportFileFromStream(str, PostProcessSteps.Triangulate | PostProcessSteps.FlipUVs | PostProcessSteps.OptimizeGraph | PostProcessSteps.OptimizeMeshes); var embeddedTextures = new List <Texture>(); for (var i = 0; i < scene.TextureCount; i++) { EmbeddedTexture assTexture = scene.Textures[i]; var embeddedTexture = new TextureAsset(); embeddedTexture.Create(assTexture.CompressedData); embeddedTextures.Add(embeddedTexture.Texture); } _materials = new List <MeshMaterial>(); for (var i = 0; i < scene.MaterialCount; i++) { Material material = scene.Materials[i]; Color4D diffColor = material.ColorDiffuse; bool embeddedTexture = material.HasTextureDiffuse && embeddedTextures.Count > material.TextureDiffuse.TextureIndex; var emotionMaterial = new MeshMaterial { Name = material.Name, DiffuseColor = new Color(new Vector4(diffColor.R, diffColor.G, diffColor.B, diffColor.A)), DiffuseTextureName = embeddedTexture ? $"EmbeddedTexture{material.TextureDiffuse.TextureIndex}" : null, DiffuseTexture = embeddedTexture ? embeddedTextures[material.TextureDiffuse.TextureIndex] : null }; _materials.Add(emotionMaterial); } _animations = new List <SkeletalAnimation>(); ProcessAnimations(scene); _meshes = new List <Mesh>(); Node rootNode = scene.RootNode; SkeletonAnimRigNode animRigRoot = ProcessNode(scene, rootNode); animRigRoot.LocalTransform *= Matrix4x4.CreateRotationX(-90 * Maths.DEG2_RAD); // Convert to right handed Z is up. Entity = new MeshEntity { Name = Name, Meshes = _meshes.ToArray(), Animations = _animations.ToArray(), AnimationRig = animRigRoot }; object scaleData = scene.RootNode.Metadata.GetValueOrDefault("UnitScaleFactor").Data; var scaleF = 1f; if (scaleData is float f) { scaleF = f; } Entity.Scale = scaleF; }