fixes and implement objects snapping
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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]]
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user