fixes and implement objects snapping

This commit is contained in:
T1ti
2023-04-07 01:50:31 +02:00
parent c06604b919
commit e6b031313d
17 changed files with 383 additions and 79 deletions

View File

@@ -812,6 +812,7 @@ void MapView::setupObjectEditorUi()
, &_rotate_along_ground
, &_rotate_along_ground_smooth
, &_rotate_along_ground_random
, &_move_model_snap_to_objects
, this
);
_tool_panel_dock->registerTool("Object Editor", objectEditor);
@@ -3453,6 +3454,7 @@ void MapView::tick (float dt)
else
{
bool snapped = false;
bool snapped_to_object = false;
if (_world->has_multiple_model_selected())
{
NOGGIT_ACTION_MGR->beginAction(this, Noggit::ActionFlags::eOBJECTS_TRANSFORMED,
@@ -3482,14 +3484,73 @@ void MapView::tick (float dt)
{
NOGGIT_ACTION_MGR->beginAction(this, Noggit::ActionFlags::eOBJECTS_TRANSFORMED,
Noggit::ActionModalityControllers::eMMB );
_world->set_selected_models_pos(_cursor_pos, false);
snapped = true;
if (_move_model_to_cursor_position.get() || _move_model_snap_to_objects.get())
{
selection_result results(intersect_result(false));
if (!results.empty())
{
for (auto result = results.begin(); result != results.end(); result++)
{
auto const& hit(result->second);
bool is_selected_model = false;
// if a terrain is found first use that (terrain cursor pos position updated on move already)
if (hit.index() == eEntry_MapChunk && _move_model_to_cursor_position.get())
{
break;
}
if (hit.index() == eEntry_Object && _move_model_snap_to_objects.get())
{
auto obj_hit = std::get<selected_object_type>(hit);
auto obj_hit_type = obj_hit->which();
// don't snap to animated models
if (obj_hit_type == eMODEL)
{
auto m2_model_hit = static_cast<ModelInstance*>(obj_hit);
if (m2_model_hit->model->animated_mesh())
continue;
}
// find and ignore current object/selected models or it will keep snaping to itself
for (auto& entry : _world->current_selection())
{
auto type = entry.index();
if (type == eEntry_Object)
{
auto& selection_obj = std::get<selected_object_type>(entry);
if (selection_obj->uid == obj_hit->uid)
{
is_selected_model = true;
break;
}
}
}
if (is_selected_model)
continue;
auto hit_pos = intersect_ray().position(result->first);
_cursor_pos = hit_pos;
snapped_to_object = true;
if (_rotate_doodads_along_doodads.get()) // TODO
_world->rotate_selected_models_to_object_normal(_rotate_along_ground_smooth.get(), obj_hit, hit_pos, glm::transpose(model_view()), _rotate_doodads_along_wmos.get());
break;
}
}
}
_world->set_selected_models_pos(_cursor_pos, false);
snapped = true;
}
}
}
if (snapped && _rotate_along_ground.get())
{
_world->rotate_selected_models_to_ground_normal(_rotate_along_ground_smooth.get());
if (!snapped_to_object)
_world->rotate_selected_models_to_ground_normal(_rotate_along_ground_smooth.get());
if (_rotate_along_ground_random.get())
{
float minX = 0, maxX = 0, minY = 0, maxY = 0, minZ = 0, maxZ = 0;
@@ -4065,6 +4126,7 @@ selection_result MapView::intersect_result(bool terrain_only)
, _draw_wmo.get()
, _draw_models.get()
, _draw_hidden_models.get()
, _draw_wmo_exterior.get()
)
);
@@ -4378,6 +4440,8 @@ void MapView::draw_map()
, _display_all_water_layers.get() ? -1 : _displayed_water_layer.get()
, _display_mode
, _draw_occlusion_boxes.get()
,false
, _draw_wmo_exterior.get()
);
// reset after each world::draw call

View File

@@ -147,6 +147,7 @@ public:
Noggit::BoolToggleProperty _draw_wmo = {true};
Noggit::BoolToggleProperty _draw_water = {true};
Noggit::BoolToggleProperty _draw_wmo_doodads = {true};
Noggit::BoolToggleProperty _draw_wmo_exterior = { true };
Noggit::BoolToggleProperty _draw_models = {true};
Noggit::BoolToggleProperty _draw_model_animations = {false};
Noggit::BoolToggleProperty _draw_hole_lines = {false};
@@ -370,8 +371,11 @@ private:
Noggit::BoolToggleProperty _locked_cursor_mode = {false};
Noggit::BoolToggleProperty _move_model_to_cursor_position = {true};
Noggit::BoolToggleProperty _move_model_snap_to_objects = { true };
Noggit::BoolToggleProperty _snap_multi_selection_to_ground = {false};
Noggit::BoolToggleProperty _rotate_along_ground = {true };
Noggit::BoolToggleProperty _rotate_doodads_along_doodads = { false };
Noggit::BoolToggleProperty _rotate_doodads_along_wmos = { false };
Noggit::BoolToggleProperty _rotate_along_ground_smooth = {true };
Noggit::BoolToggleProperty _rotate_along_ground_random = {false };
Noggit::BoolToggleProperty _use_median_pivot_point = {true};

View File

@@ -755,9 +755,9 @@ void Bone::calcMatrix(glm::mat4x4 const& model_view
}
std::vector<float> Model::intersect (glm::mat4x4 const& model_view, math::ray const& ray, int animtime)
std::vector<std::pair<float, std::tuple<int, int, int>>> Model::intersect (glm::mat4x4 const& model_view, math::ray const& ray, int animtime)
{
std::vector<float> results;
std::vector<std::pair<float, std::tuple<int, int, int>>> results;
if (!finishedLoading() || loading_failed())
{
@@ -782,7 +782,7 @@ std::vector<float> Model::intersect (glm::mat4x4 const& model_view, math::ray co
fake_geom.vertices[fake_geom.indices[i + 2]])
)
{
results.emplace_back (*distance);
results.emplace_back (*distance, std::make_tuple(i, i+1, 1+2));
return results;
}
}
@@ -800,7 +800,7 @@ std::vector<float> Model::intersect (glm::mat4x4 const& model_view, math::ray co
_vertices[_indices[i + 2]].position)
)
{
results.emplace_back (*distance);
results.emplace_back (*distance, std::make_tuple(i, i + 1, 1 + 2));
}
}
}

View File

@@ -139,7 +139,7 @@ public:
Model(const std::string& name, Noggit::NoggitRenderContext context );
std::vector<float> intersect (glm::mat4x4 const& model_view, math::ray const&, int animtime);
std::vector<std::pair<float, std::tuple<int, int, int>>> intersect (glm::mat4x4 const& model_view, math::ray const&, int animtime);
void updateEmitters(float dt);
@@ -155,6 +155,9 @@ public:
[[nodiscard]]
bool use_fake_geometry() const { return !!_fake_geometry; }
[[nodiscard]]
bool animated_mesh() const { return (animGeometry || animBones); }
[[nodiscard]]
bool is_required_when_saving() const override
{
@@ -191,6 +194,17 @@ public:
float trans;
bool animcalc;
// ===============================
// Geometry
// ===============================
std::vector<ModelVertex> _vertices;
std::vector<ModelVertex> _current_vertices;
std::vector<uint16_t> _indices;
std::optional<FakeGeometry> _fake_geometry;
private:
bool _per_instance_animation;
int _current_anim_seq;
@@ -209,16 +223,7 @@ private:
void lightsOn(OpenGL::light lbase);
void lightsOff(OpenGL::light lbase);
// ===============================
// Geometry
// ===============================
std::vector<ModelVertex> _vertices;
std::vector<ModelVertex> _current_vertices;
std::vector<uint16_t> _indices;
std::optional<FakeGeometry> _fake_geometry;
// ===============================
// Animation

View File

@@ -82,12 +82,13 @@ void ModelInstance::draw_box (glm::mat4x4 const& model_view
}
}
void ModelInstance::intersect (glm::mat4x4 const& model_view
std::vector<std::tuple<int, int, int>> ModelInstance::intersect (glm::mat4x4 const& model_view
, math::ray const& ray
, selection_result* results
, int animtime
)
{
{
std::vector<std::tuple<int, int, int>> triangle_indices;
math::ray subray (_transform_mat_inverted, ray);
if ( !subray.intersect_bounds ( fixCoordSystem (model->header.bounding_box_min)
@@ -95,15 +96,17 @@ void ModelInstance::intersect (glm::mat4x4 const& model_view
)
)
{
return;
return triangle_indices;
}
for (auto&& result : model->intersect (model_view, subray, animtime))
{
//! \todo why is only sc important? these are relative to subray,
//! so should be inverted by model_matrix?
results->emplace_back (result * scale, this);
results->emplace_back (result.first * scale, this);
triangle_indices.emplace_back(result.second);
}
return triangle_indices;
}

View File

@@ -80,12 +80,12 @@ public:
, bool is_current_selection
);
void intersect (glm::mat4x4 const& model_view
, math::ray const&
, selection_result*
, int animtime
);
std::vector<std::tuple<int, int, int>> intersect(glm::mat4x4 const& model_view
, math::ray const&
, selection_result*
, int animtime
);
bool isInFrustum(math::frustum const& frustum);
bool isInRenderDist(const float& cull_distance, const glm::vec3& camera, display_mode display);

View File

@@ -353,7 +353,7 @@ void WMO::waitForChildrenLoaded()
}
}
std::vector<float> WMO::intersect (math::ray const& ray) const
std::vector<float> WMO::intersect (math::ray const& ray, bool do_exterior) const
{
std::vector<float> results;
@@ -364,9 +364,27 @@ std::vector<float> WMO::intersect (math::ray const& ray) const
for (auto& group : groups)
{
if (!do_exterior && !group.is_indoor())
continue;
group.intersect (ray, &results);
}
if (!do_exterior && results.size())
{
// dirty way to find the furthest face and ignore invisible faces, cleaner way would be to do a direction check on faces
// float max = *std::max_element(std::begin(results), std::end(results));
// results.clear();
// results.push_back(max);
// other way, ignore the closest intersect, works well
if (results.size() > 1)
{
auto it = std::min_element(results.begin(), results.end());
results.erase(it);
}
}
return results;
}
@@ -575,6 +593,9 @@ void WMOGroup::load()
assert (fourcc == 'MOPY');
// _material_infos.resize(size / sizeof(wmo_triangle_material_info));
// f.read(_material_infos.data(), size);
f.seekRelative (size);
// - MOVI ----------------------------------------------
@@ -711,7 +732,11 @@ void WMOGroup::load()
}
else
{
f.seekRelative (size);
f.seekRelative(size);
// std::vector<wmo_bsp_node> bps_tree_nodes;
// bps_tree_nodes.resize(size / sizeof(wmo_bsp_node));
// f.read(bps_tree_nodes.data(), size);
//_bsp_tree_nodes = bps_tree_nodes;
}
}
@@ -729,6 +754,10 @@ void WMOGroup::load()
else
{
f.seekRelative (size);
// std::vector<uint16_t> bsp_indices;
// bsp_indices.resize(size / sizeof(uint16_t));
// f.read(bsp_indices.data(), size);
// _bsp_indices = bsp_indices;
}
}
@@ -1084,6 +1113,8 @@ void WMOGroup::intersect (math::ray const& ray, std::vector<float>* results) con
{
for (size_t i (batch.index_start); i < batch.index_start + batch.index_count; i += 3)
{
// TODO : only intersect visible triangles
// TODO : option to only check collision
if ( auto&& distance
= ray.intersect_triangle ( _vertices[_indices[i + 0]]
, _vertices[_indices[i + 1]]

View File

@@ -49,6 +49,59 @@ struct wmo_batch
uint8_t texture;
};
union wmo_mopy_flags
{
int8_t value;
struct
{
int8_t flag_0x01 : 1; // 0x1
int8_t no_cam_collide : 1; // 0x2
int8_t detail : 1; // 0x4
int8_t collision : 1; // 0x8
int8_t hint : 1;
int8_t render : 1;
int8_t flag_0x40 : 1; // 0x40
int8_t collide_hit : 1; // 0x80
};
};
static_assert (sizeof(wmo_mopy_flags) == sizeof(std::int8_t)
, "bitfields shall be implemented packed"
);
struct wmo_triangle_material_info
{
wmo_mopy_flags flags;
uint8_t texture;
bool isTransFace() { return flags.flag_0x01 && (flags.detail || flags.render); }
bool isColor() { return !flags.collision; }
bool isRenderFace() { return flags.render && !flags.detail; }
bool isCollidable() { return flags.collision || isRenderFace(); }
bool isCollision() { return texture == 0xff; }
};
enum wmo_mobn_flags
{
Flag_XAxis = 0x0,
Flag_YAxis = 0x1,
Flag_ZAxis = 0x2,
Flag_AxisMask = 0x3,
Flag_Leaf = 0x4,
Flag_NoChild = 0xFFFF,
};
struct wmo_bsp_node
{
uint16_t flags;
int16_t negChild; // index of bsp child node (right in this array)
int16_t posChild;
uint16_t nFaces; // num of triangle faces in MOBR
uint32_t faceStart; // index of the first triangle index(in MOBR)
float planeDist;
};
union wmo_group_flags
{
uint32_t value;
@@ -173,6 +226,7 @@ private:
std::vector<uint16_t> _doodad_ref;
std::unique_ptr<wmo_liquid> lq;
std::vector <wmo_triangle_material_info> _material_infos;
std::vector<wmo_batch> _batches;
std::vector<::glm::vec3> _vertices;
@@ -182,6 +236,9 @@ private:
std::vector<glm::vec4> _vertex_colors;
std::vector<uint16_t> _indices;
std::optional<std::vector<wmo_bsp_node>> _bsp_tree_nodes;
std::optional<std::vector<uint16_t>> _bsp_indices;
Noggit::Rendering::WMOGroupRender _renderer;
};
@@ -253,7 +310,7 @@ public:
explicit WMO(BlizzardArchive::Listfile::FileKey const& file_key, Noggit::NoggitRenderContext context );
[[nodiscard]]
std::vector<float> intersect (math::ray const&) const;
std::vector<float> intersect (math::ray const&, bool do_exterior = true) const;
void finishLoading() override;

View File

@@ -140,7 +140,7 @@ void WMOInstance::draw ( OpenGL::Scoped::use_program& wmo_shader
}
}
void WMOInstance::intersect (math::ray const& ray, selection_result* results)
void WMOInstance::intersect (math::ray const& ray, selection_result* results, bool do_exterior)
{
if (!ray.intersect_bounds (extents[0], extents[1]))
{
@@ -149,7 +149,7 @@ void WMOInstance::intersect (math::ray const& ray, selection_result* results)
math::ray subray(_transform_mat_inverted, ray);
for (auto&& result : wmo->intersect(subray))
for (auto&& result : wmo->intersect(subray, do_exterior))
{
results->emplace_back (result, this);
}
@@ -242,13 +242,14 @@ void WMOInstance::recalcExtents()
points.insert(points.end(), adjustedGroupPoints.begin(), adjustedGroupPoints.end());
if (group.has_skybox())
if (group.has_skybox() || _update_group_extents)
{
math::aabb const group_aabb(adjustedGroupPoints);
group_extents[i] = {group_aabb.min, group_aabb.max};
}
}
_update_group_extents = false;
math::aabb const wmo_aabb(points);

View File

@@ -22,6 +22,9 @@ public:
uint16_t doodadset() const { return _doodadset; }
void change_doodadset(uint16_t doodad_set);
[[nodiscard]]
std::map<int, std::pair<glm::vec3, glm::vec3>> const& getGroupExtents() { _update_group_extents = true; ensureExtents(); return group_extents; }
private:
void update_doodads();
@@ -29,6 +32,7 @@ private:
std::map<uint32_t, std::vector<wmo_doodad_instance>> _doodads_per_group;
bool _need_doodadset_update = true;
bool _update_group_extents = false;
public:
WMOInstance(BlizzardArchive::Listfile::FileKey const& file_key, ENTRY_MODF const* d, Noggit::NoggitRenderContext context);
@@ -96,7 +100,7 @@ public:
, bool draw_exterior = true
);
void intersect (math::ray const&, selection_result*);
void intersect (math::ray const&, selection_result*, bool do_exterior = true);
void recalcExtents() override;
void ensureExtents() override;

View File

@@ -321,6 +321,125 @@ void World::rotate_selected_models_randomly(float minX, float maxX, float minY,
}
}
void World::rotate_selected_models_to_object_normal(bool smoothNormals, selected_object_type object_hit, glm::vec3 hit_pos, glm::mat4x4 modelview, bool follow_wmos)
{
// TODO, doesn't work
ZoneScoped;
// only iterate 1st object for now
if (!_current_selection.size() || _current_selection.size() > 1)
return;
auto& entry = _current_selection.front();
auto type = entry.index();
if (type != eEntry_Object)
{
return;
}
auto& selection_obj = std::get<selected_object_type>(entry);
NOGGIT_CUR_ACTION->registerObjectTransformed(selection_obj);
updateTilesEntry(entry, model_update::remove);
glm::vec3 rayPos = selection_obj->pos;
math::degrees::vec3& dir = selection_obj->dir;
// auto const& hitChunkInfo = std::get<selected_chunk_type>(results.front().second);
glm::quat q;
glm::vec3 varnormal;
// Surface Normal
auto obj_hit_type = object_hit->which();
glm::vec3 p0;
glm::vec3 p1;
glm::vec3 p2;
// only do M2s for now
if (obj_hit_type == eMODEL)
{
auto m2_model_hit = static_cast<ModelInstance*>(object_hit);
selection_result results;
math::ray intersect_ray(rayPos, glm::vec3(0.f, -1.f, 0.f));
auto triangle_indices = m2_model_hit->intersect(modelview, intersect_ray, &results, 0);
// object is below ground
if (results.empty())
{
math::ray intersect_ray(rayPos, glm::vec3(0.f, 1.f, 0.f));
triangle_indices = m2_model_hit->intersect(modelview, intersect_ray, &results, 0);
}
if (triangle_indices.empty())
return;
std::tuple<int, int, int> closest_triangle = triangle_indices.front();
float closest_intersect = results.front().first;
for (int i = 0; i < results.size(); i++)
{
if (results[i].first < closest_intersect)
{
closest_intersect = results[i].first;
closest_triangle = triangle_indices[i];
}
}
auto first_triangle_indices = triangle_indices.front();
p0 = m2_model_hit->model->_vertices[m2_model_hit->model->_indices[std::get<0>(closest_triangle)]].position;
p1 = m2_model_hit->model->_vertices[m2_model_hit->model->_indices[std::get<1>(closest_triangle)]].position;
p2 = m2_model_hit->model->_vertices[m2_model_hit->model->_indices[std::get<2>(closest_triangle)]].position;
}
auto transform = object_hit->transformMatrix();
p0 = transform * glm::vec4(p0, 1.f);
p1 = transform * glm::vec4(p1, 1.f);
p2 = transform * glm::vec4(p2, 1.f);
glm::vec3 v1 = p1 - p0;
glm::vec3 v2 = p2 - p0;
auto tmpVec = glm::cross(v2, v1);
varnormal.x = tmpVec.z;
varnormal.y = tmpVec.y;
varnormal.z = tmpVec.x;
// Smooth option, gradient the normal towards closest vertex
if (smoothNormals) // Vertex Normal
{
// todo
}
glm::vec3 worldUp = glm::vec3(0, 1, 0);
glm::vec3 a = glm::cross(worldUp, varnormal);
q.x = a.x;
q.y = a.y;
q.z = a.z;
auto worldLengthSqrd = glm::length(worldUp) * glm::length(worldUp);
auto normalLengthSqrd = glm::length(varnormal) * glm::length(varnormal);
auto worldDotNormal = glm::dot(worldUp, varnormal);
q.w = std::sqrt((worldLengthSqrd * normalLengthSqrd) + (worldDotNormal));
auto normalizedQ = glm::normalize(q);
math::degrees::vec3 new_dir;
auto eulerAngles = glm::eulerAngles(normalizedQ);
dir.x = math::degrees(math::radians(eulerAngles.z))._; //Roll
dir.y = math::degrees(math::radians(eulerAngles.x))._; //Pitch
dir.z = math::degrees(math::radians(eulerAngles.y))._; //Yaw
std::get<selected_object_type>(entry)->recalcExtents();
updateTilesEntry(entry, model_update::add);
}
void World::rotate_selected_models_to_ground_normal(bool smoothNormals)
{
@@ -788,48 +907,43 @@ MapChunk* World::getChunkAt(glm::vec3 const& pos)
return nullptr;
}
bool World::isInIndoorWmoGroup(glm::vec3 const& pos)
bool World::isInIndoorWmoGroup(std::array<glm::vec3, 2> obj_bounds)
{
bool is_indoor = false;
// check if model bounds is within wmo bounds then check each indor wmo group bounds
_model_instance_storage.for_each_wmo_instance([&](WMOInstance& wmo_instance)
{
auto wmo_extents = wmo_instance.getExtents();
if (obj_bounds[1].x >= wmo_extents[0].x
&& obj_bounds[1].y >= wmo_extents[0].y
&& obj_bounds[1].z >= wmo_extents[0].z
&& wmo_extents[1].x >= obj_bounds[0].x
&& wmo_extents[1].y >= obj_bounds[0].y
&& wmo_extents[1].z >= obj_bounds[0].z)
if (pos.x >= wmo_instance.extents[0].x && pos.y >= wmo_instance.extents[0].y && pos.z >= wmo_instance.extents[0].z
&& pos.x <= wmo_instance.extents[1].x && pos.y <= wmo_instance.extents[1].y && pos.z <= wmo_instance.extents[1].z)
{
for (auto wmo_group_it = wmo_instance.wmo->groups.begin(); wmo_group_it != wmo_instance.wmo->groups.end(); ++wmo_group_it)
for (int i = 0; i < (int)wmo_instance.wmo->groups.size(); ++i)
{
if (wmo_group_it->is_indoor())
auto const& group = wmo_instance.wmo->groups[i];
if (group.is_indoor())
{
// glm::vec3 obj_pos = wmo_instance.pos + wmo_group_it->VertexBoxMax;
// must call getGroupExtent() to initialize wmo_instance.group_extents
// TODO : clear group extents to free memory ?
auto& group_extents = wmo_instance.getGroupExtents().at(i);
glm::vec3 obj_pos = wmo_instance.transformMatrix() * glm::vec4(wmo_group_it->center, 0);
obj_pos += wmo_instance.pos;
// glm::vec3 obj_pos = wmo_instance.transformMatrix() * glm::vec4(wmo_instance.pos, 0);
// TODO : do a precise calculation instead of using axis aligned bounding boxes.
auto test = obj_bounds[1].x >= group_extents.first.x
&& obj_bounds[1].y >= group_extents.first.y
&& obj_bounds[1].z >= group_extents.first.z
&& group_extents.second.x >= obj_bounds[0].x
&& group_extents.second.y >= obj_bounds[0].y
&& group_extents.second.z >= obj_bounds[0].z;
// glm::vec3 obj_pos = wmo_instance.pos;
bool in_inf_x = pos.x >= obj_pos.x + wmo_group_it->VertexBoxMin.x;
// bool in_inf_y = pos.y >= obj_pos.y + wmo_group_it->VertexBoxMin.y;
bool in_inf_y = true;
bool in_inf_z = pos.z >= obj_pos.z + wmo_group_it->VertexBoxMin.z;
// bool in_bbinf_x = pos.x >= obj_pos.x + wmo_group_it->BoundingBoxMin.x;
// bool in_bbinf_y = pos.y >= obj_pos.y + wmo_group_it->BoundingBoxMin.y;
// bool in_bbinf_z = pos.z >= obj_pos.z + wmo_group_it->BoundingBoxMin.z;
bool in_sup_x = pos.x <= obj_pos.x + wmo_group_it->VertexBoxMax.x;
bool in_sup_y = pos.y <= obj_pos.y + wmo_group_it->VertexBoxMax.y;
bool in_sup_z = pos.z <= obj_pos.z + wmo_group_it->VertexBoxMax.z;
bool in_inf = in_inf_x && in_inf_y && in_inf_z;
bool in_sup = in_sup_x && in_sup_y && in_sup_z;
// if (wmo_group_it->VertexBoxMin)
if (in_inf && in_sup)
{
is_indoor = true;
return true;
}
is_indoor = test;
return;
}
}
}
@@ -846,6 +960,7 @@ selection_result World::intersect (glm::mat4x4 const& model_view
, bool draw_wmo
, bool draw_models
, bool draw_hidden_models
, bool draw_wmo_exterior
)
{
ZoneScopedN("World::intersect()");
@@ -898,7 +1013,7 @@ selection_result World::intersect (glm::mat4x4 const& model_view
{
if (draw_hidden_models || !wmo_instance.wmo->is_hidden())
{
wmo_instance.intersect(ray, &results);
wmo_instance.intersect(ray, &results, draw_wmo_exterior);
}
});
}

View File

@@ -100,11 +100,12 @@ public:
, bool draw_wmo
, bool draw_models
, bool draw_hidden_models
, bool draw_wmo_exterior
);
MapChunk* getChunkAt(glm::vec3 const& pos);
bool isInIndoorWmoGroup(glm::vec3 const& pos);
bool isInIndoorWmoGroup(std::array<glm::vec3, 2> obj_bounds);
protected:
// Information about the currently selected model / WMO / triangle.
@@ -155,6 +156,7 @@ public:
void set_selected_models_pos(glm::vec3 const& pos, bool change_height = true);
void rotate_selected_models(math::degrees rx, math::degrees ry, math::degrees rz, bool use_pivot);
void rotate_selected_models_randomly(float minX, float maxX, float minY, float maxY, float minZ, float maxZ);
void rotate_selected_models_to_object_normal(bool smoothNormals, selected_object_type object_hit, glm::vec3 hit_pos, glm::mat4x4 modelview, bool follow_wmos);
void set_selected_models_rotation(math::degrees rx, math::degrees ry, math::degrees rz);
// Checks the normal of the terrain on model origin and rotates to that spot.

View File

@@ -59,6 +59,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
, display_mode display
, bool draw_occlusion_boxes
, bool minimap_render
, bool draw_wmo_exterior
)
{
@@ -518,6 +519,9 @@ void WorldRender::draw (glm::mat4x4 const& model_view
, _world->animtime
, _skies->hasSkies()
, display
, false
, draw_wmo_exterior
);
}
}

View File

@@ -69,6 +69,7 @@ namespace Noggit::Rendering
, display_mode display
, bool draw_occlusion_boxes = false
, bool minimap_render = false
, bool draw_wmo_exterior = true
);
bool saveMinimap (TileIndex const& tile_idx

View File

@@ -46,6 +46,7 @@ namespace Noggit
, BoolToggleProperty* rotate_along_ground
, BoolToggleProperty* rotate_along_ground_smooth
, BoolToggleProperty* rotate_along_ground_random
, BoolToggleProperty* move_model_snap_to_objects
, QWidget* parent
)
: QWidget(parent)
@@ -214,6 +215,11 @@ namespace Noggit
)
);
auto object_movement_snap_cb(new CheckBox("Mouse move snaps\nto objects"
, move_model_snap_to_objects
, this
)
);
auto object_rotateground_cb(new CheckBox("Rotate following cursor"
, rotate_along_ground
@@ -237,6 +243,7 @@ namespace Noggit
object_movement_layout->addRow(object_rotategroundsmooth_cb);
object_movement_layout->addRow(object_rotategroundrandom_cb);
object_movement_layout->addRow(object_movement_cb);
object_movement_layout->addRow(object_movement_snap_cb);
// multi model selection
auto multi_select_movement_box(new QGroupBox("Multi Selection Movement", this));
@@ -630,20 +637,12 @@ namespace Noggit
break;
}
// check if pos is valid
bool is_indoor = world->isInIndoorWmoGroup(pos);
if (is_indoor)
{
QMessageBox::warning
(nullptr
, "Warning"
, "You can't place M2 models inside WMO models interiors, they will not render."
"\nTo place objects inside WMOs, use server side gameobjects or modify the WMO doodads(with wow blender studio)."
);
}
if (obj->which() == eMODEL)
{
auto model_instance = static_cast<ModelInstance*>(obj);
float scale(1.f);
math::degrees::vec3 rotation(math::degrees(0)._, math::degrees(0)._, math::degrees(0)._);
@@ -665,6 +664,18 @@ namespace Noggit
new_obj->model->waitForChildrenLoaded();
new_obj->recalcExtents();
// check if pos is valid (not in an interior) wmo group
bool is_indoor = world->isInIndoorWmoGroup(new_obj->extents);
if (is_indoor)
{
QMessageBox::warning
(nullptr
, "Warning"
, "You can't place M2 models inside WMO models interiors, they will not render."
"\nTo place objects inside WMOs, use server side gameobjects or modify the WMO doodads(with wow blender studio)."
);
}
}
else if (obj->which() == eWMO)
{
@@ -680,7 +691,7 @@ namespace Noggit
new_obj->wmo->waitForChildrenLoaded();
new_obj->recalcExtents();
}
}
}
}
void object_editor::togglePasteMode()

View File

@@ -60,6 +60,7 @@ namespace Noggit
, BoolToggleProperty* rotate_along_ground
, BoolToggleProperty* rotate_along_ground_smooth
, BoolToggleProperty* rotate_along_ground_random
, BoolToggleProperty* move_model_snap_to_objects
, QWidget* parent = nullptr
);

View File

@@ -70,6 +70,7 @@ ViewToolbar::ViewToolbar(MapView *mapView, ViewToolbar *tb)
add_tool_icon(mapView, &mapView->_draw_models, tr("Doodads"), FontNoggit::VISIBILITY_DOODADS, tb);
add_tool_icon(mapView, &mapView->_draw_wmo, tr("WMOs"), FontNoggit::VISIBILITY_WMO, tb);
add_tool_icon(mapView, &mapView->_draw_wmo_doodads, tr("WMO doodads"), FontNoggit::VISIBILITY_WMO_DOODADS, tb);
add_tool_icon(mapView, &mapView->_draw_wmo_exterior, tr("WMO exterior"), FontNoggit::GIZMO_VISIBILITY, tb);
add_tool_icon(mapView, &mapView->_draw_terrain, tr("Terrain"), FontNoggit::VISIBILITY_TERRAIN, tb);
add_tool_icon(mapView, &mapView->_draw_water, tr("Water"), FontNoggit::VISIBILITY_WATER, tb);