piątek, 28 sierpnia 2009

Praca magisterska, md5 i nokia 3120 clasic

Dawno nic nie napisałem, ale prawda jest taka, że nie było o czym. Założenie było takie, że piszę tutaj o tym co uda mi się stworzyć w świecie programowania, a od ostatniego posta nie powstało nic. Aż do dziś ;)

Przez ostatnie niemal 2 tygodnie pisałem swoją pracę magisterską. Wciąż jestem w polu, ale w sumie wygląda to coraz lepiej. Już dawno postanowiłem, że napiszę ją w latex'u i jak na razie nie żałuje decyzji. Pisze się naprawdę sprawnie. Najfajniejsze jest to, że nie ma problemów z aktualizacją obrazków i bardzo łatwo jest rozbić cały dokument na osobne piliki. Boli tylko brak sprawdzania pisowni. Jakoś nie udało mi się tego skonfigurować. Teoretycznie Eclipse z dodatkiem Texclipse potrafi skorzystać ze słowników aspell. Niestety sprawa robiła się o kodowanie dokumentu. Nie jest to na szczęście zbyt bolesne, bo wciąż można skorzystać z innego softu, choćby open office'a.

Zanim zacząłem pisać pracę walczyłem jeszcze trochę z tym ładowaniem plików md5. Lipa. Coś mi tu umyka. Nie wiem, czy to błąd w kodzie, czy nie zrozumienie jakiegoś aspektu, np jak posługiwać się kwaternionami. W każdym bądź razie lipa. Udało mi się doprowadzić do tego, że szkielet mojego modelu animował się poprawnie. Jednak gotowiec ma coś nie tak z orientacją. Pozycje są poprawne. Brakło mi czasu, żeby problem rozwiązać.

Co do tego wszystkiego ma Nokia 3120 clasic ? Ano jest pretekstem do spłodzenia tego posta. Otóż mój szanowny rodziciel, wszedł w posiadanie tego modelu, drogą podpisania umowy z jednym z operatorów sieci gsm w Polsce. Telefon potęgą nie jest, czego się zresztą spodziewać po gratisie. Nie spodziewałem się jednak, że aż tak.
Wydawałoby się prosta sprawa skopiować kontakty z jednego telefonu na drugi. Poprzednik to sony erricson. Oba telefony posiadają moduł bluetooth. No to kopiujemy całą listę przez BT i po bólu ;)
Otóż nie. SE potrafi wysłać wszystkie kontakty na raz w formie pliku vcf. Nokia natomiast takiego pliku zrozumieć nie potrafi. Grrr.
Szybko okazało się, że problemem jest to iż jest to lista. Pojedynczą wizytówkę można przesłać. Czyli co, na SE strzałka w dół > opcje > wiecej > zaawansowane > wyslij > bt, Nokia > akceptuj > zapisz. I tak 380 razy. Masakra.
Po długim kombinowaniu wyszło, że takie wizytówki można wysyłać także z komputera. Byleby nie była to lista, a pojedyncze wpisy. Na szybko napisałem kawałek kodu do konwersji listy na osobne pliki.

Nie zagłębiałem się przy tej okazji nadmiernie w strukturę pliku .vcf. Także raczej nie należy się na to powoływać. Programik zadziałał zgodnie z oczekiwaniem, a cała operacja zakończyła się pełnym sukcesem, dając tym samym Nokii pierwszą szansę na dłuższe testy u mnie w domu. Wcześniej była tylko jedna i wytrzymała jeden dzień, zanim mnie szlag trafił ;)

/*
* main.cpp
*
* Created on: 2009-08-28
* Author: Adam Śmigielski
* E-mail: adam.smigielski@gmail.com
* Copyright: Adam Śmigielski
*/


#include <fstream>
#include <iostream>
#include <stdio.h>

using namespace
std;

int
main(int argc, char * argv[])
{

const
char * filename = 0;
if
(2 >= argc)
{

filename = argv[1];
cout << "Plik: " << filename << endl;
}

else

{

cout << "Podaj nazwe pliku" << endl;
return
1;
}


fstream in;
fstream out;
in.open(filename, fstream::in);
if
(false == in.is_open())
{

cout << "Plik nie istnieje" << endl;
return
2;
}

else

{

cout << "Otwarto plik" << endl;
}


char
buffer[256];
unsigned int
count = 0;
char
name[] = "result\\xxxx.vcf";
char
rev[] = "REV:20090828T071515Z";
int
revsize = strlen(rev);

while
(true)
{

in.getline(buffer, 256);

cout << buffer << endl;

if
(0 == strcmp(buffer, "BEGIN:VCARD"))
{

sprintf(name + 7, "%04d.vcf", count++);
cout << "Nazwa pliku: " << name << endl;

out.open(name, fstream::out);
if
(false == out.is_open())
{

cout << "Nie udalo sie otworzyc pliku" << endl;
return
2;
}

else

{

cout << "Otwarto plik" << endl;
}


out << buffer << endl;
while
(true)
{


in.getline(buffer, 256);

cout << buffer << endl;

if
(0 != strstr(buffer, "X-IRMC-LUID:"))
{

out << rev << endl;
}

else if
(0 == strcmp(buffer, "END:VCARD"))
{

out << buffer << endl;
break
;
}

else if
(0 != strstr(buffer, "TEL:"))
{

out << "TEL;CELL:" << buffer + 4 << endl;
}

else

{

out << buffer << endl;
}
}


out.close();
}

else if
(0 == strcmp(buffer, ""))
{

break
;
}
}


in.close();

return
0;
}



EDIT
Właśnie odkryłem, że mój sprytny sposób na zamieszczanie kodu wyświetla bool'e na czarno. Ehh, już zmienione. Teraz będą zielono niebieskawe :)

czwartek, 6 sierpnia 2009

Animacja szkieletowa

No temat masakra. Od poprzedniego postu cały czas walczę ze skiningiem. Na dobry początek zawiodły mnie szadery. Z jakiegoś, kompletnie nie zrozumiałego dla mnie, powodu pętla:
for (int i = 0; i < 4; i++)
wykonywała się tylko raz. Później, głównie dzięki przykładowi, zawartemu w Dx SDK, SkinMesh, udało mi się przekonać kompilator, że ta pętla to ma jednak 4 przebiegi. Co było przyczyną problemu wciąż nie wiem.
Następnie odkryłem, że kod (tym razem c++)
if (0 != flag & 1)
{
}

if
(0 != flag & 2)
{
}
...

if
(0 != flag & 32)
{
}
kompiluje się tylko do jednego, pierwszego, if'a. Cała reszta jest pomijana. Dzieje się tak przy -o0 -g3, a także dla domyślnych ustawień (czyt. bez flagi). Innych ustawień nie sprawdziłem. Nie mam pojęcia o co gcc chodziło. Czyżby bug ;)
Wyszło jeszcze kilka błędów. Wczoraj wieczorem udało mi się wyświetlić model w bind pose, ale ... . Ale wynik był zniekształcony. Geometria z całą pewnością jest poprawna, bo po ustawieniu macierzy jednostkowej wygląda jak należy. Czyli SUKCES, udało mi się w końcu zmusić wszystko do działania, a błędy muszą wynikać z nieprawidłowych macierzy.

Od początku idea aby wysyłać do karty "globalne" macierze dla każdej kości wydał mi się dziwny. Przecież to się musi rozjechać. Pomyślałem jednak, że może te wagi odpowiednio modyfikują transformację. Dzisiaj rano postanowiłem to przeanalizować matematycznie. No i oczywiście okazało się, że jest źle.

Weźmy taki przykład:
Punkt p [1, 1, 1], zaczepiony do dwóch kości, do obu po równo, czyli wagi 0,5
Kość A [0, 0, 0] zorientowana neutralnie (macierz jednostkowa)
Kość B [0, 0, 2] zorientowana neutralnie

p' = 0,5 * A * p + 0,5 * B * p
p' = [0,5 0,5 0,5] + [0,5 0,5 (0,5 * (1 + 2))] = [1, 1, 1,5]

A powinno wyjść [1, 1, 1]. Co z tym zrobić ? Bardzo UWAŻNIE przeczytać cokolwiek na ten temat. Na przykład ten temat na warsztacie. Odkryjemy w ten sposób, że macierze mają być "globalne", ale muszą uwzględniać pewien myk.

Na koniec screen, z bind pose:
Z Engine
Model pochodzi ze wspomnianej już strony: link

poniedziałek, 3 sierpnia 2009

MD5 mesh loader

Przebrnąłem przez napisanie funkcji ładującej model z pliku md5.

Na początek linki:
1 Eksporter do Blendera i modele
2 Opis formatu
3 Opis jak ładować z tego formatu
4 Temat na Warsztacie

A teraz moje uwagi i wnioski:
1. Dane joint'ów zapisane są względem początku układu, a nie rodzica. Aby wyznaczyć transformację względem rodzica należy skorzystać z tych równań:
p * q = g => q = p^-1 * g
p + v = g => v = g - p
p - parent, czyli dane rodzica
g - global, czyli dane zapisane w pliku
q i v - to kwaternion i wektor
p^-1 to oczywiście inwersja kwaternionu rodzica
2. Kwaterniony są zapisane w formie samych osi, bez w. Ja dowiedziałem się o tym z [3]. Skorzystano tu z faktu, że długość kwaternionu musi być równa 1. 1 = w*w + x*x + y*y + z*z. Chyba łatwo wyznaczyć ile wynosi w. Pamiętać tylko trzeba, że nie należy wyciągać pierwiastka kwadratowego z liczb ujemnych.
3. Pozycje wag są zapisane względem kości i to z uwzględnieniem orientacji. Aby wyznaczyć finalną pozycję, trzeba najpierw obrócić pozycję wagi o orientację kości. Dopiero tak obróconą translację możemy zsumować z pozycją kości. Wzór na rotację [3] punktu o kwaternion: a' = q * a * q^-1. Przy czym a i a' to kwaterniony z częścią rzeczywistą równą pozycji punktu i urojoną 0, trzeba o tym pamiętać.
4. Format nie przechowuje informacji o normalnych. Wyznacza się je oczywiście przy użyciu mnożenia wektorowego (cross product). Trzeba tu jednak wziąć pod uwagę, że indeksy wierzchołków są w CCW. To mnie zaskoczyło, ale tak mi wyszło. Nie wiem jednak czy to przypadkiem nie jest "wina" eksportera.
5. Plik ma 682 linijki. Czyli kupa roboty.