Назад | Содержание | Вперед
Данный формат был выбран как наиболее удобный в силу следующих причин:

Сам по себе формат достаточно прост, и загрузка не должна представлять сложностей (мне, например, помог опыт написания сканера компилятора языка "O" на втором курсе). Нужно помнить, что в 3ds max принята правая система координат (если ось X направлена вправо, а Y вверх, то Z на наблюдателя).
Рассмотрим наиболее важные части файла (здесь и далее оставлены только наиболее важные фрагменты файла).
*MATERIAL 0 {
*MATERIAL_NAME "01 - Default"
*MATERIAL_CLASS "Standard"
*MATERIAL_AMBIENT 0.5882353187 0.5882353187 0.9333333969
*MATERIAL_DIFFUSE 0.5882353187 0.5882353187 0.9333333969
*MATERIAL_SPECULAR 0.8999999762 0.8999999762 0.8999999762
*MATERIAL_SHINE 0.4499999881
*MATERIAL_SHINESTRENGTH 0.9899999499
*MATERIAL_TRANSPARENCY 0.0000000000
}
Сразу за MATERIAL идет номер. Использование материала определяется по номеру идущему за MATERIAL_REF внутри секции GEOMOBJECT.
Внутри секции перечислены имя, класс, цвет области тени, рассеянный цвет, цвет блика, яркость блика и степень блика (пропорционально степени косинуса в модели освещения Фонга), степень прозрачности (одним числом). К сожалению, стандартные экспортер не сохраняет для трассируемых материалов степень отражения и коэффициент преломления. По этой причине в программу был добавлен редактор материалов.
Также внутри этой секции можно обнаружить список используемых карт. Для нас представляют интерес следующие: MAP_AMBIENT, MAP_DIFFUSE, MAP_SPECULAR, MAP_OPACITY, MAP_BUMP, MAP_REFLECT (на самом деле карта MAP_REFLECT в 3ds max используется для других целей, но можно использовать и как карту степени отражения).
Типы карт:
*MAP_DIFFUSE {
*MAP_NAME "Map #1"
*MAP_CLASS "Bitmap"
*MAP_AMOUNT 1.0000000000
*BITMAP "..\images\DRYLEAVE.JPG"
*UVW_U_OFFSET 0.0000000000
*UVW_V_OFFSET 0.0000000000
*UVW_U_TILING 1.0000000000
*UVW_V_TILING 1.0000000000
}
*MAP_DIFFUSE {
*MAP_NAME "Map #1"
*MAP_CLASS "Checker"
*MAP_AMOUNT 1.0000000000
}
По MAP_CLASS можно определить тип карты. Для растра сохраняются имя файла с рисунком (русские символы в пути к сожалению обрабатываются неправильно), отступы и масштабирование рисунка.
*NODE_TM {
*NODE_NAME "Plane01"
*TM_ROW0 0.7007260919 0.0000000000 0.7134304047
*TM_ROW1 0.0000000000 1.0000000000 0.0000000000
*TM_ROW2 -0.7134304047 0.0000000000 0.7007260919
*TM_ROW3 0.0000000000 0.0000000000 0.0000000000
}
За перенос отвечает нижняя строка матрицы (*TM_ROW3).
*GEOMOBJECT {
*NODE_NAME "Plane01"
*NODE_TM {
*NODE_NAME "Plane01"
*TM_ROW0 0.7007260919 0.0000000000 0.7134304047
*TM_ROW1 0.0000000000 1.0000000000 0.0000000000
*TM_ROW2 -0.7134304047 0.0000000000 0.7007260919
*TM_ROW3 0.0000000000 0.0000000000 0.0000000000
}
*MESH {
*TIMEVALUE 0
*MESH_NUMVERTEX 4
*MESH_NUMFACES 2
*MESH_VERTEX_LIST {
*MESH_VERTEX 0 -35.0363044739 -50.0000000000 -35.6715202332
*MESH_VERTEX 1 35.0363044739 -50.0000000000 35.6715202332
*MESH_VERTEX 2 -35.0363044739 50.0000000000 -35.6715202332
*MESH_VERTEX 3 35.0363044739 50.0000000000 35.6715202332
}
*MESH_FACE_LIST {
*MESH_FACE 0: A: 2 B: 0 C: 3 AB: 1 BC: 0 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0
*MESH_FACE 1: A: 1 B: 3 C: 0 AB: 1 BC: 0 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 0
}
*MESH_NUMTVERTEX 8
*MESH_TVERTLIST {
*MESH_TVERT 0 0.0000000000 0.0000000000 0.0000000000
*MESH_TVERT 1 1.0000000000 0.0000000000 0.0000000000
*MESH_TVERT 2 0.0000000000 0.0000000000 0.0000000000
*MESH_TVERT 3 1.0000000000 0.0000000000 0.0000000000
*MESH_TVERT 4 0.0000000000 0.0000000000 0.0000000000
*MESH_TVERT 5 1.0000000000 0.0000000000 0.0000000000
*MESH_TVERT 6 0.0000000000 1.0000000000 0.0000000000
*MESH_TVERT 7 1.0000000000 1.0000000000 0.0000000000
}
*MESH_NUMTVFACES 2
*MESH_TFACELIST {
*MESH_TFACE 0 6 4 7
*MESH_TFACE 1 5 7 4
}
*MESH_NORMALS {
*MESH_FACENORMAL 0 0.0000000000 0.0000000000 1.0000000000
*MESH_VERTEXNORMAL 2 0.0000000000 0.0000000000 1.0000000000
*MESH_VERTEXNORMAL 0 0.0000000000 0.0000000000 1.0000000000
*MESH_VERTEXNORMAL 3 0.0000000000 0.0000000000 1.0000000000
*MESH_FACENORMAL 1 0.0000000000 0.0000000000 1.0000000000
*MESH_VERTEXNORMAL 1 0.0000000000 0.0000000000 1.0000000000
*MESH_VERTEXNORMAL 3 0.0000000000 0.0000000000 1.0000000000
*MESH_VERTEXNORMAL 0 0.0000000000 0.0000000000 1.0000000000
}
}
*PROP_MOTIONBLUR 0
*PROP_CASTSHADOW 1
*PROP_RECVSHADOW 1
*MATERIAL_REF 0
}
Основной интерес представляет секция MESH. В ней определено количество вершин (MESH_NUMVERTEX), количество треугольников (MESH_NUMFACES), текстурных координат (MESH_NUMTVERTEX).
В секции MESH_VERTEX_LIST по порядку идут MESH_VERTEX, затем номер текущей вершины (от 0 до MESH_NUMVERTEX - 1), и три числа x, y, z, в относительной системе координат (СК).
В MESH_FACE_LIST определены треугольники. За MESH_FACE идет номер текущего треугольника, двоеточие ":", затем "A:" и номер первой вершины в списке MESH_VERTEX_LIST, аналогично "B:" и "C:", затем идут флаги видимости ребер треугольника (1 - видимо, 0 - нет), MESH_SMOOTHING определяет группу сглаживания для треугольника (следует учитывать, что если группа сглаживания не одна, номера должны разделятся ","), а MESH_MTLID номер субматериала, если используется.
MESH_TVERTLIST определяет список текстурных координат вершин, аналогично MESH_VERTEX_LIST, но с текстурными координатами.
С помощью MESH_TFACELIST загружаются текстурные координаты. После MESH_TFACE идет номер треугольника, затем три индекса вершин в списке текстурных координат.
MESH_NORMALS определяет нормали в вершинах треугольника. За идентификатором MESH_FACENORMAL идет номер треугольника, его нормаль (x, y, z), затем три раза по порядку MESH_VERTEXNORMAL, включающего индекс вершины и нормаль в ней (x, y, z).
MATERIAL_REF определяет номер материала в списке MATERIAL_LIST, использующегося сеткой.
*LIGHTOBJECT {
*NODE_NAME "Light01"
*LIGHT_TYPE Directional
*NODE_TM {
*NODE_NAME "Light01"
*TM_ROW0 0.9999999404 0.0000000000 0.0000000000
*TM_ROW1 0.0000000000 0.9999999404 0.0000000000
*TM_ROW2 0.0000000000 0.0000000000 0.9999999404
*TM_ROW3 0.0000000000 0.0000000000 11.8617439270
}
*NODE_TM {
*NODE_NAME "Light01.Target"
*TM_ROW0 0.9999999404 0.0000000000 0.0000000000
*TM_ROW1 0.0000000000 0.7071066499 0.7071067691
*TM_ROW2 0.0000000000 -0.7071067691 0.7071066499
*TM_ROW3 0.0000000000 0.0000000000 0.0000000000
}
*LIGHT_SPOTSHAPE Circle
*LIGHT_SETTINGS {
*TIMEVALUE 0
*LIGHT_COLOR 1.0000000000 1.0000000000 1.0000000000
*LIGHT_INTENS 1.0000000000
*LIGHT_HOTSPOT 0.5000000000
*LIGHT_FALLOFF 45.0000000000
}
}
LIGHT_TYPE определяет тип источника света и может быть: Omni - точечный, Target - нацеленный, Directional - направленный, Free - свободный. Если источник света имеет мишень, то ее матрицу преобразования и анимацию можно получить обнаружив имя источника света с окончанием ".Target". По LIGHT_SPOTSHAPE можно определить форму освещения для направленных источников света: в виде круга (Circle) или прямоугольника (Rect). Внутри LIGHT_SETTINGS сохраняются настройки источника, как то цвет (LIGHT_COLOR), интенсивность (LIGHT_INTENS), для направленных область яркости (LIGHT_HOTSPOT) и спада (LIGHT_FALLOFF) в градусах или метрических единицах, соответственно для нацеленного и направленного. Анимация настроек также сохраняется.
*CAMERAOBJECT {
*NODE_NAME "Camera01"
*CAMERA_TYPE Target
*NODE_TM {
*NODE_NAME "Camera01"
*TM_ROW0 0.0000000000 1.0000000000 0.0000000000
*TM_ROW1 -0.5039737225 0.0000000000 0.8637189865
*TM_ROW2 0.8637189865 0.0000000000 0.5039737225
*TM_ROW3 100.0000000000 0.0000000000 58.3492660522
}
*NODE_TM {
*NODE_NAME "Camera01.Target"
*TM_ROW0 1.0000000000 0.0000000000 0.0000000000
*TM_ROW1 0.0000000000 1.0000000000 0.0000000000
*TM_ROW2 0.0000000000 0.0000000000 1.0000000000
*TM_ROW3 0.0000000000 0.0000000000 0.0000000000
}
*CAMERA_SETTINGS {
*TIMEVALUE 0
*CAMERA_NEAR 0.0000000000
*CAMERA_FAR 1000.0000000000
*CAMERA_FOV 0.7853956223
*CAMERA_TDIST 115.7783966064
}
}
Камеры как и источники света могут быть нацеленными и свободными. Для нацеленной камеры матрица преобразования и анимация мишени определяется по NODE_NAME, оканчивающемуся на ".Target".
Настройки камеры включают в себя ближний и дальний радиус видимости (CAMERA_NEAR и CAMERA_FAR), область видимости в радианах (CAMERA_FOV), расстояние до цели для свободных камер (CAMERA_TDIST).
Вид камеры находится в отрицательном направлении оси Z, когда в области проекции ось X направлена вправо, а ось Y вверх.
Экспортер может сохранять анимацию матрицы преобразования объектов. Это может быть как набор ключей, так и выборка положений. Второй вариант проще для загрузки.
*TM_ANIMATION {
*NODE_NAME "Box01"
*CONTROL_POS_TRACK {
*CONTROL_POS_SAMPLE 0 -12.9507 21.2540 0.0000
*CONTROL_POS_SAMPLE 160 -11.7180 19.5905 0.0000
*CONTROL_POS_SAMPLE 320 -8.4144 15.1323 0.0000
*CONTROL_POS_SAMPLE 480 -3.6316 8.6780 0.0000
*CONTROL_POS_SAMPLE 640 2.0388 1.0260 0.0000
*CONTROL_POS_SAMPLE 800 8.0050 -7.0253 0.0000
*CONTROL_POS_SAMPLE 960 13.6753 -14.6773 0.0000
*CONTROL_POS_SAMPLE 1120 18.4582 -21.1316 0.0000
*CONTROL_POS_SAMPLE 1280 21.7617 -25.5897 0.0000
*CONTROL_POS_SAMPLE 1440 22.9944 -27.2532 0.0000
}
*CONTROL_ROT_TRACK {
*CONTROL_ROT_SAMPLE 160 0.3802 -0.3432 0.8589 0.1164
*CONTROL_ROT_SAMPLE 320 0.3177 -0.4181 0.8510 0.3042
*CONTROL_ROT_SAMPLE 480 0.1668 -0.5195 0.8380 0.4201
*CONTROL_ROT_SAMPLE 640 -0.0792 -0.5614 0.8237 0.4664
*CONTROL_ROT_SAMPLE 800 -0.3578 -0.4610 0.8121 0.4539
*CONTROL_ROT_SAMPLE 960 -0.5523 -0.2131 0.8059 0.3977
*CONTROL_ROT_SAMPLE 1120 0.5868 -0.0843 -0.8053 5.9711
*CONTROL_ROT_SAMPLE 1280 -0.5006 0.3111 0.8078 0.2044
*CONTROL_ROT_SAMPLE 1440 -0.4103 0.4187 0.8102 0.0742
}
*CONTROL_SCALE_TRACK {
*CONTROL_SCALE_SAMPLE 0 1.0000 1.0000 1.0000 0.0000 0.0000 0.0000 0.0000
*CONTROL_SCALE_SAMPLE 160 1.0000 1.0000 1.0527 0.0000 0.0000 0.0000 0.0000
*CONTROL_SCALE_SAMPLE 320 1.0000 1.0000 1.1939 0.0000 0.0000 0.0000 0.0000
*CONTROL_SCALE_SAMPLE 480 1.0000 1.0000 1.3983 0.0000 0.0000 0.0000 0.0000
*CONTROL_SCALE_SAMPLE 640 1.0000 1.0000 1.6407 0.0000 0.0000 0.0000 0.0000
*CONTROL_SCALE_SAMPLE 800 1.0000 1.0000 1.8957 0.0000 0.0000 0.0000 0.0000
*CONTROL_SCALE_SAMPLE 960 1.0000 1.0000 2.1380 -0.0000 -0.0000 -1.0000 0.2287
*CONTROL_SCALE_SAMPLE 1120 1.0000 1.0000 2.3424 -0.0000 -0.0000 -1.0000 0.2115
*CONTROL_SCALE_SAMPLE 1280 1.0000 1.0000 2.4836 0.0000 0.0000 0.0000 0.0000
*CONTROL_SCALE_SAMPLE 1440 1.0000 1.0000 2.5363 0.0000 0.0000 0.0000 0.0000
}
}
CONTROL_POS_TRACK содержит выборку положений объекта (нижняя строка матрицы анимации). CONTROL_ROT_SAMPLE включает в себя результирующее вращение, в виде квантериона. CONTROL_SCALE_SAMPLE представляет собой результирующее масштабирование в виде вектора и квантериона.
Для создания загрузчика пришлось основательно погрузиться в исходники экспортера и помощь по 3ds max SDK. Алгоритмы работы с квантерионами были взяты из NVToolkit.
void LoaderASE::ParseNodeTMAnimation(Node *node)
{
Vector *ap_t = new Vector[scene->GetTotalFrame()]; // The translation components
Quat *ap_q = new Quat[scene->GetTotalFrame()]; // The essential rotation
Quat *ap_u = new Quat[scene->GetTotalFrame()]; // The stretch rotation. This is the axis system of the scaling application
Vector *ap_k = new Vector[scene->GetTotalFrame()]; // The stretch factors. These are the scale factors for x, y and z
// инициализируем значениями по умолчанию
for (int i = 0; i < scene->GetTotalFrame(); i++)
{
ap_t[i] = Vector(0.0f, 0.0f, 0.0f);
ap_q[i] = Quat(0.0f, 0.0f, 0.0f, 1.0f);
ap_u[i] = Quat(0.0f, 0.0f, 0.0f, 1.0f);
ap_k[i] = Vector(1.0f, 1.0f, 1.0f);
}
Check(ID_LCURVE);
while (idToken != ID_RCURVE && idToken != ID_NONE)
{
switch (idToken)
{
case ID_NODE_NAME:
{
NextToken();
String name = strToken;
Check(ID_STRING);
// какой-нибудь "Camera01.Target"
if (name != node->name)
node = NULL;
break;
}
case ID_POS_TRACK:
NextToken();
Check(ID_LCURVE);
while (idToken != ID_RCURVE && idToken != ID_NONE)
{
Check(ID_POS_SAMPLE);
int frame = TicksToSceneFrame(GetINumber());
ap_t[frame] = GetVector();
}
Check(ID_RCURVE);
break;
case ID_ROT_TRACK:
NextToken();
Check(ID_LCURVE);
while (idToken != ID_RCURVE && idToken != ID_NONE)
{
Check(ID_ROT_SAMPLE);
int frame = TicksToSceneFrame(GetINumber());
ap_q[frame] = GetQuat();
}
Check(ID_RCURVE);
break;
case ID_SCALE_TRACK:
NextToken();
Check(ID_LCURVE);
while (idToken != ID_RCURVE && idToken != ID_NONE)
{
Check(ID_SCALE_SAMPLE);
int frame = TicksToSceneFrame(GetINumber());
ap_k[frame] = GetVector();
ap_u[frame] = GetQuat();
}
Check(ID_RCURVE);
break;
case ID_LCURVE:
SkipBlock();
break;
default:
NextToken();
}
}
Check(ID_RCURVE);
// строим на основе ap_t, ap_q, ap_u, ap_k матрицы
if (node)
{
node->mTransformAnimation = new Matrix[scene->GetTotalFrame()];
Matrix current;
Matrix stm, srtm, srtminv, rtm, rtmCurrent, ptmCurrent;
MakeIdentity(&rtmCurrent);
for (int i = 0; i < scene->GetTotalFrame(); i++)
{
MakeTranslate(&ptmCurrent, ap_t[i].x, ap_t[i].y, ap_t[i].z);
MakeMatrix(&rtm, ap_q[i]);
MultMatrixRight(&rtmCurrent, rtm);
MakeMatrix(&srtm, ap_u[i]);
MakeScale(&stm, ap_k[i].x, ap_k[i].y, ap_k[i].z);
ReverseMatrix(&srtminv, srtm);
MakeIdentity(¤t);
MultMatrixRight(¤t, srtminv);
MultMatrixRight(¤t, stm);
MultMatrixRight(¤t, srtm);
MultMatrixRight(¤t, rtmCurrent);
MultMatrixRight(¤t, ptmCurrent);
node->mTransformAnimation[i] = current;
}
}
delete ap_t;
delete ap_q;
delete ap_u;
delete ap_k;
}
За дополнительной информацией обращайтесь к помощи 3ds max SDK.
Назад | Содержание | Вперед