mirror of
https://github.com/Garux/netradiant-custom.git
synced 2026-09-28 00:00:03 +02:00
split selection.cpp
This commit is contained in:
@@ -938,6 +938,13 @@ $(INSTALLDIR)/radiant.$(EXE): \
|
||||
radiant/renderstate.o \
|
||||
radiant/scenegraph.o \
|
||||
radiant/selection.o \
|
||||
radiant/selection_mtor_clip.o \
|
||||
radiant/selection_mtor_drag.o \
|
||||
radiant/selection_mtor_rotate.o \
|
||||
radiant/selection_mtor_scale.o \
|
||||
radiant/selection_mtor_skew.o \
|
||||
radiant/selection_mtor_translate.o \
|
||||
radiant/selection_mtor_uv.o \
|
||||
radiant/select.o \
|
||||
radiant/server.o \
|
||||
radiant/sockets.o \
|
||||
|
||||
+65
-5963
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pivot.h"
|
||||
#include "selectable.h"
|
||||
#include "view.h"
|
||||
#include "windowobserver.h"
|
||||
#include "rect_t.h"
|
||||
|
||||
inline int g_SELECT_EPSILON = 12;
|
||||
|
||||
using DeviceVector = Vector2;
|
||||
|
||||
class Manipulatable
|
||||
{
|
||||
public:
|
||||
virtual void Construct( const Matrix4& device2manip, const DeviceVector device_point, const AABB& bounds, const Vector3& transform_origin ) = 0;
|
||||
virtual void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const DeviceVector device_point ) = 0;
|
||||
|
||||
inline static const View* m_view = 0;
|
||||
inline static DeviceVector m_device_point;
|
||||
inline static DeviceVector m_device_epsilon;
|
||||
static void assign_static( const View& view, const DeviceVector& device_point, const DeviceVector& device_epsilon ){
|
||||
m_view = &view;
|
||||
m_device_point = device_point;
|
||||
m_device_epsilon = device_epsilon;
|
||||
}
|
||||
};
|
||||
|
||||
class Manipulator
|
||||
{
|
||||
public:
|
||||
virtual Manipulatable* GetManipulatable() = 0;
|
||||
virtual void testSelect( const View& view, const Matrix4& pivot2world ) = 0;
|
||||
virtual void highlight( const View& view, const Matrix4& pivot2world ){
|
||||
testSelect( view, pivot2world );
|
||||
}
|
||||
virtual void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ){
|
||||
}
|
||||
virtual void setSelected( bool select ) = 0;
|
||||
virtual bool isSelected() const = 0;
|
||||
};
|
||||
|
||||
|
||||
class ModifierFlagsExt : public ModifierFlags
|
||||
{
|
||||
public:
|
||||
ModifierFlagsExt( const ModifierFlags& other ) : ModifierFlags( other ){}
|
||||
bool shift() const {
|
||||
return bitfield_enabled( *this, c_modifierShift );
|
||||
}
|
||||
bool ctrl() const {
|
||||
return bitfield_enabled( *this, c_modifierControl );
|
||||
}
|
||||
bool alt() const {
|
||||
return bitfield_enabled( *this, c_modifierAlt );
|
||||
}
|
||||
};
|
||||
inline ModifierFlagsExt g_modifiers = c_modifierNone;
|
||||
|
||||
|
||||
class Translatable
|
||||
{
|
||||
public:
|
||||
virtual void translate( const Vector3& translation ) = 0;
|
||||
};
|
||||
|
||||
class Rotatable
|
||||
{
|
||||
public:
|
||||
virtual void rotate( const Quaternion& rotation ) = 0;
|
||||
};
|
||||
|
||||
class Scalable
|
||||
{
|
||||
public:
|
||||
virtual void scale( const Vector3& scaling ) = 0;
|
||||
};
|
||||
|
||||
class Skewable
|
||||
{
|
||||
public:
|
||||
virtual void skew( const Skew& skew ) = 0;
|
||||
};
|
||||
|
||||
class AllTransformable
|
||||
{
|
||||
public:
|
||||
virtual void alltransform( const Transforms& transforms, const Vector3& world_pivot ) = 0;
|
||||
};
|
||||
|
||||
|
||||
struct Pivot2World
|
||||
{
|
||||
Matrix4 m_worldSpace;
|
||||
Matrix4 m_viewpointSpace;
|
||||
Matrix4 m_viewplaneSpace;
|
||||
Vector3 m_axis_screen;
|
||||
|
||||
void update( const Matrix4& pivot2world, const Matrix4& modelview, const Matrix4& projection, const Matrix4& viewport ){
|
||||
Pivot2World_worldSpace( m_worldSpace, pivot2world, modelview, projection, viewport );
|
||||
Pivot2World_viewpointSpace( m_viewpointSpace, m_axis_screen, pivot2world, modelview, projection, viewport );
|
||||
Pivot2World_viewplaneSpace( m_viewplaneSpace, pivot2world, modelview, projection, viewport );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline void ConstructSelectionTest( View& view, const rect_t selection_box ){
|
||||
view.EnableScissor( selection_box.min[0], selection_box.max[0], selection_box.min[1], selection_box.max[1] );
|
||||
}
|
||||
|
||||
inline const rect_t SelectionBoxForPoint( const DeviceVector& device_point, const DeviceVector& device_epsilon ){
|
||||
rect_t selection_box;
|
||||
selection_box.min[0] = device_point[0] - device_epsilon[0];
|
||||
selection_box.min[1] = device_point[1] - device_epsilon[1];
|
||||
selection_box.max[0] = device_point[0] + device_epsilon[0];
|
||||
selection_box.max[1] = device_point[1] + device_epsilon[1];
|
||||
return selection_box;
|
||||
}
|
||||
|
||||
inline const rect_t SelectionBoxForArea( const DeviceVector& device_point, const DeviceVector& device_delta ){
|
||||
rect_t selection_box;
|
||||
selection_box.min[0] = device_point[0] + std::min( device_delta[0], 0.f );
|
||||
selection_box.min[1] = device_point[1] + std::min( device_delta[1], 0.f );
|
||||
selection_box.max[0] = device_point[0] + std::max( device_delta[0], 0.f );
|
||||
selection_box.max[1] = device_point[1] + std::max( device_delta[1], 0.f );
|
||||
selection_box.modifier = device_delta[0] * device_delta[1] < 0
|
||||
? rect_t::eToggle
|
||||
: device_delta[0] < 0
|
||||
? rect_t::eDeselect
|
||||
: rect_t::eSelect;
|
||||
return selection_box;
|
||||
}
|
||||
|
||||
|
||||
void Scene_forEachVisible_testselect_scene_point( const View& view, class ScenePointSelector& selector, SelectionTest& test );
|
||||
void Scene_forEachVisible_testselect_scene_point_selected_brushes( const View& view, ScenePointSelector& selector, SelectionTest& test );
|
||||
void Scene_TestSelect_Primitive( Selector& selector, SelectionTest& test, const VolumeTest& volume );
|
||||
void Scene_TestSelect_Component_Selected( Selector& selector, SelectionTest& test, const VolumeTest& volume, SelectionSystem::EComponentMode componentMode );
|
||||
DoubleVector3 testSelected_scene_snapped_point( const class SelectionVolume& test, ScenePointSelector& selector );
|
||||
void Scene_Translate_Component_Selected( scene::Graph& graph, const Vector3& translation );
|
||||
|
||||
|
||||
inline Vector3 translation_local2object( const Vector3& local, const Matrix4& local2object ){
|
||||
return matrix4_get_translation_vec3(
|
||||
matrix4_multiplied_by_matrix4(
|
||||
matrix4_translated_by_vec3( local2object, local ),
|
||||
matrix4_full_inverse( local2object )
|
||||
)
|
||||
);
|
||||
}
|
||||
inline Vector3 translation_local2object( const Vector3& localTranslation, const Matrix4& local2parent, const Matrix4& parent2local ){
|
||||
return matrix4_get_translation_vec3(
|
||||
matrix4_multiplied_by_matrix4(
|
||||
matrix4_translated_by_vec3( local2parent, localTranslation ),
|
||||
parent2local
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
inline Matrix4 transform_local2object( const Matrix4& local, const Matrix4& local2object ){
|
||||
return matrix4_multiplied_by_matrix4(
|
||||
matrix4_multiplied_by_matrix4( local2object, local ),
|
||||
matrix4_full_inverse( local2object )
|
||||
);
|
||||
}
|
||||
inline Matrix4 transform_local2object( const Matrix4& localTransform, const Matrix4& local2parent, const Matrix4& parent2local ){
|
||||
return matrix4_multiplied_by_matrix4(
|
||||
matrix4_multiplied_by_matrix4( local2parent, localTransform ),
|
||||
parent2local
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
inline Vector3 point_for_device_point( const Matrix4& device2object, const DeviceVector xy, const float z ){
|
||||
// transform from normalised device coords to object coords
|
||||
return vector4_projected( matrix4_transformed_vector4( device2object, Vector4( xy.x(), xy.y(), z, 1 ) ) );
|
||||
}
|
||||
|
||||
inline Ray ray_for_device_point( const Matrix4& device2object, const DeviceVector xy ){
|
||||
return ray_for_points( point_for_device_point( device2object, xy, -1 ), // point at x, y, zNear
|
||||
point_for_device_point( device2object, xy, 0 ) // point at x, y, zFar
|
||||
//point_for_device_point( device2object, xy, 1 ) //sometimes is inaccurate up to negative ray direction
|
||||
);
|
||||
}
|
||||
|
||||
inline Vector3 sphere_intersect_ray( const Vector3& origin, float radius, const Ray& ray ){
|
||||
const Vector3 intersection = vector3_subtracted( origin, ray.origin );
|
||||
const double a = vector3_dot( intersection, ray.direction );
|
||||
const double d = radius * radius - ( vector3_dot( intersection, intersection ) - a * a );
|
||||
|
||||
if ( d > 0 ) {
|
||||
return vector3_added( ray.origin, vector3_scaled( ray.direction, a - sqrt( d ) ) );
|
||||
// return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return vector3_added( ray.origin, vector3_scaled( ray.direction, a ) );
|
||||
// return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline Vector3 ray_intersect_ray( const Ray& ray, const Ray& other ){
|
||||
const Vector3 intersection = vector3_subtracted( ray.origin, other.origin );
|
||||
//float a = 1;//vector3_dot( ray.direction, ray.direction ); // always >= 0
|
||||
const double dot = vector3_dot( ray.direction, other.direction );
|
||||
//float c = 1;//vector3_dot( other.direction, other.direction ); // always >= 0
|
||||
const double d = vector3_dot( ray.direction, intersection );
|
||||
const double e = vector3_dot( other.direction, intersection );
|
||||
const double D = 1 - dot * dot; //a*c - dot*dot; // always >= 0
|
||||
|
||||
if ( D < 0.000001 ) {
|
||||
// the lines are almost parallel
|
||||
return vector3_added( other.origin, vector3_scaled( other.direction, e ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return vector3_added( other.origin, vector3_scaled( other.direction, ( e - dot * d ) / D ) );
|
||||
}
|
||||
}
|
||||
|
||||
const Vector3 g_origin( 0, 0, 0 );
|
||||
const float g_radius = 64;
|
||||
|
||||
inline Vector3 point_on_sphere( const Matrix4& device2object, const DeviceVector xy, const float radius = g_radius ){
|
||||
return sphere_intersect_ray( g_origin,
|
||||
radius,
|
||||
ray_for_device_point( device2object, xy ) );
|
||||
}
|
||||
|
||||
inline Vector3 point_on_axis( const Vector3& axis, const Matrix4& device2object, const DeviceVector xy ){
|
||||
return ray_intersect_ray( ray_for_device_point( device2object, xy ),
|
||||
Ray( Vector3( 0, 0, 0 ), axis ) );
|
||||
}
|
||||
|
||||
inline Vector3 point_on_plane( const Matrix4& device2object, const DeviceVector xy ){
|
||||
const Matrix4 object2device( matrix4_full_inverse( device2object ) );
|
||||
return vector4_projected( matrix4_transformed_vector4( device2object, Vector4( xy.x(), xy.y(), object2device[14] / object2device[15], 1 ) ) );
|
||||
}
|
||||
|
||||
inline Vector3 point_on_plane( const Plane3& plane, const Matrix4& object2device, const DeviceVector xy ){
|
||||
return ray_intersect_plane( ray_for_device_point( matrix4_full_inverse( object2device ), xy ),
|
||||
plane );
|
||||
}
|
||||
|
||||
//! a and b are unit vectors .. returns angle in radians
|
||||
inline float angle_between( const Vector3& a, const Vector3& b ){
|
||||
return 2.0 * atan2(
|
||||
vector3_length( vector3_subtracted( a, b ) ),
|
||||
vector3_length( vector3_added( a, b ) )
|
||||
);
|
||||
}
|
||||
|
||||
//! axis is a unit vector
|
||||
inline void constrain_to_axis( Vector3& vec, const Vector3& axis ){
|
||||
vec = vector3_normalised( vector3_added( vec, vector3_scaled( axis, -vector3_dot( vec, axis ) ) ) );
|
||||
}
|
||||
|
||||
//! a and b are unit vectors .. a and b must be orthogonal to axis .. returns angle in radians
|
||||
inline float angle_for_axis( const Vector3& a, const Vector3& b, const Vector3& axis ){
|
||||
if ( vector3_dot( axis, vector3_cross( a, b ) ) > 0 ) {
|
||||
return angle_between( a, b );
|
||||
}
|
||||
else{
|
||||
return -angle_between( a, b );
|
||||
}
|
||||
}
|
||||
|
||||
inline float distance_for_axis( const Vector3& a, const Vector3& b, const Vector3& axis ){
|
||||
return vector3_dot( b, axis ) - vector3_dot( a, axis );
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "render.h"
|
||||
#include "math/matrix.h"
|
||||
|
||||
#if defined( _DEBUG ) && !defined( _DEBUG_QUICKER )
|
||||
#define DEBUG_SELECTION
|
||||
#endif
|
||||
|
||||
#if defined( DEBUG_SELECTION )
|
||||
|
||||
class RenderableClippedPrimitive : public OpenGLRenderable
|
||||
{
|
||||
struct primitive_t
|
||||
{
|
||||
PointVertex m_points[9];
|
||||
std::size_t m_count;
|
||||
};
|
||||
Matrix4 m_inverse;
|
||||
std::vector<primitive_t> m_primitives;
|
||||
public:
|
||||
Matrix4 m_world;
|
||||
|
||||
void render( RenderStateFlags state ) const override {
|
||||
for ( std::size_t i = 0; i < m_primitives.size(); ++i )
|
||||
{
|
||||
gl().glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_primitives[i].m_points[0].colour );
|
||||
gl().glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_primitives[i].m_points[0].vertex );
|
||||
switch ( m_primitives[i].m_count )
|
||||
{
|
||||
case 1: break;
|
||||
case 2: gl().glDrawArrays( GL_LINES, 0, GLsizei( m_primitives[i].m_count ) ); break;
|
||||
default: gl().glDrawArrays( GL_POLYGON, 0, GLsizei( m_primitives[i].m_count ) ); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void construct( const Matrix4& world2device ){
|
||||
m_inverse = matrix4_full_inverse( world2device );
|
||||
m_world = g_matrix4_identity;
|
||||
}
|
||||
|
||||
void insert( const Vector4 clipped[9], std::size_t count ){
|
||||
add_one();
|
||||
|
||||
m_primitives.back().m_count = count;
|
||||
for ( std::size_t i = 0; i < count; ++i )
|
||||
{
|
||||
Vector3 world_point( vector4_projected( matrix4_transformed_vector4( m_inverse, clipped[i] ) ) );
|
||||
m_primitives.back().m_points[i].vertex = vertex3f_for_vector3( world_point );
|
||||
}
|
||||
}
|
||||
|
||||
void destroy(){
|
||||
m_primitives.clear();
|
||||
}
|
||||
private:
|
||||
void add_one(){
|
||||
m_primitives.push_back( primitive_t() );
|
||||
|
||||
const Colour4b colour_clipped( 255, 127, 0, 255 );
|
||||
|
||||
for ( std::size_t i = 0; i < 9; ++i )
|
||||
m_primitives.back().m_points[i].colour = colour_clipped;
|
||||
}
|
||||
};
|
||||
|
||||
inline Shader* g_state_clipped;
|
||||
inline RenderableClippedPrimitive g_render_clipped;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selection_.h"
|
||||
#include "grid.h"
|
||||
#include "brushmanip.h"
|
||||
|
||||
class DragNewBrush : public Manipulatable
|
||||
{
|
||||
Vector3 m_0;
|
||||
Vector3 m_size;
|
||||
float m_setSizeZ; /* store separately for fine square/cube modes handling */
|
||||
scene::Node* m_newBrushNode;
|
||||
public:
|
||||
void Construct( const Matrix4& device2manip, const DeviceVector device_point, const AABB& bounds, const Vector3& transform_origin ) override {
|
||||
m_setSizeZ = m_size[0] = m_size[1] = m_size[2] = GetGridSize();
|
||||
m_newBrushNode = 0;
|
||||
}
|
||||
void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const DeviceVector device_point ) override {
|
||||
Vector3 diff_raw = point_on_plane( Plane3( g_vector3_axis_z, vector3_dot( g_vector3_axis_z, Vector3( m_size.x(), m_size.y(), m_setSizeZ ) + m_0 ) ), m_view->GetViewMatrix(), device_point ) - m_0;
|
||||
const Vector3 xydir( vector3_normalised( Vector3( m_view->GetModelview()[2], m_view->GetModelview()[6], 0 ) ) );
|
||||
diff_raw.z() = ( point_on_plane( Plane3( xydir, vector3_dot( xydir, Vector3( m_size.x(), m_size.y(), m_setSizeZ ) + m_0 ) ), m_view->GetViewMatrix(), device_point ) - m_0 ).z();
|
||||
Vector3 diff = vector3_snapped( diff_raw, GetSnapGridSize() );
|
||||
|
||||
for ( std::size_t i = 0; i < 3; ++i )
|
||||
if( diff[i] == 0 )
|
||||
diff[i] = diff_raw[i] < 0? -GetGridSize() : GetGridSize();
|
||||
|
||||
if( g_modifiers.alt() ){ // height adjustment
|
||||
diff.x() = m_size.x();
|
||||
diff.y() = m_size.y();
|
||||
}
|
||||
else{
|
||||
diff.z() = m_size.z();
|
||||
}
|
||||
|
||||
const float z = vector4_projected( matrix4_transformed_vector4( m_view->GetViewMatrix(), Vector4( diff + m_0, 1 ) ) ).z();
|
||||
if( z != z || z > 1 ) //catch NAN and behind near, far planes cases
|
||||
return;
|
||||
|
||||
if( g_modifiers.shift() || g_modifiers.ctrl() ){ // square or cube
|
||||
const float squaresize = std::max( std::fabs( diff.x() ), std::fabs( diff.y() ) );
|
||||
diff.x() = std::copysign( squaresize, diff.x() ); //square
|
||||
diff.y() = std::copysign( squaresize, diff.y() );
|
||||
if( g_modifiers.ctrl() && !g_modifiers.alt() ) //cube
|
||||
diff.z() = std::copysign( squaresize, diff.z() );
|
||||
}
|
||||
|
||||
m_size = diff;
|
||||
if( g_modifiers.alt() )
|
||||
m_setSizeZ = diff.z();
|
||||
|
||||
Vector3 mins( m_0 );
|
||||
Vector3 maxs( m_0 + diff );
|
||||
for ( std::size_t i = 0; i < 3; ++i )
|
||||
if( mins[i] > maxs[i] )
|
||||
std::swap( mins[i], maxs[i] );
|
||||
|
||||
Scene_BrushResize_Cuboid( m_newBrushNode, aabb_for_minmax( mins, maxs ) );
|
||||
}
|
||||
void set0( const Vector3& start ){
|
||||
m_0 = start;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selection_.h"
|
||||
#include "grid.h"
|
||||
#include "brush.h"
|
||||
#include "brushnode.h"
|
||||
|
||||
class DragExtrudeFaces : public Manipulatable
|
||||
{
|
||||
Vector3 m_0;
|
||||
Plane3 m_planeSelected;
|
||||
std::size_t m_axisZ;
|
||||
Plane3 m_planeZ;
|
||||
Vector3 m_startZ;
|
||||
|
||||
bool m_originalBrushSaved;
|
||||
bool m_originalBrushChanged;
|
||||
public:
|
||||
class ExtrudeSource
|
||||
{
|
||||
public:
|
||||
BrushInstance* m_brushInstance;
|
||||
struct InFaceOutBrush{
|
||||
Face* m_face;
|
||||
PlanePoints m_planepoints;
|
||||
Brush* m_outBrush;
|
||||
};
|
||||
std::vector<InFaceOutBrush> m_faces;
|
||||
std::vector<InFaceOutBrush>::iterator faceFind( const Face* face ){
|
||||
return std::ranges::find( m_faces, face, &InFaceOutBrush::m_face );
|
||||
}
|
||||
std::vector<InFaceOutBrush>::const_iterator faceFind( const Face* face ) const {
|
||||
return std::ranges::find( m_faces, face, &InFaceOutBrush::m_face );
|
||||
}
|
||||
bool faceExcluded( const Face* face ) const {
|
||||
return faceFind( face ) == m_faces.end();
|
||||
}
|
||||
};
|
||||
std::vector<ExtrudeSource> m_extrudeSources;
|
||||
|
||||
DragExtrudeFaces() = default;
|
||||
void Construct( const Matrix4& device2manip, const DeviceVector device_point, const AABB& bounds, const Vector3& transform_origin ) override {
|
||||
m_axisZ = vector3_max_abs_component_index( m_planeSelected.normal() );
|
||||
Vector3 xydir( m_view->getViewer() - m_0 );
|
||||
xydir[m_axisZ] = 0;
|
||||
vector3_normalise( xydir );
|
||||
m_planeZ = Plane3( xydir, vector3_dot( xydir, m_0 ) );
|
||||
m_startZ = point_on_plane( m_planeZ, m_view->GetViewMatrix(), device_point );
|
||||
|
||||
m_originalBrushSaved = false;
|
||||
m_originalBrushChanged = false;
|
||||
|
||||
UndoableCommand undo( "ExtrudeBrushFaces" );
|
||||
for( ExtrudeSource& source : m_extrudeSources ){
|
||||
for( auto& infaceoutbrush : source.m_faces ){
|
||||
const Face* face = infaceoutbrush.m_face;
|
||||
|
||||
NodeSmartReference node( GlobalBrushCreator().createBrush() );
|
||||
Node_getTraversable( source.m_brushInstance->path().parent() )->insert( node );
|
||||
|
||||
scene::Path path( source.m_brushInstance->path() );
|
||||
path.pop();
|
||||
path.push( makeReference( node.get() ) );
|
||||
selectPath( path, true );
|
||||
|
||||
Brush* brush = Node_getBrush( node.get() );
|
||||
infaceoutbrush.m_outBrush = brush;
|
||||
|
||||
Face* f = brush->addFace( *face );
|
||||
f->getPlane().offset( GetGridSize() );
|
||||
f->planeChanged();
|
||||
|
||||
f = brush->addFace( *face );
|
||||
f->getPlane().reverse();
|
||||
f->planeChanged();
|
||||
|
||||
for( const WindingVertex& vertex : face->getWinding() ){
|
||||
if( vertex.adjacent != c_brush_maxFaces ){
|
||||
f = brush->addFace( **std::next( source.m_brushInstance->getBrush().begin(), vertex.adjacent ) );
|
||||
|
||||
const DoubleVector3 cross = vector3_cross( f->plane3_().normal(), face->plane3_().normal() );
|
||||
f->getPlane().copy( vertex.vertex, vertex.vertex + cross * 64, vertex.vertex + face->plane3_().normal() * 64 );
|
||||
f->planeChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const DeviceVector device_point ) override {
|
||||
Vector3 current = g_vector3_axes[m_axisZ] * vector3_dot( m_planeSelected.normal(), ( point_on_plane( m_planeZ, m_view->GetViewMatrix(), device_point ) - m_startZ ) )
|
||||
* ( m_planeSelected.normal()[m_axisZ] >= 0? 1 : -1 );
|
||||
|
||||
if( !std::isfinite( current[0] ) || !std::isfinite( current[1] ) || !std::isfinite( current[2] ) ) // catch INF case, is likely with top of the box in 2D
|
||||
return;
|
||||
|
||||
vector3_snap( current, GetSnapGridSize() );
|
||||
|
||||
const float offset = std::fabs( m_planeSelected.normal()[m_axisZ] ) * std::copysign(
|
||||
std::max( static_cast<double>( GetGridSize() ), vector3_length( current ) ),
|
||||
vector3_dot( current, m_planeSelected.normal() ) );
|
||||
|
||||
if( offset >= 0 ){ // extrude outside
|
||||
if( m_originalBrushChanged ){
|
||||
m_originalBrushChanged = false;
|
||||
for( ExtrudeSource& source : m_extrudeSources ){
|
||||
// revert original brush
|
||||
for( auto& infaceoutbrush : source.m_faces ){
|
||||
Face* face = infaceoutbrush.m_face;
|
||||
face->getPlane().copy( infaceoutbrush.m_planepoints );
|
||||
face->planeChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
for( ExtrudeSource& source : m_extrudeSources ){
|
||||
Brush& brush0 = source.m_brushInstance->getBrush();
|
||||
if( source.m_faces.size() > 1 ){
|
||||
auto *tmpbrush = new Brush( brush0 );
|
||||
offsetFaces( source, *tmpbrush, offset );
|
||||
brush_extrudeDiag( brush0, *tmpbrush, source );
|
||||
delete tmpbrush;
|
||||
}
|
||||
else{
|
||||
for( auto& infaceoutbrush : source.m_faces ){
|
||||
const Face* face = infaceoutbrush.m_face;
|
||||
Brush* brush = infaceoutbrush.m_outBrush;
|
||||
brush->clear();
|
||||
|
||||
Face* f = brush->addFace( *face );
|
||||
f->getPlane().offset( offset );
|
||||
f->planeChanged();
|
||||
|
||||
f = brush->addFace( *face );
|
||||
f->getPlane().reverse();
|
||||
f->planeChanged();
|
||||
|
||||
for( const WindingVertex& vertex : face->getWinding() ){
|
||||
if( vertex.adjacent != c_brush_maxFaces ){
|
||||
brush->addFace( **std::next( brush0.begin(), vertex.adjacent ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else{ // extrude inside
|
||||
if( !m_originalBrushSaved ){
|
||||
m_originalBrushSaved = true;
|
||||
for( ExtrudeSource& source : m_extrudeSources )
|
||||
for( auto& infaceoutbrush : source.m_faces )
|
||||
infaceoutbrush.m_face->undoSave();
|
||||
}
|
||||
m_originalBrushChanged = true;
|
||||
|
||||
for( ExtrudeSource& source : m_extrudeSources ){
|
||||
Brush& brush0 = source.m_brushInstance->getBrush();
|
||||
// revert original brush
|
||||
for( auto& infaceoutbrush : source.m_faces ){
|
||||
Face* face = infaceoutbrush.m_face;
|
||||
face->getPlane().copy( infaceoutbrush.m_planepoints );
|
||||
face->planeChanged();
|
||||
}
|
||||
if( source.m_faces.size() > 1 ){
|
||||
auto *tmpbrush = new Brush( brush0 );
|
||||
tmpbrush->evaluateBRep();
|
||||
offsetFaces( source, brush0, offset );
|
||||
if( brush0.hasContributingFaces() )
|
||||
brush_extrudeDiag( brush0, *tmpbrush, source );
|
||||
delete tmpbrush;
|
||||
}
|
||||
else{
|
||||
for( auto& infaceoutbrush : source.m_faces ){
|
||||
Face* face = infaceoutbrush.m_face;
|
||||
Brush* brush = infaceoutbrush.m_outBrush;
|
||||
brush->clear();
|
||||
|
||||
brush->copy( brush0 );
|
||||
|
||||
Face* f = brush->addFace( *face );
|
||||
f->getPlane().offset( offset );
|
||||
f->getPlane().reverse();
|
||||
f->planeChanged();
|
||||
|
||||
brush->removeEmptyFaces();
|
||||
// modify original brush
|
||||
face->getPlane().offset( offset );
|
||||
face->planeChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void set0( const Vector3& start, const Plane3& planeSelected ){
|
||||
m_0 = start;
|
||||
m_planeSelected = planeSelected;
|
||||
}
|
||||
|
||||
private:
|
||||
void offsetFaces( const ExtrudeSource& source, Brush& brush, const float offset ){
|
||||
const Brush& brush0 = source.m_brushInstance->getBrush();
|
||||
for( Brush::const_iterator i0 = brush0.begin(); i0 != brush0.end(); ++i0 ){
|
||||
const Face& face0 = *( *i0 );
|
||||
if( !source.faceExcluded( &face0 ) ){
|
||||
Face& face = *( *std::next( brush.begin(), std::distance( brush0.begin(), i0 ) ) );
|
||||
face.getPlane().offset( offset );
|
||||
face.planeChanged();
|
||||
}
|
||||
}
|
||||
brush.evaluateBRep();
|
||||
}
|
||||
/* brush0, brush2 are supposed to have same amount of faces in the same order; brush2 bigger than brush0 */
|
||||
void brush_extrudeDiag( const Brush& brush0, const Brush& brush2, ExtrudeSource& source ){
|
||||
TextureProjection projection;
|
||||
TexDef_Construct_Default( projection );
|
||||
|
||||
for( Brush::const_iterator i0 = brush0.begin(); i0 != brush0.end(); ++i0 ){
|
||||
const Face& face0 = *( *i0 );
|
||||
const Face& face2 = *( *std::next( brush2.begin(), std::distance( brush0.begin(), i0 ) ) );
|
||||
|
||||
auto infaceoutbrush_iter = source.faceFind( &face0 ); // brush0 = source.m_brushInstance->getBrush()
|
||||
if( infaceoutbrush_iter != source.m_faces.end() ) {
|
||||
if( face0.contributes() || face2.contributes() ) {
|
||||
const char* shader = face0.GetShader();
|
||||
|
||||
Brush* outBrush = ( *infaceoutbrush_iter ).m_outBrush;
|
||||
outBrush->clear();
|
||||
|
||||
if( face0.contributes() ){
|
||||
if( Face* newFace = outBrush->addFace( face0 ) ) {
|
||||
newFace->flipWinding();
|
||||
}
|
||||
}
|
||||
if( face2.contributes() ){
|
||||
outBrush->addFace( face2 );
|
||||
}
|
||||
|
||||
if( face0.contributes() && face2.contributes() ){ //sew two valid windings
|
||||
const auto addSidePlanes = [&outBrush, shader, &projection]( const Winding& winding0, const Winding& winding2, const DoubleVector3 normal, const bool swap ){
|
||||
for( std::size_t index0 = 0; index0 < winding0.numpoints; ++index0 ){
|
||||
const std::size_t next = Winding_next( winding0, index0 );
|
||||
DoubleVector3 BestPoint;
|
||||
double bestdot = -1;
|
||||
for( std::size_t index2 = 0; index2 < winding2.numpoints; ++index2 ){
|
||||
const double dot = vector3_dot(
|
||||
vector3_normalised(
|
||||
vector3_cross(
|
||||
winding0[index0].vertex - winding0[next].vertex,
|
||||
winding0[index0].vertex - winding2[index2].vertex
|
||||
)
|
||||
),
|
||||
normal
|
||||
);
|
||||
if( dot > bestdot ) {
|
||||
bestdot = dot;
|
||||
BestPoint = winding2[index2].vertex;
|
||||
}
|
||||
}
|
||||
outBrush->addPlane( winding0[swap? next : index0].vertex,
|
||||
winding0[swap? index0 : next].vertex,
|
||||
BestPoint,
|
||||
shader,
|
||||
projection );
|
||||
}
|
||||
};
|
||||
//insert side planes from each winding perspective, as their form may change after brush expansion
|
||||
addSidePlanes( face0.getWinding(), face2.getWinding(), face0.getPlane().plane3().normal(), false );
|
||||
addSidePlanes( face2.getWinding(), face0.getWinding(), face0.getPlane().plane3().normal(), true );
|
||||
}
|
||||
else{ //one valid winding: this way may produce garbage with complex brushes, extruded partially, but does preferred result with simple ones
|
||||
const auto addSidePlanes = [&outBrush, shader, &projection]( const Winding& winding0, const Brush& brush2, const Plane3 plane, const bool swap ){
|
||||
for( std::size_t index0 = 0; index0 < winding0.numpoints; ++index0 ){
|
||||
const std::size_t next = Winding_next( winding0, index0 );
|
||||
DoubleVector3 BestPoint;
|
||||
double bestdist = 999999;
|
||||
for( const Face* f : brush2 ) {
|
||||
const Winding& winding2 = f->getWinding();
|
||||
for( std::size_t index2 = 0; index2 < winding2.numpoints; ++index2 ){
|
||||
const double testdist = vector3_length( winding0[index0].vertex - winding2[index2].vertex );
|
||||
if( testdist < bestdist && plane3_distance_to_point( plane, winding2[index2].vertex ) > .05 ) {
|
||||
bestdist = testdist;
|
||||
BestPoint = winding2[index2].vertex;
|
||||
}
|
||||
}
|
||||
}
|
||||
outBrush->addPlane( winding0[swap? next : index0].vertex,
|
||||
winding0[swap? index0 : next].vertex,
|
||||
BestPoint,
|
||||
shader,
|
||||
projection );
|
||||
}
|
||||
};
|
||||
|
||||
if( face0.contributes() )
|
||||
addSidePlanes( face0.getWinding(), brush2, face0.getPlane().plane3(), false );
|
||||
else if( face2.contributes() )
|
||||
addSidePlanes( face2.getWinding(), brush0, plane3_flipped( face2.getPlane().plane3() ), true );
|
||||
}
|
||||
outBrush->removeEmptyFaces();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selection_.h"
|
||||
|
||||
class RotateFree : public Manipulatable
|
||||
{
|
||||
Vector3 m_start;
|
||||
Rotatable& m_rotatable;
|
||||
public:
|
||||
RotateFree( Rotatable& rotatable )
|
||||
: m_rotatable( rotatable ){
|
||||
}
|
||||
void Construct( const Matrix4& device2manip, const DeviceVector device_point, const AABB& bounds, const Vector3& transform_origin ) override {
|
||||
m_start = point_on_sphere( device2manip, device_point );
|
||||
vector3_normalise( m_start );
|
||||
}
|
||||
void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const DeviceVector device_point ) override {
|
||||
Vector3 current = point_on_sphere( device2manip, device_point );
|
||||
vector3_normalise( current );
|
||||
|
||||
if( g_modifiers.shift() )
|
||||
for( std::size_t i = 0; i < 3; ++i )
|
||||
if( current[i] == 0 )
|
||||
return m_rotatable.rotate( quaternion_for_axisangle( g_vector3_axes[i], float_snapped( angle_for_axis( m_start, current, g_vector3_axes[i] ), static_cast<float>( c_pi / 12.0 ) ) ) );
|
||||
|
||||
m_rotatable.rotate( quaternion_for_unit_vectors( m_start, current ) );
|
||||
// m_rotatable.rotate( quaternion_for_sphere_vectors( m_start, current ) ); //wrong math, 2x more sensitive
|
||||
}
|
||||
};
|
||||
|
||||
class RotateAxis : public Manipulatable
|
||||
{
|
||||
Vector3 m_axis;
|
||||
Vector3 m_start;
|
||||
float m_radius;
|
||||
bool m_plane_way;
|
||||
Plane3 m_plane;
|
||||
Vector3 m_origin;
|
||||
Rotatable& m_rotatable;
|
||||
public:
|
||||
RotateAxis( Rotatable& rotatable )
|
||||
: m_radius( g_radius ), m_rotatable( rotatable ){
|
||||
}
|
||||
void Construct( const Matrix4& device2manip, const DeviceVector device_point, const AABB& bounds, const Vector3& transform_origin ) override {
|
||||
const float dot = vector3_dot( m_axis, m_view->fill()? vector3_normalised( m_view->getViewer() - transform_origin ) : m_view->getViewDir() );
|
||||
m_plane_way = std::fabs( dot ) > 0.1f;
|
||||
|
||||
if( m_plane_way ){
|
||||
m_origin = transform_origin;
|
||||
m_plane = Plane3( m_axis, vector3_dot( m_axis, m_origin ) );
|
||||
m_start = point_on_plane( m_plane, m_view->GetViewMatrix(), device_point ) - m_origin;
|
||||
vector3_normalise( m_start );
|
||||
}
|
||||
else{
|
||||
m_start = point_on_sphere( device2manip, device_point, m_radius );
|
||||
constrain_to_axis( m_start, m_axis );
|
||||
}
|
||||
}
|
||||
/// \brief Converts current position to a normalised vector orthogonal to axis.
|
||||
void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const DeviceVector device_point ) override {
|
||||
Vector3 current;
|
||||
if( m_plane_way ){
|
||||
current = point_on_plane( m_plane, m_view->GetViewMatrix(), device_point ) - m_origin;
|
||||
vector3_normalise( current );
|
||||
}
|
||||
else{
|
||||
current = point_on_sphere( device2manip, device_point, m_radius );
|
||||
constrain_to_axis( current, m_axis );
|
||||
}
|
||||
|
||||
if( g_modifiers.shift() ){
|
||||
m_rotatable.rotate( quaternion_for_axisangle( m_axis, float_snapped( angle_for_axis( m_start, current, m_axis ), static_cast<float>( c_pi / 12.0 ) ) ) );
|
||||
}
|
||||
else{
|
||||
m_rotatable.rotate( quaternion_for_axisangle( m_axis, angle_for_axis( m_start, current, m_axis ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
void SetAxis( const Vector3& axis ){
|
||||
m_axis = axis;
|
||||
}
|
||||
void SetRadius( const float radius ){
|
||||
m_radius = radius;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selection_.h"
|
||||
#include "grid.h"
|
||||
|
||||
class ScaleAxis : public Manipulatable
|
||||
{
|
||||
Vector3 m_start;
|
||||
Vector3 m_axis;
|
||||
Scalable& m_scalable;
|
||||
|
||||
Vector3 m_chosen_extent;
|
||||
AABB m_bounds;
|
||||
public:
|
||||
ScaleAxis( Scalable& scalable )
|
||||
: m_scalable( scalable ){
|
||||
}
|
||||
void Construct( const Matrix4& device2manip, const DeviceVector device_point, const AABB& bounds, const Vector3& transform_origin ) override {
|
||||
m_start = point_on_axis( m_axis, device2manip, device_point );
|
||||
|
||||
m_chosen_extent = Vector3(
|
||||
std::max( bounds.origin[0] + bounds.extents[0] - transform_origin[0], - bounds.origin[0] + bounds.extents[0] + transform_origin[0] ),
|
||||
std::max( bounds.origin[1] + bounds.extents[1] - transform_origin[1], - bounds.origin[1] + bounds.extents[1] + transform_origin[1] ),
|
||||
std::max( bounds.origin[2] + bounds.extents[2] - transform_origin[2], - bounds.origin[2] + bounds.extents[2] + transform_origin[2] )
|
||||
);
|
||||
m_bounds = bounds;
|
||||
}
|
||||
void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const DeviceVector device_point ) override {
|
||||
//globalOutputStream() << "manip2object: " << manip2object << " device2manip: " << device2manip << " x: " << x << " y:" << y << '\n';
|
||||
Vector3 current = point_on_axis( m_axis, device2manip, device_point );
|
||||
Vector3 delta = vector3_subtracted( current, m_start );
|
||||
|
||||
delta = translation_local2object( delta, manip2object );
|
||||
vector3_snap( delta, GetSnapGridSize() );
|
||||
vector3_scale( delta, m_axis );
|
||||
|
||||
Vector3 start( vector3_snapped( m_start, GetSnapGridSize() != 0 ? GetSnapGridSize() : 1e-3f ) );
|
||||
for ( std::size_t i = 0; i < 3; ++i ){ //prevent snapping to 0 with big gridsize
|
||||
if( float_snapped( m_start[i], 1e-3f ) != 0 && start[i] == 0 ){
|
||||
start[i] = GetSnapGridSize();
|
||||
}
|
||||
}
|
||||
//globalOutputStream() << "m_start: " << m_start << " start: " << start << " delta: " << delta << '\n';
|
||||
/* boundless way */
|
||||
Vector3 scale(
|
||||
start[0] == 0 ? 1 : 1 + delta[0] / start[0],
|
||||
start[1] == 0 ? 1 : 1 + delta[1] / start[1],
|
||||
start[2] == 0 ? 1 : 1 + delta[2] / start[2]
|
||||
);
|
||||
/* try bbox way */
|
||||
for( std::size_t i = 0; i < 3; ++i ){
|
||||
if( m_chosen_extent[i] > 0.0625f && m_axis[i] != 0 ){ //epsilon to prevent super high scale for set of models, having really small extent, formed by origins
|
||||
scale[i] = ( m_chosen_extent[i] + delta[i] ) / m_chosen_extent[i];
|
||||
if( g_modifiers.ctrl() ){ // snap bbox dimension size to grid
|
||||
const float snappdwidth = float_snapped( scale[i] * m_bounds.extents[i] * 2.f, GetSnapGridSize() );
|
||||
scale[i] = snappdwidth / ( m_bounds.extents[i] * 2.f );
|
||||
}
|
||||
}
|
||||
}
|
||||
if( g_modifiers.shift() ){ // scale all axes equally
|
||||
for( std::size_t i = 0; i < 3; ++i ){
|
||||
if( m_axis[i] == 0 ){
|
||||
scale[i] = vector3_dot( scale, vector3_scaled( m_axis, m_axis ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
//globalOutputStream() << "scale: " << scale << '\n';
|
||||
m_scalable.scale( scale );
|
||||
}
|
||||
|
||||
void SetAxis( const Vector3& axis ){
|
||||
m_axis = axis;
|
||||
}
|
||||
};
|
||||
|
||||
class ScaleFree : public Manipulatable
|
||||
{
|
||||
Vector3 m_start;
|
||||
Vector3 m_axis;
|
||||
Vector3 m_axis2;
|
||||
Scalable& m_scalable;
|
||||
|
||||
Vector3 m_chosen_extent;
|
||||
AABB m_bounds;
|
||||
public:
|
||||
ScaleFree( Scalable& scalable )
|
||||
: m_scalable( scalable ){
|
||||
}
|
||||
void Construct( const Matrix4& device2manip, const DeviceVector device_point, const AABB& bounds, const Vector3& transform_origin ) override {
|
||||
m_start = point_on_plane( device2manip, device_point );
|
||||
|
||||
m_chosen_extent = Vector3(
|
||||
std::max( bounds.origin[0] + bounds.extents[0] - transform_origin[0], -( bounds.origin[0] - bounds.extents[0] - transform_origin[0] ) ),
|
||||
std::max( bounds.origin[1] + bounds.extents[1] - transform_origin[1], -( bounds.origin[1] - bounds.extents[1] - transform_origin[1] ) ),
|
||||
std::max( bounds.origin[2] + bounds.extents[2] - transform_origin[2], -( bounds.origin[2] - bounds.extents[2] - transform_origin[2] ) )
|
||||
);
|
||||
m_bounds = bounds;
|
||||
}
|
||||
void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const DeviceVector device_point ) override {
|
||||
Vector3 current = point_on_plane( device2manip, device_point );
|
||||
Vector3 delta = vector3_subtracted( current, m_start );
|
||||
|
||||
delta = translation_local2object( delta, manip2object );
|
||||
vector3_snap( delta, GetSnapGridSize() );
|
||||
if( m_axis != g_vector3_identity )
|
||||
delta = vector3_scaled( delta, m_axis ) + vector3_scaled( delta, m_axis2 );
|
||||
|
||||
Vector3 start( vector3_snapped( m_start, GetSnapGridSize() != 0 ? GetSnapGridSize() : 1e-3f ) );
|
||||
for ( std::size_t i = 0; i < 3; ++i ){ //prevent snapping to 0 with big gridsize
|
||||
if( float_snapped( m_start[i], 1e-3f ) != 0 && start[i] == 0 ){
|
||||
start[i] = GetSnapGridSize();
|
||||
}
|
||||
}
|
||||
|
||||
const std::size_t ignore_axis = vector3_min_abs_component_index( m_start );
|
||||
if( g_modifiers.shift() )
|
||||
start[ignore_axis] = 0;
|
||||
|
||||
Vector3 scale(
|
||||
start[0] == 0 ? 1 : 1 + delta[0] / start[0],
|
||||
start[1] == 0 ? 1 : 1 + delta[1] / start[1],
|
||||
start[2] == 0 ? 1 : 1 + delta[2] / start[2]
|
||||
);
|
||||
|
||||
//globalOutputStream() << "m_start: " << m_start << " start: " << start << " delta: " << delta << '\n';
|
||||
for( std::size_t i = 0; i < 3; ++i ){
|
||||
if( m_chosen_extent[i] > 0.0625f && start[i] != 0 ){
|
||||
scale[i] = ( m_chosen_extent[i] + delta[i] ) / m_chosen_extent[i];
|
||||
if( g_modifiers.ctrl() ){ // snap bbox dimension size to grid
|
||||
const float snappdwidth = float_snapped( scale[i] * m_bounds.extents[i] * 2.f, GetSnapGridSize() );
|
||||
scale[i] = snappdwidth / ( m_bounds.extents[i] * 2.f );
|
||||
}
|
||||
}
|
||||
}
|
||||
//globalOutputStream() << "pre snap scale: " << scale << '\n';
|
||||
if( g_modifiers.shift() ){ // snap 2 axes equally
|
||||
float bestscale = ignore_axis != 0 ? scale[0] : scale[1];
|
||||
for( std::size_t i = ignore_axis != 0 ? 1 : 2; i < 3; ++i ){
|
||||
if( ignore_axis != i && std::fabs( scale[i] ) < std::fabs( bestscale ) ){
|
||||
bestscale = scale[i];
|
||||
}
|
||||
//globalOutputStream() << "bestscale: " << bestscale << '\n';
|
||||
}
|
||||
for( std::size_t i = 0; i < 3; ++i ){
|
||||
if( ignore_axis != i ){
|
||||
scale[i] = ( scale[i] < 0 ) ? -std::fabs( bestscale ) : fabs( bestscale );
|
||||
}
|
||||
}
|
||||
}
|
||||
//globalOutputStream() << "scale: " << scale << '\n';
|
||||
m_scalable.scale( scale );
|
||||
}
|
||||
void SetAxes( const Vector3& axis, const Vector3& axis2 ){
|
||||
m_axis = axis;
|
||||
m_axis2 = axis2;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selection_.h"
|
||||
#include "grid.h"
|
||||
|
||||
class SkewAxis : public Manipulatable
|
||||
{
|
||||
Vector3 m_0;
|
||||
Plane3 m_planeZ;
|
||||
|
||||
int m_axis_which;
|
||||
int m_axis_by;
|
||||
int m_axis_by_sign;
|
||||
Skewable& m_skewable;
|
||||
|
||||
float m_axis_by_extent;
|
||||
AABB m_bounds;
|
||||
public:
|
||||
SkewAxis( Skewable& skewable )
|
||||
: m_skewable( skewable ){
|
||||
}
|
||||
void Construct( const Matrix4& device2manip, const DeviceVector device_point, const AABB& bounds, const Vector3& transform_origin ) override {
|
||||
Vector3 xydir( m_view->getViewer() - m_0 );
|
||||
xydir[m_axis_which] = 0;
|
||||
// xydir *= g_vector3_axes[vector3_max_abs_component_index( xydir )];
|
||||
vector3_normalise( xydir );
|
||||
m_planeZ = Plane3( xydir, vector3_dot( xydir, m_0 ) );
|
||||
|
||||
m_bounds = bounds;
|
||||
m_axis_by_extent = bounds.origin[m_axis_by] + bounds.extents[m_axis_by] * m_axis_by_sign - transform_origin[m_axis_by];
|
||||
}
|
||||
void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const DeviceVector device_point ) override {
|
||||
const Vector3 current = point_on_plane( m_planeZ, m_view->GetViewMatrix(), device_point ) - m_0;
|
||||
// globalOutputStream() << m_axis_which << " by axis " << m_axis_by << '\n';
|
||||
m_skewable.skew( Skew( m_axis_by * 4 + m_axis_which, m_axis_by_extent != 0? float_snapped( current[m_axis_which], GetSnapGridSize() ) / m_axis_by_extent : 0 ) );
|
||||
}
|
||||
void SetAxes( int axis_which, int axis_by, int axis_by_sign ){
|
||||
m_axis_which = axis_which;
|
||||
m_axis_by = axis_by;
|
||||
m_axis_by_sign = axis_by_sign;
|
||||
}
|
||||
void set0( const Vector3& start ){
|
||||
m_0 = start;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selection_.h"
|
||||
#include "grid.h"
|
||||
|
||||
/// \brief snaps changed axes of \p move so that \p bounds stick to closest grid lines.
|
||||
inline void aabb_snap_translation( Vector3& move, const AABB& bounds ){
|
||||
const Vector3 maxs( bounds.origin + bounds.extents );
|
||||
const Vector3 mins( bounds.origin - bounds.extents );
|
||||
// globalOutputStream() << "move: " << move << '\n';
|
||||
for( std::size_t i = 0; i < 3; ++i ){
|
||||
if( std::fabs( move[i] ) > 1e-2f ){
|
||||
const float snapto1 = float_snapped( maxs[i] + move[i], GetSnapGridSize() );
|
||||
const float snapto2 = float_snapped( mins[i] + move[i], GetSnapGridSize() );
|
||||
|
||||
const float dist1 = std::fabs( std::fabs( maxs[i] + move[i] ) - std::fabs( snapto1 ) );
|
||||
const float dist2 = std::fabs( std::fabs( mins[i] + move[i] ) - std::fabs( snapto2 ) );
|
||||
|
||||
// globalOutputStream() << "maxs[i] + move[i]: " << maxs[i] + move[i] << " snapto1: " << snapto1 << " dist1: " << dist1 << '\n';
|
||||
// globalOutputStream() << "mins[i] + move[i]: " << mins[i] + move[i] << " snapto2: " << snapto2 << " dist2: " << dist2 << '\n';
|
||||
move[i] = dist2 > dist1 ? snapto1 - maxs[i] : snapto2 - mins[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TranslateAxis : public Manipulatable
|
||||
{
|
||||
Vector3 m_start;
|
||||
Vector3 m_axis;
|
||||
Translatable& m_translatable;
|
||||
AABB m_bounds;
|
||||
public:
|
||||
TranslateAxis( Translatable& translatable )
|
||||
: m_translatable( translatable ){
|
||||
}
|
||||
void Construct( const Matrix4& device2manip, const DeviceVector device_point, const AABB& bounds, const Vector3& transform_origin ) override {
|
||||
m_start = point_on_axis( m_axis, device2manip, device_point );
|
||||
m_bounds = bounds;
|
||||
}
|
||||
void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const DeviceVector device_point ) override {
|
||||
Vector3 current = point_on_axis( m_axis, device2manip, device_point );
|
||||
current = vector3_scaled( m_axis, distance_for_axis( m_start, current, m_axis ) );
|
||||
|
||||
current = translation_local2object( current, manip2object );
|
||||
if( g_modifiers.ctrl() )
|
||||
aabb_snap_translation( current, m_bounds );
|
||||
else
|
||||
vector3_snap( current, GetSnapGridSize() );
|
||||
|
||||
m_translatable.translate( current );
|
||||
}
|
||||
|
||||
void SetAxis( const Vector3& axis ){
|
||||
m_axis = axis;
|
||||
}
|
||||
};
|
||||
|
||||
class TranslateAxis2 : public Manipulatable
|
||||
{
|
||||
Vector3 m_0;
|
||||
Plane3 m_planeSelected;
|
||||
std::size_t m_axisZ;
|
||||
Plane3 m_planeZ;
|
||||
Vector3 m_startZ;
|
||||
Translatable& m_translatable;
|
||||
AABB m_bounds;
|
||||
public:
|
||||
TranslateAxis2( Translatable& translatable )
|
||||
: m_translatable( translatable ){
|
||||
}
|
||||
void Construct( const Matrix4& device2manip, const DeviceVector device_point, const AABB& bounds, const Vector3& transform_origin ) override {
|
||||
m_axisZ = vector3_max_abs_component_index( m_planeSelected.normal() );
|
||||
Vector3 xydir( m_view->getViewer() - m_0 );
|
||||
xydir[m_axisZ] = 0;
|
||||
vector3_normalise( xydir );
|
||||
m_planeZ = Plane3( xydir, vector3_dot( xydir, m_0 ) );
|
||||
m_startZ = point_on_plane( m_planeZ, m_view->GetViewMatrix(), device_point );
|
||||
m_bounds = bounds;
|
||||
}
|
||||
void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const DeviceVector device_point ) override {
|
||||
Vector3 current = g_vector3_axes[m_axisZ] * vector3_dot( m_planeSelected.normal(), ( point_on_plane( m_planeZ, m_view->GetViewMatrix(), device_point ) - m_startZ ) )
|
||||
* ( m_planeSelected.normal()[m_axisZ] >= 0? 1 : -1 );
|
||||
|
||||
if( !std::isfinite( current[0] ) || !std::isfinite( current[1] ) || !std::isfinite( current[2] ) ) // catch INF case, is likely with top of the box in 2D
|
||||
return;
|
||||
|
||||
if( g_modifiers.ctrl() )
|
||||
aabb_snap_translation( current, m_bounds );
|
||||
else
|
||||
vector3_snap( current, GetSnapGridSize() );
|
||||
|
||||
m_translatable.translate( current );
|
||||
}
|
||||
void set0( const Vector3& start, const Plane3& planeSelected ){
|
||||
m_0 = start;
|
||||
m_planeSelected = planeSelected;
|
||||
}
|
||||
};
|
||||
|
||||
class TranslateFree : public Manipulatable
|
||||
{
|
||||
Vector3 m_start;
|
||||
Translatable& m_translatable;
|
||||
AABB m_bounds;
|
||||
public:
|
||||
TranslateFree( Translatable& translatable )
|
||||
: m_translatable( translatable ){
|
||||
}
|
||||
void Construct( const Matrix4& device2manip, const DeviceVector device_point, const AABB& bounds, const Vector3& transform_origin ) override {
|
||||
m_start = point_on_plane( device2manip, device_point );
|
||||
m_bounds = bounds;
|
||||
}
|
||||
void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const DeviceVector device_point ) override {
|
||||
Vector3 current = point_on_plane( device2manip, device_point );
|
||||
current = vector3_subtracted( current, m_start );
|
||||
|
||||
if( g_modifiers.shift() ) // snap to axis
|
||||
current *= g_vector3_axes[vector3_max_abs_component_index( current )];
|
||||
|
||||
current = translation_local2object( current, manip2object );
|
||||
|
||||
if( g_modifiers.ctrl() ) // snap aabb
|
||||
aabb_snap_translation( current, m_bounds );
|
||||
else
|
||||
vector3_snap( current, GetSnapGridSize() );
|
||||
|
||||
m_translatable.translate( current );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// \brief constructs Quaternion so that rotated box geometry ends up aligned to one or more axes (depends on how much axial \p to is).
|
||||
inline Quaternion quaternion_for_unit_vectors_for_bounds( const Vector3& axialfrom, const Vector3& to ){
|
||||
// do step by step from the larger component to the smaller one
|
||||
size_t ids[3] = { vector3_max_abs_component_index( to ), ( ids[0] + 1 ) %3, ( ids[0] + 2 ) %3 };
|
||||
if( std::fabs( to[ids[2]] ) > std::fabs( to[ids[1]] ) )
|
||||
std::swap( ids[2], ids[1] );
|
||||
|
||||
Vector3 steps[3] = { g_vector3_axes[ids[0]] * std::copysign( 1.f, to[ids[0]] ), to, to };
|
||||
|
||||
Quaternion rotation = quaternion_for_unit_vectors_safe( axialfrom, steps[0] );
|
||||
if( std::fabs( to[ids[1]] ) > 1e-6f ){
|
||||
steps[1][ids[2]] = 0;
|
||||
vector3_normalise( steps[1] );
|
||||
rotation = quaternion_multiplied_by_quaternion( quaternion_for_unit_vectors( steps[0], steps[1] ), rotation );
|
||||
if( std::fabs( to[ids[2]] ) > 1e-6f ){
|
||||
rotation = quaternion_multiplied_by_quaternion( quaternion_for_unit_vectors( steps[1], to ), rotation );
|
||||
}
|
||||
}
|
||||
return rotation;
|
||||
}
|
||||
|
||||
|
||||
#include <optional>
|
||||
struct testSelect_unselected_scene_point_return_t{ DoubleVector3 point; std::optional<Plane3> plane; };
|
||||
std::optional<testSelect_unselected_scene_point_return_t>
|
||||
testSelect_unselected_scene_point( const View& view, const DeviceVector device_point, const DeviceVector device_epsilon );
|
||||
|
||||
void Scene_BoundsSelected_withEntityBounds( scene::Graph& graph, AABB& bounds );
|
||||
|
||||
inline std::optional<Vector3> AABB_TestPoint( const View& view, const DeviceVector device_point, const DeviceVector device_epsilon, const AABB& aabb ){
|
||||
View scissored( view );
|
||||
ConstructSelectionTest( scissored, SelectionBoxForPoint( device_point, device_epsilon ) );
|
||||
|
||||
SelectionIntersection best;
|
||||
AABB_BestPoint( scissored.GetViewMatrix(), EClipCull::CW, aabb, best );
|
||||
if( best.valid() ){
|
||||
return vector4_projected( matrix4_transformed_vector4( matrix4_full_inverse( scissored.GetViewMatrix() ), Vector4( 0, 0, best.depth(), 1 ) ) );
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
class SnapBounds : public Manipulatable
|
||||
{
|
||||
Translatable& m_translatable;
|
||||
AllTransformable& m_transformable;
|
||||
AABB m_bounds;
|
||||
Vector3 m_0;
|
||||
// rotate-snap axis and sign of aabb
|
||||
size_t m_roatateAxis = 0;
|
||||
int m_rotateSign = 1;
|
||||
|
||||
std::optional<Plane3> m_along_plane;
|
||||
Vector3 m_along_plane_start_point;
|
||||
public:
|
||||
SnapBounds( Translatable& translatable, AllTransformable& transformable )
|
||||
: m_translatable( translatable ), m_transformable( transformable ){
|
||||
}
|
||||
void Construct( const Matrix4& device2manip, const DeviceVector device_point, const AABB& bounds, const Vector3& transform_origin ) override {
|
||||
if( GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive )
|
||||
Scene_BoundsSelected_withEntityBounds( GlobalSceneGraph(), m_bounds );
|
||||
else
|
||||
m_bounds = bounds;
|
||||
|
||||
// for rotate-snap deduce aabb side opposite to clicked
|
||||
if( const auto point = AABB_TestPoint( *m_view, device_point, m_device_epsilon, m_bounds ) ){
|
||||
m_0 = point.value(); // original m_0 is less reliable fallback
|
||||
}
|
||||
m_roatateAxis = 0;
|
||||
m_rotateSign = 1;
|
||||
float bestDist = FLT_MAX;
|
||||
for( size_t axis : { 0, 1, 2 } )
|
||||
for( int sign : { -1, 1 } )
|
||||
if( const float dist = std::fabs( m_0[axis] - ( m_bounds.origin[axis] + std::copysign( m_bounds.extents[axis], sign ) ) ); dist < bestDist ){
|
||||
bestDist = dist;
|
||||
m_roatateAxis = axis;
|
||||
m_rotateSign = sign;
|
||||
}
|
||||
|
||||
m_along_plane.reset();
|
||||
}
|
||||
void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const DeviceVector device_point ) override {
|
||||
Vector3 current( g_vector3_identity );
|
||||
if( g_modifiers.shift() ){ // move along plane
|
||||
if( !m_along_plane ){ // try to initialize plane from original cursor position
|
||||
if( const auto test = testSelect_unselected_scene_point( *m_view, m_device_point, m_device_epsilon );
|
||||
test && test->plane ){
|
||||
m_along_plane = test->plane;
|
||||
m_along_plane_start_point = point_on_plane( *m_along_plane, m_view->GetViewMatrix(), m_device_point );
|
||||
}
|
||||
else if( const auto test = testSelect_unselected_scene_point( *m_view, device_point, m_device_epsilon );
|
||||
test && test->plane ){ // init cursor pos was not on plane, try to fallback to current pos
|
||||
m_along_plane = test->plane;
|
||||
m_along_plane_start_point = point_on_plane( *m_along_plane, m_view->GetViewMatrix(), device_point );
|
||||
}
|
||||
}
|
||||
if( m_along_plane ){ // got plane, lez go
|
||||
current = point_on_plane( *m_along_plane, m_view->GetViewMatrix(), device_point ) - m_along_plane_start_point;
|
||||
const size_t maxi = vector3_max_abs_component_index( m_along_plane->normal() );
|
||||
vector3_snap( current, GetSnapGridSize() );
|
||||
// snap move on two axes with least normal component -> need to find out 3rd move component
|
||||
// it equals to point snap to plane with dist=0
|
||||
// normal.dot( snapped move ) = 0
|
||||
current[maxi] = -( m_along_plane->normal()[( maxi + 1 ) % 3] * current[( maxi + 1 ) % 3]
|
||||
+ m_along_plane->normal()[( maxi + 2 ) % 3] * current[( maxi + 2 ) % 3] )
|
||||
/ m_along_plane->normal()[maxi];
|
||||
return m_translatable.translate( current );
|
||||
}
|
||||
}
|
||||
else if( const auto test = testSelect_unselected_scene_point( *m_view, device_point, m_device_epsilon ) ){
|
||||
const auto choose_aabb_corner = []( const AABB& bounds, const size_t axis, const Vector3& nrm, const Vector3& ray ){
|
||||
Vector3 extents = bounds.extents;
|
||||
extents[axis] = std::copysign( extents[axis], nrm[axis] );
|
||||
extents[( axis + 1 ) % 3] = std::copysign( extents[( axis + 1 ) % 3], ray[( axis + 1 ) % 3] );
|
||||
extents[( axis + 2 ) % 3] = std::copysign( extents[( axis + 2 ) % 3], ray[( axis + 2 ) % 3] );
|
||||
return bounds.origin - extents;
|
||||
};
|
||||
const Ray ray = ray_for_device_point( matrix4_full_inverse( m_view->GetViewMatrix() ), device_point );
|
||||
const Vector3 nrm = test->plane? Vector3( test->plane->normal() ) : -ray.direction;
|
||||
if( g_modifiers.alt() ){ // rotate-snap
|
||||
const Quaternion rotation = quaternion_for_unit_vectors_for_bounds( g_vector3_axes[m_roatateAxis] * m_rotateSign, nrm );
|
||||
const Matrix4 unrot = matrix4_rotation_for_quaternion( quaternion_inverse( rotation ) );
|
||||
const Vector3 unray = matrix4_transformed_direction( unrot,
|
||||
test->plane
|
||||
? ray.direction
|
||||
// when test point has no plane data we rotate exactly to test ray... tweak ray to deduce distinct aabb corner
|
||||
: ray_for_device_point( matrix4_full_inverse( m_view->GetViewMatrix() ), device_point * 1.1f ).direction );
|
||||
const Vector3 corner = choose_aabb_corner( m_bounds, m_roatateAxis, -unray, unray );
|
||||
|
||||
Transforms transforms;
|
||||
transforms.setRotation( rotation );
|
||||
transforms.setTranslation( test->point - corner );
|
||||
return m_transformable.alltransform( transforms, corner );
|
||||
}
|
||||
else{ // move-snap
|
||||
const std::size_t axis = vector3_max_abs_component_index( nrm ); // snap bbox along this axis
|
||||
current = test->point - choose_aabb_corner( m_bounds, axis, nrm, ray.direction );
|
||||
return m_translatable.translate( current );
|
||||
}
|
||||
}
|
||||
|
||||
m_translatable.translate( current ); // fallback to move to original position
|
||||
}
|
||||
void set0( const Vector3& start ){
|
||||
m_0 = start;
|
||||
}
|
||||
static bool useCondition( const ModifierFlagsExt& modifiers, const View& view ){
|
||||
return modifiers.ctrl() && view.fill();
|
||||
}
|
||||
};
|
||||
|
||||
class TranslateFreeXY_Z : public Manipulatable
|
||||
{
|
||||
Vector3 m_0;
|
||||
std::size_t m_axisZ;
|
||||
Plane3 m_planeXY;
|
||||
Plane3 m_planeZ;
|
||||
Vector3 m_startXY;
|
||||
Vector3 m_startZ;
|
||||
Translatable& m_translatable;
|
||||
AABB m_bounds;
|
||||
SnapBounds m_snapBounds;
|
||||
public:
|
||||
inline static int m_viewdependent = 0;
|
||||
TranslateFreeXY_Z( Translatable& translatable, AllTransformable& transformable )
|
||||
: m_translatable( translatable ), m_snapBounds( translatable, transformable ){
|
||||
}
|
||||
void Construct( const Matrix4& device2manip, const DeviceVector device_point, const AABB& bounds, const Vector3& transform_origin ) override {
|
||||
m_axisZ = ( m_viewdependent || !m_view->fill() )? vector3_max_abs_component_index( m_view->getViewDir() ) : 2;
|
||||
if( m_0 == g_vector3_identity ) /* special value to indicate missing good point to start with, i.e. while dragging components by clicking anywhere; m_startXY, m_startZ != m_0 in this case */
|
||||
m_0 = transform_origin;
|
||||
m_planeXY = Plane3( g_vector3_axes[m_axisZ], m_0[m_axisZ] );
|
||||
#if 0
|
||||
Vector3 xydir( m_view->getViewDir() );
|
||||
#else
|
||||
Vector3 xydir( m_view->getViewer() - m_0 );
|
||||
#endif
|
||||
xydir[m_axisZ] = 0;
|
||||
vector3_normalise( xydir );
|
||||
m_planeZ = Plane3( xydir, vector3_dot( xydir, m_0 ) );
|
||||
m_startXY = point_on_plane( m_planeXY, m_view->GetViewMatrix(), device_point );
|
||||
m_startZ = point_on_plane( m_planeZ, m_view->GetViewMatrix(), device_point );
|
||||
m_bounds = bounds;
|
||||
|
||||
m_snapBounds.Construct( device2manip, device_point, bounds, transform_origin );
|
||||
}
|
||||
void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const DeviceVector device_point ) override {
|
||||
if( SnapBounds::useCondition( g_modifiers, *m_view ) ){
|
||||
m_snapBounds.Transform( manip2object, device2manip, device_point );
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 current;
|
||||
if( g_modifiers.alt() && m_view->fill() ) // Z only
|
||||
current = ( point_on_plane( m_planeZ, m_view->GetViewMatrix(), device_point ) - m_startZ ) * g_vector3_axes[m_axisZ];
|
||||
else{
|
||||
current = point_on_plane( m_planeXY, m_view->GetViewMatrix(), device_point ) - m_startXY;
|
||||
current[m_axisZ] = 0;
|
||||
}
|
||||
|
||||
if( g_modifiers.shift() ) // snap to axis
|
||||
current *= g_vector3_axes[vector3_max_abs_component_index( current )];
|
||||
|
||||
if( g_modifiers.ctrl() ) // snap aabb
|
||||
aabb_snap_translation( current, m_bounds );
|
||||
else
|
||||
vector3_snap( current, GetSnapGridSize() );
|
||||
|
||||
m_translatable.translate( current );
|
||||
}
|
||||
void set0( const Vector3& start ){
|
||||
m_0 = start;
|
||||
m_snapBounds.set0( start );
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "selection_mtor_clip.h"
|
||||
|
||||
#include "selection_.h"
|
||||
#include "selection_render.h"
|
||||
#include "selection_volume.h"
|
||||
#include "selection_selector.h"
|
||||
#include "selection_mtable_translate.h"
|
||||
#include "selectionlib.h"
|
||||
#include "clippertool.h"
|
||||
#include "grid.h"
|
||||
#include "brush.h"
|
||||
|
||||
class ClipManipulatorImpl final : public ClipManipulator, public ManipulatorSelectionChangeable, public Translatable, public AllTransformable, public Manipulatable
|
||||
{
|
||||
struct ClipperPoint : public OpenGLRenderable, public SelectableBool
|
||||
{
|
||||
PointVertex m_p; //for render
|
||||
ClipperPoint():
|
||||
m_p( vertex3f_identity ), m_set( false ) {
|
||||
}
|
||||
void render( RenderStateFlags state ) const override {
|
||||
gl().glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_p.colour );
|
||||
gl().glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_p.vertex );
|
||||
gl().glDrawArrays( GL_POINTS, 0, 1 );
|
||||
|
||||
gl().glColor4ub( m_p.colour.r, m_p.colour.g, m_p.colour.b, m_p.colour.a ); ///?
|
||||
gl().glRasterPos3f( m_namePos.x(), m_namePos.y(), m_namePos.z() );
|
||||
GlobalOpenGL().drawChar( m_name );
|
||||
}
|
||||
void setColour( const Colour4b& colour ) {
|
||||
m_p.colour = colour;
|
||||
}
|
||||
bool m_set;
|
||||
DoubleVector3 m_point;
|
||||
DoubleVector3 m_pointNonTransformed;
|
||||
char m_name;
|
||||
Vector3 m_namePos;
|
||||
};
|
||||
Matrix4& m_pivot2world;
|
||||
ClipperPoint m_points[3];
|
||||
TranslateFreeXY_Z m_dragXY_Z;
|
||||
const AABB& m_bounds;
|
||||
Vector3 m_viewdir;
|
||||
public:
|
||||
ClipManipulatorImpl( Matrix4& pivot2world, const AABB& bounds ) : m_pivot2world( pivot2world ), m_dragXY_Z( *this, *this ), m_bounds( bounds ){
|
||||
m_points[0].m_name = '1';
|
||||
m_points[1].m_name = '2';
|
||||
m_points[2].m_name = '3';
|
||||
}
|
||||
|
||||
void UpdateColours() {
|
||||
for( std::size_t i = 0; i < 3; ++i )
|
||||
m_points[i].setColour( colourSelected( g_colour_screen, m_points[i].isSelected() ) );
|
||||
}
|
||||
|
||||
void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ) override {
|
||||
// temp hack
|
||||
UpdateColours();
|
||||
|
||||
renderer.SetState( m_state, Renderer::eWireframeOnly );
|
||||
renderer.SetState( m_state, Renderer::eFullMaterials );
|
||||
|
||||
const Matrix4 proj( matrix4_multiplied_by_matrix4( volume.GetViewport(), volume.GetViewMatrix() ) );
|
||||
const Matrix4 proj_inv( matrix4_full_inverse( proj ) );
|
||||
for( std::size_t i = 0; i < 3; ++i )
|
||||
if( m_points[i].m_set ){
|
||||
m_points[i].m_p.vertex = vertex3f_for_vector3( m_points[i].m_point );
|
||||
renderer.addRenderable( m_points[i], g_matrix4_identity );
|
||||
const Vector3 pos = vector4_projected( matrix4_transformed_vector4( proj, Vector4( m_points[i].m_point, 1 ) ) ) + Vector3( 2, 0, 0 );
|
||||
m_points[i].m_namePos = vector4_projected( matrix4_transformed_vector4( proj_inv, Vector4( pos, 1 ) ) );
|
||||
}
|
||||
}
|
||||
/* these three functions and m_viewdir for 2 points only */
|
||||
void viewdir_set( const Vector3 viewdir ){
|
||||
const std::size_t maxi = vector3_max_abs_component_index( viewdir );
|
||||
m_viewdir = ( viewdir[maxi] > 0 )? g_vector3_axes[maxi] : -g_vector3_axes[maxi];
|
||||
}
|
||||
void viewdir_fixup(){
|
||||
if( std::fabs( vector3_length( m_points[1].m_point - m_points[0].m_point ) ) > 1e-3 //two non coincident points
|
||||
&& std::fabs( vector3_dot( m_viewdir, vector3_normalised( m_points[1].m_point - m_points[0].m_point ) ) ) > 0.999 ){ //on axis = m_viewdir
|
||||
viewdir_set( m_view->getViewDir() );
|
||||
if( std::fabs( vector3_dot( m_viewdir, vector3_normalised( m_points[1].m_point - m_points[0].m_point ) ) ) > 0.999 ){
|
||||
const Matrix4 screen2world( matrix4_full_inverse( m_view->GetViewMatrix() ) );
|
||||
Vector3 p[2];
|
||||
for( std::size_t i = 0; i < 2; ++i ){
|
||||
p[i] = vector4_projected( matrix4_transformed_vector4( m_view->GetViewMatrix(), Vector4( m_points[i].m_point, 1 ) ) );
|
||||
}
|
||||
const float depthdir = p[1].z() > p[0].z()? -1 : 1;
|
||||
for( std::size_t i = 0; i < 2; ++i ){
|
||||
p[i].z() = -1;
|
||||
p[i] = vector4_projected( matrix4_transformed_vector4( screen2world, Vector4( p[i], 1 ) ) );
|
||||
}
|
||||
viewdir_set( ( p[1] - p[0] ) * depthdir );
|
||||
}
|
||||
}
|
||||
}
|
||||
void viewdir_make_cut_worthy( const Plane3& plane ){
|
||||
const std::size_t maxi = vector3_max_abs_component_index( plane.normal() );
|
||||
if( plane3_valid( plane )
|
||||
&& aabb_valid( m_bounds )
|
||||
&& std::fabs( plane.normal()[maxi] ) > 0.999 ){ //axial plane
|
||||
const double anchor = plane.normal()[maxi] * plane.dist();
|
||||
if( anchor > m_bounds.origin[maxi] ){
|
||||
if( ( anchor - ( m_bounds.origin[maxi] + m_bounds.extents[maxi] ) ) > -0.1 )
|
||||
viewdir_set( -g_vector3_axes[maxi] );
|
||||
}
|
||||
else{
|
||||
if( ( -anchor + ( m_bounds.origin[maxi] - m_bounds.extents[maxi] ) ) > -0.1 )
|
||||
viewdir_set( g_vector3_axes[maxi] );
|
||||
}
|
||||
}
|
||||
}
|
||||
void updatePlane(){
|
||||
std::size_t npoints = 0;
|
||||
for(; npoints < 3; )
|
||||
if( m_points[npoints].m_set )
|
||||
++npoints;
|
||||
else
|
||||
break;
|
||||
|
||||
switch ( npoints )
|
||||
{
|
||||
case 1:
|
||||
Clipper_setPlanePoints( ClipperPoints( m_points[0].m_point, m_points[0].m_point, m_points[0].m_point, npoints ) );
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
if( m_view->fill() ){ //3d
|
||||
viewdir_fixup();
|
||||
m_points[2].m_point = m_points[0].m_point - m_viewdir * vector3_length( m_points[0].m_point - m_points[1].m_point );
|
||||
viewdir_make_cut_worthy( plane3_for_points( m_points[0].m_point, m_points[1].m_point, m_points[2].m_point ) );
|
||||
}
|
||||
m_points[2].m_point = m_points[0].m_point - m_viewdir * vector3_length( m_points[0].m_point - m_points[1].m_point );
|
||||
} // fall through
|
||||
case 3:
|
||||
Clipper_setPlanePoints( ClipperPoints( m_points[0].m_point, m_points[1].m_point, m_points[2].m_point, npoints ) );
|
||||
break;
|
||||
|
||||
default:
|
||||
Clipper_setPlanePoints( ClipperPoints() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::size_t newPointIndex( bool viewfill ) const {
|
||||
const std::size_t maxi = ( !viewfill && Clipper_get2pointsIn2d() )? 2 : 3;
|
||||
std::size_t i;
|
||||
for( i = 0; i < maxi; ++i )
|
||||
if( !m_points[i].m_set )
|
||||
break;
|
||||
return i % maxi;
|
||||
}
|
||||
void newPoint( const DoubleVector3& point, const View& view ){
|
||||
const std::size_t i = newPointIndex( view.fill() );
|
||||
if( i == 0 )
|
||||
m_points[1].m_set = m_points[2].m_set = false;
|
||||
m_points[i].m_set = true;
|
||||
m_points[i].m_point = point;
|
||||
|
||||
SelectionPool selector;
|
||||
selector.addSelectable( SelectionIntersection( 0, 0 ), &m_points[i] );
|
||||
selectionChange( selector );
|
||||
|
||||
if( i == 1 )
|
||||
viewdir_set( m_view->getViewDir() );
|
||||
|
||||
updatePlane();
|
||||
}
|
||||
bool testSelect_scene( const View& view, DoubleVector3& point ) const {
|
||||
SelectionVolume test( view );
|
||||
ScenePointSelector selector;
|
||||
Scene_forEachVisible_testselect_scene_point( view, selector, test );
|
||||
test.BeginMesh( g_matrix4_identity, true );
|
||||
if( selector.isSelected() ){
|
||||
point = testSelected_scene_snapped_point( test, selector );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void testSelect( const View& view, const Matrix4& pivot2world ) override {
|
||||
if( g_modifiers != c_modifierNone && !quickCondition( g_modifiers, view ) )
|
||||
return selectionChange( nullptr );
|
||||
|
||||
testSelect_points( view );
|
||||
if( !isSelected() ){
|
||||
if( view.fill() ){
|
||||
DoubleVector3 point;
|
||||
if( testSelect_scene( view, point ) )
|
||||
newPoint( point, view );
|
||||
}
|
||||
else{
|
||||
DoubleVector3 point = vector4_projected( matrix4_transformed_vector4( matrix4_full_inverse( view.GetViewMatrix() ), Vector4( 0, 0, 0, 1 ) ) );
|
||||
vector3_snap( point, GetSnapGridSize() );
|
||||
{
|
||||
const std::size_t maxi = vector3_max_abs_component_index( view.getViewDir() );
|
||||
const std::size_t i = newPointIndex( false );
|
||||
point[maxi] = m_bounds.origin[maxi] + ( i == 2? -1 : 1 ) * m_bounds.extents[maxi];
|
||||
}
|
||||
newPoint( point, view );
|
||||
}
|
||||
}
|
||||
for( std::size_t i = 0; i < 3; ++i )
|
||||
if( m_points[i].isSelected() ){
|
||||
m_points[i].m_pointNonTransformed = m_points[i].m_point;
|
||||
m_pivot2world = matrix4_translation_for_vec3( m_points[i].m_pointNonTransformed );
|
||||
break;
|
||||
}
|
||||
}
|
||||
void highlight( const View& view, const Matrix4& pivot2world ) override {
|
||||
testSelect_points( view );
|
||||
}
|
||||
void testSelect_points( const View& view ){
|
||||
if( g_modifiers != c_modifierNone && !quickCondition( g_modifiers, view ) )
|
||||
return selectionChange( nullptr );
|
||||
|
||||
SelectionPool selector;
|
||||
{
|
||||
const Matrix4 local2view( view.GetViewMatrix() );
|
||||
|
||||
for( std::size_t i = 0; i < 3; ++i ){
|
||||
if( m_points[i].m_set ){
|
||||
SelectionIntersection best;
|
||||
Point_BestPoint( local2view, PointVertex( vertex3f_for_vector3( m_points[i].m_point ) ), best );
|
||||
selector.addSelectable( best, &m_points[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
selectionChange( selector );
|
||||
}
|
||||
void reset( bool initFromFace ) override {
|
||||
for( std::size_t i = 0; i < 3; ++i ){
|
||||
m_points[i].m_set = false;
|
||||
m_points[i].setSelected( false ); ///?
|
||||
}
|
||||
if( initFromFace && !g_SelectedFaceInstances.empty() && g_SelectedFaceInstances.last().getFace().contributes() ){
|
||||
const Winding& w = g_SelectedFaceInstances.last().getFace().getWinding();
|
||||
for( std::size_t i = 0; i < 3; ++i ){
|
||||
m_points[i].m_set = true;
|
||||
m_points[i].m_point = w[i].vertex;
|
||||
}
|
||||
}
|
||||
updatePlane();
|
||||
}
|
||||
/* Translatable */
|
||||
void translate( const Vector3& translation ) override { //in 2d and ( 3d + m_dragXY_Z )
|
||||
for( std::size_t i = 0; i < 3; ++i )
|
||||
if( m_points[i].isSelected() ){
|
||||
m_points[i].m_point = m_points[i].m_pointNonTransformed + translation;
|
||||
updatePlane();
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* AllTransformable */
|
||||
void alltransform( const Transforms& transforms, const Vector3& world_pivot ) override {
|
||||
ERROR_MESSAGE( "unreachable" );
|
||||
}
|
||||
/* Manipulatable */
|
||||
void Construct( const Matrix4& device2manip, const DeviceVector device_point, const AABB& bounds, const Vector3& transform_origin ) override {
|
||||
m_dragXY_Z.set0( transform_origin );
|
||||
m_dragXY_Z.Construct( device2manip, device_point, AABB( transform_origin, g_vector3_identity ), transform_origin );
|
||||
}
|
||||
void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const DeviceVector device_point ) override {
|
||||
// any 2D or 3D with modifiers besides SnapBounds
|
||||
if( !( g_modifiers == c_modifierNone && m_view->fill() ) && !SnapBounds::useCondition( g_modifiers, *m_view ) )
|
||||
return m_dragXY_Z.Transform( manip2object, device2manip, device_point );
|
||||
|
||||
View scissored( *m_view );
|
||||
ConstructSelectionTest( scissored, SelectionBoxForPoint( device_point, m_device_epsilon ) );
|
||||
|
||||
DoubleVector3 point;
|
||||
if( testSelect_scene( scissored, point ) )
|
||||
for( std::size_t i = 0; i < 3; ++i )
|
||||
if( m_points[i].isSelected() ){
|
||||
m_points[i].m_point = point;
|
||||
updatePlane();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Manipulatable* GetManipulatable() override {
|
||||
return this;
|
||||
}
|
||||
|
||||
void setSelected( bool select ) override {
|
||||
for( std::size_t i = 0; i < 3; ++i )
|
||||
m_points[i].setSelected( select );
|
||||
}
|
||||
bool isSelected() const override {
|
||||
return m_points[0].isSelected() || m_points[1].isSelected() || m_points[2].isSelected();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
ClipManipulator* New_ClipManipulator( Matrix4& pivot2world, const AABB& bounds ){
|
||||
return new ClipManipulatorImpl( pivot2world, bounds );
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selection_.h"
|
||||
|
||||
class ClipManipulator : public Manipulator
|
||||
{
|
||||
public:
|
||||
virtual void reset( bool initFromFace ) = 0;
|
||||
|
||||
inline static Shader* m_state;
|
||||
|
||||
static bool quickCondition( const ModifierFlags& modifiers, const View& view ){
|
||||
return modifiers == c_modifierControl && !view.fill();
|
||||
}
|
||||
};
|
||||
|
||||
ClipManipulator* New_ClipManipulator( Matrix4& pivot2world, const AABB& bounds );
|
||||
@@ -0,0 +1,605 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "selection_mtor_drag.h"
|
||||
|
||||
#include "selection_.h"
|
||||
#include "selection_render.h"
|
||||
#include "selection_volume.h"
|
||||
#include "selection_selector.h"
|
||||
#include "selection_mtable_translate.h"
|
||||
#include "selection_mtable_brush.h"
|
||||
#include "selection_mtable_extrude.h"
|
||||
#include "selectionlib.h"
|
||||
#include "brush.h"
|
||||
#include "grid.h"
|
||||
|
||||
|
||||
inline PlaneSelectable* Instance_getPlaneSelectable( scene::Instance& instance ){
|
||||
return InstanceTypeCast<PlaneSelectable>::cast( instance );
|
||||
}
|
||||
|
||||
class PlaneSelectableSelectPlanes : public scene::Graph::Walker
|
||||
{
|
||||
Selector& m_selector;
|
||||
SelectionTest& m_test;
|
||||
PlaneCallback m_selectedPlaneCallback;
|
||||
public:
|
||||
PlaneSelectableSelectPlanes( Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback )
|
||||
: m_selector( selector ), m_test( test ), m_selectedPlaneCallback( selectedPlaneCallback ){
|
||||
}
|
||||
bool pre( const scene::Path& path, scene::Instance& instance ) const override {
|
||||
if ( path.top().get().visible() && Instance_isSelected( instance ) ) {
|
||||
PlaneSelectable* planeSelectable = Instance_getPlaneSelectable( instance );
|
||||
if ( planeSelectable != 0 ) {
|
||||
planeSelectable->selectPlanes( m_selector, m_test, m_selectedPlaneCallback );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class PlaneSelectableSelectReversedPlanes : public scene::Graph::Walker
|
||||
{
|
||||
Selector& m_selector;
|
||||
const SelectedPlanes& m_selectedPlanes;
|
||||
public:
|
||||
PlaneSelectableSelectReversedPlanes( Selector& selector, const SelectedPlanes& selectedPlanes )
|
||||
: m_selector( selector ), m_selectedPlanes( selectedPlanes ){
|
||||
}
|
||||
bool pre( const scene::Path& path, scene::Instance& instance ) const override {
|
||||
if ( path.top().get().visible() && Instance_isSelected( instance ) ) {
|
||||
PlaneSelectable* planeSelectable = Instance_getPlaneSelectable( instance );
|
||||
if ( planeSelectable != 0 ) {
|
||||
planeSelectable->selectReversedPlanes( m_selector, m_selectedPlanes );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void Scene_forEachPlaneSelectable_selectPlanes( scene::Graph& graph, Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback ){
|
||||
graph.traverse( PlaneSelectableSelectPlanes( selector, test, selectedPlaneCallback ) );
|
||||
}
|
||||
|
||||
void Scene_forEachPlaneSelectable_selectReversedPlanes( scene::Graph& graph, Selector& selector, const SelectedPlanes& selectedPlanes ){
|
||||
graph.traverse( PlaneSelectableSelectReversedPlanes( selector, selectedPlanes ) );
|
||||
}
|
||||
|
||||
|
||||
class PlaneLess
|
||||
{
|
||||
public:
|
||||
bool operator()( const Plane3& plane, const Plane3& other ) const {
|
||||
return std::tie( plane.a, plane.b, plane.c, plane.d )
|
||||
< std::tie( other.a, other.b, other.c, other.d );
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::set<Plane3, PlaneLess> PlaneSet;
|
||||
|
||||
|
||||
class SelectedPlaneSet : public SelectedPlanes
|
||||
{
|
||||
PlaneSet m_selectedPlanes;
|
||||
public:
|
||||
bool empty() const {
|
||||
return m_selectedPlanes.empty();
|
||||
}
|
||||
|
||||
void insert( const Plane3& plane ){
|
||||
m_selectedPlanes.insert( plane );
|
||||
}
|
||||
bool contains( const Plane3& plane ) const override {
|
||||
return m_selectedPlanes.contains( plane );
|
||||
}
|
||||
typedef MemberCaller<SelectedPlaneSet, void(const Plane3&), &SelectedPlaneSet::insert> InsertCaller;
|
||||
};
|
||||
|
||||
|
||||
bool Scene_forEachPlaneSelectable_selectPlanes( scene::Graph& graph, Selector& selector, SelectionTest& test ){
|
||||
SelectedPlaneSet selectedPlanes;
|
||||
|
||||
Scene_forEachPlaneSelectable_selectPlanes( graph, selector, test, SelectedPlaneSet::InsertCaller( selectedPlanes ) );
|
||||
Scene_forEachPlaneSelectable_selectReversedPlanes( graph, selector, selectedPlanes );
|
||||
|
||||
return !selectedPlanes.empty();
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<typename Functor>
|
||||
class PlaneselectableVisibleSelectedVisitor : public SelectionSystem::Visitor
|
||||
{
|
||||
const Functor& m_functor;
|
||||
public:
|
||||
PlaneselectableVisibleSelectedVisitor( const Functor& functor ) : m_functor( functor ){
|
||||
}
|
||||
void visit( scene::Instance& instance ) const override {
|
||||
PlaneSelectable* planeSelectable = Instance_getPlaneSelectable( instance );
|
||||
if ( planeSelectable != 0
|
||||
&& instance.path().top().get().visible() ) {
|
||||
m_functor( *planeSelectable );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Functor>
|
||||
inline const Functor& Scene_forEachVisibleSelectedPlaneselectable( const Functor& functor ){
|
||||
GlobalSelectionSystem().foreachSelected( PlaneselectableVisibleSelectedVisitor<Functor>( functor ) );
|
||||
return functor;
|
||||
}
|
||||
|
||||
PlaneSelectable::BestPlaneData Scene_forEachPlaneSelectable_bestPlane( SelectionTest& test ){
|
||||
PlaneSelectable::BestPlaneData planeData;
|
||||
auto bestPlaneDirect = [&test, &planeData]( PlaneSelectable& planeSelectable ){
|
||||
planeSelectable.bestPlaneDirect( test, planeData );
|
||||
};
|
||||
Scene_forEachVisibleSelectedPlaneselectable( bestPlaneDirect );
|
||||
if( !planeData.valid() ){
|
||||
auto bestPlaneIndirect = [&test, &planeData]( PlaneSelectable& planeSelectable ){
|
||||
planeSelectable.bestPlaneIndirect( test, planeData );
|
||||
};
|
||||
Scene_forEachVisibleSelectedPlaneselectable( bestPlaneIndirect );
|
||||
}
|
||||
return planeData;
|
||||
}
|
||||
|
||||
bool Scene_forEachPlaneSelectable_selectPlanes2( SelectionTest& test, TranslateAxis2& translateAxis ){
|
||||
const auto planeData = Scene_forEachPlaneSelectable_bestPlane( test );
|
||||
|
||||
if( planeData.valid() ){
|
||||
const Plane3 plane = planeData.m_plane;
|
||||
if( planeData.direct() ){ // direct
|
||||
translateAxis.set0( point_on_plane( plane, test.getVolume().GetViewMatrix(), DeviceVector( 0, 0 ) ), plane );
|
||||
}
|
||||
else{ // indirect
|
||||
test.BeginMesh( g_matrix4_identity );
|
||||
/* may introduce some screen space offset in manipulatable to handle far-from-edge clicks perfectly; thought clicking not so far isn't too nasty, right? */
|
||||
translateAxis.set0( vector4_projected( matrix4_transformed_vector4( test.getScreen2world(), Vector4( planeData.m_closestPoint, 1 ) ) ), plane );
|
||||
}
|
||||
|
||||
auto selectByPlane = [plane]( PlaneSelectable& planeSelectable ){
|
||||
planeSelectable.selectByPlane( plane );
|
||||
};
|
||||
Scene_forEachVisibleSelectedPlaneselectable( selectByPlane );
|
||||
}
|
||||
|
||||
return planeData.valid();
|
||||
}
|
||||
|
||||
|
||||
PlaneSelectable::BestPlaneData Scene_forEachSelectedBrush_bestPlane( SelectionTest& test ){
|
||||
PlaneSelectable::BestPlaneData planeData;
|
||||
auto bestPlaneDirect = [&test, &planeData]( BrushInstance& brushInstance ){
|
||||
brushInstance.bestPlaneDirect( test, planeData );
|
||||
};
|
||||
Scene_forEachVisibleSelectedBrush( bestPlaneDirect );
|
||||
if( !planeData.valid() ){
|
||||
auto bestPlaneIndirect = [&test, &planeData]( BrushInstance& brushInstance ){
|
||||
brushInstance.bestPlaneIndirect( test, planeData );
|
||||
};
|
||||
Scene_forEachVisibleSelectedBrush( bestPlaneIndirect );
|
||||
}
|
||||
return planeData;
|
||||
}
|
||||
|
||||
PlaneSelectable::BestPlaneData Scene_forEachBrush_bestPlane( SelectionTest& test ){
|
||||
if( g_SelectedFaceInstances.empty() ){
|
||||
return Scene_forEachSelectedBrush_bestPlane( test );
|
||||
}
|
||||
else{
|
||||
PlaneSelectable::BestPlaneData planeData;
|
||||
auto bestPlaneDirect = [&test, &planeData]( BrushInstance& brushInstance ){
|
||||
if( brushInstance.isSelected() || brushInstance.isSelectedComponents() )
|
||||
brushInstance.bestPlaneDirect( test, planeData );
|
||||
};
|
||||
|
||||
Scene_forEachVisibleBrush( GlobalSceneGraph(), bestPlaneDirect );
|
||||
if( !planeData.valid() ){
|
||||
auto bestPlaneIndirect = [&test, &planeData]( BrushInstance& brushInstance ){
|
||||
if( brushInstance.isSelected() || brushInstance.isSelectedComponents() )
|
||||
brushInstance.bestPlaneIndirect( test, planeData );
|
||||
};
|
||||
Scene_forEachVisibleBrush( GlobalSceneGraph(), bestPlaneIndirect );
|
||||
}
|
||||
return planeData;
|
||||
}
|
||||
}
|
||||
|
||||
bool Scene_forEachBrush_setupExtrude( SelectionTest& test, DragExtrudeFaces& extrudeFaces ){
|
||||
const auto planeData = Scene_forEachBrush_bestPlane( test );
|
||||
|
||||
if( planeData.valid() ){
|
||||
const Plane3 plane = planeData.m_plane;
|
||||
if( planeData.direct() ){ // direct
|
||||
extrudeFaces.set0( point_on_plane( plane, test.getVolume().GetViewMatrix(), DeviceVector( 0, 0 ) ), plane );
|
||||
}
|
||||
else{ // indirect
|
||||
test.BeginMesh( g_matrix4_identity );
|
||||
/* may introduce some screen space offset in manipulatable to handle far-from-edge clicks perfectly; thought clicking not so far isn't too nasty, right? */
|
||||
extrudeFaces.set0( vector4_projected( matrix4_transformed_vector4( test.getScreen2world(), Vector4( planeData.m_closestPoint, 1 ) ) ), plane );
|
||||
}
|
||||
extrudeFaces.m_extrudeSources.clear();
|
||||
auto gatherExtrude = [plane, &extrudeFaces]( BrushInstance& brushInstance ){
|
||||
if( brushInstance.isSelected() || brushInstance.isSelectedComponents() ){
|
||||
bool m_pushed = false;
|
||||
auto gatherFaceInstances = [plane, &extrudeFaces, &brushInstance, &m_pushed]( FaceInstance& face ){
|
||||
if( face.isSelected() || plane3_equal( plane, face.getFace().plane3() ) ){
|
||||
if( !m_pushed ){
|
||||
extrudeFaces.m_extrudeSources.emplace_back();
|
||||
extrudeFaces.m_extrudeSources.back().m_brushInstance = &brushInstance;
|
||||
m_pushed = true;
|
||||
}
|
||||
extrudeFaces.m_extrudeSources.back().m_faces.emplace_back();
|
||||
extrudeFaces.m_extrudeSources.back().m_faces.back().m_face = &face.getFace();
|
||||
extrudeFaces.m_extrudeSources.back().m_faces.back().m_planepoints = face.getFace().getPlane().getPlanePoints();
|
||||
}
|
||||
};
|
||||
Brush_ForEachFaceInstance( brushInstance, gatherFaceInstances );
|
||||
|
||||
brushInstance.setSelectedComponents( false, SelectionSystem::eFace );
|
||||
brushInstance.setSelected( false );
|
||||
}
|
||||
};
|
||||
Scene_forEachVisibleBrush( GlobalSceneGraph(), gatherExtrude );
|
||||
}
|
||||
|
||||
return planeData.valid();
|
||||
}
|
||||
|
||||
|
||||
bool selection_selectVerticesOrFaceVertices( SelectionTest& test ){
|
||||
{ /* try to hit vertices */
|
||||
DeepBestSelector deepSelector;
|
||||
Scene_TestSelect_Component_Selected( deepSelector, test, test.getVolume(), SelectionSystem::eVertex );
|
||||
if( !deepSelector.best().empty() ){
|
||||
for ( Selectable* s : deepSelector.best() )
|
||||
s->setSelected( true );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/* otherwise select vertices of brush faces, which lay on best plane */
|
||||
const auto planeData = Scene_forEachSelectedBrush_bestPlane( test );
|
||||
|
||||
if( planeData.valid() ){
|
||||
auto selectVerticesOnPlane = [plane = planeData.m_plane]( BrushInstance& brushInstance ){
|
||||
brushInstance.selectVerticesOnPlane( plane );
|
||||
};
|
||||
Scene_forEachVisibleSelectedBrush( selectVerticesOnPlane );
|
||||
}
|
||||
return planeData.valid();
|
||||
}
|
||||
|
||||
|
||||
bool scene_insert_brush_vertices( const View& view, TranslateFreeXY_Z& freeDragXY_Z ){
|
||||
SelectionVolume test( view );
|
||||
ScenePointSelector selector;
|
||||
if( view.fill() )
|
||||
Scene_forEachVisible_testselect_scene_point( view, selector, test );
|
||||
else
|
||||
Scene_forEachVisible_testselect_scene_point_selected_brushes( view, selector, test );
|
||||
test.BeginMesh( g_matrix4_identity, true );
|
||||
if( selector.isSelected() ){
|
||||
freeDragXY_Z.set0( vector4_projected( matrix4_transformed_vector4( test.getScreen2world(), Vector4( 0, 0, selector.best().depth(), 1 ) ) ) );
|
||||
DoubleVector3 point = testSelected_scene_snapped_point( test, selector );
|
||||
if( !view.fill() ){
|
||||
point -= view.getViewDir() * GetGridSize();
|
||||
}
|
||||
Brush::VertexModeVertices vertexModeVertices;
|
||||
vertexModeVertices.push_back( Brush::VertexModeVertex( point, true ) );
|
||||
if( selector.face() )
|
||||
vertexModeVertices.back().m_faces.push_back( selector.face() );
|
||||
|
||||
UndoableCommand undo( "InsertBrushVertices" );
|
||||
Scene_forEachSelectedBrush( [&vertexModeVertices]( BrushInstance& brush ){ brush.insert_vertices( vertexModeVertices ); } );
|
||||
return true;
|
||||
}
|
||||
else if( !view.fill() ){ //+two points
|
||||
freeDragXY_Z.set0( g_vector3_identity );
|
||||
const AABB bounds = GlobalSelectionSystem().getBoundsSelected();
|
||||
if( aabb_valid( bounds ) ){
|
||||
DoubleVector3 xy = vector4_projected( matrix4_transformed_vector4( test.getScreen2world(), Vector4( 0, 0, 0, 1 ) ) );
|
||||
vector3_snap( xy, GetSnapGridSize() );
|
||||
DoubleVector3 a( xy ), b( xy );
|
||||
const std::size_t max = vector3_max_abs_component_index( view.getViewDir() );
|
||||
a[max] = bounds.origin[max] + bounds.extents[max];
|
||||
b[max] = bounds.origin[max] - bounds.extents[max];
|
||||
Brush::VertexModeVertices vertexModeVertices;
|
||||
vertexModeVertices.push_back( Brush::VertexModeVertex( a, true ) );
|
||||
vertexModeVertices.push_back( Brush::VertexModeVertex( b, true ) );
|
||||
|
||||
UndoableCommand undo( "InsertBrushVertices" );
|
||||
Scene_forEachSelectedBrush( [&vertexModeVertices]( BrushInstance& brush ){ brush.insert_vertices( vertexModeVertices ); } );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template<typename Functor>
|
||||
class ComponentSelectionTestableVisibleSelectedVisitor : public SelectionSystem::Visitor
|
||||
{
|
||||
const Functor& m_functor;
|
||||
public:
|
||||
ComponentSelectionTestableVisibleSelectedVisitor( const Functor& functor ) : m_functor( functor ){
|
||||
}
|
||||
void visit( scene::Instance& instance ) const override {
|
||||
ComponentSelectionTestable* componentSelectionTestable = Instance_getComponentSelectionTestable( instance );
|
||||
if ( componentSelectionTestable != 0
|
||||
&& instance.path().top().get().visible() ) {
|
||||
m_functor( *componentSelectionTestable );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Functor>
|
||||
inline const Functor& Scene_forEachVisibleSelectedComponentSelectionTestable( const Functor& functor ){
|
||||
GlobalSelectionSystem().foreachSelected( ComponentSelectionTestableVisibleSelectedVisitor<Functor>( functor ) );
|
||||
return functor;
|
||||
}
|
||||
|
||||
|
||||
class ResizeTranslatable : public Translatable
|
||||
{
|
||||
void translate( const Vector3& translation ) override {
|
||||
Scene_Translate_Component_Selected( GlobalSceneGraph(), translation );
|
||||
}
|
||||
};
|
||||
|
||||
class DragManipulatorImpl final : public DragManipulator
|
||||
{
|
||||
ResizeTranslatable m_resize;
|
||||
TranslateFree m_freeResize;
|
||||
TranslateAxis2 m_axisResize;
|
||||
TranslateFreeXY_Z m_freeDragXY_Z;
|
||||
DragNewBrush m_dragNewBrush;
|
||||
DragExtrudeFaces m_dragExtrudeFaces;
|
||||
bool m_dragSelected; //drag selected primitives or components
|
||||
bool m_selected; //components selected temporally for drag
|
||||
bool m_selected2; //planeselectables in cam with alt
|
||||
bool m_newBrush;
|
||||
bool m_extrudeFaces;
|
||||
public:
|
||||
DragManipulatorImpl( Translatable& translatable, AllTransformable& transformable ) :
|
||||
m_resize(), m_freeResize( m_resize ), m_axisResize( m_resize ), m_freeDragXY_Z( translatable, transformable ), m_renderCircle( 2 << 3 ){
|
||||
setSelected( false );
|
||||
draw_circle( m_renderCircle.m_vertices.size() >> 3, 5, m_renderCircle.m_vertices.data(), RemapXYZ() );
|
||||
}
|
||||
|
||||
Manipulatable* GetManipulatable() override {
|
||||
if( m_newBrush )
|
||||
return &m_dragNewBrush;
|
||||
else if( m_extrudeFaces )
|
||||
return &m_dragExtrudeFaces;
|
||||
else if( m_selected )
|
||||
return &m_freeResize;
|
||||
else if( m_selected2 )
|
||||
return &m_axisResize;
|
||||
else
|
||||
return &m_freeDragXY_Z;
|
||||
}
|
||||
|
||||
void testSelect( const View& view, const Matrix4& pivot2world ) override {
|
||||
SelectionPool selector;
|
||||
SelectionVolume test( view );
|
||||
|
||||
if( g_modifiers == ( c_modifierAlt | c_modifierControl )
|
||||
&& GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive
|
||||
&& ( GlobalSelectionSystem().countSelected() != 0 || !g_SelectedFaceInstances.empty() ) ){ // extrude
|
||||
m_extrudeFaces = Scene_forEachBrush_setupExtrude( test, m_dragExtrudeFaces );
|
||||
}
|
||||
else if( GlobalSelectionSystem().countSelected() != 0 ){
|
||||
if ( GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ){
|
||||
if( g_modifiers == c_modifierAlt ){
|
||||
if( view.fill() ){ // alt resize
|
||||
m_selected2 = Scene_forEachPlaneSelectable_selectPlanes2( test, m_axisResize );
|
||||
}
|
||||
else{ // alt vertices drag
|
||||
m_selected = selection_selectVerticesOrFaceVertices( test );
|
||||
}
|
||||
}
|
||||
else if( g_modifiers == c_modifierNone ){
|
||||
BooleanSelector booleanSelector;
|
||||
Scene_TestSelect_Primitive( booleanSelector, test, view );
|
||||
|
||||
if ( booleanSelector.isSelected() ) { /* hit a primitive */
|
||||
m_dragSelected = true; /* drag a primitive */
|
||||
test.BeginMesh( g_matrix4_identity, true );
|
||||
m_freeDragXY_Z.set0( vector4_projected( matrix4_transformed_vector4( test.getScreen2world(), Vector4( 0, 0, booleanSelector.bestIntersection().depth(), 1 ) ) ) );
|
||||
}
|
||||
else{ /* haven't hit a primitive */
|
||||
m_selected = Scene_forEachPlaneSelectable_selectPlanes( GlobalSceneGraph(), selector, test ); /* select faces on planeSelectables */
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( g_modifiers == c_modifierNone ){ // components
|
||||
BestSelector bestSelector;
|
||||
Scene_TestSelect_Component_Selected( bestSelector, test, view, GlobalSelectionSystem().ComponentMode() ); /* drag components */
|
||||
for ( Selectable* s : bestSelector.best() ){
|
||||
if ( !s->isSelected() )
|
||||
GlobalSelectionSystem().setSelectedAllComponents( false );
|
||||
selector.addSelectable( SelectionIntersection( 0, 0 ), s );
|
||||
m_dragSelected = true;
|
||||
}
|
||||
if( bestSelector.bestIntersection().valid() ){
|
||||
test.BeginMesh( g_matrix4_identity, true );
|
||||
m_freeDragXY_Z.set0( vector4_projected( matrix4_transformed_vector4( test.getScreen2world(), Vector4( 0, 0, bestSelector.bestIntersection().depth(), 1 ) ) ) );
|
||||
}
|
||||
else{
|
||||
if( GlobalSelectionSystem().countSelectedComponents() != 0 ){ /* drag, even if hit nothing, but got selected */
|
||||
m_dragSelected = true;
|
||||
m_freeDragXY_Z.set0( g_vector3_identity );
|
||||
}
|
||||
else if( GlobalSelectionSystem().ComponentMode() == SelectionSystem::eVertex ){ /* otherwise insert */
|
||||
m_dragSelected = g_bTmpComponentMode = scene_insert_brush_vertices( view, m_freeDragXY_Z ); //hack: indicating not a tmp mode
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( SelectableSortedSet::value_type& value : selector )
|
||||
value.second->setSelected( true );
|
||||
g_bTmpComponentMode = m_selected | m_selected2;
|
||||
}
|
||||
else if( GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive && g_3DCreateBrushes && g_modifiers == c_modifierNone ){
|
||||
m_newBrush = true;
|
||||
BestPointSelector bestPointSelector;
|
||||
Scene_TestSelect_Primitive( bestPointSelector, test, view );
|
||||
Vector3 start;
|
||||
test.BeginMesh( g_matrix4_identity, true );
|
||||
if( bestPointSelector.isSelected() ){
|
||||
start = vector4_projected( matrix4_transformed_vector4( test.getScreen2world(), Vector4( 0, 0, bestPointSelector.best().depth(), 1 ) ) );
|
||||
}
|
||||
else{
|
||||
const Vector3 pnear = vector4_projected( matrix4_transformed_vector4( test.getScreen2world(), Vector4( 0, 0, -1, 1 ) ) );
|
||||
const Vector3 pfar = vector4_projected( matrix4_transformed_vector4( test.getScreen2world(), Vector4( 0, 0, 1, 1 ) ) );
|
||||
start = vector3_normalised( pfar - pnear ) * ( 256.f + GetGridSize() * sqrt( 3.0 ) ) + pnear;
|
||||
}
|
||||
vector3_snap( start, GetSnapGridSize() );
|
||||
m_dragNewBrush.set0( start );
|
||||
}
|
||||
}
|
||||
|
||||
void setSelected( bool select ) override {
|
||||
m_dragSelected = select;
|
||||
m_selected = select;
|
||||
m_selected2 = select;
|
||||
m_newBrush = select;
|
||||
m_extrudeFaces = select;
|
||||
}
|
||||
bool isSelected() const override {
|
||||
return m_dragSelected || m_selected || m_selected2 || m_newBrush || m_extrudeFaces;
|
||||
}
|
||||
|
||||
void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ) override {
|
||||
if( !m_polygons.empty() ){
|
||||
renderer.SetState( m_state_wire, Renderer::eWireframeOnly );
|
||||
renderer.SetState( m_state_wire, Renderer::eFullMaterials );
|
||||
if( m_polygons.back().size() == 1 ){
|
||||
Pivot2World_viewplaneSpace( m_renderCircle.m_viewplaneSpace, matrix4_translation_for_vec3( m_polygons.back()[0] ), volume.GetModelview(), volume.GetProjection(), volume.GetViewport() );
|
||||
renderer.addRenderable( m_renderCircle, m_renderCircle.m_viewplaneSpace );
|
||||
}
|
||||
else{
|
||||
renderer.addRenderable( m_renderPoly, g_matrix4_identity );
|
||||
}
|
||||
}
|
||||
}
|
||||
void highlight( const View& view, const Matrix4& pivot2world ) override {
|
||||
SelectionVolume test( view );
|
||||
std::vector<std::vector<Vector3>> polygons;
|
||||
/* conditions structure respects one in testSelect() */
|
||||
if( g_modifiers == ( c_modifierAlt | c_modifierControl )
|
||||
&& GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive
|
||||
&& ( GlobalSelectionSystem().countSelected() != 0 || !g_SelectedFaceInstances.empty() ) ){ // extrude
|
||||
if( const auto planeData = Scene_forEachBrush_bestPlane( test ); planeData.valid() ){
|
||||
auto gatherPolygonsByPlane = [plane = planeData.m_plane, &polygons]( BrushInstance& brushInstance ){
|
||||
if( brushInstance.isSelected() || brushInstance.isSelectedComponents() )
|
||||
brushInstance.gatherPolygonsByPlane( plane, polygons, false );
|
||||
};
|
||||
Scene_forEachVisibleBrush( GlobalSceneGraph(), gatherPolygonsByPlane );
|
||||
}
|
||||
}
|
||||
else if( GlobalSelectionSystem().countSelected() != 0 ){
|
||||
if ( GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ){
|
||||
if( g_modifiers == c_modifierAlt ){
|
||||
if( view.fill() ){ // alt resize
|
||||
if( const auto planeData = Scene_forEachPlaneSelectable_bestPlane( test ); planeData.valid() ){
|
||||
auto gatherPolygonsByPlane = [plane = planeData.m_plane, &polygons]( PlaneSelectable& planeSelectable ){
|
||||
planeSelectable.gatherPolygonsByPlane( plane, polygons );
|
||||
};
|
||||
Scene_forEachVisibleSelectedPlaneselectable( gatherPolygonsByPlane );
|
||||
}
|
||||
}
|
||||
else{ // alt vertices drag
|
||||
SelectionIntersection intersection;
|
||||
const SelectionSystem::EComponentMode mode = SelectionSystem::eVertex;
|
||||
auto gatherComponentsHighlight = [&polygons, &intersection, &test, mode]( const ComponentSelectionTestable& componentSelectionTestable ){
|
||||
componentSelectionTestable.gatherComponentsHighlight( polygons, intersection, test, mode );
|
||||
};
|
||||
Scene_forEachVisibleSelectedComponentSelectionTestable( gatherComponentsHighlight );
|
||||
|
||||
if( polygons.empty() ){
|
||||
if( const auto planeData = Scene_forEachSelectedBrush_bestPlane( test ); planeData.valid() ){
|
||||
auto gatherPolygonsByPlane = [plane = planeData.m_plane, &polygons]( BrushInstance& brushInstance ){
|
||||
brushInstance.gatherPolygonsByPlane( plane, polygons );
|
||||
};
|
||||
Scene_forEachVisibleSelectedBrush( gatherPolygonsByPlane );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( g_modifiers == c_modifierNone // components
|
||||
|| g_modifiers == c_modifierShift // hack: these respect to the RadiantSelectionSystem::SelectPoint
|
||||
|| ( g_modifiers == c_modifierControl && GlobalSelectionSystem().ComponentMode() == SelectionSystem::EComponentMode::eFace ) ){
|
||||
SelectionIntersection intersection;
|
||||
const SelectionSystem::EComponentMode mode = GlobalSelectionSystem().ComponentMode();
|
||||
auto gatherComponentsHighlight = [&polygons, &intersection, &test, mode]( const ComponentSelectionTestable& componentSelectionTestable ){
|
||||
componentSelectionTestable.gatherComponentsHighlight( polygons, intersection, test, mode );
|
||||
};
|
||||
Scene_forEachVisibleSelectedComponentSelectionTestable( gatherComponentsHighlight );
|
||||
}
|
||||
}
|
||||
|
||||
if( m_polygons != polygons ){
|
||||
m_polygons.swap( polygons );
|
||||
SceneChangeNotify();
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::vector<std::vector<Vector3>> m_polygons;
|
||||
struct RenderablePoly: public OpenGLRenderable
|
||||
{
|
||||
const std::vector<std::vector<Vector3>>& m_polygons;
|
||||
|
||||
RenderablePoly( const std::vector<std::vector<Vector3>>& polygons ) : m_polygons( polygons ){
|
||||
}
|
||||
void render( RenderStateFlags state ) const override {
|
||||
gl().glPolygonOffset( -2, -2 );
|
||||
for( const auto& poly : m_polygons ){
|
||||
gl().glVertexPointer( 3, GL_FLOAT, sizeof( m_polygons[0][0] ), poly[0].data() );
|
||||
gl().glDrawArrays( GL_POLYGON, 0, GLsizei( poly.size() ) );
|
||||
}
|
||||
gl().glPolygonOffset( -1, 1 ); // restore default
|
||||
}
|
||||
};
|
||||
RenderablePoly m_renderPoly{ m_polygons };
|
||||
struct RenderableCircle : public OpenGLRenderable
|
||||
{
|
||||
Array<PointVertex> m_vertices;
|
||||
Matrix4 m_viewplaneSpace;
|
||||
|
||||
RenderableCircle( std::size_t size ) : m_vertices( size ){
|
||||
}
|
||||
void render( RenderStateFlags state ) const override {
|
||||
gl().glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_vertices.data()->vertex );
|
||||
gl().glDrawArrays( GL_LINE_LOOP, 0, GLsizei( m_vertices.size() ) );
|
||||
}
|
||||
};
|
||||
RenderableCircle m_renderCircle;
|
||||
};
|
||||
|
||||
|
||||
DragManipulator* New_DragManipulator( Translatable& translatable, AllTransformable& transformable ){
|
||||
return new DragManipulatorImpl( translatable, transformable );
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selection_.h"
|
||||
|
||||
class DragManipulator : public Manipulator
|
||||
{
|
||||
public:
|
||||
inline static Shader* m_state_wire;
|
||||
};
|
||||
|
||||
DragManipulator* New_DragManipulator( Translatable& translatable, AllTransformable& transformable );
|
||||
|
||||
inline bool g_bTmpComponentMode = false;
|
||||
inline bool g_3DCreateBrushes = true;
|
||||
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "selection_mtor_rotate.h"
|
||||
|
||||
#include "selection_.h"
|
||||
#include "selection_render.h"
|
||||
#include "selection_volume.h"
|
||||
#include "selection_selector.h"
|
||||
#include "selection_mtable_rotate.h"
|
||||
#include "selectionlib.h"
|
||||
|
||||
template<typename remap_policy>
|
||||
void draw_semicircle( const std::size_t segments, const float radius, PointVertex* vertices, remap_policy remap ){
|
||||
const double increment = c_pi / double( segments << 2 );
|
||||
|
||||
std::size_t count = 0;
|
||||
float x = radius;
|
||||
float y = 0;
|
||||
remap_policy::set( vertices[segments << 2].vertex, -radius, 0, 0 );
|
||||
while ( count < segments )
|
||||
{
|
||||
PointVertex* i = vertices + count;
|
||||
PointVertex* j = vertices + ( ( segments << 1 ) - ( count + 1 ) );
|
||||
|
||||
PointVertex* k = i + ( segments << 1 );
|
||||
PointVertex* l = j + ( segments << 1 );
|
||||
|
||||
#if 0
|
||||
PointVertex* m = i + ( segments << 2 );
|
||||
PointVertex* n = j + ( segments << 2 );
|
||||
PointVertex* o = k + ( segments << 2 );
|
||||
PointVertex* p = l + ( segments << 2 );
|
||||
#endif
|
||||
|
||||
remap_policy::set( i->vertex, x,-y, 0 );
|
||||
remap_policy::set( k->vertex,-y,-x, 0 );
|
||||
#if 0
|
||||
remap_policy::set( m->vertex,-x, y, 0 );
|
||||
remap_policy::set( o->vertex, y, x, 0 );
|
||||
#endif
|
||||
|
||||
++count;
|
||||
|
||||
{
|
||||
const double theta = increment * count;
|
||||
x = radius * cos( theta );
|
||||
y = radius * sin( theta );
|
||||
}
|
||||
|
||||
remap_policy::set( j->vertex, y,-x, 0 );
|
||||
remap_policy::set( l->vertex,-x,-y, 0 );
|
||||
#if 0
|
||||
remap_policy::set( n->vertex,-y, x, 0 );
|
||||
remap_policy::set( p->vertex, x, y, 0 );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline Vector3 normalised_safe( const Vector3& self ){
|
||||
if ( vector3_equal( self, g_vector3_identity ) ) {
|
||||
return g_vector3_identity;
|
||||
}
|
||||
return vector3_normalised( self );
|
||||
}
|
||||
|
||||
|
||||
|
||||
class RotateManipulatorImpl final : public RotateManipulator, public ManipulatorSelectionChangeable
|
||||
{
|
||||
RotateFree m_free;
|
||||
RotateAxis m_axis;
|
||||
Vector3 m_axis_screen;
|
||||
RenderableSemiCircle m_circle_x;
|
||||
RenderableSemiCircle m_circle_y;
|
||||
RenderableSemiCircle m_circle_z;
|
||||
RenderableCircle m_circle_screen;
|
||||
RenderableCircle m_circle_sphere;
|
||||
SelectableBool m_selectable_x;
|
||||
SelectableBool m_selectable_y;
|
||||
SelectableBool m_selectable_z;
|
||||
SelectableBool m_selectable_screen;
|
||||
SelectableBool m_selectable_sphere;
|
||||
Pivot2World m_pivot;
|
||||
Matrix4 m_local2world_x;
|
||||
Matrix4 m_local2world_y;
|
||||
Matrix4 m_local2world_z;
|
||||
bool m_circle_x_visible;
|
||||
bool m_circle_y_visible;
|
||||
bool m_circle_z_visible;
|
||||
public:
|
||||
RotateManipulatorImpl( Rotatable& rotatable, std::size_t segments, float radius ) :
|
||||
m_free( rotatable ),
|
||||
m_axis( rotatable ),
|
||||
m_circle_x( ( segments << 2 ) + 1 ),
|
||||
m_circle_y( ( segments << 2 ) + 1 ),
|
||||
m_circle_z( ( segments << 2 ) + 1 ),
|
||||
m_circle_screen( segments << 3 ),
|
||||
m_circle_sphere( segments << 3 ){
|
||||
draw_semicircle( segments, radius, m_circle_x.m_vertices.data(), RemapYZX() );
|
||||
draw_semicircle( segments, radius, m_circle_y.m_vertices.data(), RemapZXY() );
|
||||
draw_semicircle( segments, radius, m_circle_z.m_vertices.data(), RemapXYZ() );
|
||||
|
||||
draw_circle( segments, radius * 1.15f, m_circle_screen.m_vertices.data(), RemapXYZ() );
|
||||
draw_circle( segments, radius, m_circle_sphere.m_vertices.data(), RemapXYZ() );
|
||||
}
|
||||
|
||||
|
||||
void UpdateColours(){
|
||||
m_circle_x.setColour( colourSelected( g_colour_x, m_selectable_x.isSelected() ) );
|
||||
m_circle_y.setColour( colourSelected( g_colour_y, m_selectable_y.isSelected() ) );
|
||||
m_circle_z.setColour( colourSelected( g_colour_z, m_selectable_z.isSelected() ) );
|
||||
m_circle_screen.setColour( colourSelected( g_colour_screen, m_selectable_screen.isSelected() ) );
|
||||
m_circle_sphere.setColour( colourSelected( g_colour_sphere, false ) );
|
||||
}
|
||||
|
||||
void updateCircleTransforms(){
|
||||
Vector3 localViewpoint( matrix4_transformed_direction( matrix4_transposed( m_pivot.m_worldSpace ), m_pivot.m_viewpointSpace.z().vec3() ) );
|
||||
|
||||
m_circle_x_visible = !vector3_equal_epsilon( g_vector3_axis_x, localViewpoint, 1e-6f );
|
||||
if ( m_circle_x_visible ) {
|
||||
m_local2world_x = g_matrix4_identity;
|
||||
m_local2world_x.y().vec3() = normalised_safe(
|
||||
vector3_cross( g_vector3_axis_x, localViewpoint )
|
||||
);
|
||||
m_local2world_x.z().vec3() = normalised_safe(
|
||||
vector3_cross( m_local2world_x.x().vec3(), m_local2world_x.y().vec3() )
|
||||
);
|
||||
matrix4_premultiply_by_matrix4( m_local2world_x, m_pivot.m_worldSpace );
|
||||
}
|
||||
|
||||
m_circle_y_visible = !vector3_equal_epsilon( g_vector3_axis_y, localViewpoint, 1e-6f );
|
||||
if ( m_circle_y_visible ) {
|
||||
m_local2world_y = g_matrix4_identity;
|
||||
m_local2world_y.z().vec3() = normalised_safe(
|
||||
vector3_cross( g_vector3_axis_y, localViewpoint )
|
||||
);
|
||||
m_local2world_y.x().vec3() = normalised_safe(
|
||||
vector3_cross( m_local2world_y.y().vec3(), m_local2world_y.z().vec3() )
|
||||
);
|
||||
matrix4_premultiply_by_matrix4( m_local2world_y, m_pivot.m_worldSpace );
|
||||
}
|
||||
|
||||
m_circle_z_visible = !vector3_equal_epsilon( g_vector3_axis_z, localViewpoint, 1e-6f );
|
||||
if ( m_circle_z_visible ) {
|
||||
m_local2world_z = g_matrix4_identity;
|
||||
m_local2world_z.x().vec3() = normalised_safe(
|
||||
vector3_cross( g_vector3_axis_z, localViewpoint )
|
||||
);
|
||||
m_local2world_z.y().vec3() = normalised_safe(
|
||||
vector3_cross( m_local2world_z.z().vec3(), m_local2world_z.x().vec3() )
|
||||
);
|
||||
matrix4_premultiply_by_matrix4( m_local2world_z, m_pivot.m_worldSpace );
|
||||
}
|
||||
}
|
||||
|
||||
void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ) override {
|
||||
m_pivot.update( pivot2world, volume.GetModelview(), volume.GetProjection(), volume.GetViewport() );
|
||||
updateCircleTransforms();
|
||||
|
||||
// temp hack
|
||||
UpdateColours();
|
||||
|
||||
renderer.SetState( m_state_outer, Renderer::eWireframeOnly );
|
||||
renderer.SetState( m_state_outer, Renderer::eFullMaterials );
|
||||
|
||||
renderer.addRenderable( m_circle_screen, m_pivot.m_viewpointSpace );
|
||||
renderer.addRenderable( m_circle_sphere, m_pivot.m_viewpointSpace );
|
||||
|
||||
if ( m_circle_x_visible ) {
|
||||
renderer.addRenderable( m_circle_x, m_local2world_x );
|
||||
}
|
||||
if ( m_circle_y_visible ) {
|
||||
renderer.addRenderable( m_circle_y, m_local2world_y );
|
||||
}
|
||||
if ( m_circle_z_visible ) {
|
||||
renderer.addRenderable( m_circle_z, m_local2world_z );
|
||||
}
|
||||
}
|
||||
void testSelect( const View& view, const Matrix4& pivot2world ) override {
|
||||
if( g_modifiers != c_modifierNone )
|
||||
return selectionChange( nullptr );
|
||||
|
||||
m_pivot.update( pivot2world, view.GetModelview(), view.GetProjection(), view.GetViewport() );
|
||||
updateCircleTransforms();
|
||||
|
||||
SelectionPool selector;
|
||||
|
||||
{
|
||||
{
|
||||
const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_local2world_x ) );
|
||||
|
||||
#if defined( DEBUG_SELECTION )
|
||||
g_render_clipped.construct( view.GetViewMatrix() );
|
||||
#endif
|
||||
|
||||
SelectionIntersection best;
|
||||
LineStrip_BestPoint( local2view, m_circle_x.m_vertices.data(), m_circle_x.m_vertices.size(), best );
|
||||
selector.addSelectable( best, &m_selectable_x );
|
||||
}
|
||||
|
||||
{
|
||||
const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_local2world_y ) );
|
||||
|
||||
#if defined( DEBUG_SELECTION )
|
||||
g_render_clipped.construct( view.GetViewMatrix() );
|
||||
#endif
|
||||
|
||||
SelectionIntersection best;
|
||||
LineStrip_BestPoint( local2view, m_circle_y.m_vertices.data(), m_circle_y.m_vertices.size(), best );
|
||||
selector.addSelectable( best, &m_selectable_y );
|
||||
}
|
||||
|
||||
{
|
||||
const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_local2world_z ) );
|
||||
|
||||
#if defined( DEBUG_SELECTION )
|
||||
g_render_clipped.construct( view.GetViewMatrix() );
|
||||
#endif
|
||||
|
||||
SelectionIntersection best;
|
||||
LineStrip_BestPoint( local2view, m_circle_z.m_vertices.data(), m_circle_z.m_vertices.size(), best );
|
||||
selector.addSelectable( best, &m_selectable_z );
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_pivot.m_viewpointSpace ) );
|
||||
|
||||
{
|
||||
SelectionIntersection best;
|
||||
LineLoop_BestPoint( local2view, m_circle_screen.m_vertices.data(), m_circle_screen.m_vertices.size(), best );
|
||||
selector.addSelectable( best, &m_selectable_screen );
|
||||
}
|
||||
|
||||
// {
|
||||
// SelectionIntersection best;
|
||||
// Circle_BestPoint( local2view, EClipCull::CW, m_circle_sphere.m_vertices.data(), m_circle_sphere.m_vertices.size(), best );
|
||||
// selector.addSelectable( best, &m_selectable_sphere );
|
||||
// }
|
||||
}
|
||||
|
||||
m_axis_screen = m_pivot.m_axis_screen;
|
||||
|
||||
if ( selector.failed() )
|
||||
selector.addSelectable( SelectionIntersection( 0, 0 ), &m_selectable_sphere );
|
||||
|
||||
selectionChange( selector );
|
||||
}
|
||||
|
||||
Manipulatable* GetManipulatable() override {
|
||||
if ( m_selectable_x.isSelected() ) {
|
||||
m_axis.SetAxis( g_vector3_axis_x );
|
||||
return &m_axis;
|
||||
}
|
||||
else if ( m_selectable_y.isSelected() ) {
|
||||
m_axis.SetAxis( g_vector3_axis_y );
|
||||
return &m_axis;
|
||||
}
|
||||
else if ( m_selectable_z.isSelected() ) {
|
||||
m_axis.SetAxis( g_vector3_axis_z );
|
||||
return &m_axis;
|
||||
}
|
||||
else if ( m_selectable_screen.isSelected() ) {
|
||||
m_axis.SetAxis( m_axis_screen );
|
||||
return &m_axis;
|
||||
}
|
||||
else{
|
||||
return &m_free;
|
||||
}
|
||||
}
|
||||
|
||||
void setSelected( bool select ) override {
|
||||
m_selectable_x.setSelected( select );
|
||||
m_selectable_y.setSelected( select );
|
||||
m_selectable_z.setSelected( select );
|
||||
m_selectable_screen.setSelected( select );
|
||||
m_selectable_sphere.setSelected( select );
|
||||
}
|
||||
bool isSelected() const override {
|
||||
return m_selectable_x.isSelected()
|
||||
|| m_selectable_y.isSelected()
|
||||
|| m_selectable_z.isSelected()
|
||||
|| m_selectable_screen.isSelected()
|
||||
|| m_selectable_sphere.isSelected();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
RotateManipulator* New_RotateManipulator( Rotatable& rotatable, std::size_t segments, float radius ){
|
||||
return new RotateManipulatorImpl( rotatable, segments, radius );
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selection_.h"
|
||||
|
||||
class RotateManipulator : public Manipulator
|
||||
{
|
||||
public:
|
||||
inline static Shader* m_state_outer;
|
||||
};
|
||||
|
||||
RotateManipulator* New_RotateManipulator( Rotatable& rotatable, std::size_t segments, float radius );
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "selection_mtor_scale.h"
|
||||
|
||||
#include "selection_.h"
|
||||
#include "selection_render.h"
|
||||
#include "selection_volume.h"
|
||||
#include "selection_selector.h"
|
||||
#include "selection_mtable_scale.h"
|
||||
#include "selectionlib.h"
|
||||
|
||||
class ScaleManipulatorImpl final : public ScaleManipulator, public ManipulatorSelectionChangeable
|
||||
{
|
||||
ScaleFree m_free;
|
||||
ScaleAxis m_axis;
|
||||
RenderableLine m_arrow_x;
|
||||
RenderableLine m_arrow_y;
|
||||
RenderableLine m_arrow_z;
|
||||
RenderableQuad m_quad_screen;
|
||||
SelectableBool m_selectable_x;
|
||||
SelectableBool m_selectable_y;
|
||||
SelectableBool m_selectable_z;
|
||||
SelectableBool m_selectable_screen;
|
||||
Pivot2World m_pivot;
|
||||
public:
|
||||
ScaleManipulatorImpl( Scalable& scalable, std::size_t segments, float length ) :
|
||||
m_free( scalable ),
|
||||
m_axis( scalable ){
|
||||
draw_arrowline( length, m_arrow_x.m_line, 0 );
|
||||
draw_arrowline( length, m_arrow_y.m_line, 1 );
|
||||
draw_arrowline( length, m_arrow_z.m_line, 2 );
|
||||
|
||||
draw_quad( 16, m_quad_screen.m_quad );
|
||||
}
|
||||
|
||||
void UpdateColours(){
|
||||
m_arrow_x.setColour( colourSelected( g_colour_x, m_selectable_x.isSelected() ) );
|
||||
m_arrow_y.setColour( colourSelected( g_colour_y, m_selectable_y.isSelected() ) );
|
||||
m_arrow_z.setColour( colourSelected( g_colour_z, m_selectable_z.isSelected() ) );
|
||||
m_quad_screen.setColour( colourSelected( g_colour_screen, m_selectable_screen.isSelected() ) );
|
||||
}
|
||||
|
||||
void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ) override {
|
||||
m_pivot.update( pivot2world, volume.GetModelview(), volume.GetProjection(), volume.GetViewport() );
|
||||
|
||||
// temp hack
|
||||
UpdateColours();
|
||||
|
||||
renderer.addRenderable( m_arrow_x, m_pivot.m_worldSpace );
|
||||
renderer.addRenderable( m_arrow_y, m_pivot.m_worldSpace );
|
||||
renderer.addRenderable( m_arrow_z, m_pivot.m_worldSpace );
|
||||
|
||||
renderer.addRenderable( m_quad_screen, m_pivot.m_viewpointSpace );
|
||||
}
|
||||
void testSelect( const View& view, const Matrix4& pivot2world ) override {
|
||||
if( g_modifiers != c_modifierNone )
|
||||
return selectionChange( nullptr );
|
||||
|
||||
m_pivot.update( pivot2world, view.GetModelview(), view.GetProjection(), view.GetViewport() );
|
||||
|
||||
SelectionPool selector;
|
||||
|
||||
{
|
||||
const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_pivot.m_worldSpace ) );
|
||||
|
||||
#if defined( DEBUG_SELECTION )
|
||||
g_render_clipped.construct( view.GetViewMatrix() );
|
||||
#endif
|
||||
|
||||
{
|
||||
SelectionIntersection best;
|
||||
Line_BestPoint( local2view, m_arrow_x.m_line, best );
|
||||
selector.addSelectable( best, &m_selectable_x );
|
||||
}
|
||||
|
||||
{
|
||||
SelectionIntersection best;
|
||||
Line_BestPoint( local2view, m_arrow_y.m_line, best );
|
||||
selector.addSelectable( best, &m_selectable_y );
|
||||
}
|
||||
|
||||
{
|
||||
SelectionIntersection best;
|
||||
Line_BestPoint( local2view, m_arrow_z.m_line, best );
|
||||
selector.addSelectable( best, &m_selectable_z );
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_pivot.m_viewpointSpace ) );
|
||||
|
||||
{
|
||||
SelectionIntersection best;
|
||||
Quad_BestPoint( local2view, EClipCull::CW, m_quad_screen.m_quad, best );
|
||||
selector.addSelectable( best, &m_selectable_screen );
|
||||
}
|
||||
}
|
||||
|
||||
selectionChange( selector );
|
||||
}
|
||||
|
||||
Manipulatable* GetManipulatable() override {
|
||||
if ( m_selectable_x.isSelected() ) {
|
||||
m_axis.SetAxis( g_vector3_axis_x );
|
||||
return &m_axis;
|
||||
}
|
||||
else if ( m_selectable_y.isSelected() ) {
|
||||
m_axis.SetAxis( g_vector3_axis_y );
|
||||
return &m_axis;
|
||||
}
|
||||
else if ( m_selectable_z.isSelected() ) {
|
||||
m_axis.SetAxis( g_vector3_axis_z );
|
||||
return &m_axis;
|
||||
}
|
||||
else{
|
||||
m_free.SetAxes( g_vector3_identity, g_vector3_identity );
|
||||
return &m_free;
|
||||
}
|
||||
}
|
||||
|
||||
void setSelected( bool select ) override {
|
||||
m_selectable_x.setSelected( select );
|
||||
m_selectable_y.setSelected( select );
|
||||
m_selectable_z.setSelected( select );
|
||||
m_selectable_screen.setSelected( select );
|
||||
}
|
||||
bool isSelected() const override {
|
||||
return m_selectable_x.isSelected()
|
||||
|| m_selectable_y.isSelected()
|
||||
|| m_selectable_z.isSelected()
|
||||
|| m_selectable_screen.isSelected();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
ScaleManipulator* New_ScaleManipulator( Scalable& scalable, std::size_t segments, float length ){
|
||||
return new ScaleManipulatorImpl( scalable, segments, length );
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selection_.h"
|
||||
|
||||
class ScaleManipulator : public Manipulator
|
||||
{
|
||||
};
|
||||
|
||||
ScaleManipulator* New_ScaleManipulator( Scalable& scalable, std::size_t segments, float length );
|
||||
@@ -0,0 +1,392 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "selection_mtor_skew.h"
|
||||
|
||||
#include "selection_.h"
|
||||
#include "dragplanes.h"
|
||||
#include "selection_render.h"
|
||||
#include "selection_volume.h"
|
||||
#include "selection_selector.h"
|
||||
#include "selection_mtable_translate.h"
|
||||
#include "selection_mtable_rotate.h"
|
||||
#include "selection_mtable_scale.h"
|
||||
#include "selection_mtable_skew.h"
|
||||
|
||||
class SkewManipulatorImpl final : public SkewManipulator, public ManipulatorSelectionChangeable
|
||||
{
|
||||
SkewAxis m_skew;
|
||||
TranslateFreeXY_Z m_translateFreeXY_Z;
|
||||
ScaleAxis m_scaleAxis;
|
||||
ScaleFree m_scaleFree;
|
||||
RotateAxis m_rotateAxis;
|
||||
AABB m_bounds_draw;
|
||||
const AABB& m_bounds;
|
||||
Matrix4& m_pivot2world;
|
||||
const bool& m_pivotIsCustom;
|
||||
/*
|
||||
RenderableLine m_lineXy_;
|
||||
RenderableLine m_lineXy;
|
||||
RenderableLine m_lineXz_;
|
||||
RenderableLine m_lineXz;
|
||||
RenderableLine m_lineYz_;
|
||||
RenderableLine m_lineYz;
|
||||
RenderableLine m_lineYx_;
|
||||
RenderableLine m_lineYx;
|
||||
RenderableLine m_lineZx_;
|
||||
RenderableLine m_lineZx;
|
||||
RenderableLine m_lineZy_;
|
||||
RenderableLine m_lineZy;
|
||||
*/
|
||||
RenderableLine m_lines[3][2][2];
|
||||
SelectableBool m_selectables[3][2][2]; //[X][YZ][-+]
|
||||
SelectableBool m_selectable_translateFree;
|
||||
DragPlanes m_selectables_scale; //+-X+-Y+-Z
|
||||
SelectableBool m_selectables_rotate[3][2][2]; //[X][-+Y][-+Z]
|
||||
Pivot2World m_pivot;
|
||||
Matrix4 m_worldSpace;
|
||||
RenderableArrowHead m_arrow;
|
||||
Matrix4 m_arrow_modelview;
|
||||
Matrix4 m_arrow_modelview2;
|
||||
RenderablePoint m_point;
|
||||
public:
|
||||
SkewManipulatorImpl( Skewable& skewable, Translatable& translatable, Scalable& scalable, Rotatable& rotatable,
|
||||
AllTransformable& transformable, const AABB& bounds, Matrix4& pivot2world, const bool& pivotIsCustom, const std::size_t segments = 2 ) :
|
||||
m_skew( skewable ),
|
||||
m_translateFreeXY_Z( translatable, transformable ),
|
||||
m_scaleAxis( scalable ),
|
||||
m_scaleFree( scalable ),
|
||||
m_rotateAxis( rotatable ),
|
||||
m_bounds( bounds ),
|
||||
m_pivot2world( pivot2world ),
|
||||
m_pivotIsCustom( pivotIsCustom ),
|
||||
m_selectables_scale( {} ),
|
||||
m_arrow( 3 * 2 * ( segments << 3 ) ) {
|
||||
for ( int i = 0; i < 3; ++i ){
|
||||
for ( int j = 0; j < 2; ++j ){
|
||||
const int x = i;
|
||||
const int y = ( i + j + 1 ) % 3;
|
||||
Vertex3f& xy_ = m_lines[i][j][0].m_line[0].vertex;
|
||||
Vertex3f& x_y_ = m_lines[i][j][0].m_line[1].vertex;
|
||||
Vertex3f& xy = m_lines[i][j][1].m_line[0].vertex;
|
||||
Vertex3f& x_y = m_lines[i][j][1].m_line[1].vertex;
|
||||
xy = x_y = xy_ = x_y_ = vertex3f_identity;
|
||||
xy[x] = xy_[x] = 1;
|
||||
x_y[x] = x_y_[x] = -1;
|
||||
xy[y] = x_y[y] = 1;
|
||||
xy_[y] = x_y_[y] = -1;
|
||||
}
|
||||
}
|
||||
draw_arrowhead( segments, 0, m_arrow.m_vertices.data(), TripleRemapXYZ<Vertex3f>(), TripleRemapXYZ<Normal3f>() );
|
||||
m_arrow.setColour( g_colour_selected );
|
||||
m_point.setColour( g_colour_selected );
|
||||
}
|
||||
|
||||
void UpdateColours() {
|
||||
for ( int i = 0; i < 3; ++i )
|
||||
for ( int j = 0; j < 2; ++j )
|
||||
for ( int k = 0; k < 2; ++k )
|
||||
m_lines[i][j][k].setColour( colourSelected( g_colour_screen, m_selectables[i][j][k].isSelected() ) );
|
||||
for ( int i = 0; i < 3; ++i )
|
||||
for ( int j = 0; j < 2; ++j )
|
||||
if( m_selectables_scale.getSelectables()[i * 2 + j].isSelected() ){
|
||||
m_lines[( i + 1 ) % 3][1][j ^ 1].setColour( g_colour_selected );
|
||||
m_lines[( i + 2 ) % 3][0][j ^ 1].setColour( g_colour_selected );
|
||||
}
|
||||
}
|
||||
|
||||
void updateModelview( const VolumeTest& volume, const Matrix4& pivot2world ){
|
||||
//m_pivot.update( pivot2world, volume.GetModelview(), volume.GetProjection(), volume.GetViewport() );
|
||||
//m_pivot.update( matrix4_translation_for_vec3( matrix4_get_translation_vec3( pivot2world ) ), volume.GetModelview(), volume.GetProjection(), volume.GetViewport() );
|
||||
m_pivot.update( matrix4_translation_for_vec3( m_bounds.origin ), volume.GetModelview(), volume.GetProjection(), volume.GetViewport() );
|
||||
//m_pivot.update( g_matrix4_identity, volume.GetModelview(), volume.GetProjection(), volume.GetViewport() ); //no shaking in cam due to low precision this way; smooth and sometimes very incorrect result
|
||||
// globalOutputStream() << m_pivot.m_worldSpace << '\n';
|
||||
Matrix4& m = m_pivot.m_worldSpace; /* go affine to increase precision */
|
||||
m[1] = m[2] = m[3] = m[4] = m[6] = m[7] = m[8] = m[9] = m[11] = 0;
|
||||
m[15] = 1;
|
||||
m_bounds_draw = aabb_for_oriented_aabb( m_bounds, matrix4_affine_inverse( m_pivot.m_worldSpace ) ); //screen scale
|
||||
for ( int i = 0; i < 3; ++i ){
|
||||
if( m_bounds_draw.extents[i] < 16 )
|
||||
m_bounds_draw.extents[i] = 18;
|
||||
else
|
||||
m_bounds_draw.extents[i] += 2.0f;
|
||||
}
|
||||
m_bounds_draw = aabb_for_oriented_aabb( m_bounds_draw, m_pivot.m_worldSpace ); //world scale
|
||||
m_bounds_draw.origin = m_bounds.origin;
|
||||
|
||||
m_worldSpace = matrix4_multiplied_by_matrix4( matrix4_translation_for_vec3( m_bounds_draw.origin ), matrix4_scale_for_vec3( m_bounds_draw.extents ) );
|
||||
matrix4_premultiply_by_matrix4( m_worldSpace, matrix4_translation_for_vec3( -matrix4_get_translation_vec3( pivot2world ) ) );
|
||||
matrix4_premultiply_by_matrix4( m_worldSpace, pivot2world );
|
||||
|
||||
// globalOutputStream() << m_worldSpace << '\n';
|
||||
// globalOutputStream() << pivot2world << '\n';
|
||||
}
|
||||
|
||||
void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ) override {
|
||||
updateModelview( volume, pivot2world );
|
||||
|
||||
// temp hack
|
||||
UpdateColours();
|
||||
|
||||
renderer.SetState( m_state_wire, Renderer::eWireframeOnly );
|
||||
renderer.SetState( m_state_wire, Renderer::eFullMaterials );
|
||||
|
||||
for ( int i = 0; i < 3; ++i )
|
||||
for ( int j = 0; j < 2; ++j ){
|
||||
#if 0
|
||||
const Vector3 dir = ( m_lines[i][j][0].m_line[0].vertex - m_lines[i][j][1].m_line[0].vertex ) / 2;
|
||||
const float dot = vector3_dot( dir, m_pivot.m_axis_screen );
|
||||
if( dot > 0.9999f )
|
||||
renderer.addRenderable( m_lines[i][j][0], m_worldSpace );
|
||||
else if( dot < -0.9999f )
|
||||
renderer.addRenderable( m_lines[i][j][1], m_worldSpace );
|
||||
else{
|
||||
renderer.addRenderable( m_lines[i][j][0], m_worldSpace );
|
||||
renderer.addRenderable( m_lines[i][j][1], m_worldSpace );
|
||||
}
|
||||
#else
|
||||
if( m_selectables[i][j][0].isSelected() ){ /* add selected last to get highlighted one rendered on top in 2d */
|
||||
renderer.addRenderable( m_lines[i][j][1], m_worldSpace );
|
||||
renderer.addRenderable( m_lines[i][j][0], m_worldSpace );
|
||||
}
|
||||
else{
|
||||
renderer.addRenderable( m_lines[i][j][0], m_worldSpace );
|
||||
renderer.addRenderable( m_lines[i][j][1], m_worldSpace );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
for ( int i = 0; i < 3; ++i )
|
||||
for ( int j = 0; j < 2; ++j )
|
||||
for ( int k = 0; k < 2; ++k )
|
||||
if( m_selectables[i][j][k].isSelected() ){
|
||||
Vector3 origin = matrix4_transformed_point( m_worldSpace, m_lines[i][j][k].m_line[0].vertex );
|
||||
Vector3 origin2 = matrix4_transformed_point( m_worldSpace, m_lines[i][j][k].m_line[1].vertex );
|
||||
|
||||
Pivot2World_worldSpace( m_arrow_modelview, matrix4_translation_for_vec3( origin ), volume.GetModelview(), volume.GetProjection(), volume.GetViewport() );
|
||||
Pivot2World_worldSpace( m_arrow_modelview2, matrix4_translation_for_vec3( origin2 ), volume.GetModelview(), volume.GetProjection(), volume.GetViewport() );
|
||||
|
||||
const Matrix4 rot( i == 0? g_matrix4_identity: i == 1? matrix4_rotation_for_sincos_z( 1, 0 ): matrix4_rotation_for_sincos_y( -1, 0 ) );
|
||||
matrix4_multiply_by_matrix4( m_arrow_modelview, rot );
|
||||
matrix4_multiply_by_matrix4( m_arrow_modelview2, rot );
|
||||
const float x = 0.7f;
|
||||
matrix4_multiply_by_matrix4( m_arrow_modelview, matrix4_scale_for_vec3( Vector3( x, x, x ) ) );
|
||||
matrix4_multiply_by_matrix4( m_arrow_modelview2, matrix4_scale_for_vec3( Vector3( -x, x, x ) ) );
|
||||
|
||||
renderer.SetState( m_state_fill, Renderer::eWireframeOnly );
|
||||
renderer.SetState( m_state_fill, Renderer::eFullMaterials );
|
||||
renderer.addRenderable( m_arrow, m_arrow_modelview );
|
||||
renderer.addRenderable( m_arrow, m_arrow_modelview2 );
|
||||
return;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < 3; ++i )
|
||||
for ( int j = 0; j < 2; ++j )
|
||||
for ( int k = 0; k < 2; ++k )
|
||||
if( m_selectables_rotate[i][j][k].isSelected() ){
|
||||
renderer.SetState( m_state_point, Renderer::eWireframeOnly );
|
||||
renderer.SetState( m_state_point, Renderer::eFullMaterials );
|
||||
renderer.addRenderable( m_point, m_worldSpace );
|
||||
renderer.addRenderable( m_point, m_worldSpace );
|
||||
return;
|
||||
}
|
||||
}
|
||||
void testSelect( const View& view, const Matrix4& pivot2world ) override {
|
||||
updateModelview( view, pivot2world );
|
||||
SelectionPool selector;
|
||||
const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_worldSpace ) );
|
||||
|
||||
if( g_modifiers == c_modifierAlt && view.fill() )
|
||||
goto testSelectBboxPlanes;
|
||||
if( g_modifiers != c_modifierNone )
|
||||
return selectionChange( nullptr );
|
||||
|
||||
/* try corner points to rotate */
|
||||
for ( int i = 0; i < 3; ++i )
|
||||
for ( int j = 0; j < 2; ++j )
|
||||
for ( int k = 0; k < 2; ++k ){
|
||||
m_point.m_point.vertex[i] = 0;
|
||||
m_point.m_point.vertex[( i + 1 ) % 3] = j? 1 : -1;
|
||||
m_point.m_point.vertex[( i + 2 ) % 3] = k? 1 : -1;
|
||||
SelectionIntersection best;
|
||||
Point_BestPoint( local2view, m_point.m_point, best );
|
||||
selector.addSelectable( best, &m_selectables_rotate[i][j][k] );
|
||||
}
|
||||
if( !selector.failed() ) {
|
||||
( *selector.begin() ).second->setSelected( true );
|
||||
for ( int i = 0; i < 3; ++i )
|
||||
for ( int j = 0; j < 2; ++j )
|
||||
for ( int k = 0; k < 2; ++k )
|
||||
if( m_selectables_rotate[i][j][k].isSelected() ){
|
||||
m_point.m_point.vertex[i] = 0;
|
||||
m_point.m_point.vertex[( i + 1 ) % 3] = j? 1 : -1;
|
||||
m_point.m_point.vertex[( i + 2 ) % 3] = k? 1 : -1;
|
||||
if( !m_pivotIsCustom ){
|
||||
const Vector3 origin = m_bounds.origin + m_point.m_point.vertex * -1 * m_bounds.extents;
|
||||
m_pivot2world = matrix4_translation_for_vec3( origin );
|
||||
}
|
||||
/* set radius */
|
||||
if( std::fabs( vector3_dot( m_pivot.m_axis_screen, g_vector3_axes[i] ) ) < 0.2 ){
|
||||
Vector3 origin = matrix4_get_translation_vec3( m_pivot2world );
|
||||
Vector3 point = m_bounds_draw.origin + m_point.m_point.vertex * m_bounds_draw.extents;
|
||||
const Matrix4 inv = matrix4_affine_inverse( m_pivot.m_worldSpace );
|
||||
matrix4_transform_point( inv, origin );
|
||||
matrix4_transform_point( inv, point );
|
||||
point -= origin;
|
||||
point = vector3_added( point, vector3_scaled( m_pivot.m_axis_screen, -vector3_dot( point, m_pivot.m_axis_screen ) ) ); //constrain_to_axis
|
||||
m_rotateAxis.SetRadius( vector3_length( point ) - g_SELECT_EPSILON / 2.0 - 1.0 ); /* use smaller radius to constrain to one rotation direction in 2D */
|
||||
//globalOutputStream() << "radius " << ( vector3_length( point ) - g_SELECT_EPSILON / 2.0 - 1.0 ) << '\n';
|
||||
}
|
||||
else{
|
||||
m_rotateAxis.SetRadius( g_radius );
|
||||
//globalOutputStream() << "g_radius\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
/* try lines to skew */
|
||||
for ( int i = 0; i < 3; ++i )
|
||||
for ( int j = 0; j < 2; ++j )
|
||||
for ( int k = 0; k < 2; ++k ){
|
||||
SelectionIntersection best;
|
||||
Line_BestPoint( local2view, m_lines[i][j][k].m_line, best );
|
||||
selector.addSelectable( best, &m_selectables[i][j][k] );
|
||||
}
|
||||
|
||||
if( !selector.failed() ) {
|
||||
( *selector.begin() ).second->setSelected( true );
|
||||
m_skew.set0( vector4_projected( matrix4_transformed_vector4( matrix4_full_inverse( view.GetViewMatrix() ), Vector4( 0, 0, selector.begin()->first.depth(), 1 ) ) ) );
|
||||
if( !m_pivotIsCustom )
|
||||
for ( int i = 0; i < 3; ++i )
|
||||
for ( int j = 0; j < 2; ++j )
|
||||
for ( int k = 0; k < 2; ++k )
|
||||
if( m_selectables[i][j][k].isSelected() ){
|
||||
const int axis_by = ( i + j + 1 ) % 3;
|
||||
Vector3 origin = m_bounds.origin;
|
||||
origin[axis_by] += k? -m_bounds.extents[axis_by] : m_bounds.extents[axis_by];
|
||||
m_pivot2world = matrix4_translation_for_vec3( origin );
|
||||
}
|
||||
}
|
||||
else{ /* try bbox to translate */
|
||||
SelectionIntersection best;
|
||||
AABB_BestPoint( local2view, EClipCull::CW, AABB( Vector3( 0, 0, 0 ), Vector3( 1, 1, 1 ) ), best );
|
||||
selector.addSelectable( best, &m_selectable_translateFree );
|
||||
if( !selector.failed() )
|
||||
m_translateFreeXY_Z.set0( vector4_projected( matrix4_transformed_vector4( matrix4_full_inverse( view.GetViewMatrix() ), Vector4( 0, 0, selector.begin()->first.depth(), 1 ) ) ) );
|
||||
}
|
||||
}
|
||||
testSelectBboxPlanes:
|
||||
/* try bbox planes to scale */
|
||||
if( selector.failed() ){
|
||||
SelectionVolume test( view );
|
||||
test.BeginMesh( g_matrix4_identity, true );
|
||||
|
||||
if( g_modifiers == c_modifierAlt ){
|
||||
PlaneSelectable::BestPlaneData planeData;
|
||||
m_selectables_scale.bestPlaneDirect( m_bounds_draw, test, planeData );
|
||||
if( !planeData.valid() ){
|
||||
m_selectables_scale.bestPlaneIndirect( m_bounds_draw, test, planeData );
|
||||
}
|
||||
if( planeData.valid() ){
|
||||
m_selectables_scale.selectByPlane( m_bounds_draw, planeData.m_plane );
|
||||
}
|
||||
}
|
||||
else{
|
||||
m_selectables_scale.selectPlanes( m_bounds_draw, selector, test, {} );
|
||||
for( auto& [ intersection, selectable ] : selector )
|
||||
selectable->setSelected( true );
|
||||
}
|
||||
|
||||
std::uintptr_t newsel = 0;
|
||||
Vector3 origin = m_bounds.origin;
|
||||
for ( int i = 0; i < 3; ++i )
|
||||
for ( int j = 0; j < 2; ++j )
|
||||
if( m_selectables_scale.getSelectables()[i * 2 + j].isSelected() ){
|
||||
origin[i] += j? m_bounds.extents[i] : -m_bounds.extents[i];
|
||||
newsel += reinterpret_cast<std::uintptr_t>( &m_selectables_scale.getSelectables()[i * 2 + j] ); // hack: store up to 2 pointers in one
|
||||
}
|
||||
if( !m_pivotIsCustom )
|
||||
m_pivot2world = matrix4_translation_for_vec3( origin );
|
||||
return selectionChange( reinterpret_cast<const ObservedSelectable *>( newsel ) );
|
||||
}
|
||||
|
||||
selectionChange( selector );
|
||||
}
|
||||
|
||||
Manipulatable* GetManipulatable() override {
|
||||
for ( int i = 0; i < 3; ++i )
|
||||
for ( int j = 0; j < 2; ++j )
|
||||
for ( int k = 0; k < 2; ++k )
|
||||
if( m_selectables[i][j][k].isSelected() ){
|
||||
m_skew.SetAxes( i, ( i + j + 1 ) % 3, k? 1 : -1 );
|
||||
return &m_skew;
|
||||
}
|
||||
else if( m_selectables_rotate[i][j][k].isSelected() ){
|
||||
m_rotateAxis.SetAxis( g_vector3_axes[i] );
|
||||
return &m_rotateAxis;
|
||||
}
|
||||
{
|
||||
Vector3 axes[2] = { g_vector3_identity, g_vector3_identity };
|
||||
Vector3* axis = axes;
|
||||
for ( int i = 0; i < 3; ++i )
|
||||
for ( int j = 0; j < 2; ++j )
|
||||
if( m_selectables_scale.getSelectables()[i * 2 + j].isSelected() )
|
||||
( *axis++ )[i] = j? -1 : 1;
|
||||
if( axis - axes == 2 ){
|
||||
m_scaleFree.SetAxes( axes[0], axes[1] );
|
||||
return &m_scaleFree;
|
||||
}
|
||||
else if( axis - axes == 1 ){
|
||||
m_scaleAxis.SetAxis( axes[0] );
|
||||
return &m_scaleAxis;
|
||||
}
|
||||
}
|
||||
return &m_translateFreeXY_Z;
|
||||
}
|
||||
|
||||
void setSelected( bool select ) override {
|
||||
m_selectable_translateFree.setSelected( select );
|
||||
for ( int i = 0; i < 3; ++i )
|
||||
for ( int j = 0; j < 2; ++j )
|
||||
for ( int k = 0; k < 2; ++k ){
|
||||
m_selectables[i][j][k].setSelected( select );
|
||||
m_selectables_rotate[i][j][k].setSelected( select );
|
||||
}
|
||||
m_selectables_scale.setSelected( select );
|
||||
}
|
||||
bool isSelected() const override {
|
||||
bool selected = false;
|
||||
for ( int i = 0; i < 3; ++i )
|
||||
for ( int j = 0; j < 2; ++j )
|
||||
for ( int k = 0; k < 2; ++k ){
|
||||
selected |= m_selectables[i][j][k].isSelected();
|
||||
selected |= m_selectables_rotate[i][j][k].isSelected();
|
||||
}
|
||||
selected |= m_selectables_scale.isSelected();
|
||||
return selected | m_selectable_translateFree.isSelected();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
SkewManipulator* New_SkewManipulator( Skewable& skewable, Translatable& translatable, Scalable& scalable, Rotatable& rotatable,
|
||||
AllTransformable& transformable, const AABB& bounds, Matrix4& pivot2world, const bool& pivotIsCustom, const std::size_t segments /*= 2*/ ){
|
||||
return new SkewManipulatorImpl( skewable, translatable, scalable, rotatable, transformable, bounds, pivot2world, pivotIsCustom, segments );
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selection_.h"
|
||||
|
||||
class SkewManipulator : public Manipulator
|
||||
{
|
||||
public:
|
||||
inline static Shader* m_state_wire;
|
||||
inline static Shader* m_state_fill;
|
||||
inline static Shader* m_state_point;
|
||||
};
|
||||
|
||||
SkewManipulator* New_SkewManipulator( Skewable& skewable, Translatable& translatable, Scalable& scalable, Rotatable& rotatable,
|
||||
AllTransformable& transformable, const AABB& bounds, Matrix4& pivot2world, const bool& pivotIsCustom, const std::size_t segments = 2 );
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "selection_mtor_translate.h"
|
||||
|
||||
#include "selection_.h"
|
||||
#include "selection_render.h"
|
||||
#include "selection_volume.h"
|
||||
#include "selection_selector.h"
|
||||
#include "selection_mtable_translate.h"
|
||||
#include "selectionlib.h"
|
||||
|
||||
class TranslateManipulatorImpl final : public TranslateManipulator, public ManipulatorSelectionChangeable
|
||||
{
|
||||
TranslateFree m_free;
|
||||
TranslateAxis m_axis;
|
||||
RenderableLine m_arrow_x;
|
||||
RenderableLine m_arrow_y;
|
||||
RenderableLine m_arrow_z;
|
||||
RenderableArrowHead m_arrow_head_x;
|
||||
RenderableArrowHead m_arrow_head_y;
|
||||
RenderableArrowHead m_arrow_head_z;
|
||||
RenderableQuad m_quad_screen;
|
||||
SelectableBool m_selectable_x;
|
||||
SelectableBool m_selectable_y;
|
||||
SelectableBool m_selectable_z;
|
||||
SelectableBool m_selectable_screen;
|
||||
Pivot2World m_pivot;
|
||||
public:
|
||||
TranslateManipulatorImpl( Translatable& translatable, std::size_t segments, float length ) :
|
||||
m_free( translatable ),
|
||||
m_axis( translatable ),
|
||||
m_arrow_head_x( 3 * 2 * ( segments << 3 ) ),
|
||||
m_arrow_head_y( 3 * 2 * ( segments << 3 ) ),
|
||||
m_arrow_head_z( 3 * 2 * ( segments << 3 ) ){
|
||||
draw_arrowline( length, m_arrow_x.m_line, 0 );
|
||||
draw_arrowhead( segments, length, m_arrow_head_x.m_vertices.data(), TripleRemapXYZ<Vertex3f>(), TripleRemapXYZ<Normal3f>() );
|
||||
draw_arrowline( length, m_arrow_y.m_line, 1 );
|
||||
draw_arrowhead( segments, length, m_arrow_head_y.m_vertices.data(), TripleRemapYZX<Vertex3f>(), TripleRemapYZX<Normal3f>() );
|
||||
draw_arrowline( length, m_arrow_z.m_line, 2 );
|
||||
draw_arrowhead( segments, length, m_arrow_head_z.m_vertices.data(), TripleRemapZXY<Vertex3f>(), TripleRemapZXY<Normal3f>() );
|
||||
|
||||
draw_quad( 16, m_quad_screen.m_quad );
|
||||
}
|
||||
|
||||
void UpdateColours(){
|
||||
m_arrow_x.setColour( colourSelected( g_colour_x, m_selectable_x.isSelected() ) );
|
||||
m_arrow_head_x.setColour( colourSelected( g_colour_x, m_selectable_x.isSelected() ) );
|
||||
m_arrow_y.setColour( colourSelected( g_colour_y, m_selectable_y.isSelected() ) );
|
||||
m_arrow_head_y.setColour( colourSelected( g_colour_y, m_selectable_y.isSelected() ) );
|
||||
m_arrow_z.setColour( colourSelected( g_colour_z, m_selectable_z.isSelected() ) );
|
||||
m_arrow_head_z.setColour( colourSelected( g_colour_z, m_selectable_z.isSelected() ) );
|
||||
m_quad_screen.setColour( colourSelected( g_colour_screen, m_selectable_screen.isSelected() ) );
|
||||
}
|
||||
|
||||
bool manipulator_show_axis( const Pivot2World& pivot, const Vector3& axis ){
|
||||
return std::fabs( vector3_dot( pivot.m_axis_screen, axis ) ) < 0.95;
|
||||
}
|
||||
|
||||
void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ) override {
|
||||
m_pivot.update( pivot2world, volume.GetModelview(), volume.GetProjection(), volume.GetViewport() );
|
||||
|
||||
// temp hack
|
||||
UpdateColours();
|
||||
|
||||
Vector3 x = vector3_normalised( m_pivot.m_worldSpace.x().vec3() );
|
||||
bool show_x = manipulator_show_axis( m_pivot, x );
|
||||
|
||||
Vector3 y = vector3_normalised( m_pivot.m_worldSpace.y().vec3() );
|
||||
bool show_y = manipulator_show_axis( m_pivot, y );
|
||||
|
||||
Vector3 z = vector3_normalised( m_pivot.m_worldSpace.z().vec3() );
|
||||
bool show_z = manipulator_show_axis( m_pivot, z );
|
||||
|
||||
renderer.SetState( m_state_wire, Renderer::eWireframeOnly );
|
||||
renderer.SetState( m_state_wire, Renderer::eFullMaterials );
|
||||
|
||||
if ( show_x ) {
|
||||
renderer.addRenderable( m_arrow_x, m_pivot.m_worldSpace );
|
||||
}
|
||||
if ( show_y ) {
|
||||
renderer.addRenderable( m_arrow_y, m_pivot.m_worldSpace );
|
||||
}
|
||||
if ( show_z ) {
|
||||
renderer.addRenderable( m_arrow_z, m_pivot.m_worldSpace );
|
||||
}
|
||||
|
||||
renderer.addRenderable( m_quad_screen, m_pivot.m_viewplaneSpace );
|
||||
|
||||
renderer.SetState( m_state_fill, Renderer::eWireframeOnly );
|
||||
renderer.SetState( m_state_fill, Renderer::eFullMaterials );
|
||||
|
||||
if ( show_x ) {
|
||||
renderer.addRenderable( m_arrow_head_x, m_pivot.m_worldSpace );
|
||||
}
|
||||
if ( show_y ) {
|
||||
renderer.addRenderable( m_arrow_head_y, m_pivot.m_worldSpace );
|
||||
}
|
||||
if ( show_z ) {
|
||||
renderer.addRenderable( m_arrow_head_z, m_pivot.m_worldSpace );
|
||||
}
|
||||
}
|
||||
void testSelect( const View& view, const Matrix4& pivot2world ) override {
|
||||
if( g_modifiers != c_modifierNone )
|
||||
return selectionChange( nullptr );
|
||||
|
||||
m_pivot.update( pivot2world, view.GetModelview(), view.GetProjection(), view.GetViewport() );
|
||||
|
||||
SelectionPool selector;
|
||||
|
||||
Vector3 x = vector3_normalised( m_pivot.m_worldSpace.x().vec3() );
|
||||
bool show_x = manipulator_show_axis( m_pivot, x );
|
||||
|
||||
Vector3 y = vector3_normalised( m_pivot.m_worldSpace.y().vec3() );
|
||||
bool show_y = manipulator_show_axis( m_pivot, y );
|
||||
|
||||
Vector3 z = vector3_normalised( m_pivot.m_worldSpace.z().vec3() );
|
||||
bool show_z = manipulator_show_axis( m_pivot, z );
|
||||
|
||||
{
|
||||
const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_pivot.m_viewpointSpace ) );
|
||||
|
||||
{
|
||||
SelectionIntersection best;
|
||||
Quad_BestPoint( local2view, EClipCull::CW, m_quad_screen.m_quad, best );
|
||||
if ( best.valid() ) {
|
||||
best = SelectionIntersection( 0, 0 );
|
||||
selector.addSelectable( best, &m_selectable_screen );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_pivot.m_worldSpace ) );
|
||||
|
||||
#if defined( DEBUG_SELECTION )
|
||||
g_render_clipped.construct( view.GetViewMatrix() );
|
||||
#endif
|
||||
|
||||
if ( show_x ) {
|
||||
SelectionIntersection best;
|
||||
Line_BestPoint( local2view, m_arrow_x.m_line, best );
|
||||
Triangles_BestPoint( local2view, EClipCull::CW, m_arrow_head_x.m_vertices.begin(), m_arrow_head_x.m_vertices.end(), best );
|
||||
selector.addSelectable( best, &m_selectable_x );
|
||||
}
|
||||
|
||||
if ( show_y ) {
|
||||
SelectionIntersection best;
|
||||
Line_BestPoint( local2view, m_arrow_y.m_line, best );
|
||||
Triangles_BestPoint( local2view, EClipCull::CW, m_arrow_head_y.m_vertices.begin(), m_arrow_head_y.m_vertices.end(), best );
|
||||
selector.addSelectable( best, &m_selectable_y );
|
||||
}
|
||||
|
||||
if ( show_z ) {
|
||||
SelectionIntersection best;
|
||||
Line_BestPoint( local2view, m_arrow_z.m_line, best );
|
||||
Triangles_BestPoint( local2view, EClipCull::CW, m_arrow_head_z.m_vertices.begin(), m_arrow_head_z.m_vertices.end(), best );
|
||||
selector.addSelectable( best, &m_selectable_z );
|
||||
}
|
||||
}
|
||||
|
||||
selectionChange( selector );
|
||||
}
|
||||
|
||||
Manipulatable* GetManipulatable() override {
|
||||
if ( m_selectable_x.isSelected() ) {
|
||||
m_axis.SetAxis( g_vector3_axis_x );
|
||||
return &m_axis;
|
||||
}
|
||||
else if ( m_selectable_y.isSelected() ) {
|
||||
m_axis.SetAxis( g_vector3_axis_y );
|
||||
return &m_axis;
|
||||
}
|
||||
else if ( m_selectable_z.isSelected() ) {
|
||||
m_axis.SetAxis( g_vector3_axis_z );
|
||||
return &m_axis;
|
||||
}
|
||||
else
|
||||
{
|
||||
return &m_free;
|
||||
}
|
||||
}
|
||||
|
||||
void setSelected( bool select ) override {
|
||||
m_selectable_x.setSelected( select );
|
||||
m_selectable_y.setSelected( select );
|
||||
m_selectable_z.setSelected( select );
|
||||
m_selectable_screen.setSelected( select );
|
||||
}
|
||||
bool isSelected() const override {
|
||||
return m_selectable_x.isSelected()
|
||||
|| m_selectable_y.isSelected()
|
||||
|| m_selectable_z.isSelected()
|
||||
|| m_selectable_screen.isSelected();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
TranslateManipulator* New_TranslateManipulator( Translatable& translatable, std::size_t segments, float length ){
|
||||
return new TranslateManipulatorImpl( translatable, segments, length );
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selection_.h"
|
||||
|
||||
class TranslateManipulator : public Manipulator
|
||||
{
|
||||
public:
|
||||
inline static Shader* m_state_wire;
|
||||
inline static Shader* m_state_fill;
|
||||
};
|
||||
|
||||
TranslateManipulator* New_TranslateManipulator( Translatable& translatable, std::size_t segments, float length );
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selection_.h"
|
||||
|
||||
class UVManipulator : public Manipulator
|
||||
{
|
||||
public:
|
||||
virtual ~UVManipulator() = default;
|
||||
virtual void freezeTransform() = 0;
|
||||
inline static Shader* m_state_line;
|
||||
inline static Shader* m_state_point;
|
||||
};
|
||||
|
||||
UVManipulator* New_UVManipulator();
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "render.h"
|
||||
#include "renderable.h"
|
||||
|
||||
const Colour4b g_colour_sphere( 0, 0, 0, 255 );
|
||||
const Colour4b g_colour_screen( 0, 255, 255, 255 );
|
||||
const Colour4b g_colour_selected( 255, 255, 0, 255 );
|
||||
|
||||
inline const Colour4b& colourSelected( const Colour4b& colour, bool selected ){
|
||||
return ( selected ) ? g_colour_selected : colour;
|
||||
}
|
||||
|
||||
|
||||
struct RenderablePoint : public OpenGLRenderable
|
||||
{
|
||||
PointVertex m_point;
|
||||
RenderablePoint():
|
||||
m_point( vertex3f_identity ) {
|
||||
}
|
||||
void render( RenderStateFlags state ) const override {
|
||||
gl().glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_point.colour );
|
||||
gl().glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_point.vertex );
|
||||
gl().glDrawArrays( GL_POINTS, 0, 1 );
|
||||
}
|
||||
void setColour( const Colour4b & colour ) {
|
||||
m_point.colour = colour;
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderableLine : public OpenGLRenderable
|
||||
{
|
||||
PointVertex m_line[2];
|
||||
|
||||
RenderableLine() {
|
||||
}
|
||||
void render( RenderStateFlags state ) const override {
|
||||
gl().glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_line[0].colour );
|
||||
gl().glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_line[0].vertex );
|
||||
gl().glDrawArrays( GL_LINES, 0, 2 );
|
||||
}
|
||||
void setColour( const Colour4b& colour ) {
|
||||
m_line[0].colour = colour;
|
||||
m_line[1].colour = colour;
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderableQuad : public OpenGLRenderable
|
||||
{
|
||||
PointVertex m_quad[4];
|
||||
void render( RenderStateFlags state ) const override {
|
||||
gl().glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_quad[0].colour );
|
||||
gl().glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_quad[0].vertex );
|
||||
gl().glDrawArrays( GL_LINE_LOOP, 0, 4 );
|
||||
}
|
||||
void setColour( const Colour4b& colour ){
|
||||
m_quad[0].colour = colour;
|
||||
m_quad[1].colour = colour;
|
||||
m_quad[2].colour = colour;
|
||||
m_quad[3].colour = colour;
|
||||
}
|
||||
};
|
||||
|
||||
template<GLenum MODE>
|
||||
struct RenderableCircle___ : public OpenGLRenderable
|
||||
{
|
||||
Array<PointVertex> m_vertices;
|
||||
|
||||
RenderableCircle___( std::size_t size ) : m_vertices( size ){
|
||||
}
|
||||
void render( RenderStateFlags state ) const override {
|
||||
gl().glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_vertices.data()->colour );
|
||||
gl().glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_vertices.data()->vertex );
|
||||
gl().glDrawArrays( MODE, 0, GLsizei( m_vertices.size() ) );
|
||||
}
|
||||
void setColour( const Colour4b& colour ){
|
||||
for ( auto& v : m_vertices )
|
||||
v.colour = colour;
|
||||
}
|
||||
};
|
||||
using RenderableCircle = RenderableCircle___<GL_LINE_LOOP>;
|
||||
using RenderableSemiCircle = RenderableCircle___<GL_LINE_STRIP>;
|
||||
|
||||
|
||||
struct FlatShadedVertex
|
||||
{
|
||||
Vertex3f vertex;
|
||||
Colour4b colour;
|
||||
Normal3f normal;
|
||||
};
|
||||
|
||||
struct RenderableArrowHead : public OpenGLRenderable
|
||||
{
|
||||
Array<FlatShadedVertex> m_vertices;
|
||||
|
||||
RenderableArrowHead( std::size_t size )
|
||||
: m_vertices( size ) {
|
||||
}
|
||||
void render( RenderStateFlags state ) const override {
|
||||
gl().glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( FlatShadedVertex ), &m_vertices.data()->colour );
|
||||
gl().glVertexPointer( 3, GL_FLOAT, sizeof( FlatShadedVertex ), &m_vertices.data()->vertex );
|
||||
gl().glNormalPointer( GL_FLOAT, sizeof( FlatShadedVertex ), &m_vertices.data()->normal );
|
||||
gl().glDrawArrays( GL_TRIANGLES, 0, GLsizei( m_vertices.size() ) );
|
||||
}
|
||||
void setColour( const Colour4b& colour ) {
|
||||
for ( auto& v : m_vertices )
|
||||
{
|
||||
v.colour = colour;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const float arrowhead_length = 16;
|
||||
const float arrowhead_radius = 4;
|
||||
|
||||
inline void draw_arrowline( const float length, PointVertex* line, const std::size_t axis ){
|
||||
( *line++ ).vertex = vertex3f_identity;
|
||||
( *line ).vertex = vertex3f_identity;
|
||||
vertex3f_to_array( ( *line ).vertex )[axis] = length - arrowhead_length;
|
||||
}
|
||||
|
||||
template<typename VertexRemap, typename NormalRemap>
|
||||
void draw_arrowhead( const std::size_t segments, const float length, FlatShadedVertex* vertices, VertexRemap, NormalRemap ){
|
||||
std::size_t head_tris = ( segments << 3 );
|
||||
const double head_segment = c_2pi / head_tris;
|
||||
for ( std::size_t i = 0; i < head_tris; ++i )
|
||||
{
|
||||
{
|
||||
FlatShadedVertex& point = vertices[i * 6 + 0];
|
||||
VertexRemap::x( point.vertex ) = length - arrowhead_length;
|
||||
VertexRemap::y( point.vertex ) = arrowhead_radius * cos( i * head_segment );
|
||||
VertexRemap::z( point.vertex ) = arrowhead_radius * sin( i * head_segment );
|
||||
NormalRemap::x( point.normal ) = arrowhead_radius / arrowhead_length;
|
||||
NormalRemap::y( point.normal ) = cos( i * head_segment );
|
||||
NormalRemap::z( point.normal ) = sin( i * head_segment );
|
||||
}
|
||||
{
|
||||
FlatShadedVertex& point = vertices[i * 6 + 1];
|
||||
VertexRemap::x( point.vertex ) = length;
|
||||
VertexRemap::y( point.vertex ) = 0;
|
||||
VertexRemap::z( point.vertex ) = 0;
|
||||
NormalRemap::x( point.normal ) = arrowhead_radius / arrowhead_length;
|
||||
NormalRemap::y( point.normal ) = cos( ( i + 0.5 ) * head_segment );
|
||||
NormalRemap::z( point.normal ) = sin( ( i + 0.5 ) * head_segment );
|
||||
}
|
||||
{
|
||||
FlatShadedVertex& point = vertices[i * 6 + 2];
|
||||
VertexRemap::x( point.vertex ) = length - arrowhead_length;
|
||||
VertexRemap::y( point.vertex ) = arrowhead_radius * cos( ( i + 1 ) * head_segment );
|
||||
VertexRemap::z( point.vertex ) = arrowhead_radius * sin( ( i + 1 ) * head_segment );
|
||||
NormalRemap::x( point.normal ) = arrowhead_radius / arrowhead_length;
|
||||
NormalRemap::y( point.normal ) = cos( ( i + 1 ) * head_segment );
|
||||
NormalRemap::z( point.normal ) = sin( ( i + 1 ) * head_segment );
|
||||
}
|
||||
|
||||
{
|
||||
FlatShadedVertex& point = vertices[i * 6 + 3];
|
||||
VertexRemap::x( point.vertex ) = length - arrowhead_length;
|
||||
VertexRemap::y( point.vertex ) = 0;
|
||||
VertexRemap::z( point.vertex ) = 0;
|
||||
NormalRemap::x( point.normal ) = -1;
|
||||
NormalRemap::y( point.normal ) = 0;
|
||||
NormalRemap::z( point.normal ) = 0;
|
||||
}
|
||||
{
|
||||
FlatShadedVertex& point = vertices[i * 6 + 4];
|
||||
VertexRemap::x( point.vertex ) = length - arrowhead_length;
|
||||
VertexRemap::y( point.vertex ) = arrowhead_radius * cos( i * head_segment );
|
||||
VertexRemap::z( point.vertex ) = arrowhead_radius * sin( i * head_segment );
|
||||
NormalRemap::x( point.normal ) = -1;
|
||||
NormalRemap::y( point.normal ) = 0;
|
||||
NormalRemap::z( point.normal ) = 0;
|
||||
}
|
||||
{
|
||||
FlatShadedVertex& point = vertices[i * 6 + 5];
|
||||
VertexRemap::x( point.vertex ) = length - arrowhead_length;
|
||||
VertexRemap::y( point.vertex ) = arrowhead_radius * cos( ( i + 1 ) * head_segment );
|
||||
VertexRemap::z( point.vertex ) = arrowhead_radius * sin( ( i + 1 ) * head_segment );
|
||||
NormalRemap::x( point.normal ) = -1;
|
||||
NormalRemap::y( point.normal ) = 0;
|
||||
NormalRemap::z( point.normal ) = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Triple>
|
||||
class TripleRemapXYZ
|
||||
{
|
||||
public:
|
||||
static float& x( Triple& triple ){
|
||||
return triple.x();
|
||||
}
|
||||
static float& y( Triple& triple ){
|
||||
return triple.y();
|
||||
}
|
||||
static float& z( Triple& triple ){
|
||||
return triple.z();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Triple>
|
||||
class TripleRemapYZX
|
||||
{
|
||||
public:
|
||||
static float& x( Triple& triple ){
|
||||
return triple.y();
|
||||
}
|
||||
static float& y( Triple& triple ){
|
||||
return triple.z();
|
||||
}
|
||||
static float& z( Triple& triple ){
|
||||
return triple.x();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Triple>
|
||||
class TripleRemapZXY
|
||||
{
|
||||
public:
|
||||
static float& x( Triple& triple ){
|
||||
return triple.z();
|
||||
}
|
||||
static float& y( Triple& triple ){
|
||||
return triple.x();
|
||||
}
|
||||
static float& z( Triple& triple ){
|
||||
return triple.y();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selection_.h"
|
||||
|
||||
typedef std::multimap<SelectionIntersection, Selectable*> SelectableSortedSet;
|
||||
|
||||
class SelectionPool : public Selector
|
||||
{
|
||||
SelectableSortedSet m_pool;
|
||||
SelectionIntersection m_intersection;
|
||||
Selectable* m_selectable;
|
||||
|
||||
public:
|
||||
void pushSelectable( Selectable& selectable ) override {
|
||||
m_intersection = SelectionIntersection();
|
||||
m_selectable = &selectable;
|
||||
}
|
||||
void popSelectable() override {
|
||||
addSelectable( m_intersection, m_selectable );
|
||||
m_intersection = SelectionIntersection();
|
||||
}
|
||||
void addIntersection( const SelectionIntersection& intersection ) override {
|
||||
assign_if_closer( m_intersection, intersection );
|
||||
}
|
||||
void addSelectable( const SelectionIntersection& intersection, Selectable* selectable ){
|
||||
if ( intersection.valid() ) {
|
||||
m_pool.insert( SelectableSortedSet::value_type( intersection, selectable ) );
|
||||
}
|
||||
}
|
||||
|
||||
typedef SelectableSortedSet::iterator iterator;
|
||||
|
||||
iterator begin(){
|
||||
return m_pool.begin();
|
||||
}
|
||||
iterator end(){
|
||||
return m_pool.end();
|
||||
}
|
||||
|
||||
bool failed(){
|
||||
return m_pool.empty();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ManipulatorSelectionChangeable
|
||||
{
|
||||
const Selectable* m_selectable_prev_ptr = nullptr;
|
||||
public:
|
||||
void selectionChange( const Selectable *se ){
|
||||
if( m_selectable_prev_ptr != se ){
|
||||
m_selectable_prev_ptr = se;
|
||||
SceneChangeNotify();
|
||||
}
|
||||
}
|
||||
void selectionChange( SelectionPool& selector ){
|
||||
Selectable *se = nullptr;
|
||||
if ( !selector.failed() ) {
|
||||
se = selector.begin()->second;
|
||||
se->setSelected( true );
|
||||
}
|
||||
selectionChange( se );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class BooleanSelector : public Selector
|
||||
{
|
||||
SelectionIntersection m_bestIntersection;
|
||||
Selectable* m_selectable;
|
||||
public:
|
||||
BooleanSelector() : m_bestIntersection( SelectionIntersection() ){
|
||||
}
|
||||
|
||||
void pushSelectable( Selectable& selectable ) override {
|
||||
m_selectable = &selectable;
|
||||
}
|
||||
void popSelectable() override {
|
||||
}
|
||||
void addIntersection( const SelectionIntersection& intersection ) override {
|
||||
if ( m_selectable->isSelected() ) {
|
||||
assign_if_closer( m_bestIntersection, intersection );
|
||||
}
|
||||
}
|
||||
|
||||
bool isSelected(){
|
||||
return m_bestIntersection.valid();
|
||||
}
|
||||
const SelectionIntersection& bestIntersection() const {
|
||||
return m_bestIntersection;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<float DEPTH_EPSILON>
|
||||
class BestSelector___ : public Selector
|
||||
{
|
||||
protected:
|
||||
SelectionIntersection m_intersection;
|
||||
Selectable* m_selectable;
|
||||
SelectionIntersection m_bestIntersection;
|
||||
std::list<Selectable*> m_bestSelectable;
|
||||
public:
|
||||
BestSelector___() : m_bestIntersection( SelectionIntersection() ), m_bestSelectable( 0 ){
|
||||
}
|
||||
|
||||
void pushSelectable( Selectable& selectable ) override {
|
||||
m_intersection = SelectionIntersection();
|
||||
m_selectable = &selectable;
|
||||
}
|
||||
void popSelectable() override {
|
||||
if ( m_intersection.equalEpsilon( m_bestIntersection, 0.25f, DEPTH_EPSILON ) ) {
|
||||
m_bestSelectable.push_back( m_selectable );
|
||||
m_bestIntersection = m_intersection;
|
||||
}
|
||||
else if ( m_intersection < m_bestIntersection ) {
|
||||
m_bestSelectable.clear();
|
||||
m_bestSelectable.push_back( m_selectable );
|
||||
m_bestIntersection = m_intersection;
|
||||
}
|
||||
m_intersection = SelectionIntersection();
|
||||
}
|
||||
void addIntersection( const SelectionIntersection& intersection ) override {
|
||||
assign_if_closer( m_intersection, intersection );
|
||||
}
|
||||
|
||||
std::list<Selectable*>& best(){
|
||||
return m_bestSelectable;
|
||||
}
|
||||
const SelectionIntersection& bestIntersection() const {
|
||||
return m_bestIntersection;
|
||||
}
|
||||
};
|
||||
using BestSelector = BestSelector___<2e-6f>;
|
||||
using DeepBestSelector = BestSelector___<2.f>;
|
||||
|
||||
|
||||
class BestPointSelector : public Selector
|
||||
{
|
||||
SelectionIntersection m_bestIntersection;
|
||||
public:
|
||||
BestPointSelector() : m_bestIntersection( SelectionIntersection() ){
|
||||
}
|
||||
|
||||
void pushSelectable( Selectable& selectable ) override {
|
||||
}
|
||||
void popSelectable() override {
|
||||
}
|
||||
void addIntersection( const SelectionIntersection& intersection ) override {
|
||||
assign_if_closer( m_bestIntersection, intersection );
|
||||
}
|
||||
|
||||
bool isSelected(){
|
||||
return m_bestIntersection.valid();
|
||||
}
|
||||
const SelectionIntersection& best() const {
|
||||
return m_bestIntersection;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ScenePointSelector : public Selector
|
||||
{
|
||||
SelectionIntersection m_bestIntersection;
|
||||
class Face* m_face;
|
||||
public:
|
||||
ScenePointSelector() : m_bestIntersection( SelectionIntersection() ), m_face( 0 ) {
|
||||
}
|
||||
|
||||
void pushSelectable( Selectable& selectable ) override {
|
||||
}
|
||||
void popSelectable() override {
|
||||
}
|
||||
void addIntersection( const SelectionIntersection& intersection ) override {
|
||||
if( SelectionIntersection_closer( intersection, m_bestIntersection ) ) {
|
||||
m_bestIntersection = intersection;
|
||||
m_face = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void addIntersection( const SelectionIntersection& intersection, Face* face ) {
|
||||
if( SelectionIntersection_closer( intersection, m_bestIntersection ) ) {
|
||||
m_bestIntersection = intersection;
|
||||
m_face = face;
|
||||
}
|
||||
}
|
||||
bool isSelected() {
|
||||
return m_bestIntersection.valid();
|
||||
}
|
||||
const SelectionIntersection& best() {
|
||||
return m_bestIntersection;
|
||||
}
|
||||
const Face* face() {
|
||||
return m_face;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,503 @@
|
||||
/*
|
||||
Copyright (C) 2001-2006, William Joseph.
|
||||
All Rights Reserved.
|
||||
|
||||
This file is part of GtkRadiant.
|
||||
|
||||
GtkRadiant is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GtkRadiant is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GtkRadiant; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "generic/vector.h"
|
||||
#include "selectable.h"
|
||||
#include "math/line.h"
|
||||
#include "view.h"
|
||||
#include "math/frustum.h"
|
||||
#include "render.h"
|
||||
#include "selection_debug.h"
|
||||
|
||||
enum class EClipCull
|
||||
{
|
||||
None,
|
||||
CW,
|
||||
CCW,
|
||||
};
|
||||
|
||||
typedef Vector3 point_t;
|
||||
typedef const Vector3* point_iterator_t;
|
||||
|
||||
// crossing number test for a point in a polygon
|
||||
// This code is patterned after [Franklin, 2000]
|
||||
inline bool point_test_polygon_2d( const point_t& P, point_iterator_t start, point_iterator_t finish ){
|
||||
std::size_t crossings = 0;
|
||||
|
||||
// loop through all edges of the polygon
|
||||
for ( point_iterator_t prev = finish - 1, cur = start; cur != finish; prev = cur, ++cur )
|
||||
{ // edge from (*prev) to (*cur)
|
||||
if ( ( ( ( *prev )[1] <= P[1] ) && ( ( *cur )[1] > P[1] ) ) // an upward crossing
|
||||
|| ( ( ( *prev )[1] > P[1] ) && ( ( *cur )[1] <= P[1] ) ) ) { // a downward crossing
|
||||
// compute the actual edge-ray intersect x-coordinate
|
||||
const float vt = ( P[1] - ( *prev )[1] ) / ( ( *cur )[1] - ( *prev )[1] );
|
||||
if ( P[0] < ( *prev )[0] + vt * ( ( *cur )[0] - ( *prev )[0] ) ) { // P[0] < intersect
|
||||
++crossings; // a valid crossing of y=P[1] right of P[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
return ( crossings & 0x1 ) != 0; // 0 if even (out), and 1 if odd (in)
|
||||
}
|
||||
|
||||
inline double triangle_signed_area_XY( const Vector3& p0, const Vector3& p1, const Vector3& p2 ){
|
||||
return ( ( p1[0] - p0[0] ) * ( p2[1] - p0[1] ) ) - ( ( p2[0] - p0[0] ) * ( p1[1] - p0[1] ) );
|
||||
}
|
||||
|
||||
|
||||
inline SelectionIntersection select_point_from_clipped( Vector4& clipped ){
|
||||
return SelectionIntersection( clipped[2] / clipped[3], vector3_length_squared( Vector3( clipped[0] / clipped[3], clipped[1] / clipped[3], 0 ) ) );
|
||||
}
|
||||
|
||||
inline void BestPoint( std::size_t count, Vector4 clipped[9], SelectionIntersection& best, EClipCull cull, const Plane3* plane = 0 ){
|
||||
Vector3 normalised[9];
|
||||
|
||||
{
|
||||
for ( std::size_t i = 0; i < count; ++i )
|
||||
{
|
||||
normalised[i][0] = clipped[i][0] / clipped[i][3];
|
||||
normalised[i][1] = clipped[i][1] / clipped[i][3];
|
||||
normalised[i][2] = clipped[i][2] / clipped[i][3];
|
||||
}
|
||||
}
|
||||
|
||||
if ( cull != EClipCull::None && count > 2 ) {
|
||||
double signed_area = triangle_signed_area_XY( normalised[0], normalised[1], normalised[2] );
|
||||
|
||||
if ( ( cull == EClipCull::CW && signed_area > 0 )
|
||||
|| ( cull == EClipCull::CCW && signed_area < 0 ) ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( count == 2 ) {
|
||||
const Vector3 point = line_closest_point( Line( normalised[0], normalised[1] ), Vector3( 0, 0, 0 ) );
|
||||
assign_if_closer( best, SelectionIntersection( point.z(), vector3_length_squared( Vector3( point.x(), point.y(), 0 ) ) ) );
|
||||
}
|
||||
else if ( count > 2 && !point_test_polygon_2d( Vector3( 0, 0, 0 ), normalised, normalised + count ) ) {
|
||||
Plane3 plaine;
|
||||
if( !plane ){
|
||||
plaine = plane3_for_points( normalised[0], normalised[1], normalised[2] );
|
||||
plane = &plaine;
|
||||
}
|
||||
//globalOutputStream() << plane.a << ' ' << plane.b << ' ' << plane.c << ' ' << '\n';
|
||||
const point_iterator_t end = normalised + count;
|
||||
for ( point_iterator_t previous = end - 1, current = normalised; current != end; previous = current, ++current )
|
||||
{
|
||||
Vector3 point = line_closest_point( Line( *previous, *current ), Vector3( 0, 0, 0 ) );
|
||||
const float depth = point.z();
|
||||
point.z() = 0;
|
||||
const float distance = vector3_length_squared( point );
|
||||
|
||||
if( plane->c == 0 ){
|
||||
assign_if_closer( best, SelectionIntersection( depth, distance ) );
|
||||
}
|
||||
else{
|
||||
assign_if_closer( best, SelectionIntersection( depth, distance, ray_distance_to_plane(
|
||||
Ray( Vector3( 0, 0, 0 ), Vector3( 0, 0, 1 ) ),
|
||||
*plane
|
||||
) ) );
|
||||
// globalOutputStream() << ray_distance_to_plane(
|
||||
// Ray( Vector3( 0, 0, 0 ), Vector3( 0, 0, 1 ) ),
|
||||
// plane
|
||||
// ) << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( count > 2 ) {
|
||||
Plane3 plaine;
|
||||
if( !plane ){
|
||||
plaine = plane3_for_points( normalised[0], normalised[1], normalised[2] );
|
||||
plane = &plaine;
|
||||
}
|
||||
assign_if_closer(
|
||||
best,
|
||||
SelectionIntersection(
|
||||
ray_distance_to_plane(
|
||||
Ray( Vector3( 0, 0, 0 ), Vector3( 0, 0, 1 ) ),
|
||||
*plane
|
||||
),
|
||||
0,
|
||||
ray_distance_to_plane(
|
||||
Ray( Vector3( 10, 8, 0 ), Vector3( 0, 0, 1 ) ),
|
||||
*plane
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#if defined( DEBUG_SELECTION )
|
||||
if ( count >= 2 ) {
|
||||
g_render_clipped.insert( clipped, count );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void Point_BestPoint( const Matrix4& local2view, const PointVertex& vertex, SelectionIntersection& best ){
|
||||
Vector4 clipped;
|
||||
if ( matrix4_clip_point( local2view, vertex3f_to_vector3( vertex.vertex ), clipped ) == c_CLIP_PASS ) {
|
||||
assign_if_closer( best, select_point_from_clipped( clipped ) );
|
||||
}
|
||||
}
|
||||
|
||||
inline void LineStrip_BestPoint( const Matrix4& local2view, const PointVertex* vertices, const std::size_t size, SelectionIntersection& best ){
|
||||
Vector4 clipped[2];
|
||||
for ( std::size_t i = 0; ( i + 1 ) < size; ++i )
|
||||
{
|
||||
const std::size_t count = matrix4_clip_line( local2view, vertex3f_to_vector3( vertices[i].vertex ), vertex3f_to_vector3( vertices[i + 1].vertex ), clipped );
|
||||
BestPoint( count, clipped, best, EClipCull::None );
|
||||
}
|
||||
}
|
||||
|
||||
inline void LineLoop_BestPoint( const Matrix4& local2view, const PointVertex* vertices, const std::size_t size, SelectionIntersection& best ){
|
||||
Vector4 clipped[2];
|
||||
for ( std::size_t i = 0; i < size; ++i )
|
||||
{
|
||||
const std::size_t count = matrix4_clip_line( local2view, vertex3f_to_vector3( vertices[i].vertex ), vertex3f_to_vector3( vertices[( i + 1 ) % size].vertex ), clipped );
|
||||
BestPoint( count, clipped, best, EClipCull::None );
|
||||
}
|
||||
}
|
||||
|
||||
inline void Line_BestPoint( const Matrix4& local2view, const PointVertex vertices[2], SelectionIntersection& best ){
|
||||
Vector4 clipped[2];
|
||||
const std::size_t count = matrix4_clip_line( local2view, vertex3f_to_vector3( vertices[0].vertex ), vertex3f_to_vector3( vertices[1].vertex ), clipped );
|
||||
BestPoint( count, clipped, best, EClipCull::None );
|
||||
}
|
||||
|
||||
inline void Circle_BestPoint( const Matrix4& local2view, EClipCull cull, const PointVertex* vertices, const std::size_t size, SelectionIntersection& best ){
|
||||
Vector4 clipped[9];
|
||||
for ( std::size_t i = 0; i < size; ++i )
|
||||
{
|
||||
const std::size_t count = matrix4_clip_triangle( local2view, g_vector3_identity, vertex3f_to_vector3( vertices[i].vertex ), vertex3f_to_vector3( vertices[( i + 1 ) % size].vertex ), clipped );
|
||||
BestPoint( count, clipped, best, cull );
|
||||
}
|
||||
}
|
||||
|
||||
inline void Quad_BestPoint( const Matrix4& local2view, EClipCull cull, const PointVertex* vertices, SelectionIntersection& best ){
|
||||
Vector4 clipped[9];
|
||||
{
|
||||
const std::size_t count = matrix4_clip_triangle( local2view, vertex3f_to_vector3( vertices[0].vertex ), vertex3f_to_vector3( vertices[1].vertex ), vertex3f_to_vector3( vertices[3].vertex ), clipped );
|
||||
BestPoint( count, clipped, best, cull );
|
||||
}
|
||||
{
|
||||
const std::size_t count = matrix4_clip_triangle( local2view, vertex3f_to_vector3( vertices[1].vertex ), vertex3f_to_vector3( vertices[2].vertex ), vertex3f_to_vector3( vertices[3].vertex ), clipped );
|
||||
BestPoint( count, clipped, best, cull );
|
||||
}
|
||||
}
|
||||
|
||||
inline void AABB_BestPoint( const Matrix4& local2view, EClipCull cull, const AABB& aabb, SelectionIntersection& best ){
|
||||
const IndexPointer::index_type indices_[24] = {
|
||||
2, 1, 5, 6,
|
||||
1, 0, 4, 5,
|
||||
0, 1, 2, 3,
|
||||
3, 7, 4, 0,
|
||||
3, 2, 6, 7,
|
||||
7, 6, 5, 4,
|
||||
};
|
||||
|
||||
const std::array<Vector3, 8> points = aabb_corners( aabb );
|
||||
|
||||
const IndexPointer indices( indices_, 24 );
|
||||
|
||||
Vector4 clipped[9];
|
||||
for ( IndexPointer::iterator i( indices.begin() ); i != indices.end(); i += 4 )
|
||||
{
|
||||
BestPoint(
|
||||
matrix4_clip_triangle(
|
||||
local2view,
|
||||
points[*i],
|
||||
points[*( i + 1 )],
|
||||
points[*( i + 3 )],
|
||||
clipped
|
||||
),
|
||||
clipped,
|
||||
best,
|
||||
cull
|
||||
);
|
||||
BestPoint(
|
||||
matrix4_clip_triangle(
|
||||
local2view,
|
||||
points[*( i + 1 )],
|
||||
points[*( i + 2 )],
|
||||
points[*( i + 3 )],
|
||||
clipped
|
||||
),
|
||||
clipped,
|
||||
best,
|
||||
cull
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef FlatShadedVertex* FlatShadedVertexIterator;
|
||||
inline void Triangles_BestPoint( const Matrix4& local2view, EClipCull cull, FlatShadedVertexIterator first, FlatShadedVertexIterator last, SelectionIntersection& best ){
|
||||
for ( FlatShadedVertexIterator x( first ), y( first + 1 ), z( first + 2 ); x != last; x += 3, y += 3, z += 3 )
|
||||
{
|
||||
Vector4 clipped[9];
|
||||
BestPoint(
|
||||
matrix4_clip_triangle(
|
||||
local2view,
|
||||
reinterpret_cast<const Vector3&>( ( *x ).vertex ),
|
||||
reinterpret_cast<const Vector3&>( ( *y ).vertex ),
|
||||
reinterpret_cast<const Vector3&>( ( *z ).vertex ),
|
||||
clipped
|
||||
),
|
||||
clipped,
|
||||
best,
|
||||
cull
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SelectionVolume : public SelectionTest
|
||||
{
|
||||
Matrix4 m_local2view;
|
||||
const View& m_view;
|
||||
EClipCull m_cull;
|
||||
#if 0
|
||||
Vector3 m_near;
|
||||
Vector3 m_far;
|
||||
#endif
|
||||
Matrix4 m_screen2world;
|
||||
public:
|
||||
SelectionVolume( const View& view )
|
||||
: m_view( view ){
|
||||
}
|
||||
|
||||
const VolumeTest& getVolume() const override {
|
||||
return m_view;
|
||||
}
|
||||
#if 0
|
||||
const Vector3& getNear() const override {
|
||||
return m_near;
|
||||
}
|
||||
const Vector3& getFar() const override {
|
||||
return m_far;
|
||||
}
|
||||
#endif
|
||||
const Matrix4& getScreen2world() const override {
|
||||
return m_screen2world;
|
||||
}
|
||||
|
||||
void BeginMesh( const Matrix4& localToWorld, bool twoSided ) override {
|
||||
m_local2view = matrix4_multiplied_by_matrix4( m_view.GetViewMatrix(), localToWorld );
|
||||
|
||||
// Cull back-facing polygons based on winding being clockwise or counter-clockwise.
|
||||
// Don't cull if the view is wireframe and the polygons are two-sided.
|
||||
m_cull = twoSided && !m_view.fill() ? EClipCull::None : ( matrix4_handedness( localToWorld ) == MATRIX4_RIGHTHANDED ) ? EClipCull::CW : EClipCull::CCW;
|
||||
|
||||
{
|
||||
m_screen2world = matrix4_full_inverse( m_local2view );
|
||||
#if 0
|
||||
m_near = vector4_projected(
|
||||
matrix4_transformed_vector4(
|
||||
m_screen2world,
|
||||
Vector4( 0, 0, -1, 1 )
|
||||
)
|
||||
);
|
||||
|
||||
m_far = vector4_projected(
|
||||
matrix4_transformed_vector4(
|
||||
m_screen2world,
|
||||
Vector4( 0, 0, 1, 1 )
|
||||
)
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined( DEBUG_SELECTION )
|
||||
g_render_clipped.construct( m_view.GetViewMatrix() );
|
||||
#endif
|
||||
}
|
||||
void TestPoint( const Vector3& point, SelectionIntersection& best ) override {
|
||||
Vector4 clipped;
|
||||
if ( matrix4_clip_point( m_local2view, point, clipped ) == c_CLIP_PASS ) {
|
||||
best = select_point_from_clipped( clipped );
|
||||
}
|
||||
}
|
||||
void TestPolygon( const VertexPointer& vertices, std::size_t count, SelectionIntersection& best, const PlanePoints& planepoints ) override {
|
||||
const PlanePoints pts {
|
||||
vector4_projected( matrix4_transformed_vector4( m_local2view, BasicVector4<double>( planepoints[0], 1 ) ) ),
|
||||
vector4_projected( matrix4_transformed_vector4( m_local2view, BasicVector4<double>( planepoints[1], 1 ) ) ),
|
||||
vector4_projected( matrix4_transformed_vector4( m_local2view, BasicVector4<double>( planepoints[2], 1 ) ) )
|
||||
};
|
||||
const Plane3 planeTransformed( plane3_for_points( pts ) );
|
||||
|
||||
Vector4 clipped[9];
|
||||
for ( std::size_t i = 0; i + 2 < count; ++i )
|
||||
{
|
||||
BestPoint(
|
||||
matrix4_clip_triangle(
|
||||
m_local2view,
|
||||
reinterpret_cast<const DoubleVector3&>( vertices[0] ),
|
||||
reinterpret_cast<const DoubleVector3&>( vertices[i + 1] ),
|
||||
reinterpret_cast<const DoubleVector3&>( vertices[i + 2] ),
|
||||
clipped
|
||||
),
|
||||
clipped,
|
||||
best,
|
||||
m_cull,
|
||||
&planeTransformed
|
||||
);
|
||||
}
|
||||
}
|
||||
void TestLineLoop( const VertexPointer& vertices, std::size_t count, SelectionIntersection& best ) override {
|
||||
if ( count == 0 ) {
|
||||
return;
|
||||
}
|
||||
Vector4 clipped[9];
|
||||
for ( VertexPointer::iterator i = vertices.begin(), end = i + count, prev = i + ( count - 1 ); i != end; prev = i, ++i )
|
||||
{
|
||||
BestPoint(
|
||||
matrix4_clip_line(
|
||||
m_local2view,
|
||||
reinterpret_cast<const Vector3&>( ( *prev ) ),
|
||||
reinterpret_cast<const Vector3&>( ( *i ) ),
|
||||
clipped
|
||||
),
|
||||
clipped,
|
||||
best,
|
||||
m_cull
|
||||
);
|
||||
}
|
||||
}
|
||||
void TestLineStrip( const VertexPointer& vertices, std::size_t count, SelectionIntersection& best ) override {
|
||||
if ( count == 0 ) {
|
||||
return;
|
||||
}
|
||||
Vector4 clipped[9];
|
||||
for ( VertexPointer::iterator i = vertices.begin(), end = i + count, next = i + 1; next != end; i = next, ++next )
|
||||
{
|
||||
BestPoint(
|
||||
matrix4_clip_line(
|
||||
m_local2view,
|
||||
reinterpret_cast<const Vector3&>( ( *i ) ),
|
||||
reinterpret_cast<const Vector3&>( ( *next ) ),
|
||||
clipped
|
||||
),
|
||||
clipped,
|
||||
best,
|
||||
m_cull
|
||||
);
|
||||
}
|
||||
}
|
||||
void TestLines( const VertexPointer& vertices, std::size_t count, SelectionIntersection& best ) override {
|
||||
if ( count == 0 ) {
|
||||
return;
|
||||
}
|
||||
Vector4 clipped[9];
|
||||
for ( VertexPointer::iterator i = vertices.begin(), end = i + count; i != end; i += 2 )
|
||||
{
|
||||
BestPoint(
|
||||
matrix4_clip_line(
|
||||
m_local2view,
|
||||
reinterpret_cast<const Vector3&>( ( *i ) ),
|
||||
reinterpret_cast<const Vector3&>( ( *( i + 1 ) ) ),
|
||||
clipped
|
||||
),
|
||||
clipped,
|
||||
best,
|
||||
m_cull
|
||||
);
|
||||
}
|
||||
}
|
||||
void TestTriangles( const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best ) override {
|
||||
Vector4 clipped[9];
|
||||
for ( IndexPointer::iterator i( indices.begin() ); i != indices.end(); i += 3 )
|
||||
{
|
||||
BestPoint(
|
||||
matrix4_clip_triangle(
|
||||
m_local2view,
|
||||
reinterpret_cast<const Vector3&>( vertices[*i] ),
|
||||
reinterpret_cast<const Vector3&>( vertices[*( i + 1 )] ),
|
||||
reinterpret_cast<const Vector3&>( vertices[*( i + 2 )] ),
|
||||
clipped
|
||||
),
|
||||
clipped,
|
||||
best,
|
||||
m_cull
|
||||
);
|
||||
}
|
||||
}
|
||||
void TestQuads( const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best ) override {
|
||||
Vector4 clipped[9];
|
||||
for ( IndexPointer::iterator i( indices.begin() ); i != indices.end(); i += 4 )
|
||||
{
|
||||
BestPoint(
|
||||
matrix4_clip_triangle(
|
||||
m_local2view,
|
||||
reinterpret_cast<const Vector3&>( vertices[*i] ),
|
||||
reinterpret_cast<const Vector3&>( vertices[*( i + 1 )] ),
|
||||
reinterpret_cast<const Vector3&>( vertices[*( i + 3 )] ),
|
||||
clipped
|
||||
),
|
||||
clipped,
|
||||
best,
|
||||
m_cull
|
||||
);
|
||||
BestPoint(
|
||||
matrix4_clip_triangle(
|
||||
m_local2view,
|
||||
reinterpret_cast<const Vector3&>( vertices[*( i + 1 )] ),
|
||||
reinterpret_cast<const Vector3&>( vertices[*( i + 2 )] ),
|
||||
reinterpret_cast<const Vector3&>( vertices[*( i + 3 )] ),
|
||||
clipped
|
||||
),
|
||||
clipped,
|
||||
best,
|
||||
m_cull
|
||||
);
|
||||
}
|
||||
}
|
||||
void TestQuadStrip( const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best ) override {
|
||||
Vector4 clipped[9];
|
||||
for ( IndexPointer::iterator i( indices.begin() ); i + 2 != indices.end(); i += 2 )
|
||||
{
|
||||
BestPoint(
|
||||
matrix4_clip_triangle(
|
||||
m_local2view,
|
||||
reinterpret_cast<const Vector3&>( vertices[*i] ),
|
||||
reinterpret_cast<const Vector3&>( vertices[*( i + 1 )] ),
|
||||
reinterpret_cast<const Vector3&>( vertices[*( i + 2 )] ),
|
||||
clipped
|
||||
),
|
||||
clipped,
|
||||
best,
|
||||
m_cull
|
||||
);
|
||||
BestPoint(
|
||||
matrix4_clip_triangle(
|
||||
m_local2view,
|
||||
reinterpret_cast<const Vector3&>( vertices[*( i + 2 )] ),
|
||||
reinterpret_cast<const Vector3&>( vertices[*( i + 1 )] ),
|
||||
reinterpret_cast<const Vector3&>( vertices[*( i + 3 )] ),
|
||||
clipped
|
||||
),
|
||||
clipped,
|
||||
best,
|
||||
m_cull
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user