public void OptimizeBones() { // Для всех вершин мержу веса костей в одно значение если есть 2 (и более) одинаковые кости в вершине // далее нормализирую веса в каждой вершине. Индексы костей при этом сортируются в порядке возрастания (в конце идут неиспользуемые кости с индексом и весом 0) var indexedWeights = new Dictionary <byte, float>(); foreach (var mesh in Meshes) { foreach (var vertex in mesh.Vertices) { int i; indexedWeights.Clear(); for (i = 0; i < 4; i++) { indexedWeights[vertex.BoneIndexes[i]] = 0f; } for (i = 0; i < 4; i++) { indexedWeights[vertex.BoneIndexes[i]] += vertex.BoneWeights[i]; } var newIndexes = new ByteVec4(); var newWeights = new Vec4(); i = 0; foreach (var indexedWeight in indexedWeights.OrderBy(x => x.Key)) { newIndexes[i] = indexedWeight.Key; newWeights[i] = indexedWeight.Value; i++; } newWeights.NormalizeSum(); vertex.BoneIndexes = newIndexes; vertex.BoneWeights = newWeights; } } // Перестраиваем массив костей, удаляя пустые костаи и заменяя старые индексы в вершинах на новые // ToDo: сделать это, но только сейчас есть проблема с поиском индексов, которые ссылаются на эти кости. // проблема в том что отсюда надо знать какие меши ссылаются на эти кости. Сейчас уже есть основной меш + аттачменты. // сначало нужно сделать систему при которой скелет будет знать какие мешы к нему привязаны а потом уже делать эту оптимизацию // дальше еще есть проблема что индексы кости просто в разных объектах могут висеть (в Wh* классах точно они есть), надо убедиться что их дальше использования не будет. // Все исползования должны быть только по ссылке на WowBone. }
public void PostRenderCommand(Vector position, float z_orther, float rotate, Vector scale, HalfVector anchor, ByteVec4 color, bool vertical_flip, bool horizon_flip) => PostRenderCommand(position, z_orther, _bound, rotate, scale, anchor, color, vertical_flip, horizon_flip);
public WowObject BuildFromCharacterWhModel(WhModel whCharacterModel) { // ToDo: Смотреть как работает, Wow.Model.draw, // по сути в зависимости от алгоритма отрисовки идет и алгоритм построения объектов для дальнейшей отрисовки (то что тут происодит) // Сам перс var characterObject = new WowObject(); // Добавляем MainMesh characterObject.Meshes.Add(MakeMeshFromWhModel(whCharacterModel)); characterObject.Bones = MakeBoneHierarchyFromWhModel(whCharacterModel); characterObject.OptimizeBones(); TranslateBonePositionsFromGlobalToLocal(characterObject); // Рога if (whCharacterModel.HornsModel != null) { AddAdditionalMeshToCharacterObject(characterObject, whCharacterModel.HornsModel); } // Маунт if (whCharacterModel.Mount != null) { // ToDo: хз че тут делать, создать его вообще отдельно? //var mountObject = new WowObject() //{ // Parent = characterObject //}; //// MainMesh (не факт что я вообще тут правильно делаю, т.к. еще не тестил) //mountObject.Meshes.Add(MakeMeshFromWhModel(whCharacterModel.Mount)); //characterObject.Children.Add(mountObject); } // Итемы foreach (var whItemInSlot in whCharacterModel.Items) { var whItem = whItemInSlot.Value; if (whItem?.Models == null) { continue; } foreach (var whItemModel in whItem.Models) { if (whItemModel?.Model == null) { continue; } if (whItemModel.Bone > -1 && whItemModel.Bone < whCharacterModel.Bones.Length) { var itemPosition = Vec3.Scale(Vec3.ConvertPositionFromWh(whItemModel.Attachment.Position), _scale); var itemObject = new WowObject() { Parent = characterObject, GlobalPosition = itemPosition, }; // MainMesh - по сути он использоваться будет тлолько в AttachedWowObjects кости itemObject.Meshes.Add(MakeMeshFromWhModel(whItemModel.Model)); // Если у итема есть кость, к которой можно прикрепиться if (itemObject.Parent != null && whItemModel.Attachment.Bone >= 0) { var parentAttachmentBone = itemObject.Parent.Bones[whItemModel.Attachment.Bone]; var whParentAttachmentBone = whItemModel.Model.Parent.Bones[whItemModel.Attachment.Bone]; // записываем объект этого итема в кость, к которой крепимся(у родительского объекта) parentAttachmentBone.AttachedWowObjects.Add(itemObject); // меняем данные о костях в вершинах так итема так, чтобы при привязке к скелету родителя этот итем двигался вместе с костью // (при этом в иерархии объектов он не будет в ноде кости, а будет на том же уровне что и родительский объект, к скелету которого мы привязываем итем) itemObject.MainMesh.ApplyTransform( whParentAttachmentBone.LastUpdatedTranslation, whParentAttachmentBone.LastUpdatedRotation, whParentAttachmentBone.LastUpdatedScale); var eachVertexItemBoneIndexes = new ByteVec4((byte)parentAttachmentBone.Index, 0, 0, 0); var eachVertexItemBoneWeights = new Vec4(1, 0, 0, 0); foreach (var vertex in itemObject.MainMesh.Vertices) { vertex.BoneIndexes = eachVertexItemBoneIndexes; vertex.BoneWeights = eachVertexItemBoneWeights; } } if (whItem.Visual?.Models != null && whItemModel.Model.Loaded) { foreach (var visual in whItem.Visual.Models) { if (visual != null) { var visualPosition = Vec3.Scale(Vec3.ConvertPositionFromWh(visual.Attachment.Position), _scale); var visualObject = new WowObject() { Parent = characterObject, GlobalPosition = visualPosition }; // MainMesh (не факт что я вообще тут правильно делаю, т.к. еще не тестил) visualObject.Meshes.Add(MakeMeshFromWhModel(visual.Model)); } } } } else if (whItemModel.Bone == -1) { AddAdditionalMeshToCharacterObject(characterObject, whItemModel.Model); } } } foreach (var collection in whCharacterModel.CollectionModels.Values) { if (collection == null) { continue; } AddAdditionalMeshToCharacterObject(characterObject, collection); } return(characterObject); }
public void PostRenderCommand(Vector position, float z_other, Vector bound, float rotate, Vector scale, HalfVector anchor, ByteVec4 color, bool vertical_flip, bool horizon_flip) { /*-----------------CURRENT VERSION------------------ - * anchor(Hlaf) color(byte) modelMatrix * vec2(2) vec4(4) Matrix3x2(6) */ var is_xflip = Math.Sign(scale.X); var is_yflip = Math.Sign(scale.Y); //adjust scale transform which value is negative horizon_flip = horizon_flip | (is_xflip < 0); vertical_flip = vertical_flip | (is_yflip < 0); float scalex = is_xflip * scale.X * bound.X; float scaley = is_yflip * scale.Y * bound.Y; //Create ModelMatrix float cosa = (float)Math.Cos(rotate); float sina = (float)Math.Sin(rotate); Matrix3x2 model = Matrix3x2.Zero; model.Row0.X = cosa * scalex; model.Row0.Y = -sina * scalex; model.Row1.X = sina * scaley; model.Row1.Y = cosa * scaley; model.Row2.X = position.X - RenderKernel.SB_WIDTH / 2f; model.Row2.Y = -position.Y + RenderKernel.SB_HEIGHT / 2f; unsafe { //Anchor write fixed(byte *ptr = &PostData[_currentPostBaseIndex]) { //anchor int *hpv = (int *)(ptr + 0); *hpv = *(int *)&anchor; //color int *ip = (int *)(ptr + 4); *ip = *(int *)&color; //flip write Half *hp = (Half *)(ptr + 8); hp[0] = horizon_flip ? HalfNegativeOne : HalfOne; hp[1] = vertical_flip ? HalfNegativeOne : HalfOne; var copyLen = 2 * 3 * sizeof(float); var basePtr = (byte *)&model.Row0.X; for (int i = 0; i < copyLen; i++) { ptr[i + 12] = *(basePtr + i); } } } CurrentPostCount++; _currentPostBaseIndex += _VertexSize; if (CurrentPostCount >= Capacity) { FlushDraw(); } }