Skip to content

Instantly share code, notes, and snippets.

@w0wca7a
Created February 27, 2026 10:46
Show Gist options
  • Select an option

  • Save w0wca7a/979ccea6e06a2b12f904262e60670ef1 to your computer and use it in GitHub Desktop.

Select an option

Save w0wca7a/979ccea6e06a2b12f904262e60670ef1 to your computer and use it in GitHub Desktop.
Silk.NET.Assimp blendshapes handler
using Silk.NET.Assimp;
using System;
using System.Numerics;
using static MeshHandler.Program.Animation;
namespace MeshHandler;
public class Program
{
Assimp assimp = Assimp.GetApi();
//List<Material> materials = new List<Material>();
//List<Mesh> meshes = new List<Mesh>();
public static void Main(string[] args)
{
var program = new Program();
program.Run();
}
private unsafe void Run()
{
// Флаги для импорта (рекомендуемые настройки)
const uint flags = (uint)(
//PostProcessSteps.Triangulate | // Триангуляция mesh'ей
//PostProcessSteps.GenerateSmoothNormals | // Генерация нормалей
//PostProcessSteps.FlipUVs | // Корректировка UV для OpenGL
//PostProcessSteps.CalculateTangentSpace | // Для нормал маппинга
//PostProcessSteps.JoinIdenticalVertices | // Оптимизация вершин
//PostProcessSteps.ImproveCacheLocality | // Улучшение кэша
//PostProcessSteps.OptimizeMeshes // Оптимизация мешей
PostProcessSteps.None
);
//string path = @"C:\Users\damzi\Desktop\andryuha\andryuha\Resources\Test\headWithMorps.fbx";
//string path = @"C:\Users\damzi\Desktop\andryuha\andryuha\Resources\Test\headWithMorpsGrouped.fbx";
string path = @"C:\Users\damzi\Desktop\andryuha\andryuha\Resources\Test\Test.fbx";
var scene = assimp.ImportFile(path, flags);
if (scene == null || scene->MFlags == Assimp.SceneFlagsIncomplete || scene->MRootNode == null)
{
string error = assimp.GetErrorStringS();
Console.WriteLine($"Ошибка загрузки: {error}");
return;
}
// Scene handler
ProcessNode(scene->MRootNode, scene);
}
private unsafe void ProcessNode(Node* node, Scene* scene)
{
// Обрабатываем все меши в текущем узле
for (int i = 0; i < node->MNumMeshes; i++)
{
Mesh* mesh = scene->MMeshes[node->MMeshes[i]];
var meshData = ProcessMeshes(mesh, scene);
}
// Рекурсивно обрабатываем дочерние узлы
for (int i = 0; i < node->MNumChildren; i++)
{
ProcessNode(node->MChildren[i], scene);
}
}
private unsafe MeshData ProcessMeshes(Mesh* mesh, Scene* scene)
{
MeshData meshData = null;
// Извлечение вершин
for (int i = 0; i < mesh->MNumVertices; i++)
{
Vector3 vertex = mesh->MVertices[i];
// Используйте координаты: vertex.X, vertex.Y, vertex.Z
// Нормали (если есть)
if (mesh->MNormals != null)
{
Vector3 normal = mesh->MNormals[i];
}
// Текстурные координаты (обычно до 8 каналов)
if (mesh->MTextureCoords[0] != null)
{
Vector3 texCoord = mesh->MTextureCoords[0][i];
// Для 2D текстур используйте texCoord.X, texCoord.Y
}
// Цвета вершин (если есть)
if (mesh->MColors[0] != null)
{
Vector4 color = mesh->MColors[0][i];
}
}
// Извлечение индексов (для индексированного рендеринга)
for (int i = 0; i < mesh->MNumFaces; i++)
{
Face face = mesh->MFaces[i];
for (int j = 0; j < face.MNumIndices; j++)
{
uint index = face.MIndices[j];
// Используйте индекс
}
}
// Информация о материале
if (mesh->MMaterialIndex >= 0)
{
Material* material = scene->MMaterials[mesh->MMaterialIndex];
// Получение пути к текстуре диффузного цвета
AssimpString path;
if (assimp.GetMaterialTexture(material, TextureType.Diffuse, 0, &path, null, null, null, null, null, null)
== Return.Success)
{
string texturePath = path.AsString;
// Загрузите текстуру через Silk.NET.OpenGL
}
}
//Проверяем наличие анимаций Blendshapes
if (mesh->MNumAnimMeshes > 0)
{
for (int i = 0; i < mesh->MNumAnimMeshes; i++)
{
//Animation* anim = scene->MAnimations[i];
if (mesh->MAnimMeshes[i] == null) continue;
var animMesh = mesh->MAnimMeshes[i];
string name = animMesh->MName.AsString;
float weight = animMesh->MWeight;
uint numVertices = animMesh->MNumVertices;
Vector3[] vertices = new Vector3[numVertices];
Vector3[] normals = null;
Vector3[] tangents = null;
Vector3[] bitangents = null;
Vector4[][] colors = new Vector4[8][];
// Vertices
for (int v = 0; v < numVertices; v++)
{
vertices[v] = animMesh->MVertices[v];
}
// Normals
if (animMesh->MNormals != null)
{
Vector3[] result = new Vector3[numVertices];
for (int j = 0; j < numVertices; j++)
{
result[j] = animMesh->MNormals[j];
}
normals = result;
}
// Tangents
if (animMesh->MTangents != null)
{
Vector3[] result = new Vector3[numVertices];
for (int t = 0; t < numVertices; t++)
{
result[t] = animMesh->MTangents[t];
}
tangents = result;
}
// Bitangents
if (animMesh->MBitangents != null)
{
Vector3[] result = new Vector3[numVertices];
for (int b = 0; b < numVertices; b++)
{
result[b] = animMesh->MTangents[b];
}
bitangents = result;
}
// Colors
for (int colorSet = 0; colorSet < 8; colorSet++)
{
if (animMesh->MColors[colorSet] != null)
{
Vector4[] result = new Vector4[numVertices];
for (int c = 0; c < numVertices; c++)
{
result[c] = animMesh->MColors[colorSet][c];
}
colors[colorSet] = result;
}
}
/*
// Поиск каналов морфинга
for (int j = 0; j < anim->MNumMorphMeshChannels; j++)
{
MorphMeshChannel* morphChannel = anim->MMorphMeshChannels[j];
// Обработка ключей морфинга
for (int k = 0; k < morphChannel->MNumKeys; k++)
{
MorphMeshKey key = morphChannel->MKeys[k];
// key.MTime - время ключа
// key.MValue - вес морфинга (0.0 - 1.0)
}
}
*/
meshData = new MeshData
{
Name = name,
Weight = weight,
VertexCount = (int)numVertices,
Vertices = vertices,
Normals = normals,
Tangents = tangents,
Bitangents = bitangents,
Colors = colors
};
}
}
// Обработка морфинга
var animations = ProcessAnimations(mesh, scene);
return meshData;
}
private unsafe Dictionary<string, Animation> ProcessAnimations(Mesh* mesh, Scene* scene)
{
Dictionary<string, Animation> animations = [];
var ani = scene->MAnimations;
var aniCount = scene->MNumAnimations;
if (ani == null || aniCount == 0) return animations;
for (int i = 0; i < aniCount; i++)
{
var anim = ani[i];
MeshMorph[] meshMorphs = null;
if (anim-> MNumMorphMeshChannels > 0)
{
meshMorphs = new MeshMorph[anim->MNumMorphMeshChannels];
for (int mm = 0; mm < anim->MNumMorphMeshChannels; mm++)
{
var _keys = anim->MMorphMeshChannels[mm]->MKeys;
var keysCount = anim->MMorphMeshChannels[mm]->MNumKeys;
MorphMeshKey[] keys = new MorphMeshKey[keysCount];
if (_keys != null || keysCount > 0)
{
for (int k = 0; k < keysCount; k++)
{
var valuesCount = _keys[k].MNumValuesAndWeights;
uint* valuesPtr = _keys[k].MValues;
double* weightsPtr = _keys[k].MWeights;
var valuesArray = new Span<uint>(valuesPtr, (int)valuesCount).ToArray();
var weightsArray = new Span<double>(weightsPtr, (int)valuesCount).ToArray();
var time = _keys[valuesCount].MTime;
var values = valuesArray;
var weights = weightsArray;
var valuesAndWeightsCount = valuesCount;
var morphMeshKey = new MorphMeshKey();
morphMeshKey.Time = time;
morphMeshKey.Values = values;
morphMeshKey.Weights = weights;
morphMeshKey.ValuesAndWeightsCount = valuesAndWeightsCount;
keys[k] = morphMeshKey;
//{
// Time = time,
// Values = values,
// Weights = weights,
// ValuesAndWeightsCount = valuesAndWeightsCount
//};
}
}
MeshMorph meshMorph = new()
{
Name = anim->MMorphMeshChannels[mm]->MName.AsString,
KeysCount = keysCount,
Keys = keys
};
meshMorphs[mm] = meshMorph;
}
}
var importedAnimation = new Animation()
{
Name = anim->MName.AsString,
Duration = anim->MDuration,
TicksPerSecond = anim->MTicksPerSecond,
ChannelsCount = anim->MNumChannels,
MeshChannelsCount = anim->MNumMeshChannels,
MorphMeshChannelsCount = anim->MNumMorphMeshChannels,
MorphMeshChannels = [.. meshMorphs]
};
animations.Add(importedAnimation.Name, importedAnimation);
}
return animations;
}
public class MeshData
{
public string Name { get; set; }
public float Weight { get; set; }
public int VertexCount { get; set; }
public Vector3[] Vertices { get; set; }
public Vector3[] Normals { get; set; }
public Vector3[] Tangents { get; set; }
public Vector3[] Bitangents { get; set; }
public Vector4[][] Colors { get; set; } = new Vector4[8][];
}
public class Animation
{
public string Name { get; set; }
public double Duration { get; set; }
public double TicksPerSecond { get; set; }
public uint ChannelsCount { get; set; }
public Dictionary<string, float[]> Channels;
//public NodeAnim** mChannels = null,
public uint MeshChannelsCount { get; set; }
public Dictionary<string, float[]> MeshChannels;
//MeshAnim** mMeshChannels = null,
public uint MorphMeshChannelsCount { get; set; }
public List<MeshMorph> MorphMeshChannels = [];
//public MeshMorphAnim** mMorphMeshChannels = null
public class MeshMorph
{
public string Name { get; set; }
public uint KeysCount { get; set; }
public MorphMeshKey[] Keys { get; set; }
}
public class MorphMeshKey
{
public double Time { get; set; }
public uint[] Values { get; set; }
public double[] Weights { get; set; }
public uint ValuesAndWeightsCount { get; set; }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment