- Author
- Владимир Маковецкий
Введение
Новый инструмент для комнаты скульптинга можно создать добавлением в проект одного cpp файла и, желательно, хидера к нему.
Создать cpp
и h
файлы можно в Visual Studio прямо из Solution Explorer.
Через меню кнопки Browse
определяем место на ПК где будем хранить файлы нашего мини-проекта. Для удобства назовем папку "Tutorials".
Дальше создаем хидер также через кнопки контекстного меню: Add / New Item / Header
, с названием "MyBoxTutorial.h". Также создаем файл с основным кодом нашего мини проекта через то же контекстное меню, но уже выбираем файл с расширением ".cpp" : Add / New Item
, и называем его соответственно предыдущему файлу с иным типом "MyBoxTutorial.cpp".
Код для создания нового инструмента
Рассмотрим необходимый код для создания нового инструмента 3DCoat. Здесь мы создаём инструмент, который только **отображается** на панели инструментов.
Код "MyBoxTutorial.h":
public:
virtual int GetPrevVoxelTool(){ return -2; }
virtual int GetPrevSurfaceTool(){ return VOX_TRANSPOSE; }
virtual const char*
GetID(){
return "MyBoxTool"; }
virtual const char* GetHint(){ return "MyBoxTool_HINT"; }
};
Код "MyBoxTutorial.cpp":
#include "../stdafx.h"
#include "MyBoxTutorial.h"
MyBoxTutorialExt* m_MyBoxTool;
void InitMyBoxTool(){
m_MyBoxTool = new MyBoxTutorialExt;
}
CallItLater _MyBoxTutorialExt(&InitMyBoxTool, VOX_EXTENSION);
После сборки и запуске 3DCoat видим созданный нами выше инструмент в левой панели UI:
Создание формы с параметрами
Необходимо в хидере (файл "MyBoxTutorial.h") зарегистрировать класс для создания куба, так как именно с этой формой мы будем работать. Дальше сможем заменить форму на любую удобную для нас.
Добавляем в хидер след. код:
float BoxWidth = 10.0f;
float BoxHeight = 10.0f;
float BoxLength = 10.0f;
float XPos = 0.0f;
float YPos = 0.0f;
float ZPos = 0.0f;
SAVE(MyBoxTutorialExt){
REG_FSLIDER(BoxWidth, 0, 50, 10);
REG_FSLIDER(BoxHeight, 0, 50, 10);
REG_FSLIDER(BoxLength, 0, 50, 10);
REG_FSLIDER(XPos, -50, 50, 0);
REG_FSLIDER(YPos, -50, 50, 0);
REG_FSLIDER(ZPos, -50, 50, 0);
}ENDSAVE;
В результате получили инструмент в виде маленького куба. Теперь можно менять его позицию в пространстве и угол наклона по осям.
Отрисовка куба
Для отрисовки перепишем функцию Render, которая вызывается каждый раз при перерисовке вьюпорта, когда инструмент активен. Саму сетку куба будем генерировать в отдельной функции (если в этом есть необходимость).
Добавим в "MyBoxTutorial.h" необходимые для этого методы и переменные:
StaticMesh* boxStaticMesh = 0;
DWORD LastHash;
virtual void Render();
void MakeMesh();
А в "MyBoxTutorial.cpp" напишем реализацию:
void MyBoxTutorialExt::MakeMesh(){
DWORD currentHash = GetHash();
if (currentHash == LastHash && LastTransMatrix ==
voxtool().CHandler.CP.GlobalTransform && boxStaticMesh != 0) return;
LastHash = currentHash;
LastTransMatrix = voxtool().CHandler.CP.GlobalTransform;
boxMeshContainer.CreateCube(
Vector3D(BoxWidth, BoxHeight, BoxLength));
boxMeshContainer.QuadQuantSubd(Matrix4D::Identity, 0.5, 0);
boxMeshContainer.InvertRaw();
boxMeshContainer.Transform(Matrix4D::Translation(
Vector3D(XPos, YPos, ZPos)));
boxMeshContainer.Transform(voxtool().CHandler.CP.GlobalTransform);
if (boxStaticMesh != 0) delete boxStaticMesh;
boxStaticMesh = boxMeshContainer.CreateStaticMeshMC();
}
void MyBoxTutorialExt::Render(){
MakeMesh();
static int shader = IRS->GetShaderID("raw");
IRS->SetShader(shader);
static int vColor = IRS->GetShaderVarID(shader, "Color");
if (vColor != -1) IRS->SetShaderVar(shader, vColor,
Vector4D(1.0f, 0.65f, 0.0f, 0.5f));
static int vLightDir = IRS->GetShaderVarID(shader, "LDir");
if (vLightDir != -1){
Vector3D V1 = GetViewer()->GetRight() + GetViewer()->GetUp() + GetViewer()->GetForward();
V1 *= -1;
IRS->SetShaderVar(shader, vLightDir, V1);
}
comms::cRender::SetCullMode(comms::cCullMode::CounterClockwise);
boxStaticMesh->render();
comms::cRender::SetCullMode(comms::cCullMode::Clockwise);
boxStaticMesh->render();
comms::cRender::SetCullMode(comms::cCullMode::None);
}
Наслаждаемся результатом:
Создаём кнопку для добавления куба на сцену
Для этого создадим в классе процедуру, которая будет выполнять добавление куба в выбранный слой. Назовем ее Apply(). В файле "MyBoxTutorial.h" объявим ее в классе, а в регистрации класса, перед строкой }ENDSAVE;
, зарегистрируем этот метода следующей строкой:
REG_CMETHOD(MyBoxTutorialExt, Apply);
И добавим тело функции в "MyBoxTutorial.cpp":
void MyBoxTutorialExt::Apply(){
CurVObj->MergeModelSymm(boxMeshContainer, m, false);
CurVObj->WholeDirty = true;
}
На панели инструмента появилась кнопка Apply, которая успешно добавляет образец бокса в выбранный слой:
Управление манипулятором
В "MyBoxTutorial.h", в объявлении нашего класса, декларируем необходимые переменные и функции:
bool NeedResetPosition = true;
bool LocalSpace = true;
virtual void OnActivate();
void ResetAxis();
void ResetPosition();
void ResetSize();
Зарегистрируем их в макросе SAVE(MyBoxTutorialExt){
, перед кнопкой Apply:
COLUMNS(3);
REG_CMETHOD(MyBoxTutorialExt, ResetSize);
REG_CMETHOD(MyBoxTutorialExt, ResetAxis);
REG_CMETHOD(MyBoxTutorialExt, ResetPosition);
REG_MEMBER(_bool, LocalSpace);
Добавим реализацию новых функций в "MyBoxTutorial.cpp":
void MyBoxTutorialExt::OnActivate(){
voxtool().CHandler.CP.AxisX = Vector3D::AxisX;
voxtool().CHandler.CP.AxisY = Vector3D::AxisY;
voxtool().CHandler.CP.AxisZ = Vector3D::AxisZ;
voxtool().CHandler.CP.Pos = Vector3D::Zero;
voxtool().CHandler.CP.GlobalTransform = Matrix4D::Identity;
NeedResetPosition = true;
}
void MyBoxTutorialExt::ResetAxis(){
float sx = voxtool().CHandler.CP.GlobalTransform.Row0().Length();
float sy = voxtool().CHandler.CP.GlobalTransform.Row1().Length();
float sz = voxtool().CHandler.CP.GlobalTransform.Row2().Length();
voxtool().CHandler.CP.GlobalTransform.Row0().ToVec3() = Vector3D::AxisX * sx;
voxtool().CHandler.CP.GlobalTransform.Row1().ToVec3() = Vector3D::AxisY * sy;
voxtool().CHandler.CP.GlobalTransform.Row2().ToVec3() = Vector3D::AxisZ * sz;
voxtool().CHandler.CP.AxisX = Vector3D::AxisX;
voxtool().CHandler.CP.AxisY = Vector3D::AxisY;
voxtool().CHandler.CP.AxisZ = Vector3D::AxisZ;
}
void MyBoxTutorialExt::ResetPosition(){
voxtool().CHandler.ResetPosition();
voxtool().CHandler.CP.GlobalTransform.Row3().ToVec3() = Vector3D::Zero;
}
void MyBoxTutorialExt::ResetSize(){
voxtool().CHandler.CP.GlobalTransform.Row0().ToVec3().Normalize();
voxtool().CHandler.CP.GlobalTransform.Row1().ToVec3().Normalize();
voxtool().CHandler.CP.GlobalTransform.Row2().ToVec3().Normalize();
}
Добавляем в конце функции Render()
:
if (NeedResetPosition){
voxtool().CHandler.CP.Pos = Vector3D::Zero;
NeedResetPosition = false;
}
voxtool().CHandler.CP.LocalSpace = LocalSpace;
Результат после запуска 3DCoat:
Создание языковых файлов
Файлы локализации в 3DCoat - обычные XML файлы, где каждый TextItem - отдельный объект перевода, тег ID в нем - ключевое слово, а Text - переведенный текст.
Для добавления своих ключевых слов добавляем следующий текст в файл Languages/English.xml:
<TextItem>
<ID>MyBoxTool</ID>
<Text>My Box Tool</Text>
</TextItem>
<TextItem>
<ID>MyBoxTool_HINT</ID>
</TextItem>
<TextItem>
<ID>BoxWidth</ID>
<Text>Width</Text>
</TextItem>
<TextItem>
<ID>BoxHeight</ID>
<Text>Height</Text>
</TextItem>
<TextItem>
<ID>BoxLength</ID>
<Text>Length</Text>
</TextItem>
<TextItem>
<ID>XPos</ID>
<Text>X Pos</Text>
</TextItem>
<TextItem>
<ID>YPos</ID>
<Text>Y Pos</Text>
</TextItem>
<TextItem>
<ID>ZPos</ID>
<Text>Z Pos</Text>
</TextItem>
Результат:
Назначаем иконку нашему инструменту
Для назначения значка инструменту скопируем значок в формате png размером 128х128 в папку textures/icons64/
, после чего добавить в файл ui2.xml в список icons
информацию с ключевым словом названия инструмента и названием файла:
<IconicText>
<ID>MyBoxTool</ID>
<ToolName></ToolName>
<Texture>MyBoxTutorial.png</Texture>
</IconicText>
Работа с курсором
Этот фрагмент создаём для удобности работы с нашим кубом с помощью курсора и клавиш клавиатуры, а также быстрого размещения и масштабирования бокса. Для этого нам необходимо расширить программный код.
Итак, добавим в хидер следующий код:
virtual bool AllowCubeHandler() {
return !MoveToBrush;
};
virtual bool OnKey(char KeyCode);
float BoxWidth = 10.0f;
float BoxHeight = 10.0f;
float BoxLength = 10.0f;
float XPos = 0.0f;
float YPos = 0.0f;
float ZPos = 0.0f;
bool MoveToBrush = false;
REG_MEMBER(_bool, MoveToBrush);
Изменения, которые коснулись файла cpp:
bool MyBoxTutorialExt::OnKey(char KeyCode){
if (KeyCode == VK_RETURN) {
Apply();
return true;
}
return false;
}
void MyBoxTutorialExt::Render(){
if (MoveToBrush && (Widgets::lPressed || Widgets::rPressed)){
float scaling = GeneralPen::PenRadius / (BoxWidth + BoxHeight + BoxLength)*3.0;
Vector3D z = SurfaceModificator::LastNormal;
z.Normalize();
Vector3D x = Vector3D::Cross(Vector3D::AxisY, z);
x.Normalize();
Matrix4D& m = voxtool().CHandler.CP.GlobalTransform;
x *= scaling;
y *= scaling;
z *= scaling;
m.SetRow0(x.x, x.y, x.z, 0.0f);
m.SetRow1(y.x, y.y, y.z, 0.0f);
m.SetRow2(z.x, z.y, z.z, 0.0f);
m.SetRow3(pos.x, pos.y, pos.z, 1.0f);
}
Вот что у нас получилось:
Поздравляем! Ты создал свой первый инструмент в 3DCoat)
Исходники
Исходники проекта можешь запросто скачать по этой ссылке.
- See Also
- Создание эффекта слоёв рисования
-
Настройка IDE для сборки проекта
-
Работа с UI 3DCoat