Skip to content

Commit

Permalink
Resolve #4285: added option to export lights as small octahedrons whe…
Browse files Browse the repository at this point in the history
…n saving as model.
  • Loading branch information
codereader committed Apr 30, 2018
1 parent ef42033 commit c3efa6d
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 0 deletions.
1 change: 1 addition & 0 deletions include/imodel.h
Expand Up @@ -152,6 +152,7 @@ class IModelExporter
// Adds the given set of polygons to the named surface
// The given transform is applied to the surface before the vertices are added to the queue.
// Note: Scaling components of the matrix are not treated separately here.
// Note 2: Polygons are expected to have their vertices defined counter-clockwise
virtual void addPolygons(const std::string& materialName,
const std::vector<ModelPolygon>& polys, const Matrix4& localToWorld) = 0;

Expand Down
88 changes: 88 additions & 0 deletions install/ui/exportasmodeldialog.fbp
Expand Up @@ -980,6 +980,94 @@
<event name="OnUpdateUI"></event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">6</property>
<property name="flag">wxBOTTOM|wxTOP</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Export Lights as Objects (small octahedrons)</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">ExportDialogExportLightsAsObjects</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnChar"></event>
<event name="OnCheckBox"></event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
<event name="OnKeyUp"></event>
<event name="OnKillFocus"></event>
<event name="OnLeaveWindow"></event>
<event name="OnLeftDClick"></event>
<event name="OnLeftDown"></event>
<event name="OnLeftUp"></event>
<event name="OnMiddleDClick"></event>
<event name="OnMiddleDown"></event>
<event name="OnMiddleUp"></event>
<event name="OnMotion"></event>
<event name="OnMouseEvents"></event>
<event name="OnMouseWheel"></event>
<event name="OnPaint"></event>
<event name="OnRightDClick"></event>
<event name="OnRightDown"></event>
<event name="OnRightUp"></event>
<event name="OnSetFocus"></event>
<event name="OnSize"></event>
<event name="OnUpdateUI"></event>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1">
Expand Down
9 changes: 9 additions & 0 deletions install/ui/exportasmodeldialog.xrc
Expand Up @@ -124,6 +124,15 @@
<checked>0</checked>
</object>
</object>
<object class="sizeritem">
<option>0</option>
<flag>wxBOTTOM|wxTOP</flag>
<border>6</border>
<object class="wxCheckBox" name="ExportDialogExportLightsAsObjects">
<label>Export Lights as Objects (small octahedrons)</label>
<checked>0</checked>
</object>
</object>
</object>
</object>
<object class="sizeritem">
Expand Down
1 change: 1 addition & 0 deletions radiant/map/algorithm/Export.cpp
Expand Up @@ -55,6 +55,7 @@ void exportSelectedAsModel(const ModelExportOptions& options)

exporter.setCenterObjects(options.centerObjects);
exporter.setSkipCaulkMaterial(options.skipCaulk);
exporter.setExportLightsAsObjects(options.exportLightsAsObjects);

if (options.useEntityOrigin)
{
Expand Down
1 change: 1 addition & 0 deletions radiant/map/algorithm/Export.h
Expand Up @@ -17,6 +17,7 @@ struct ModelExportOptions
bool centerObjects; // whether to center objects
bool replaceSelectionWithModel; // delete the selection and put the exported model in its place
bool useEntityOrigin; // use entity origin as model origin (only applicable if a single entity is selected)
bool exportLightsAsObjects; // will export lights as small octahedrons
};

/**
Expand Down
56 changes: 56 additions & 0 deletions radiant/model/ModelExporter.cpp
Expand Up @@ -46,6 +46,22 @@ ArbitraryMeshVertex convertPatchVertex(const VertexNT& in)
return out;
}

// Create a polygon out of 3 vertices defined in counter-clockwise winding
// Only the normal will be calculated, texcoord, tangent and bitangents will be zero
model::ModelPolygon createPolyCCW(const Vertex3f& a, const Vertex3f& b, const Vertex3f& c)
{
model::ModelPolygon poly;

poly.a.vertex = a;
poly.b.vertex = b;
poly.c.vertex = c;

// Calc normals for all three vertices
poly.a.normal = poly.b.normal = poly.c.normal = (b-a).crossProduct(c-a).getNormalised();

return poly;
}

}

ModelExporter::ModelExporter(const model::IModelExporterPtr& exporter) :
Expand All @@ -55,6 +71,7 @@ ModelExporter::ModelExporter(const model::IModelExporterPtr& exporter) :
_centerObjects(false),
_origin(0,0,0),
_useOriginAsCenter(false),
_exportLightsAsObjects(false),
_centerTransform(Matrix4::getIdentity())
{
if (!_exporter)
Expand All @@ -80,6 +97,11 @@ void ModelExporter::setOrigin(const Vector3& origin)
_useOriginAsCenter = true;
}

void ModelExporter::setExportLightsAsObjects(bool enabled)
{
_exportLightsAsObjects = enabled;
}

bool ModelExporter::pre(const scene::INodePtr& node)
{
// Skip worldspawn
Expand Down Expand Up @@ -137,6 +159,10 @@ void ModelExporter::processNodes()
{
processPatch(node);
}
else if (_exportLightsAsObjects && Node_getLightNode(node))
{
processLight(node);
}
}
}

Expand Down Expand Up @@ -239,6 +265,36 @@ void ModelExporter::processBrush(const scene::INodePtr& node)
}
}

void ModelExporter::processLight(const scene::INodePtr& node)
{
// Export lights as small polyhedron
static const double EXTENTS = 8.0;
std::vector<model::ModelPolygon> polys;

Vertex3f up(0, 0, EXTENTS);
Vertex3f down(0, 0, -EXTENTS);
Vertex3f north(0, EXTENTS, 0);
Vertex3f south(0, -EXTENTS, 0);
Vertex3f east(EXTENTS, 0, 0);
Vertex3f west(-EXTENTS, 0, 0);

// Upper semi-diamond
polys.push_back(createPolyCCW(up, south, east));
polys.push_back(createPolyCCW(up, east, north));
polys.push_back(createPolyCCW(up, north, west));
polys.push_back(createPolyCCW(up, west, south));

// Lower semi-diamond
polys.push_back(createPolyCCW(down, south, west));
polys.push_back(createPolyCCW(down, west, north));
polys.push_back(createPolyCCW(down, north, east));
polys.push_back(createPolyCCW(down, east, south));

Matrix4 exportTransform = node->localToWorld().getPremultipliedBy(_centerTransform);

_exporter->addPolygons("lights/default", polys, exportTransform);
}

bool ModelExporter::isExportableMaterial(const std::string& materialName)
{
return !_skipCaulk || materialName != _caulkMaterial;
Expand Down
7 changes: 7 additions & 0 deletions radiant/model/ModelExporter.h
Expand Up @@ -28,6 +28,9 @@ class ModelExporter :
Vector3 _origin;
bool _useOriginAsCenter;

// Whether lights should be exported too (as small diamond-shaped objects)
bool _exportLightsAsObjects;

std::list<scene::INodePtr> _nodes;

// The translation centering the objects
Expand All @@ -46,6 +49,9 @@ class ModelExporter :
// Define the origin to use for centering the objects
void setOrigin(const Vector3& origin);

// Set whether lights should be exported too (as small diamond-shaped objects)
void setExportLightsAsObjects(bool enabled);

bool pre(const scene::INodePtr& node) override;

// Processes the nodes previously collected in the pre() method
Expand Down Expand Up @@ -73,6 +79,7 @@ class ModelExporter :

void processBrush(const scene::INodePtr& node);
void processPatch(const scene::INodePtr& node);
void processLight(const scene::INodePtr& node);
};

}
8 changes: 8 additions & 0 deletions radiant/ui/modelexport/ExportAsModelDialog.cpp
Expand Up @@ -39,6 +39,7 @@ namespace
const char* RKEY_MODEL_EXPORT_OUTPUT_PATH = "user/ui/exportAsModel/outputPath";
const char* RKEY_MODEL_EXPORT_OUTPUT_FORMAT = "user/ui/exportAsModel/outputFormat";
const char* RKEY_MODEL_EXPORT_USE_ENTITY_ORIGIN = "user/ui/exportAsModel/keepEntityOrigin";
const char* RKEY_MODEL_EXPORT_EXPORT_LIGHTS_AS_OBJECTS = "user/ui/exportAsModel/exportLightsAsObjects";
}

ExportAsModelDialog::ExportAsModelDialog(wxWindow* parent) :
Expand Down Expand Up @@ -134,6 +135,9 @@ void ExportAsModelDialog::populateWindow()
bool keepEntityOrigin = registry::getValue<bool>(RKEY_MODEL_EXPORT_USE_ENTITY_ORIGIN);
wxCheckBox* keepOriginBox = findNamedObject<wxCheckBox>(this, "ExportDialogUseEntityOrigin");

bool exportLightsAsObjects = registry::getValue<bool>(RKEY_MODEL_EXPORT_EXPORT_LIGHTS_AS_OBJECTS);
findNamedObject<wxCheckBox>(this, "ExportDialogExportLightsAsObjects")->SetValue(exportLightsAsObjects);

const SelectionInfo& info = GlobalSelectionSystem().getSelectionInfo();

if (info.totalCount == 1 && info.entityCount == 1)
Expand Down Expand Up @@ -165,6 +169,7 @@ void ExportAsModelDialog::onExport(wxCommandEvent& ev)
options.outputFormat = wxutil::ChoiceHelper::GetSelectedStoredString(findNamedObject<wxChoice>(this, "ExportDialogFormatChoice"));
options.replaceSelectionWithModel = findNamedObject<wxCheckBox>(this, "ExportDialogReplaceWithModel")->GetValue();
options.useEntityOrigin = findNamedObject<wxCheckBox>(this, "ExportDialogUseEntityOrigin")->GetValue();
options.exportLightsAsObjects = findNamedObject<wxCheckBox>(this, "ExportDialogExportLightsAsObjects")->GetValue();

if (options.outputFilename.empty())
{
Expand Down Expand Up @@ -266,6 +271,9 @@ void ExportAsModelDialog::saveOptionsToRegistry()

registry::setValue(RKEY_MODEL_EXPORT_USE_ENTITY_ORIGIN,
findNamedObject<wxCheckBox>(this, "ExportDialogUseEntityOrigin")->GetValue());

registry::setValue(RKEY_MODEL_EXPORT_EXPORT_LIGHTS_AS_OBJECTS,
findNamedObject<wxCheckBox>(this, "ExportDialogExportLightsAsObjects")->GetValue());
}

void ExportAsModelDialog::ShowDialog(const cmd::ArgumentList& args)
Expand Down

0 comments on commit c3efa6d

Please sign in to comment.