3DCoat  3D-COAT 4.9.xx
3DCoat is the one application that has all the tools you need to take your 3D idea from a block of digital clay all the way to a production ready, fully textured organic or hard surface model.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Создание эффекта слоёв рисования
Author
Владимир Маковецкий

Создание базового класса, добавляем пункт в меню

В этом примере мы рассмотрим как создать эффект для обработки слоев рисования. Итак созданим папку нашего модуля также как и в предидущем уроке, только сменим название на PaintEffectTutorial. Помним о создании также всех необходимых файлов проекта, в нашем случае это файл хидера и основного кода (PaintEffectTutorial.cpp и PaintEffectTutorial.h).

Рассмотрим пример кода для создания минимального набора, который создаст только пункт меню, но еще не будет выполнять никаких действий.

Код из файла хидера:

#pragma once
// Создаем клас усналедованый от BasicTool для нашего эффекта
class PaintEffectTutorial :public BasicTool{
public:
// Указатель на екземпляр класса
static PaintEffectTutorial* m_PaintEffectTutorial;
// Функция которая будет вызиваться при нажатии на кнопку меню
virtual void Activate();
//описание инструмента
virtual const char* GetHint(){
return "PAINTEFFECT_TOOL_HINT";
}
//Название инструмента
virtual const char* GetMessage(){
return "PAINTEFFECT_TOOL";
}
// не отображать на панели инструментов
virtual bool CheckIfInToolPanel(){ return false; }
//Количество пунктов меню
virtual int GetNumCommands(){ return 1; }
// Функция которая будет выполнятся принажатии пункта меню
virtual void PerformCommand(int idx){ Activate(); };
//Название пункта меню
virtual const char* GetCommandMessage(int idx){ return GetMessage(); }
//Пподсказка пр наведении на пункт меню
virtual const char* GetCommandHint(int idx){ return GetHint(); }
// Помещаем пункт меню в меню номер 2 (текстурирование)
virtual int GetMenuItemCategoty(int idx){ return 2; }
};

Код из файла CPP:

#include "../stdafx.h"
#include "PaintEffectTutorial.h"
//Создаем указатель на екземпляр класса
PaintEffectTutorial* PaintEffectTutorial::m_PaintEffectTutorial;
// Функция которая будет вызиваться при нажатии на кнопку меню
void PaintEffectTutorial::Activate(){
}
// Функция которая создаст и установит экземпляр класса отвечающую за наш эффект
void InitPaintEffectTutorial(){
//Создаем эекземпляр класса
PaintEffectTutorial::m_PaintEffectTutorial = new PaintEffectTutorial();
// Устанавливаем инструмент для отображения в меню
BasicTool::InstallTool(PaintEffectTutorial::m_PaintEffectTutorial, "PaintEffectTutorial");
}
// С помощью макроса CallItLater создаем обьект во время окна приветвия
CallItLater _PaintEffectTutorial(&InitPaintEffectTutorial, BASIC_TOOL);

После сборки и запуска 3DCoat видим:

1.png

Обработка слоя в режиме попиксельного рисования

В качестве примера сделаем все каналы слоя наполовину красными, шероховатыми диэлектриками. В файл cpp добавим в функцию Activate след. код:

////// Изменение слоя в режиме попиксельного редактирования с UV разверткой
// Структура с информацией о одельном участке текстуры
//(все текстурыы разбиты на маленькие квадратики 16х16)
MapQuadRef mapQuadRef;
//Здадаем специальный слой с которым мы будем работать
mapQuadRef.NeedSpecificLayer = PMS.CurrentLayerID;
// информация о слое с которым мы будем работать
OneLayerInfo* layerInfo = PMS.GetLayer(PMS.CurrentLayerID);
// Количество полигонов с которых состоит модель
int FacesCount = PMS.FacesInfo.GetAmount();
//Проверяем количество точек которые находятся
//в полиагональном меше и если их количество больше
// 5, то есть возможность обработки его текстуры
if (PMS.VertsUV.GetAmount() > 5)
// Цикл в котором обрабатываются всеточки полигонов
for (int faceID = 0; faceID < FacesCount; faceID++){
//Информация о полигоне
FaceInfo* faceInfo = &PMS.FacesInfo[faceID];
//Номер развертки на котрой размещен данный полигон
int UVIdx = faceInfo->uvIdx;
// проверяем скрыт ли обьект полностью или данный
//полигон а также доступен ли он для редактирования
// иными словами игнорируем скрытые и заблокированые полигоны
if (faceInfo->Visible && faceInfo->Editable){
//Также проверяем предназначен ли тип полигона для поппиксельного редактирования
if (faceInfo->isPPP){
// Класс со всеми рабочими данными текстуры развертки на которой находится данный полигон
ModelPointsScope* modelPointsScope = PMS.GetMPS(faceInfo->RTIdx);
if (modelPointsScope){
//Макрос который перебирает все пиксели на текстуре,
//передавая все данные об очередном пикселе в заданые нами переменные
// в iPos позицию пикселя в мире, мировых координатах.
// в iNorm запись нормали (напрвление точки полигона без попиксельных изменений)
// в iUV позиция точки на текстуре от 0 до 1 (если 0 по х - это левый край текстуры, если 1 по х - правый край текстуры)
// в iUVPX позицияточки на текстуре (UV развертке) в пикселях
UVTriPxForeach(faceID, Vector3D iPos, Vector3D iNorm, Vector3D iUV, Vector3D iUVPX) {
// Получаем определенный участок (клетка 16х16) текстуры по UV координатам точки
if (modelPointsScope->GetQuadRef(mapQuadRef, iUVPX.x, iUVPX.y, true, true, true)){
// Если такого участка на данном слое еще нет - создаем его
if (!mapQuadRef.LR){
// Функция создания новой клетки в слое текстуры
mapQuadRef.LR = mapQuadRef.mq->AddLayerRef(&PMS, layerInfo);
}
/// указатель на цвет пикселя текстуры
unsigned char* pixelColor = (unsigned char*)&mapQuadRef.LR->Points[mapQuadRef.ofs].Color;
//Уменьшаем в два раза высоту, металичность, глянцевость
//и маску свойств материала на пикселе в качестве нашего эффекта
mapQuadRef.LR->Points[mapQuadRef.ofs].Dv *= 0.5;
mapQuadRef.LR->Points[mapQuadRef.ofs].Metalness *= 0.5;
mapQuadRef.LR->Points[mapQuadRef.ofs].SpecMask *= 0.5;
mapQuadRef.LR->Points[mapQuadRef.ofs].Specular *= 0.5;
//Синий и зеленый канал цвета пикселя уменьшаем (затемняем) в два раза
pixelColor[0] = pixelColor[0] / 2;
pixelColor[1] = pixelColor[1] / 2;
//Красный и альфа(непрозрачность) канал цвета пикселя увеличиваем до среднего значения с 255
pixelColor[2] = (pixelColor[2] + 255) / 2;
pixelColor[3] = (pixelColor[3] + 255) / 2;
}
} EndUVTriPxForeach // Конец макроса перебора пикселей полигона
}
}
}
}
//Пересчет финальной картинки текстуры с учетом изменений в слое
PMS.InvalidateLayer(PMS.CurrentLayerID);
PMS.SetAllDirty();
PMS.UpdateDirtyFaces(false);

Результат работы эффекта:

2.png

Создание того же эффекта в режиме pTex

Добавим код программы к существующему, но вданом случае для создания того же эффекта что и выше, но уже в режиме pTex. Добавим код функции Activate перед строкой PMS.InvalidateLayer(PMS.CurrentLayerID):

//Функция обработки слоя в режиме pTex
for (int faceID = 0; faceID < FacesCount; faceID++){
//Информация о полигоне
FaceInfo* faceInfo = &PMS.FacesInfo[faceID];
// проверяем скрыт ли обьект полностью или данный
//полигон а также доступен ли он для редактирования
// иными словами игнорируем скрытые и заблокированые полигоны
if (!faceInfo->isPPP && faceInfo->Visible && faceInfo->Editable && faceInfo->Points){
// информация о слое с которым мы будем работать
OneLayerPoint* lp = PMS.GetCurrentLayer(faceID)->Points;
// Цикл в котором обрабатываются все точки полигонов
for (int y = 0; y<faceInfo->SubSizeY; y++){
for (int x = 0; x<faceInfo->SubSizeX; x++){
// указатель на цвет пикселя текстуры
byte* pixelColor = (byte*)&lp->Color;
//Синий и зеленый канал цвета пикселя уменьшаем (затемняем) в два раза
pixelColor[0] = pixelColor[0] / 2;
pixelColor[1] = pixelColor[1] / 2; //Красный и альфа(непрозрачность) канал цвета пикселя увеличиваем до среднего значения с 255
pixelColor[2] = (pixelColor[2] + 255) / 2;
pixelColor[3] = (pixelColor[3] + 255) / 2;
//Уменьшаем в два раза высоту, металичность, глянцевость
//и маску свойств материала на пикселе в качестве нашего эффекта
lp->Transparency = (lp->Transparency + 1.0f) / 2.0f;
lp->EffectDisplacement /= 2.0f;
lp->Dv /= 2.0f;
lp->Metallness /= 2.0f;
lp->SpecOpacity /= 2.0f;
// Переводим указатель на следующий пиксель
lp++;
}
}
PMS.CalcFaceAttributesWithLayers(faceID);
}
}

Результат работы:

3.png

Создание того же эффекта в сюрфейсном режиме

Расмотрим код программы, необходимый для нашего эффекта в сюрфейсном режиме:

// Проходимся по весм обьектам скульптинга
for (int objectId = 0; objectId < VORenderQueue::LastVisTree.GetAmount(); objectId++){
// Указатель на вертексный обьект из комнаты скульптинга
VolumeObject* vo = VORenderQueue::LastVisTree[objectId].VO;
// Макрос перебора всех клеток из которых состоит обьект
scan(vo->Cells, VolumeCell** pvc, tri_DWORD* T){
//Указатель на клетку
VolumeCell* vc = *pvc;
if (vc->Attr){
// Указываем на то, что клетка нуждается в перещете результатов
vo->MarkDirtyCell(T->V1, T->V2, T->V3, true, true);
// Количество точек (вертексов) даной клетки
int nv = vc->Attr->Verts.GetAmount();
// Создаем выбраный слой текстурирования внутри клетки если его еще нету
VoxLayer* vl = vc->Attr->Insertlayer(layerInfo->LayerID);
//Цикл перебора всех точек (вертексов)
for (int k = 0; k < nv; k++){
// указатель на цвет пикселя текстуры
byte* pixelColor = (byte*)&vl->Color[k];
//Синий и зеленый канал цвета пикселя уменьшаем (затемняем) в два раза
pixelColor[0] = pixelColor[0] / 2;
pixelColor[1] = pixelColor[1] / 2;
//Красный и альфа(непрозрачность) канал цвета пикселя увеличиваем до среднего значения с 255
pixelColor[2] = (pixelColor[2] + 255) / 2;
pixelColor[3] = (pixelColor[3] + 255) / 2;
//Уменьшаем в два раза металичность и глянцевость
if (vl->Specular.GetAmount() > k){
byte* specParams = (byte*)&vl->Specular[k];
specParams[0] /= 2;
specParams[1] /= 2;
specParams[2] /= 2;
specParams[3] /= 2;
}
if (vl->resMetall.GetAmount() > k) vl->resMetall[k] /= 2;
}
}
}scan_end;
}

Результат в сюрфейсном режиме:

4.png

Поздравляем! Ты научился создавать свои эффекты в 3DCoat)

Исходники

Исходники этого проекта можешь запросто скачать по ссылке.

See Also
Работа с VoxelExtension или Создание своего инструмента в 3DCoat
Настройка IDE для сборки проекта
Работа с UI 3DCoat