split selection.cpp

This commit is contained in:
Garux
2026-01-03 20:27:45 +05:00
parent 410ba15aea
commit 5535babca9
27 changed files with 6480 additions and 5963 deletions
+7
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+291
View File
@@ -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 );
}
+92
View File
@@ -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
+84
View File
@@ -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;
}
};
+323
View File
@@ -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();
}
}
}
}
};
+106
View File
@@ -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;
}
};
+178
View File
@@ -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;
}
};
+66
View File
@@ -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;
}
};
+368
View File
@@ -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 );
}
};
+316
View File
@@ -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 );
}
+38
View File
@@ -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 );
+605
View File
@@ -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 );
}
+35
View File
@@ -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;
+311
View File
@@ -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 );
}
+32
View File
@@ -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 );
+157
View File
@@ -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 );
}
+30
View File
@@ -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 );
+392
View File
@@ -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 );
}
+35
View File
@@ -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 );
+219
View File
@@ -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 );
}
+33
View File
@@ -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
+35
View File
@@ -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();
+250
View File
@@ -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();
}
};
+217
View File
@@ -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;
}
};
+503
View File
@@ -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
);
}
}
};