Назад | Содержание | Вперед

Обратная трассировка лучей

Формат 3ds max ASE

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

Пример файла ASE.

Сам по себе формат достаточно прост, и загрузка не должна представлять сложностей (мне, например, помог опыт написания сканера компилятора языка "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.

Назад | Содержание | Вперед


©Павел Коколемин

Рейтинг@Mail.ru