From: Thomas Debesse Date: Tue, 26 May 2020 14:00:22 +0000 (+0200) Subject: Merge commit '3a78d902017a780e65f21f12c709aa746dfcab84' into garux-merge X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=6a7a6b309311c16138981200f4539770755c243a;p=xonotic%2Fnetradiant.git Merge commit '3a78d902017a780e65f21f12c709aa746dfcab84' into garux-merge --- 6a7a6b309311c16138981200f4539770755c243a diff --cc libs/gtkutil/xorrectangle.cpp index b9b15faf,e4f46c6b..13b9ba2b --- a/libs/gtkutil/xorrectangle.cpp +++ b/libs/gtkutil/xorrectangle.cpp @@@ -1,103 -1,22 +1,104 @@@ -/* - Copyright (C) 2001-2006, William Joseph. - All Rights Reserved. +#include "xorrectangle.h" - This file is part of GtkRadiant. +#include - 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. +#include "gtkutil/glwidget.h" +#include "igl.h" - 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. +#include - 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 "stream/stringstream.h" -#include "xorrectangle.h" +bool XORRectangle::initialised() const +{ + return !!cr; +} + +void XORRectangle::lazy_init() +{ + if (!initialised()) { + cr = gdk_cairo_create(gtk_widget_get_window(m_widget)); + } +} + +void XORRectangle::draw() const +{ + const int x = float_to_integer(m_rectangle.x); + const int y = float_to_integer(m_rectangle.y); + const int w = float_to_integer(m_rectangle.w); + const int h = float_to_integer(m_rectangle.h); + GtkAllocation allocation; + gtk_widget_get_allocation(m_widget, &allocation); + cairo_rectangle(cr, x, -(h) - (y - allocation.height), w, h); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_set_operator(cr, CAIRO_OPERATOR_DIFFERENCE); + cairo_stroke(cr); +} + +XORRectangle::XORRectangle(ui::GLArea widget) : m_widget(widget), cr(0) +{ +} + +XORRectangle::~XORRectangle() +{ + if (initialised()) { + cairo_destroy(cr); + } +} + +void XORRectangle::set(rectangle_t rectangle) +{ + if (gtk_widget_get_realized(m_widget)) { + if( m_rectangle.w != rectangle.w || m_rectangle.h != rectangle.h ){ + //if( !(m_rectangle.w == 0 && m_rectangle.h == 0 && rectangle.w == 0 && rectangle.h == 0) ){ + //globalOutputStream() << "m_x" << m_rectangle.x << " m_y" << m_rectangle.y << " m_w" << m_rectangle.w << " m_h" << m_rectangle.h << "\n"; + //globalOutputStream() << "__x" << rectangle.x << " __y" << rectangle.y << " __w" << rectangle.w << " __h" << rectangle.h << "\n"; + if ( glwidget_make_current( m_widget ) != FALSE ) { + GlobalOpenGL_debugAssertNoErrors(); + + gint width, height; + gdk_gl_drawable_get_size( gtk_widget_get_gl_drawable( m_widget ), &width, &height ); + + glViewport( 0, 0, width, height ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0, width, 0, height, -100, 100 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + glDisable( GL_DEPTH_TEST ); + + glDrawBuffer( GL_FRONT ); + + glEnable( GL_BLEND ); + glBlendFunc( GL_ONE_MINUS_DST_COLOR, GL_ZERO ); + + glLineWidth( 2 ); + glColor3f( 1, 1, 1 ); + glDisable( GL_TEXTURE_2D ); + glBegin( GL_LINE_LOOP ); + glVertex2f( m_rectangle.x, m_rectangle.y + m_rectangle.h ); + glVertex2f( m_rectangle.x + m_rectangle.w, m_rectangle.y + m_rectangle.h ); + glVertex2f( m_rectangle.x + m_rectangle.w, m_rectangle.y ); + glVertex2f( m_rectangle.x, m_rectangle.y ); + glEnd(); + + glBegin( GL_LINE_LOOP ); + glVertex2f( rectangle.x, rectangle.y + rectangle.h ); + glVertex2f( rectangle.x + rectangle.w, rectangle.y + rectangle.h ); + glVertex2f( rectangle.x + rectangle.w, rectangle.y ); + glVertex2f( rectangle.x, rectangle.y ); + glEnd(); + ++ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glDrawBuffer( GL_BACK ); + GlobalOpenGL_debugAssertNoErrors(); + //glwidget_swap_buffers( m_widget ); + glwidget_make_current( m_widget ); + } + } + m_rectangle = rectangle; + } +} diff --cc libs/gtkutil/xorrectangle.h.orig index 00000000,00000000..e1043d76 new file mode 100644 --- /dev/null +++ b/libs/gtkutil/xorrectangle.h.orig @@@ -1,0 -1,0 +1,143 @@@ ++/* ++ 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 ++ */ ++ ++#if !defined ( INCLUDED_XORRECTANGLE_H ) ++#define INCLUDED_XORRECTANGLE_H ++ ++#include ++#include ++#include "math/vector.h" ++ ++class rectangle_t ++{ ++public: ++rectangle_t() ++ : x( 0 ), y( 0 ), w( 0 ), h( 0 ) ++{} ++rectangle_t( float _x, float _y, float _w, float _h ) ++ : x( _x ), y( _y ), w( _w ), h( _h ) ++{} ++float x; ++float y; ++float w; ++float h; ++}; ++ ++struct Coord2D ++{ ++ float x, y; ++ Coord2D( float _x, float _y ) ++ : x( _x ), y( _y ){ ++ } ++}; ++ ++inline Coord2D coord2d_device2screen( const Coord2D& coord, unsigned int width, unsigned int height ){ ++ return Coord2D( ( ( coord.x + 1.0f ) * 0.5f ) * width, ( ( coord.y + 1.0f ) * 0.5f ) * height ); ++} ++ ++inline rectangle_t rectangle_from_area( const float min[2], const float max[2], unsigned int width, unsigned int height ){ ++ Coord2D botleft( coord2d_device2screen( Coord2D( min[0], min[1] ), width, height ) ); ++ Coord2D topright( coord2d_device2screen( Coord2D( max[0], max[1] ), width, height ) ); ++ return rectangle_t( botleft.x, botleft.y, topright.x - botleft.x, topright.y - botleft.y ); ++} ++ ++class XORRectangle ++{ ++ ++rectangle_t m_rectangle; ++ ++ui::GLArea m_widget; ++cairo_t *cr; ++ ++bool initialised() const; ++void lazy_init(); ++void draw() const; ++ ++public: ++<<<<<<< HEAD ++XORRectangle( ui::GLArea widget ); ++~XORRectangle(); ++void set( rectangle_t rectangle ); ++======= ++XORRectangle( GtkWidget* widget ) : m_widget( widget ){ ++} ++~XORRectangle(){ ++} ++void set( rectangle_t rectangle ){ ++ if ( GTK_WIDGET_REALIZED( m_widget ) ) { ++ if( m_rectangle.w != rectangle.w || m_rectangle.h != rectangle.h ){ ++ //if( !(m_rectangle.w == 0 && m_rectangle.h == 0 && rectangle.w == 0 && rectangle.h == 0) ){ ++ //globalOutputStream() << "m_x" << m_rectangle.x << " m_y" << m_rectangle.y << " m_w" << m_rectangle.w << " m_h" << m_rectangle.h << "\n"; ++ //globalOutputStream() << "__x" << rectangle.x << " __y" << rectangle.y << " __w" << rectangle.w << " __h" << rectangle.h << "\n"; ++ if ( glwidget_make_current( m_widget ) != FALSE ) { ++ GlobalOpenGL_debugAssertNoErrors(); ++ ++ gint width, height; ++ gdk_gl_drawable_get_size( gtk_widget_get_gl_drawable( m_widget ), &width, &height ); ++ ++ glViewport( 0, 0, width, height ); ++ glMatrixMode( GL_PROJECTION ); ++ glLoadIdentity(); ++ glOrtho( 0, width, 0, height, -100, 100 ); ++ ++ glMatrixMode( GL_MODELVIEW ); ++ glLoadIdentity(); ++ ++ glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); ++ glDisable( GL_DEPTH_TEST ); ++ ++ glDrawBuffer( GL_FRONT ); ++ ++ glEnable( GL_BLEND ); ++ glBlendFunc( GL_ONE_MINUS_DST_COLOR, GL_ZERO ); ++ ++ glLineWidth( 2 ); ++ glColor3f( 1, 1, 1 ); ++ glDisable( GL_TEXTURE_2D ); ++ glBegin( GL_LINE_LOOP ); ++ glVertex2f( m_rectangle.x, m_rectangle.y + m_rectangle.h ); ++ glVertex2f( m_rectangle.x + m_rectangle.w, m_rectangle.y + m_rectangle.h ); ++ glVertex2f( m_rectangle.x + m_rectangle.w, m_rectangle.y ); ++ glVertex2f( m_rectangle.x, m_rectangle.y ); ++ glEnd(); ++ ++ glBegin( GL_LINE_LOOP ); ++ glVertex2f( rectangle.x, rectangle.y + rectangle.h ); ++ glVertex2f( rectangle.x + rectangle.w, rectangle.y + rectangle.h ); ++ glVertex2f( rectangle.x + rectangle.w, rectangle.y ); ++ glVertex2f( rectangle.x, rectangle.y ); ++ glEnd(); ++ ++ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); ++ glDrawBuffer( GL_BACK ); ++ GlobalOpenGL_debugAssertNoErrors(); ++ //glwidget_swap_buffers( m_widget ); ++ glwidget_make_current( m_widget ); ++ } ++ } ++ m_rectangle = rectangle; ++ } ++} ++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84 ++}; ++ ++ ++#endif diff --cc radiant/brushmanip.cpp.orig index 00000000,00000000..ab329b85 new file mode 100644 --- /dev/null +++ b/radiant/brushmanip.cpp.orig @@@ -1,0 -1,0 +1,1332 @@@ ++/* ++ Copyright (C) 1999-2006 Id Software, Inc. and contributors. ++ For a list of contributors, see the accompanying CONTRIBUTORS file. ++ ++ 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 "brushmanip.h" ++ ++ ++#include "gtkutil/widget.h" ++#include "gtkutil/menu.h" ++#include "gtkmisc.h" ++#include "brushnode.h" ++#include "map.h" ++#include "texwindow.h" ++#include "gtkdlgs.h" ++#include "commands.h" ++#include "mainframe.h" ++#include "dialog.h" ++#include "xywindow.h" ++#include "preferences.h" ++ ++#include ++#include ++ ++void Brush_ConstructCuboid( Brush& brush, const AABB& bounds, const char* shader, const TextureProjection& projection ){ ++ const unsigned char box[3][2] = { { 0, 1 }, { 2, 0 }, { 1, 2 } }; ++ Vector3 mins( vector3_subtracted( bounds.origin, bounds.extents ) ); ++ Vector3 maxs( vector3_added( bounds.origin, bounds.extents ) ); ++ ++ brush.clear(); ++ brush.reserve( 6 ); ++ ++ { ++ for ( int i = 0; i < 3; ++i ) ++ { ++ Vector3 planepts1( maxs ); ++ Vector3 planepts2( maxs ); ++ planepts2[box[i][0]] = mins[box[i][0]]; ++ planepts1[box[i][1]] = mins[box[i][1]]; ++ ++ brush.addPlane( maxs, planepts1, planepts2, shader, projection ); ++ } ++ } ++ { ++ for ( int i = 0; i < 3; ++i ) ++ { ++ Vector3 planepts1( mins ); ++ Vector3 planepts2( mins ); ++ planepts1[box[i][0]] = maxs[box[i][0]]; ++ planepts2[box[i][1]] = maxs[box[i][1]]; ++ ++ brush.addPlane( mins, planepts1, planepts2, shader, projection ); ++ } ++ } ++} ++ ++inline float max_extent( const Vector3& extents ){ ++ return std::max( std::max( extents[0], extents[1] ), extents[2] ); ++} ++ ++inline float max_extent_2d( const Vector3& extents, int axis ){ ++ switch ( axis ) ++ { ++ case 0: ++ return std::max( extents[1], extents[2] ); ++ case 1: ++ return std::max( extents[0], extents[2] ); ++ default: ++ return std::max( extents[0], extents[1] ); ++ } ++} ++ ++const std::size_t c_brushPrism_minSides = 3; ++const std::size_t c_brushPrism_maxSides = c_brush_maxFaces - 2; ++const char* const c_brushPrism_name = "brushPrism"; ++ ++void Brush_ConstructPrism( Brush& brush, const AABB& bounds, std::size_t sides, int axis, const char* shader, const TextureProjection& projection ){ ++ if ( sides < c_brushPrism_minSides ) { ++ globalErrorStream() << c_brushPrism_name << ": sides " << Unsigned( sides ) << ": too few sides, minimum is " << Unsigned( c_brushPrism_minSides ) << "\n"; ++ return; ++ } ++ if ( sides > c_brushPrism_maxSides ) { ++ globalErrorStream() << c_brushPrism_name << ": sides " << Unsigned( sides ) << ": too many sides, maximum is " << Unsigned( c_brushPrism_maxSides ) << "\n"; ++ return; ++ } ++ ++ brush.clear(); ++ brush.reserve( sides + 2 ); ++ ++ Vector3 mins( vector3_subtracted( bounds.origin, bounds.extents ) ); ++ Vector3 maxs( vector3_added( bounds.origin, bounds.extents ) ); ++ ++ float radius = max_extent_2d( bounds.extents, axis ); ++ const Vector3& mid = bounds.origin; ++ Vector3 planepts[3]; ++ ++ planepts[2][( axis + 1 ) % 3] = mins[( axis + 1 ) % 3]; ++ planepts[2][( axis + 2 ) % 3] = mins[( axis + 2 ) % 3]; ++ planepts[2][axis] = maxs[axis]; ++ planepts[1][( axis + 1 ) % 3] = maxs[( axis + 1 ) % 3]; ++ planepts[1][( axis + 2 ) % 3] = mins[( axis + 2 ) % 3]; ++ planepts[1][axis] = maxs[axis]; ++ planepts[0][( axis + 1 ) % 3] = maxs[( axis + 1 ) % 3]; ++ planepts[0][( axis + 2 ) % 3] = maxs[( axis + 2 ) % 3]; ++ planepts[0][axis] = maxs[axis]; ++ ++ brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection ); ++ ++ planepts[0][( axis + 1 ) % 3] = mins[( axis + 1 ) % 3]; ++ planepts[0][( axis + 2 ) % 3] = mins[( axis + 2 ) % 3]; ++ planepts[0][axis] = mins[axis]; ++ planepts[1][( axis + 1 ) % 3] = maxs[( axis + 1 ) % 3]; ++ planepts[1][( axis + 2 ) % 3] = mins[( axis + 2 ) % 3]; ++ planepts[1][axis] = mins[axis]; ++ planepts[2][( axis + 1 ) % 3] = maxs[( axis + 1 ) % 3]; ++ planepts[2][( axis + 2 ) % 3] = maxs[( axis + 2 ) % 3]; ++ planepts[2][axis] = mins[axis]; ++ ++ brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection ); ++ ++ for ( std::size_t i = 0 ; i < sides ; ++i ) ++ { ++ double sv = sin( i * 3.14159265 * 2 / sides ); ++ double cv = cos( i * 3.14159265 * 2 / sides ); ++ ++// planepts[0][( axis + 1 ) % 3] = static_cast( floor( mid[( axis + 1 ) % 3] + radius * cv + 0.5 ) ); ++// planepts[0][( axis + 2 ) % 3] = static_cast( floor( mid[( axis + 2 ) % 3] + radius * sv + 0.5 ) ); ++ planepts[0][( axis + 1 ) % 3] = static_cast( mid[( axis + 1 ) % 3] + radius * cv ); ++ planepts[0][( axis + 2 ) % 3] = static_cast( mid[( axis + 2 ) % 3] + radius * sv ); ++ planepts[0][axis] = mins[axis]; ++ ++ planepts[1][( axis + 1 ) % 3] = planepts[0][( axis + 1 ) % 3]; ++ planepts[1][( axis + 2 ) % 3] = planepts[0][( axis + 2 ) % 3]; ++ planepts[1][axis] = maxs[axis]; ++ ++// planepts[2][( axis + 1 ) % 3] = static_cast( floor( planepts[0][( axis + 1 ) % 3] - radius * sv + 0.5 ) ); ++// planepts[2][( axis + 2 ) % 3] = static_cast( floor( planepts[0][( axis + 2 ) % 3] + radius * cv + 0.5 ) ); ++ planepts[2][( axis + 1 ) % 3] = static_cast( planepts[0][( axis + 1 ) % 3] - radius * sv ); ++ planepts[2][( axis + 2 ) % 3] = static_cast( planepts[0][( axis + 2 ) % 3] + radius * cv ); ++ planepts[2][axis] = maxs[axis]; ++ //globalOutputStream() << planepts[0] << " " << planepts[2] << " #" << i << " sin " << sv << " cos " << cv << "\n"; ++ ++ brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection ); ++ } ++} ++ ++const std::size_t c_brushCone_minSides = 3; ++const std::size_t c_brushCone_maxSides = c_brush_maxFaces - 1; ++const char* const c_brushCone_name = "brushCone"; ++ ++void Brush_ConstructCone( Brush& brush, const AABB& bounds, std::size_t sides, const char* shader, const TextureProjection& projection ){ ++ if ( sides < c_brushCone_minSides ) { ++ globalErrorStream() << c_brushCone_name << ": sides " << Unsigned( sides ) << ": too few sides, minimum is " << Unsigned( c_brushCone_minSides ) << "\n"; ++ return; ++ } ++ if ( sides > c_brushCone_maxSides ) { ++ globalErrorStream() << c_brushCone_name << ": sides " << Unsigned( sides ) << ": too many sides, maximum is " << Unsigned( c_brushCone_maxSides ) << "\n"; ++ return; ++ } ++ ++ brush.clear(); ++ brush.reserve( sides + 1 ); ++ ++ Vector3 mins( vector3_subtracted( bounds.origin, bounds.extents ) ); ++ Vector3 maxs( vector3_added( bounds.origin, bounds.extents ) ); ++ ++ float radius = max_extent( bounds.extents ); ++ const Vector3& mid = bounds.origin; ++ Vector3 planepts[3]; ++ ++ planepts[0][0] = mins[0]; planepts[0][1] = mins[1]; planepts[0][2] = mins[2]; ++ planepts[1][0] = maxs[0]; planepts[1][1] = mins[1]; planepts[1][2] = mins[2]; ++ planepts[2][0] = maxs[0]; planepts[2][1] = maxs[1]; planepts[2][2] = mins[2]; ++ ++ brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection ); ++ ++ for ( std::size_t i = 0 ; i < sides ; ++i ) ++ { ++ double sv = sin( i * 3.14159265 * 2 / sides ); ++ double cv = cos( i * 3.14159265 * 2 / sides ); ++ ++ planepts[0][0] = static_cast( mid[0] + radius * cv ); ++ planepts[0][1] = static_cast( mid[1] + radius * sv ); ++// planepts[0][0] = static_cast( floor( mid[0] + radius * cv + 0.5 ) ); ++// planepts[0][1] = static_cast( floor( mid[1] + radius * sv + 0.5 ) ); ++ planepts[0][2] = mins[2]; ++ ++ planepts[1][0] = mid[0]; ++ planepts[1][1] = mid[1]; ++ planepts[1][2] = maxs[2]; ++ ++ planepts[2][0] = static_cast( planepts[0][0] - radius * sv ); ++ planepts[2][1] = static_cast( planepts[0][1] + radius * cv ); ++// planepts[2][0] = static_cast( floor( planepts[0][0] - radius * sv + 0.5 ) ); ++// planepts[2][1] = static_cast( floor( planepts[0][1] + radius * cv + 0.5 ) ); ++ planepts[2][2] = maxs[2]; ++ ++ brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection ); ++ } ++} ++ ++const std::size_t c_brushSphere_minSides = 3; ++const std::size_t c_brushSphere_maxSides = 31; ++const char* const c_brushSphere_name = "brushSphere"; ++ ++void Brush_ConstructSphere( Brush& brush, const AABB& bounds, std::size_t sides, const char* shader, const TextureProjection& projection ){ ++ if ( sides < c_brushSphere_minSides ) { ++ globalErrorStream() << c_brushSphere_name << ": sides " << Unsigned( sides ) << ": too few sides, minimum is " << Unsigned( c_brushSphere_minSides ) << "\n"; ++ return; ++ } ++ if ( sides > c_brushSphere_maxSides ) { ++ globalErrorStream() << c_brushSphere_name << ": sides " << Unsigned( sides ) << ": too many sides, maximum is " << Unsigned( c_brushSphere_maxSides ) << "\n"; ++ return; ++ } ++ ++ brush.clear(); ++ brush.reserve( sides * sides ); ++ ++ float radius = max_extent( bounds.extents ); ++ const Vector3& mid = bounds.origin; ++ Vector3 planepts[3]; ++ ++ double dt = 2 * c_pi / sides; ++ double dp = c_pi / sides; ++ for ( std::size_t i = 0; i < sides; i++ ) ++ { ++ for ( std::size_t j = 0; j < sides - 1; j++ ) ++ { ++ double t = i * dt; ++ double p = float(j * dp - c_pi / 2); ++ ++ planepts[0] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t, p ), radius ) ); ++ planepts[1] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t, p + dp ), radius ) ); ++ planepts[2] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t + dt, p + dp ), radius ) ); ++ ++ brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection ); ++ } ++ } ++ ++ { ++ double p = ( sides - 1 ) * dp - c_pi / 2; ++ for ( std::size_t i = 0; i < sides; i++ ) ++ { ++ double t = i * dt; ++ ++ planepts[0] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t, p ), radius ) ); ++ planepts[1] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t + dt, p + dp ), radius ) ); ++ planepts[2] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t + dt, p ), radius ) ); ++ ++ brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection ); ++ } ++ } ++} ++ ++const std::size_t c_brushRock_minSides = 10; ++const std::size_t c_brushRock_maxSides = 1000; ++const char* const c_brushRock_name = "brushRock"; ++ ++void Brush_ConstructRock( Brush& brush, const AABB& bounds, std::size_t sides, const char* shader, const TextureProjection& projection ){ ++ if ( sides < c_brushRock_minSides ) { ++ globalErrorStream() << c_brushRock_name << ": sides " << Unsigned( sides ) << ": too few sides, minimum is " << Unsigned( c_brushRock_minSides ) << "\n"; ++ return; ++ } ++ if ( sides > c_brushRock_maxSides ) { ++ globalErrorStream() << c_brushRock_name << ": sides " << Unsigned( sides ) << ": too many sides, maximum is " << Unsigned( c_brushRock_maxSides ) << "\n"; ++ return; ++ } ++ ++ brush.clear(); ++ brush.reserve( sides * sides ); ++ ++ float radius = max_extent( bounds.extents ); ++ const Vector3& mid = bounds.origin; ++ Vector3 planepts[3]; ++ ++ for ( std::size_t j = 0; j < sides; j++ ) ++ { ++ planepts[0][0] = rand() - ( RAND_MAX / 2 ); ++ planepts[0][1] = rand() - ( RAND_MAX / 2 ); ++ planepts[0][2] = rand() - ( RAND_MAX / 2 ); ++ vector3_normalise( planepts[0] ); ++ ++ // find two vectors that are perpendicular to planepts[0] ++ ComputeAxisBase( planepts[0], planepts[1], planepts[2] ); ++ ++ planepts[0] = vector3_added( mid, vector3_scaled( planepts[0], radius ) ); ++ planepts[1] = vector3_added( planepts[0], vector3_scaled( planepts[1], radius ) ); ++ planepts[2] = vector3_added( planepts[0], vector3_scaled( planepts[2], radius ) ); ++ ++#if 0 ++ // make sure the orientation is right ++ if ( vector3_dot( vector3_subtracted( planepts[0], mid ), vector3_cross( vector3_subtracted( planepts[1], mid ), vector3_subtracted( planepts[2], mid ) ) ) > 0 ) { ++ Vector3 h; ++ h = planepts[1]; ++ planepts[1] = planepts[2]; ++ planepts[2] = h; ++ globalOutputStream() << "flip\n"; ++ } ++ else{ ++ globalOutputStream() << "no flip\n"; ++ } ++#endif ++ ++ brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection ); ++ } ++} ++ ++int GetViewAxis(){ ++ switch ( GlobalXYWnd_getCurrentViewType() ) ++ { ++ case XY: ++ return 2; ++ case XZ: ++ return 1; ++ case YZ: ++ return 0; ++ } ++ return 2; ++} ++ ++void Brush_ConstructPrefab( Brush& brush, EBrushPrefab type, const AABB& bounds, std::size_t sides, const char* shader, const TextureProjection& projection ){ ++ switch ( type ) ++ { ++ case eBrushCuboid: ++ { ++ UndoableCommand undo( "brushCuboid" ); ++ ++ Brush_ConstructCuboid( brush, bounds, shader, projection ); ++ } ++ break; ++ case eBrushPrism: ++ { ++ int axis = GetViewAxis(); ++ StringOutputStream command; ++ command << c_brushPrism_name << " -sides " << Unsigned( sides ) << " -axis " << axis; ++ UndoableCommand undo( command.c_str() ); ++ ++ Brush_ConstructPrism( brush, bounds, sides, axis, shader, projection ); ++ } ++ break; ++ case eBrushCone: ++ { ++ StringOutputStream command; ++ command << c_brushCone_name << " -sides " << Unsigned( sides ); ++ UndoableCommand undo( command.c_str() ); ++ ++ Brush_ConstructCone( brush, bounds, sides, shader, projection ); ++ } ++ break; ++ case eBrushSphere: ++ { ++ StringOutputStream command; ++ command << c_brushSphere_name << " -sides " << Unsigned( sides ); ++ UndoableCommand undo( command.c_str() ); ++ ++ Brush_ConstructSphere( brush, bounds, sides, shader, projection ); ++ } ++ break; ++ case eBrushRock: ++ { ++ StringOutputStream command; ++ command << c_brushRock_name << " -sides " << Unsigned( sides ); ++ UndoableCommand undo( command.c_str() ); ++ ++ Brush_ConstructRock( brush, bounds, sides, shader, projection ); ++ } ++ break; ++ } ++} ++ ++ ++void ConstructRegionBrushes( scene::Node* brushes[6], const Vector3& region_mins, const Vector3& region_maxs ){ ++ { ++ // set mins ++ Vector3 mins( region_mins[0] - 32, region_mins[1] - 32, region_mins[2] - 32 ); ++ ++ // vary maxs ++ for ( std::size_t i = 0; i < 3; i++ ) ++ { ++ Vector3 maxs( region_maxs[0] + 32, region_maxs[1] + 32, region_maxs[2] + 32 ); ++ maxs[i] = region_mins[i]; ++ Brush_ConstructCuboid( *Node_getBrush( *brushes[i] ), aabb_for_minmax( mins, maxs ), texdef_name_default(), TextureProjection() ); ++ } ++ } ++ ++ { ++ // set maxs ++ Vector3 maxs( region_maxs[0] + 32, region_maxs[1] + 32, region_maxs[2] + 32 ); ++ ++ // vary mins ++ for ( std::size_t i = 0; i < 3; i++ ) ++ { ++ Vector3 mins( region_mins[0] - 32, region_mins[1] - 32, region_mins[2] - 32 ); ++ mins[i] = region_maxs[i]; ++ Brush_ConstructCuboid( *Node_getBrush( *brushes[i + 3] ), aabb_for_minmax( mins, maxs ), texdef_name_default(), TextureProjection() ); ++ } ++ } ++} ++ ++ ++void Scene_BrushSetTexdef_Selected( scene::Graph& graph, const TextureProjection& projection ){ ++ Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) { ++ face.SetTexdef(projection); ++ }); ++ SceneChangeNotify(); ++} ++ ++void Scene_BrushSetTexdef_Component_Selected( scene::Graph& graph, const TextureProjection& projection ){ ++ Scene_ForEachSelectedBrushFace(graph, [&](Face &face) { ++ face.SetTexdef(projection); ++ }); ++ SceneChangeNotify(); ++} ++ ++ ++<<<<<<< HEAD ++======= ++class FaceSetFlags ++{ ++const ContentsFlagsValue& m_flags; ++public: ++FaceSetFlags( const ContentsFlagsValue& flags ) : m_flags( flags ){ ++} ++void operator()( Face& face ) const { ++ face.SetFlags( m_flags ); ++} ++}; ++ ++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84 ++void Scene_BrushSetFlags_Selected( scene::Graph& graph, const ContentsFlagsValue& flags ){ ++ Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) { ++ face.SetFlags(flags); ++ }); ++ SceneChangeNotify(); ++} ++ ++void Scene_BrushSetFlags_Component_Selected( scene::Graph& graph, const ContentsFlagsValue& flags ){ ++ Scene_ForEachSelectedBrushFace(graph, [&](Face &face) { ++ face.SetFlags(flags); ++ }); ++ SceneChangeNotify(); ++} ++ ++void Scene_BrushShiftTexdef_Selected( scene::Graph& graph, float s, float t ){ ++ Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) { ++ face.ShiftTexdef(s, t); ++ }); ++ SceneChangeNotify(); ++} ++ ++void Scene_BrushShiftTexdef_Component_Selected( scene::Graph& graph, float s, float t ){ ++ Scene_ForEachSelectedBrushFace(graph, [&](Face &face) { ++ face.ShiftTexdef(s, t); ++ }); ++ SceneChangeNotify(); ++} ++ ++void Scene_BrushScaleTexdef_Selected( scene::Graph& graph, float s, float t ){ ++ Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) { ++ face.ScaleTexdef(s, t); ++ }); ++ SceneChangeNotify(); ++} ++ ++void Scene_BrushScaleTexdef_Component_Selected( scene::Graph& graph, float s, float t ){ ++ Scene_ForEachSelectedBrushFace(graph, [&](Face &face) { ++ face.ScaleTexdef(s, t); ++ }); ++ SceneChangeNotify(); ++} ++ ++void Scene_BrushRotateTexdef_Selected( scene::Graph& graph, float angle ){ ++ Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) { ++ face.RotateTexdef(angle); ++ }); ++ SceneChangeNotify(); ++} ++ ++void Scene_BrushRotateTexdef_Component_Selected( scene::Graph& graph, float angle ){ ++ Scene_ForEachSelectedBrushFace(graph, [&](Face &face) { ++ face.RotateTexdef(angle); ++ }); ++ SceneChangeNotify(); ++} ++ ++ ++void Scene_BrushSetShader_Selected( scene::Graph& graph, const char* name ){ ++ Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) { ++ face.SetShader(name); ++ }); ++ SceneChangeNotify(); ++} ++ ++void Scene_BrushSetShader_Component_Selected( scene::Graph& graph, const char* name ){ ++ Scene_ForEachSelectedBrushFace(graph, [&](Face &face) { ++ face.SetShader(name); ++ }); ++ SceneChangeNotify(); ++} ++ ++void Scene_BrushSetDetail_Selected( scene::Graph& graph, bool detail ){ ++ Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) { ++ face.setDetail(detail); ++ }); ++ SceneChangeNotify(); ++} ++ ++bool Face_FindReplaceShader( Face& face, const char* find, const char* replace ){ ++ if ( shader_equal( face.GetShader(), find ) ) { ++ face.SetShader( replace ); ++ return true; ++ } ++ return false; ++} ++ ++bool DoingSearch( const char *repl ){ ++ return ( repl == NULL || ( strcmp( "textures/", repl ) == 0 ) ); ++} ++ ++void Scene_BrushFindReplaceShader( scene::Graph& graph, const char* find, const char* replace ){ ++ if ( DoingSearch( replace ) ) { ++ Scene_ForEachBrush_ForEachFaceInstance(graph, [&](FaceInstance &faceinst) { ++ if (shader_equal(faceinst.getFace().GetShader(), find)) { ++ faceinst.setSelected(SelectionSystem::eFace, true); ++ } ++ }); ++ } ++ else ++ { ++ Scene_ForEachBrush_ForEachFace(graph, [&](Face &face) { Face_FindReplaceShader(face, find, replace); }); ++ } ++} ++ ++void Scene_BrushFindReplaceShader_Selected( scene::Graph& graph, const char* find, const char* replace ){ ++ if ( DoingSearch( replace ) ) { ++ Scene_ForEachSelectedBrush_ForEachFaceInstance(graph, [&](FaceInstance &faceinst) { ++ if (shader_equal(faceinst.getFace().GetShader(), find)) { ++ faceinst.setSelected(SelectionSystem::eFace, true); ++ } ++ }); ++ } ++ else ++ { ++ Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) { ++ Face_FindReplaceShader(face, find, replace); ++ }); ++ } ++} ++ ++// TODO: find for components ++// d1223m: dont even know what they are... ++void Scene_BrushFindReplaceShader_Component_Selected( scene::Graph& graph, const char* find, const char* replace ){ ++ if ( DoingSearch( replace ) ) { ++ ++ } ++ else ++ { ++ Scene_ForEachSelectedBrushFace(graph, [&](Face &face) { ++ Face_FindReplaceShader(face, find, replace); ++ }); ++ } ++} ++ ++ ++void Scene_BrushFitTexture_Selected( scene::Graph& graph, float s_repeat, float t_repeat ){ ++ Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) { ++ face.FitTexture(s_repeat, t_repeat); ++ }); ++ SceneChangeNotify(); ++} ++ ++void Scene_BrushFitTexture_Component_Selected( scene::Graph& graph, float s_repeat, float t_repeat ){ ++ Scene_ForEachSelectedBrushFace(graph, [&](Face &face) { ++ face.FitTexture(s_repeat, t_repeat); ++ }); ++ SceneChangeNotify(); ++} ++ ++TextureProjection g_defaultTextureProjection; ++ ++const TextureProjection& TextureTransform_getDefault(){ ++ TexDef_Construct_Default( g_defaultTextureProjection ); ++ return g_defaultTextureProjection; ++} ++ ++void Scene_BrushConstructPrefab( scene::Graph& graph, EBrushPrefab type, std::size_t sides, const char* shader ){ ++ if ( GlobalSelectionSystem().countSelected() != 0 ) { ++ const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path(); ++ ++ Brush* brush = Node_getBrush( path.top() ); ++ if ( brush != 0 ) { ++ AABB bounds = brush->localAABB(); // copy bounds because the brush will be modified ++ Brush_ConstructPrefab( *brush, type, bounds, sides, shader, TextureTransform_getDefault() ); ++ SceneChangeNotify(); ++ } ++ } ++} ++ ++void Scene_BrushResize_Selected( scene::Graph& graph, const AABB& bounds, const char* shader ){ ++ if ( GlobalSelectionSystem().countSelected() != 0 ) { ++ const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path(); ++ ++ Brush* brush = Node_getBrush( path.top() ); ++ if ( brush != 0 ) { ++ Brush_ConstructCuboid( *brush, bounds, shader, TextureTransform_getDefault() ); ++ SceneChangeNotify(); ++ } ++ } ++} ++ ++bool Brush_hasShader( const Brush& brush, const char* name ){ ++ for ( Brush::const_iterator i = brush.begin(); i != brush.end(); ++i ) ++ { ++ if ( shader_equal( ( *i )->GetShader(), name ) ) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++class BrushSelectByShaderWalker : public scene::Graph::Walker ++{ ++const char* m_name; ++public: ++BrushSelectByShaderWalker( const char* name ) ++ : m_name( name ){ ++} ++ ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ if ( path.top().get().visible() ) { ++ Brush* brush = Node_getBrush( path.top() ); ++ if ( brush != 0 && Brush_hasShader( *brush, m_name ) ) { ++ Instance_getSelectable( instance )->setSelected( true ); ++ } ++ } ++ else{ ++ return false; ++ } ++ return true; ++} ++}; ++ ++void Scene_BrushSelectByShader( scene::Graph& graph, const char* name ){ ++ graph.traverse( BrushSelectByShaderWalker( name ) ); ++} ++ ++void Scene_BrushSelectByShader_Component( scene::Graph& graph, const char* name ){ ++ Scene_ForEachSelectedBrush_ForEachFaceInstance(graph, [&](FaceInstance &face) { ++ printf("checking %s = %s\n", face.getFace().GetShader(), name); ++ if (shader_equal(face.getFace().GetShader(), name)) { ++ face.setSelected( SelectionSystem::eFace, true ); ++ } ++ }); ++} ++ ++void Scene_BrushGetTexdef_Selected( scene::Graph& graph, TextureProjection& projection ){ ++ bool done = false; ++ Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) { ++ if (!done) { ++ done = true; ++ face.GetTexdef(projection); ++} ++ }); ++} ++ ++void Scene_BrushGetTexdef_Component_Selected( scene::Graph& graph, TextureProjection& projection ){ ++#if 1 ++ if ( !g_SelectedFaceInstances.empty() ) { ++ FaceInstance& faceInstance = g_SelectedFaceInstances.last(); ++ faceInstance.getFace().GetTexdef( projection ); ++ } ++#else ++ FaceGetTexdef visitor( projection ); ++ Scene_ForEachSelectedBrushFace( graph, visitor ); ++#endif ++} ++ ++void Scene_BrushGetShaderSize_Component_Selected( scene::Graph& graph, size_t& width, size_t& height ){ ++ if ( !g_SelectedFaceInstances.empty() ) { ++ FaceInstance& faceInstance = g_SelectedFaceInstances.last(); ++ width = faceInstance.getFace().getShader().width(); ++ height = faceInstance.getFace().getShader().height(); ++ } ++} ++ ++ ++void Scene_BrushGetFlags_Selected( scene::Graph& graph, ContentsFlagsValue& flags ){ ++#if 1 ++ if ( GlobalSelectionSystem().countSelected() != 0 ) { ++ BrushInstance* brush = Instance_getBrush( GlobalSelectionSystem().ultimateSelected() ); ++ if ( brush != 0 ) { ++ bool done = false; ++ Brush_forEachFace(*brush, [&](Face &face) { ++ if (!done) { ++ done = true; ++ face.GetFlags(flags); ++ } ++ }); ++ } ++ } ++#else ++ Scene_ForEachSelectedBrush_ForEachFace( graph, FaceGetFlags( flags ) ); ++#endif ++} ++ ++void Scene_BrushGetFlags_Component_Selected( scene::Graph& graph, ContentsFlagsValue& flags ){ ++#if 1 ++ if ( !g_SelectedFaceInstances.empty() ) { ++ FaceInstance& faceInstance = g_SelectedFaceInstances.last(); ++ faceInstance.getFace().GetFlags( flags ); ++ } ++#else ++ Scene_ForEachSelectedBrushFace( graph, FaceGetFlags( flags ) ); ++#endif ++} ++ ++ ++void Scene_BrushGetShader_Selected( scene::Graph& graph, CopiedString& shader ){ ++#if 1 ++ if ( GlobalSelectionSystem().countSelected() != 0 ) { ++ BrushInstance* brush = Instance_getBrush( GlobalSelectionSystem().ultimateSelected() ); ++ if ( brush != 0 ) { ++ bool done = false; ++ Brush_forEachFace(*brush, [&](Face &face) { ++ if (!done) { ++ done = true; ++ shader = face.GetShader(); ++ } ++ }); ++ } ++ } ++#else ++ Scene_ForEachSelectedBrush_ForEachFace( graph, FaceGetShader( shader ) ); ++#endif ++} ++ ++void Scene_BrushGetShader_Component_Selected( scene::Graph& graph, CopiedString& shader ){ ++#if 1 ++ if ( !g_SelectedFaceInstances.empty() ) { ++ FaceInstance& faceInstance = g_SelectedFaceInstances.last(); ++ shader = faceInstance.getFace().GetShader(); ++ } ++#else ++ FaceGetShader visitor( shader ); ++ Scene_ForEachSelectedBrushFace( graph, visitor ); ++#endif ++} ++ ++ ++class filter_face_shader : public FaceFilter ++{ ++const char* m_shader; ++public: ++filter_face_shader( const char* shader ) : m_shader( shader ){ ++} ++ ++bool filter( const Face& face ) const { ++ return shader_equal( face.GetShader(), m_shader ); ++} ++}; ++ ++class filter_face_shader_prefix : public FaceFilter ++{ ++const char* m_prefix; ++public: ++filter_face_shader_prefix( const char* prefix ) : m_prefix( prefix ){ ++} ++ ++bool filter( const Face& face ) const { ++ return shader_equal_n( face.GetShader(), m_prefix, strlen( m_prefix ) ); ++} ++}; ++ ++class filter_face_flags : public FaceFilter ++{ ++int m_flags; ++public: ++filter_face_flags( int flags ) : m_flags( flags ){ ++} ++ ++bool filter( const Face& face ) const { ++ return ( face.getShader().shaderFlags() & m_flags ) != 0; ++} ++}; ++ ++class filter_face_contents : public FaceFilter ++{ ++int m_contents; ++public: ++filter_face_contents( int contents ) : m_contents( contents ){ ++} ++ ++bool filter( const Face& face ) const { ++ return ( face.getShader().m_flags.m_contentFlags & m_contents ) != 0; ++} ++}; ++ ++ ++ ++class filter_brush_any_face : public BrushFilter ++{ ++FaceFilter* m_filter; ++public: ++filter_brush_any_face( FaceFilter* filter ) : m_filter( filter ){ ++} ++ ++bool filter( const Brush& brush ) const { ++ bool filtered = false; ++ Brush_forEachFace(brush, [&](Face &face) { ++ if (m_filter->filter(face)) { ++ filtered = true; ++ } ++ }); ++ return filtered; ++} ++}; ++ ++class filter_brush_all_faces : public BrushFilter ++{ ++FaceFilter* m_filter; ++public: ++filter_brush_all_faces( FaceFilter* filter ) : m_filter( filter ){ ++} ++bool filter( const Brush& brush ) const { ++ bool filtered = true; ++ Brush_forEachFace(brush, [&](Face &face) { ++ if (!m_filter->filter(face)) { ++ filtered = false; ++ } ++ }); ++ return filtered; ++} ++}; ++ ++ ++filter_face_flags g_filter_face_clip( QER_CLIP ); ++filter_brush_all_faces g_filter_brush_clip( &g_filter_face_clip ); ++ ++filter_face_shader g_filter_face_clip_q2( "textures/clip" ); ++filter_brush_all_faces g_filter_brush_clip_q2( &g_filter_face_clip_q2 ); ++ ++filter_face_shader g_filter_face_weapclip( "textures/common/weapclip" ); ++filter_brush_all_faces g_filter_brush_weapclip( &g_filter_face_weapclip ); ++ ++filter_face_shader g_filter_face_commonclip( "textures/common/clip" ); ++filter_brush_all_faces g_filter_brush_commonclip( &g_filter_face_commonclip ); ++ ++filter_face_shader g_filter_face_fullclip( "textures/common/fullclip" ); ++filter_brush_all_faces g_filter_brush_fullclip( &g_filter_face_fullclip ); ++ ++filter_face_shader g_filter_face_botclip( "textures/common/botclip" ); ++filter_brush_all_faces g_filter_brush_botclip( &g_filter_face_botclip ); ++ ++filter_face_shader_prefix g_filter_face_caulk( "textures/common/caulk" ); ++filter_brush_all_faces g_filter_brush_caulk( &g_filter_face_caulk ); ++ ++filter_face_shader_prefix g_filter_face_caulk_ja( "textures/system/caulk" ); ++filter_brush_all_faces g_filter_brush_caulk_ja( &g_filter_face_caulk_ja ); ++ ++filter_face_flags g_filter_face_liquids( QER_LIQUID ); ++filter_brush_any_face g_filter_brush_liquids( &g_filter_face_liquids ); ++ ++filter_face_shader_prefix g_filter_face_liquidsdir( "textures/liquids/" ); ++filter_brush_any_face g_filter_brush_liquidsdir( &g_filter_face_liquidsdir ); ++ ++filter_face_shader g_filter_face_hint( "textures/common/hint" ); ++filter_brush_any_face g_filter_brush_hint( &g_filter_face_hint ); ++ ++filter_face_shader g_filter_face_hintlocal( "textures/common/hintlocal" ); ++filter_brush_any_face g_filter_brush_hintlocal( &g_filter_face_hintlocal ); ++ ++filter_face_shader g_filter_face_hint_q2( "textures/hint" ); ++filter_brush_any_face g_filter_brush_hint_q2( &g_filter_face_hint_q2 ); ++ ++filter_face_shader g_filter_face_hint_ja( "textures/system/hint" ); ++filter_brush_any_face g_filter_brush_hint_ja( &g_filter_face_hint_ja ); ++ ++filter_face_shader g_filter_face_areaportal( "textures/common/areaportal" ); ++filter_brush_any_face g_filter_brush_areaportal( &g_filter_face_areaportal ); ++ ++filter_face_shader g_filter_face_visportal( "textures/editor/visportal" ); ++filter_brush_any_face g_filter_brush_visportal( &g_filter_face_visportal ); ++ ++filter_face_shader g_filter_face_clusterportal( "textures/common/clusterportal" ); ++filter_brush_all_faces g_filter_brush_clusterportal( &g_filter_face_clusterportal ); ++ ++filter_face_shader g_filter_face_lightgrid( "textures/common/lightgrid" ); ++filter_brush_all_faces g_filter_brush_lightgrid( &g_filter_face_lightgrid ); ++ ++filter_face_flags g_filter_face_translucent( QER_TRANS | QER_ALPHATEST ); ++filter_brush_any_face g_filter_brush_translucent( &g_filter_face_translucent ); ++ ++filter_face_contents g_filter_face_detail( BRUSH_DETAIL_MASK ); ++filter_brush_all_faces g_filter_brush_detail( &g_filter_face_detail ); ++ ++filter_face_shader_prefix g_filter_face_decals( "textures/decals/" ); ++filter_brush_any_face g_filter_brush_decals( &g_filter_face_decals ); ++ ++ ++void BrushFilters_construct(){ ++ add_brush_filter( g_filter_brush_clip, EXCLUDE_CLIP ); ++ add_brush_filter( g_filter_brush_clip_q2, EXCLUDE_CLIP ); ++ add_brush_filter( g_filter_brush_weapclip, EXCLUDE_CLIP ); ++ add_brush_filter( g_filter_brush_fullclip, EXCLUDE_CLIP ); ++ add_brush_filter( g_filter_brush_commonclip, EXCLUDE_CLIP ); ++ add_brush_filter( g_filter_brush_botclip, EXCLUDE_BOTCLIP ); ++ add_brush_filter( g_filter_brush_caulk, EXCLUDE_CAULK ); ++ add_brush_filter( g_filter_brush_caulk_ja, EXCLUDE_CAULK ); ++ add_face_filter( g_filter_face_caulk, EXCLUDE_CAULK ); ++ add_face_filter( g_filter_face_caulk_ja, EXCLUDE_CAULK ); ++ add_brush_filter( g_filter_brush_liquids, EXCLUDE_LIQUIDS ); ++ add_brush_filter( g_filter_brush_liquidsdir, EXCLUDE_LIQUIDS ); ++ add_brush_filter( g_filter_brush_hint, EXCLUDE_HINTSSKIPS ); ++ add_brush_filter( g_filter_brush_hintlocal, EXCLUDE_HINTSSKIPS ); ++ add_brush_filter( g_filter_brush_hint_q2, EXCLUDE_HINTSSKIPS ); ++ add_brush_filter( g_filter_brush_hint_ja, EXCLUDE_HINTSSKIPS ); ++ add_brush_filter( g_filter_brush_clusterportal, EXCLUDE_CLUSTERPORTALS ); ++ add_brush_filter( g_filter_brush_visportal, EXCLUDE_VISPORTALS ); ++ add_brush_filter( g_filter_brush_areaportal, EXCLUDE_AREAPORTALS ); ++ add_brush_filter( g_filter_brush_translucent, EXCLUDE_TRANSLUCENT ); ++ add_brush_filter( g_filter_brush_detail, EXCLUDE_DETAILS ); ++ add_brush_filter( g_filter_brush_detail, EXCLUDE_STRUCTURAL, true ); ++ add_brush_filter( g_filter_brush_lightgrid, EXCLUDE_LIGHTGRID ); ++ add_brush_filter( g_filter_brush_decals, EXCLUDE_DECALS ); ++} ++ ++#if 0 ++ ++void normalquantisation_draw(){ ++ glPointSize( 1 ); ++ glBegin( GL_POINTS ); ++ for ( std::size_t i = 0; i <= c_quantise_normal; ++i ) ++ { ++ for ( std::size_t j = 0; j <= c_quantise_normal; ++j ) ++ { ++ Normal3f vertex( normal3f_normalised( Normal3f( ++ static_cast( c_quantise_normal - j - i ), ++ static_cast( i ), ++ static_cast( j ) ++ ) ) ); ++ VectorScale( normal3f_to_array( vertex ), 64.f, normal3f_to_array( vertex ) ); ++ glVertex3fv( normal3f_to_array( vertex ) ); ++ vertex.x = -vertex.x; ++ glVertex3fv( normal3f_to_array( vertex ) ); ++ } ++ } ++ glEnd(); ++} ++ ++class RenderableNormalQuantisation : public OpenGLRenderable ++{ ++public: ++void render( RenderStateFlags state ) const { ++ normalquantisation_draw(); ++} ++}; ++ ++const float g_test_quantise_normal = 1.f / static_cast( 1 << 3 ); ++ ++class TestNormalQuantisation ++{ ++void check_normal( const Normal3f& normal, const Normal3f& other ){ ++ spherical_t spherical = spherical_from_normal3f( normal ); ++ double longditude = RAD2DEG( spherical.longditude ); ++ double latitude = RAD2DEG( spherical.latitude ); ++ double x = cos( spherical.longditude ) * sin( spherical.latitude ); ++ double y = sin( spherical.longditude ) * sin( spherical.latitude ); ++ double z = cos( spherical.latitude ); ++ ++ ASSERT_MESSAGE( normal3f_dot( normal, other ) > 0.99, "bleh" ); ++} ++ ++void test_normal( const Normal3f& normal ){ ++ Normal3f test = normal3f_from_spherical( spherical_from_normal3f( normal ) ); ++ check_normal( normal, test ); ++ ++ EOctant octant = normal3f_classify_octant( normal ); ++ Normal3f folded = normal3f_fold_octant( normal, octant ); ++ ESextant sextant = normal3f_classify_sextant( folded ); ++ folded = normal3f_fold_sextant( folded, sextant ); ++ ++ double scale = static_cast( c_quantise_normal ) / ( folded.x + folded.y + folded.z ); ++ ++ double zbits = folded.z * scale; ++ double ybits = folded.y * scale; ++ ++ std::size_t zbits_q = static_cast( zbits ); ++ std::size_t ybits_q = static_cast( ybits ); ++ ++ ASSERT_MESSAGE( zbits_q <= ( c_quantise_normal / 8 ) * 3, "bleh" ); ++ ASSERT_MESSAGE( ybits_q <= ( c_quantise_normal / 2 ), "bleh" ); ++ ASSERT_MESSAGE( zbits_q + ( ( c_quantise_normal / 2 ) - ybits_q ) <= ( c_quantise_normal / 2 ), "bleh" ); ++ ++ std::size_t y_t = ( zbits_q < ( c_quantise_normal / 4 ) ) ? ybits_q : ( c_quantise_normal / 2 ) - ybits_q; ++ std::size_t z_t = ( zbits_q < ( c_quantise_normal / 4 ) ) ? zbits_q : ( c_quantise_normal / 2 ) - zbits_q; ++ std::size_t index = ( c_quantise_normal / 4 ) * y_t + z_t; ++ ASSERT_MESSAGE( index <= ( c_quantise_normal / 4 ) * ( c_quantise_normal / 2 ), "bleh" ); ++ ++ Normal3f tmp( c_quantise_normal - zbits_q - ybits_q, ybits_q, zbits_q ); ++ tmp = normal3f_normalised( tmp ); ++ ++ Normal3f unfolded = normal3f_unfold_octant( normal3f_unfold_sextant( tmp, sextant ), octant ); ++ ++ check_normal( normal, unfolded ); ++ ++ double dot = normal3f_dot( normal, unfolded ); ++ float length = VectorLength( normal3f_to_array( unfolded ) ); ++ float inv_length = 1 / length; ++ ++ Normal3f quantised = normal3f_quantised( normal ); ++ check_normal( normal, quantised ); ++} ++void test2( const Normal3f& normal, const Normal3f& other ){ ++ if ( normal3f_quantised( normal ) != normal3f_quantised( other ) ) { ++ int bleh = 0; ++ } ++} ++ ++static Normal3f normalise( float x, float y, float z ){ ++ return normal3f_normalised( Normal3f( x, y, z ) ); ++} ++ ++float vec_rand(){ ++ return static_cast( rand() - ( RAND_MAX / 2 ) ); ++} ++ ++Normal3f normal3f_rand(){ ++ return normalise( vec_rand(), vec_rand(), vec_rand() ); ++} ++ ++public: ++TestNormalQuantisation(){ ++ for ( int i = 4096; i > 0; --i ) ++ test_normal( normal3f_rand() ); ++ ++ test_normal( normalise( 1, 0, 0 ) ); ++ test_normal( normalise( 0, 1, 0 ) ); ++ test_normal( normalise( 0, 0, 1 ) ); ++ test_normal( normalise( 1, 1, 0 ) ); ++ test_normal( normalise( 1, 0, 1 ) ); ++ test_normal( normalise( 0, 1, 1 ) ); ++ ++ test_normal( normalise( 10000, 10000, 10000 ) ); ++ test_normal( normalise( 10000, 10000, 10001 ) ); ++ test_normal( normalise( 10000, 10000, 10002 ) ); ++ test_normal( normalise( 10000, 10000, 10010 ) ); ++ test_normal( normalise( 10000, 10000, 10020 ) ); ++ test_normal( normalise( 10000, 10000, 10030 ) ); ++ test_normal( normalise( 10000, 10000, 10100 ) ); ++ test_normal( normalise( 10000, 10000, 10101 ) ); ++ test_normal( normalise( 10000, 10000, 10102 ) ); ++ test_normal( normalise( 10000, 10000, 10200 ) ); ++ test_normal( normalise( 10000, 10000, 10201 ) ); ++ test_normal( normalise( 10000, 10000, 10202 ) ); ++ test_normal( normalise( 10000, 10000, 10203 ) ); ++ test_normal( normalise( 10000, 10000, 10300 ) ); ++ ++ ++ test2( normalise( 10000, 10000, 10000 ), normalise( 10000, 10000, 10001 ) ); ++ test2( normalise( 10000, 10000, 10001 ), normalise( 10000, 10001, 10000 ) ); ++} ++}; ++ ++TestNormalQuantisation g_testNormalQuantisation; ++ ++ ++#endif ++ ++#if 0 ++class TestSelectableObserver : public observer_template ++{ ++public: ++void notify( const Selectable& arguments ){ ++ bool bleh = arguments.isSelected(); ++} ++}; ++ ++inline void test_bleh(){ ++ TestSelectableObserver test; ++ ObservableSelectableInstance< SingleObservable< SelectionChangeCallback > > bleh; ++ bleh.attach( test ); ++ bleh.setSelected( true ); ++ bleh.detach( test ); ++} ++ ++class TestBleh ++{ ++public: ++TestBleh(){ ++ test_bleh(); ++} ++}; ++ ++const TestBleh testbleh; ++#endif ++ ++ ++#if 0 ++class TestRefcountedString ++{ ++public: ++TestRefcountedString(){ ++ { ++ // copy construct ++ SmartString string1( "string1" ); ++ SmartString string2( string1 ); ++ SmartString string3( string2 ); ++ } ++ { ++ // refcounted assignment ++ SmartString string1( "string1" ); ++ SmartString string2( "string2" ); ++ string1 = string2; ++ } ++ { ++ // copy assignment ++ SmartString string1( "string1" ); ++ SmartString string2( "string2" ); ++ string1 = string2.c_str(); ++ } ++ { ++ // self-assignment ++ SmartString string1( "string1" ); ++ string1 = string1; ++ } ++ { ++ // self-assignment via another reference ++ SmartString string1( "string1" ); ++ SmartString string2( string1 ); ++ string1 = string2; ++ } ++} ++}; ++ ++const TestRefcountedString g_testRefcountedString; ++ ++#endif ++ ++void Select_MakeDetail(){ ++ UndoableCommand undo( "brushSetDetail" ); ++ Scene_BrushSetDetail_Selected( GlobalSceneGraph(), true ); ++} ++ ++void Select_MakeStructural(){ ++ UndoableCommand undo( "brushClearDetail" ); ++ Scene_BrushSetDetail_Selected( GlobalSceneGraph(), false ); ++} ++ ++class BrushMakeSided ++{ ++std::size_t m_count; ++public: ++BrushMakeSided( std::size_t count ) ++ : m_count( count ){ ++} ++ ++void set(){ ++ Scene_BrushConstructPrefab( GlobalSceneGraph(), eBrushPrism, m_count, TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) ); ++} ++ ++typedef MemberCaller SetCaller; ++}; ++ ++ ++BrushMakeSided g_brushmakesided3( 3 ); ++BrushMakeSided g_brushmakesided4( 4 ); ++BrushMakeSided g_brushmakesided5( 5 ); ++BrushMakeSided g_brushmakesided6( 6 ); ++BrushMakeSided g_brushmakesided7( 7 ); ++BrushMakeSided g_brushmakesided8( 8 ); ++BrushMakeSided g_brushmakesided9( 9 ); ++ ++inline int axis_for_viewtype( int viewtype ){ ++ switch ( viewtype ) ++ { ++ case XY: ++ return 2; ++ case XZ: ++ return 1; ++ case YZ: ++ return 0; ++ } ++ return 2; ++} ++ ++class BrushPrefab ++{ ++EBrushPrefab m_type; ++public: ++BrushPrefab( EBrushPrefab type ) ++ : m_type( type ){ ++} ++ ++void set(){ ++ DoSides( m_type, axis_for_viewtype( GetViewAxis() ) ); ++} ++ ++typedef MemberCaller SetCaller; ++}; ++ ++BrushPrefab g_brushprism( eBrushPrism ); ++BrushPrefab g_brushcone( eBrushCone ); ++BrushPrefab g_brushsphere( eBrushSphere ); ++BrushPrefab g_brushrock( eBrushRock ); ++ ++ ++void FlipClip(); ++ ++void SplitClip(); ++ ++void Clip(); ++ ++void OnClipMode( bool enable ); ++ ++bool ClipMode(); ++ ++ ++void ClipSelected(){ ++ if ( ClipMode() ) { ++ UndoableCommand undo( "clipperClip" ); ++ Clip(); ++ } ++} ++ ++void SplitSelected(){ ++ if ( ClipMode() ) { ++ UndoableCommand undo( "clipperSplit" ); ++ SplitClip(); ++ } ++} ++ ++void FlipClipper(){ ++ FlipClip(); ++} ++ ++ ++Callback g_texture_lock_status_changed; ++ConstReferenceCaller &), PropertyImpl::Export> g_texdef_movelock_caller( g_brush_texturelock_enabled ); ++ToggleItem g_texdef_movelock_item( g_texdef_movelock_caller ); ++ ++void Texdef_ToggleMoveLock(){ ++ g_brush_texturelock_enabled = !g_brush_texturelock_enabled; ++ g_texdef_movelock_item.update(); ++ g_texture_lock_status_changed(); ++} ++ ++ ++void Brush_registerCommands(){ ++ GlobalToggles_insert( "TogTexLock", makeCallbackF(Texdef_ToggleMoveLock), ToggleItem::AddCallbackCaller( g_texdef_movelock_item ), Accelerator( 'T', (GdkModifierType)GDK_SHIFT_MASK ) ); ++ ++ GlobalCommands_insert( "BrushPrism", BrushPrefab::SetCaller( g_brushprism ) ); ++ GlobalCommands_insert( "BrushCone", BrushPrefab::SetCaller( g_brushcone ) ); ++ GlobalCommands_insert( "BrushSphere", BrushPrefab::SetCaller( g_brushsphere ) ); ++ GlobalCommands_insert( "BrushRock", BrushPrefab::SetCaller( g_brushrock ) ); ++ ++ GlobalCommands_insert( "Brush3Sided", BrushMakeSided::SetCaller( g_brushmakesided3 ), Accelerator( '3', (GdkModifierType)GDK_CONTROL_MASK ) ); ++ GlobalCommands_insert( "Brush4Sided", BrushMakeSided::SetCaller( g_brushmakesided4 ), Accelerator( '4', (GdkModifierType)GDK_CONTROL_MASK ) ); ++ GlobalCommands_insert( "Brush5Sided", BrushMakeSided::SetCaller( g_brushmakesided5 ), Accelerator( '5', (GdkModifierType)GDK_CONTROL_MASK ) ); ++ GlobalCommands_insert( "Brush6Sided", BrushMakeSided::SetCaller( g_brushmakesided6 ), Accelerator( '6', (GdkModifierType)GDK_CONTROL_MASK ) ); ++ GlobalCommands_insert( "Brush7Sided", BrushMakeSided::SetCaller( g_brushmakesided7 ), Accelerator( '7', (GdkModifierType)GDK_CONTROL_MASK ) ); ++ GlobalCommands_insert( "Brush8Sided", BrushMakeSided::SetCaller( g_brushmakesided8 ), Accelerator( '8', (GdkModifierType)GDK_CONTROL_MASK ) ); ++ GlobalCommands_insert( "Brush9Sided", BrushMakeSided::SetCaller( g_brushmakesided9 ), Accelerator( '9', (GdkModifierType)GDK_CONTROL_MASK ) ); ++ ++ GlobalCommands_insert( "ClipSelected", makeCallbackF(ClipSelected), Accelerator( GDK_KEY_Return ) ); ++ GlobalCommands_insert( "SplitSelected", makeCallbackF(SplitSelected), Accelerator( GDK_KEY_Return, (GdkModifierType)GDK_SHIFT_MASK ) ); ++ GlobalCommands_insert( "FlipClip", makeCallbackF(FlipClipper), Accelerator( GDK_KEY_Return, (GdkModifierType)GDK_CONTROL_MASK ) ); ++ ++ GlobalCommands_insert( "MakeDetail", makeCallbackF(Select_MakeDetail), Accelerator( 'M', (GdkModifierType)GDK_CONTROL_MASK ) ); ++ GlobalCommands_insert( "MakeStructural", makeCallbackF(Select_MakeStructural), Accelerator( 'S', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) ); ++} ++ ++void Brush_constructMenu( ui::Menu menu ){ ++ create_menu_item_with_mnemonic( menu, "Prism...", "BrushPrism" ); ++ create_menu_item_with_mnemonic( menu, "Cone...", "BrushCone" ); ++ create_menu_item_with_mnemonic( menu, "Sphere...", "BrushSphere" ); ++ create_menu_item_with_mnemonic( menu, "Rock...", "BrushRock" ); ++ menu_separator( menu ); ++ { ++ auto menu_in_menu = create_sub_menu_with_mnemonic( menu, "CSG" ); ++ if ( g_Layout_enableDetachableMenus.m_value ) { ++ menu_tearoff( menu_in_menu ); ++ } ++ create_menu_item_with_mnemonic( menu_in_menu, "CSG _Subtract", "CSGSubtract" ); ++ create_menu_item_with_mnemonic( menu_in_menu, "CSG _Merge", "CSGMerge" ); ++ create_menu_item_with_mnemonic( menu_in_menu, "Make _Room", "CSGRoom" ); ++ create_menu_item_with_mnemonic( menu_in_menu, "CSG _Tool", "CSGTool" ); ++ } ++ menu_separator( menu ); ++ { ++ auto menu_in_menu = create_sub_menu_with_mnemonic( menu, "Clipper" ); ++ if ( g_Layout_enableDetachableMenus.m_value ) { ++ menu_tearoff( menu_in_menu ); ++ } ++ ++ create_menu_item_with_mnemonic( menu_in_menu, "Clip selection", "ClipSelected" ); ++ create_menu_item_with_mnemonic( menu_in_menu, "Split selection", "SplitSelected" ); ++ create_menu_item_with_mnemonic( menu_in_menu, "Flip Clip orientation", "FlipClip" ); ++ } ++ menu_separator( menu ); ++ create_menu_item_with_mnemonic( menu, "Make detail", "MakeDetail" ); ++ create_menu_item_with_mnemonic( menu, "Make structural", "MakeStructural" ); ++// create_menu_item_with_mnemonic( menu, "Snap selection to _grid", "SnapToGrid" ); ++ ++ create_check_menu_item_with_mnemonic( menu, "Texture Lock", "TogTexLock" ); ++ menu_separator( menu ); ++ create_menu_item_with_mnemonic( menu, "Copy Face Texture", "FaceCopyTexture" ); ++ create_menu_item_with_mnemonic( menu, "Paste Face Texture", "FacePasteTexture" ); ++ ++ command_connect_accelerator( "Brush3Sided" ); ++ command_connect_accelerator( "Brush4Sided" ); ++ command_connect_accelerator( "Brush5Sided" ); ++ command_connect_accelerator( "Brush6Sided" ); ++ command_connect_accelerator( "Brush7Sided" ); ++ command_connect_accelerator( "Brush8Sided" ); ++ command_connect_accelerator( "Brush9Sided" ); ++} diff --cc radiant/camwindow.cpp index eafc1da0,01a7f491..376a31fb --- a/radiant/camwindow.cpp +++ b/radiant/camwindow.cpp @@@ -908,48 -913,48 +908,49 @@@ void KeyEvent_disconnect( const char* n } void CamWnd_registerCommands( CamWnd& camwnd ){ - GlobalKeyEvents_insert( "CameraForward", Accelerator( GDK_KEY_Up ), + GlobalKeyEvents_insert( "CameraForward", accelerator_null(), - ReferenceCaller( camwnd.getCamera() ), - ReferenceCaller( camwnd.getCamera() ) + ReferenceCaller( camwnd.getCamera() ), + ReferenceCaller( camwnd.getCamera() ) ); - GlobalKeyEvents_insert( "CameraBack", Accelerator( GDK_KEY_Down ), + GlobalKeyEvents_insert( "CameraBack", accelerator_null(), - ReferenceCaller( camwnd.getCamera() ), - ReferenceCaller( camwnd.getCamera() ) + ReferenceCaller( camwnd.getCamera() ), + ReferenceCaller( camwnd.getCamera() ) ); - GlobalKeyEvents_insert( "CameraLeft", Accelerator( GDK_KEY_Left ), + GlobalKeyEvents_insert( "CameraLeft", accelerator_null(), - ReferenceCaller( camwnd.getCamera() ), - ReferenceCaller( camwnd.getCamera() ) + ReferenceCaller( camwnd.getCamera() ), + ReferenceCaller( camwnd.getCamera() ) ); - GlobalKeyEvents_insert( "CameraRight", Accelerator( GDK_KEY_Right ), + GlobalKeyEvents_insert( "CameraRight", accelerator_null(), - ReferenceCaller( camwnd.getCamera() ), - ReferenceCaller( camwnd.getCamera() ) + ReferenceCaller( camwnd.getCamera() ), + ReferenceCaller( camwnd.getCamera() ) ); - GlobalKeyEvents_insert( "CameraStrafeRight", Accelerator( GDK_KEY_period ), + GlobalKeyEvents_insert( "CameraStrafeRight", accelerator_null(), - ReferenceCaller( camwnd.getCamera() ), - ReferenceCaller( camwnd.getCamera() ) + ReferenceCaller( camwnd.getCamera() ), + ReferenceCaller( camwnd.getCamera() ) ); - GlobalKeyEvents_insert( "CameraStrafeLeft", Accelerator( GDK_KEY_comma ), + GlobalKeyEvents_insert( "CameraStrafeLeft", accelerator_null(), - ReferenceCaller( camwnd.getCamera() ), - ReferenceCaller( camwnd.getCamera() ) + ReferenceCaller( camwnd.getCamera() ), + ReferenceCaller( camwnd.getCamera() ) ); - GlobalKeyEvents_insert( "CameraUp", Accelerator( 'D' ), + GlobalKeyEvents_insert( "CameraUp", accelerator_null(), - ReferenceCaller( camwnd.getCamera() ), - ReferenceCaller( camwnd.getCamera() ) + ReferenceCaller( camwnd.getCamera() ), + ReferenceCaller( camwnd.getCamera() ) ); - GlobalKeyEvents_insert( "CameraDown", Accelerator( 'C' ), + GlobalKeyEvents_insert( "CameraDown", accelerator_null(), - ReferenceCaller( camwnd.getCamera() ), - ReferenceCaller( camwnd.getCamera() ) + ReferenceCaller( camwnd.getCamera() ), + ReferenceCaller( camwnd.getCamera() ) ); - GlobalKeyEvents_insert( "CameraAngleDown", Accelerator( 'A' ), - ReferenceCaller( camwnd.getCamera() ), - ReferenceCaller( camwnd.getCamera() ) - ); - GlobalKeyEvents_insert( "CameraAngleUp", Accelerator( 'Z' ), + GlobalKeyEvents_insert( "CameraAngleUp", accelerator_null(), - ReferenceCaller( camwnd.getCamera() ), - ReferenceCaller( camwnd.getCamera() ) + ReferenceCaller( camwnd.getCamera() ), + ReferenceCaller( camwnd.getCamera() ) ); + GlobalKeyEvents_insert( "CameraAngleDown", accelerator_null(), - ReferenceCaller( camwnd.getCamera() ), - ReferenceCaller( camwnd.getCamera() ) ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) + ); - GlobalKeyEvents_insert( "CameraFreeMoveForward", Accelerator( GDK_KEY_Up ), ++ + GlobalKeyEvents_insert( "CameraFreeMoveForward", accelerator_null(), FreeMoveCameraMoveForwardKeyDownCaller( camwnd.getCamera() ), FreeMoveCameraMoveForwardKeyUpCaller( camwnd.getCamera() ) ); @@@ -992,17 -997,17 +993,17 @@@ FreeMoveCameraMoveDownKeyUpCaller( camwnd.getCamera() ) ); - GlobalCommands_insert( "CameraForward", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_Up ) ); - GlobalCommands_insert( "CameraBack", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_Down ) ); - GlobalCommands_insert( "CameraLeft", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_Left ) ); - GlobalCommands_insert( "CameraRight", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_Right ) ); - GlobalCommands_insert( "CameraStrafeRight", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_period ) ); - GlobalCommands_insert( "CameraStrafeLeft", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_comma ) ); - GlobalCommands_insert( "CameraForward", ReferenceCaller( camwnd.getCamera() ) ); - GlobalCommands_insert( "CameraBack", ReferenceCaller( camwnd.getCamera() ) ); - GlobalCommands_insert( "CameraLeft", ReferenceCaller( camwnd.getCamera() ) ); - GlobalCommands_insert( "CameraRight", ReferenceCaller( camwnd.getCamera() ) ); - GlobalCommands_insert( "CameraStrafeRight", ReferenceCaller( camwnd.getCamera() ) ); - GlobalCommands_insert( "CameraStrafeLeft", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraForward", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraBack", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraLeft", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraRight", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraStrafeRight", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraStrafeLeft", ReferenceCaller( camwnd.getCamera() ) ); - GlobalCommands_insert( "CameraUp", ReferenceCaller( camwnd.getCamera() ), Accelerator( 'D' ) ); - GlobalCommands_insert( "CameraDown", ReferenceCaller( camwnd.getCamera() ), Accelerator( 'C' ) ); - GlobalCommands_insert( "CameraAngleUp", ReferenceCaller( camwnd.getCamera() ), Accelerator( 'A' ) ); - GlobalCommands_insert( "CameraAngleDown", ReferenceCaller( camwnd.getCamera() ), Accelerator( 'Z' ) ); - GlobalCommands_insert( "CameraUp", ReferenceCaller( camwnd.getCamera() ) ); - GlobalCommands_insert( "CameraDown", ReferenceCaller( camwnd.getCamera() ) ); - GlobalCommands_insert( "CameraAngleUp", ReferenceCaller( camwnd.getCamera() ) ); - GlobalCommands_insert( "CameraAngleDown", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraUp", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraDown", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraAngleUp", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraAngleDown", ReferenceCaller( camwnd.getCamera() ) ); } void CamWnd_Move_Enable( CamWnd& camwnd ){ @@@ -1998,20 -1997,23 +1999,23 @@@ void CamWnd_Construct() GlobalShortcuts_insert( "CameraFreeMoveLeft2", Accelerator( GDK_Left ) ); GlobalShortcuts_insert( "CameraFreeMoveRight2", Accelerator( GDK_Right ) ); + GlobalShortcuts_insert( "CameraFreeMoveUp", accelerator_null() ); + GlobalShortcuts_insert( "CameraFreeMoveDown", accelerator_null() ); + - GlobalToggles_insert( "ShowStats", FreeCaller(), ToggleItem::AddCallbackCaller( g_show_stats ) ); - - GlobalPreferenceSystem().registerPreference( "ShowStats", BoolImportStringCaller( g_camwindow_globals_private.m_showStats ), BoolExportStringCaller( g_camwindow_globals_private.m_showStats ) ); - GlobalPreferenceSystem().registerPreference( "MoveSpeed", IntImportStringCaller( g_camwindow_globals_private.m_nMoveSpeed ), IntExportStringCaller( g_camwindow_globals_private.m_nMoveSpeed ) ); - GlobalPreferenceSystem().registerPreference( "CamLinkSpeed", BoolImportStringCaller( g_camwindow_globals_private.m_bCamLinkSpeed ), BoolExportStringCaller( g_camwindow_globals_private.m_bCamLinkSpeed ) ); - GlobalPreferenceSystem().registerPreference( "AngleSpeed", IntImportStringCaller( g_camwindow_globals_private.m_nAngleSpeed ), IntExportStringCaller( g_camwindow_globals_private.m_nAngleSpeed ) ); - GlobalPreferenceSystem().registerPreference( "CamInverseMouse", BoolImportStringCaller( g_camwindow_globals_private.m_bCamInverseMouse ), BoolExportStringCaller( g_camwindow_globals_private.m_bCamInverseMouse ) ); - GlobalPreferenceSystem().registerPreference( "CamDiscrete", makeBoolStringImportCallback( CamWndMoveDiscreteImportCaller() ), BoolExportStringCaller( g_camwindow_globals_private.m_bCamDiscrete ) ); - GlobalPreferenceSystem().registerPreference( "CubicClipping", BoolImportStringCaller( g_camwindow_globals_private.m_bCubicClipping ), BoolExportStringCaller( g_camwindow_globals_private.m_bCubicClipping ) ); - GlobalPreferenceSystem().registerPreference( "CubicScale", IntImportStringCaller( g_camwindow_globals.m_nCubicScale ), IntExportStringCaller( g_camwindow_globals.m_nCubicScale ) ); - GlobalPreferenceSystem().registerPreference( "SI_Colors4", Vector3ImportStringCaller( g_camwindow_globals.color_cameraback ), Vector3ExportStringCaller( g_camwindow_globals.color_cameraback ) ); - GlobalPreferenceSystem().registerPreference( "SI_Colors12", Vector3ImportStringCaller( g_camwindow_globals.color_selbrushes3d ), Vector3ExportStringCaller( g_camwindow_globals.color_selbrushes3d ) ); - GlobalPreferenceSystem().registerPreference( "CameraRenderMode", makeIntStringImportCallback( RenderModeImportCaller() ), makeIntStringExportCallback( RenderModeExportCaller() ) ); - GlobalPreferenceSystem().registerPreference( "StrafeMode", IntImportStringCaller( g_camwindow_globals_private.m_nStrafeMode ), IntExportStringCaller( g_camwindow_globals_private.m_nStrafeMode ) ); + GlobalToggles_insert( "ShowStats", makeCallbackF(ShowStatsToggle), ToggleItem::AddCallbackCaller( g_show_stats ) ); + + GlobalPreferenceSystem().registerPreference( "ShowStats", make_property_string( g_camwindow_globals_private.m_showStats ) ); + GlobalPreferenceSystem().registerPreference( "MoveSpeed", make_property_string( g_camwindow_globals_private.m_nMoveSpeed ) ); + GlobalPreferenceSystem().registerPreference( "CamLinkSpeed", make_property_string( g_camwindow_globals_private.m_bCamLinkSpeed ) ); + GlobalPreferenceSystem().registerPreference( "AngleSpeed", make_property_string( g_camwindow_globals_private.m_nAngleSpeed ) ); + GlobalPreferenceSystem().registerPreference( "CamInverseMouse", make_property_string( g_camwindow_globals_private.m_bCamInverseMouse ) ); + GlobalPreferenceSystem().registerPreference( "CamDiscrete", make_property_string()); + GlobalPreferenceSystem().registerPreference( "CubicClipping", make_property_string( g_camwindow_globals_private.m_bCubicClipping ) ); + GlobalPreferenceSystem().registerPreference( "CubicScale", make_property_string( g_camwindow_globals.m_nCubicScale ) ); + GlobalPreferenceSystem().registerPreference( "SI_Colors4", make_property_string( g_camwindow_globals.color_cameraback ) ); + GlobalPreferenceSystem().registerPreference( "SI_Colors12", make_property_string( g_camwindow_globals.color_selbrushes3d ) ); + GlobalPreferenceSystem().registerPreference( "CameraRenderMode", make_property_string() ); + GlobalPreferenceSystem().registerPreference( "StrafeMode", make_property_string( g_camwindow_globals_private.m_nStrafeMode ) ); CamWnd_constructStatic(); diff --cc radiant/camwindow.cpp.orig index 00000000,00000000..f1ba3b4c new file mode 100644 --- /dev/null +++ b/radiant/camwindow.cpp.orig @@@ -1,0 -1,0 +1,2136 @@@ ++/* ++ Copyright (C) 1999-2006 Id Software, Inc. and contributors. ++ For a list of contributors, see the accompanying CONTRIBUTORS file. ++ ++ 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 ++ */ ++ ++// ++// Camera Window ++// ++// Leonardo Zide (leo@lokigames.com) ++// ++ ++#include "camwindow.h" ++ ++#include ++#include ++ ++#include "debugging/debugging.h" ++ ++#include "iscenegraph.h" ++#include "irender.h" ++#include "igl.h" ++#include "icamera.h" ++#include "cullable.h" ++#include "renderable.h" ++#include "preferencesystem.h" ++ ++#include "signal/signal.h" ++#include "container/array.h" ++#include "scenelib.h" ++#include "render.h" ++#include "cmdlib.h" ++#include "math/frustum.h" ++ ++#include "gtkutil/widget.h" ++#include "gtkutil/button.h" ++#include "gtkutil/toolbar.h" ++#include "gtkutil/glwidget.h" ++#include "gtkutil/xorrectangle.h" ++#include "gtkmisc.h" ++#include "selection.h" ++#include "mainframe.h" ++#include "preferences.h" ++#include "commands.h" ++#include "xywindow.h" ++#include "windowobservers.h" ++#include "renderstate.h" ++ ++#include "timer.h" ++ ++Signal0 g_cameraMoved_callbacks; ++ ++void AddCameraMovedCallback( const SignalHandler& handler ){ ++ g_cameraMoved_callbacks.connectLast( handler ); ++} ++ ++void CameraMovedNotify(){ ++ g_cameraMoved_callbacks(); ++} ++ ++ ++struct camwindow_globals_private_t ++{ ++ int m_nMoveSpeed; ++ bool m_bCamLinkSpeed; ++ int m_nAngleSpeed; ++ bool m_bCamInverseMouse; ++ bool m_bCamDiscrete; ++ bool m_bCubicClipping; ++ bool m_showStats; ++ int m_nStrafeMode; ++ ++ camwindow_globals_private_t() : ++ m_nMoveSpeed( 100 ), ++ m_bCamLinkSpeed( true ), ++ m_nAngleSpeed( 3 ), ++ m_bCamInverseMouse( false ), ++ m_bCamDiscrete( true ), ++ m_bCubicClipping( false ), ++ m_showStats( true ), ++ m_nStrafeMode( 0 ){ ++ } ++ ++}; ++ ++camwindow_globals_private_t g_camwindow_globals_private; ++ ++ ++const Matrix4 g_opengl2radiant( ++ 0, 0,-1, 0, ++ -1, 0, 0, 0, ++ 0, 1, 0, 0, ++ 0, 0, 0, 1 ++ ); ++ ++const Matrix4 g_radiant2opengl( ++ 0,-1, 0, 0, ++ 0, 0, 1, 0, ++ -1, 0, 0, 0, ++ 0, 0, 0, 1 ++ ); ++ ++struct camera_t; ++void Camera_mouseMove( camera_t& camera, int x, int y ); ++ ++enum camera_draw_mode ++{ ++ cd_wire, ++ cd_solid, ++ cd_texture, ++ cd_lighting ++}; ++ ++struct camera_t ++{ ++ int width, height; ++ ++ bool timing; ++ ++ Vector3 origin; ++ Vector3 angles; ++ ++ Vector3 color; // background ++ ++ Vector3 forward, right; // move matrix (TTimo: used to have up but it was not updated) ++ Vector3 vup, vpn, vright; // view matrix (taken from the modelview matrix) ++ ++ Matrix4 projection; ++ Matrix4 modelview; ++ ++ bool m_strafe; // true when in strafemode toggled by the ctrl-key ++ bool m_strafe_forward; // true when in strafemode by ctrl-key and shift is pressed for forward strafing ++ ++ unsigned int movementflags; // movement flags ++ Timer m_keycontrol_timer; ++ guint m_keymove_handler; ++ ++ ++ float fieldOfView; ++ ++ DeferredMotionDelta m_mouseMove; ++ ++ static void motionDelta( int x, int y, void* data ){ ++ Camera_mouseMove( *reinterpret_cast( data ), x, y ); ++ } ++ ++ View* m_view; ++ Callback m_update; ++ ++ static camera_draw_mode draw_mode; ++ ++ camera_t( View* view, const Callback& update ) ++ : width( 0 ), ++ height( 0 ), ++ timing( false ), ++ origin( 0, 0, 0 ), ++ angles( 0, 0, 0 ), ++ color( 0, 0, 0 ), ++ movementflags( 0 ), ++ m_keymove_handler( 0 ), ++ fieldOfView( 110.0f ), ++ m_mouseMove( motionDelta, this ), ++ m_view( view ), ++ m_update( update ){ ++ } ++}; ++ ++camera_draw_mode camera_t::draw_mode = cd_texture; ++ ++inline Matrix4 projection_for_camera( float near_z, float far_z, float fieldOfView, int width, int height ){ ++ const float half_width = static_cast( near_z * tan( degrees_to_radians( fieldOfView * 0.5 ) ) ); ++ const float half_height = half_width * ( static_cast( height ) / static_cast( width ) ); ++ ++ return matrix4_frustum( ++ -half_width, ++ half_width, ++ -half_height, ++ half_height, ++ near_z, ++ far_z ++ ); ++} ++ ++float Camera_getFarClipPlane( camera_t& camera ){ ++ return ( g_camwindow_globals_private.m_bCubicClipping ) ? pow( 2.0, ( g_camwindow_globals.m_nCubicScale + 7 ) / 2.0 ) : 32768.0f; ++} ++ ++void Camera_updateProjection( camera_t& camera ){ ++ float farClip = Camera_getFarClipPlane( camera ); ++ camera.projection = projection_for_camera( farClip / 4096.0f, farClip, camera.fieldOfView, camera.width, camera.height ); ++ ++ camera.m_view->Construct( camera.projection, camera.modelview, camera.width, camera.height ); ++} ++ ++void Camera_updateVectors( camera_t& camera ){ ++ for ( int i = 0 ; i < 3 ; i++ ) ++ { ++ camera.vright[i] = camera.modelview[( i << 2 ) + 0]; ++ camera.vup[i] = camera.modelview[( i << 2 ) + 1]; ++ camera.vpn[i] = camera.modelview[( i << 2 ) + 2]; ++ } ++} ++ ++void Camera_updateModelview( camera_t& camera ){ ++ camera.modelview = g_matrix4_identity; ++ ++ // roll, pitch, yaw ++ Vector3 radiant_eulerXYZ( 0, -camera.angles[CAMERA_PITCH], camera.angles[CAMERA_YAW] ); ++ ++ matrix4_translate_by_vec3( camera.modelview, camera.origin ); ++ matrix4_rotate_by_euler_xyz_degrees( camera.modelview, radiant_eulerXYZ ); ++ matrix4_multiply_by_matrix4( camera.modelview, g_radiant2opengl ); ++ matrix4_affine_invert( camera.modelview ); ++ ++ Camera_updateVectors( camera ); ++ ++ camera.m_view->Construct( camera.projection, camera.modelview, camera.width, camera.height ); ++} ++ ++ ++void Camera_Move_updateAxes( camera_t& camera ){ ++ double ya = degrees_to_radians( camera.angles[CAMERA_YAW] ); ++ ++ // the movement matrix is kept 2d ++ camera.forward[0] = static_cast( cos( ya ) ); ++ camera.forward[1] = static_cast( sin( ya ) ); ++ camera.forward[2] = 0; ++ camera.right[0] = camera.forward[1]; ++ camera.right[1] = -camera.forward[0]; ++} ++ ++void Camera_Freemove_updateAxes( camera_t& camera ){ ++ camera.right = camera.vright; ++ camera.forward = vector3_negated( camera.vpn ); ++} ++ ++const Vector3& Camera_getOrigin( camera_t& camera ){ ++ return camera.origin; ++} ++ ++void Camera_setOrigin( camera_t& camera, const Vector3& origin ){ ++ camera.origin = origin; ++ Camera_updateModelview( camera ); ++ camera.m_update(); ++ CameraMovedNotify(); ++} ++ ++const Vector3& Camera_getAngles( camera_t& camera ){ ++ return camera.angles; ++} ++ ++void Camera_setAngles( camera_t& camera, const Vector3& angles ){ ++ camera.angles = angles; ++ Camera_updateModelview( camera ); ++ camera.m_update(); ++ CameraMovedNotify(); ++} ++ ++ ++void Camera_FreeMove( camera_t& camera, int dx, int dy ){ ++ // free strafe mode, toggled by the ctrl key with optional shift for forward movement ++ if ( camera.m_strafe ) { ++ float strafespeed = 0.65f; ++ ++ if ( g_camwindow_globals_private.m_bCamLinkSpeed ) { ++ strafespeed = (float)g_camwindow_globals_private.m_nMoveSpeed / 100; ++ } ++ ++ camera.origin -= camera.vright * strafespeed * dx; ++ if ( camera.m_strafe_forward ) { ++ camera.origin -= camera.vpn * strafespeed * dy; ++ } ++ else{ ++ camera.origin += camera.vup * strafespeed * dy; ++ } ++ } ++ else // free rotation ++ { ++ const float dtime = 0.1f; ++ ++ if ( g_camwindow_globals_private.m_bCamInverseMouse ) { ++ camera.angles[CAMERA_PITCH] -= dy * dtime * g_camwindow_globals_private.m_nAngleSpeed; ++ } ++ else{ ++ camera.angles[CAMERA_PITCH] += dy * dtime * g_camwindow_globals_private.m_nAngleSpeed; ++ } ++ ++ camera.angles[CAMERA_YAW] += dx * dtime * g_camwindow_globals_private.m_nAngleSpeed; ++ ++ if ( camera.angles[CAMERA_PITCH] > 90 ) { ++ camera.angles[CAMERA_PITCH] = 90; ++ } ++ else if ( camera.angles[CAMERA_PITCH] < -90 ) { ++ camera.angles[CAMERA_PITCH] = -90; ++ } ++ ++ if ( camera.angles[CAMERA_YAW] >= 360 ) { ++ camera.angles[CAMERA_YAW] -= 360; ++ } ++ else if ( camera.angles[CAMERA_YAW] <= 0 ) { ++ camera.angles[CAMERA_YAW] += 360; ++ } ++ } ++ ++ Camera_updateModelview( camera ); ++ Camera_Freemove_updateAxes( camera ); ++} ++ ++void Cam_MouseControl( camera_t& camera, int x, int y ){ ++<<<<<<< HEAD ++ float xf = (float)( x - camera.width / 2 ) / ( camera.width / 2 ); ++ float yf = (float)( y - camera.height / 2 ) / ( camera.height / 2 ); ++======= ++// int xl, xh; ++// int yl, yh; ++ float xf, yf; ++ ++ xf = (float)( x - camera.width / 2 ) / ( camera.width / 2 ); ++ yf = (float)( y - camera.height / 2 ) / ( camera.height / 2 ); ++ ++// xl = camera.width / 3; ++// xh = xl * 2; ++// yl = camera.height / 3; ++// yh = yl * 2; ++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84 ++ ++ xf *= 1.0f - fabsf( yf ); ++ if ( xf < 0 ) { ++ xf += 0.1f; ++ if ( xf > 0 ) { ++ xf = 0; ++ } ++ } ++ else ++ { ++ xf -= 0.1f; ++ if ( xf < 0 ) { ++ xf = 0; ++ } ++ } ++ ++ vector3_add( camera.origin, vector3_scaled( camera.forward, yf * 0.1f * g_camwindow_globals_private.m_nMoveSpeed ) ); ++ camera.angles[CAMERA_YAW] += xf * -0.1f * g_camwindow_globals_private.m_nAngleSpeed; ++ ++ Camera_updateModelview( camera ); ++} ++ ++void Camera_mouseMove( camera_t& camera, int x, int y ){ ++ //globalOutputStream() << "mousemove... "; ++ Camera_FreeMove( camera, -x, -y ); ++ camera.m_update(); ++ CameraMovedNotify(); ++} ++ ++const unsigned int MOVE_NONE = 0; ++const unsigned int MOVE_FORWARD = 1 << 0; ++const unsigned int MOVE_BACK = 1 << 1; ++const unsigned int MOVE_ROTRIGHT = 1 << 2; ++const unsigned int MOVE_ROTLEFT = 1 << 3; ++const unsigned int MOVE_STRAFERIGHT = 1 << 4; ++const unsigned int MOVE_STRAFELEFT = 1 << 5; ++const unsigned int MOVE_UP = 1 << 6; ++const unsigned int MOVE_DOWN = 1 << 7; ++const unsigned int MOVE_PITCHUP = 1 << 8; ++const unsigned int MOVE_PITCHDOWN = 1 << 9; ++const unsigned int MOVE_ALL = MOVE_FORWARD | MOVE_BACK | MOVE_ROTRIGHT | MOVE_ROTLEFT | MOVE_STRAFERIGHT | MOVE_STRAFELEFT | MOVE_UP | MOVE_DOWN | MOVE_PITCHUP | MOVE_PITCHDOWN; ++ ++void Cam_KeyControl( camera_t& camera, float dtime ){ ++ // Update angles ++ if ( camera.movementflags & MOVE_ROTLEFT ) { ++ camera.angles[CAMERA_YAW] += 15 * dtime * g_camwindow_globals_private.m_nAngleSpeed; ++ } ++ if ( camera.movementflags & MOVE_ROTRIGHT ) { ++ camera.angles[CAMERA_YAW] -= 15 * dtime * g_camwindow_globals_private.m_nAngleSpeed; ++ } ++ if ( camera.movementflags & MOVE_PITCHUP ) { ++ camera.angles[CAMERA_PITCH] += 15 * dtime * g_camwindow_globals_private.m_nAngleSpeed; ++ if ( camera.angles[CAMERA_PITCH] > 90 ) { ++ camera.angles[CAMERA_PITCH] = 90; ++ } ++ } ++ if ( camera.movementflags & MOVE_PITCHDOWN ) { ++ camera.angles[CAMERA_PITCH] -= 15 * dtime * g_camwindow_globals_private.m_nAngleSpeed; ++ if ( camera.angles[CAMERA_PITCH] < -90 ) { ++ camera.angles[CAMERA_PITCH] = -90; ++ } ++ } ++ ++ Camera_updateModelview( camera ); ++ Camera_Freemove_updateAxes( camera ); ++ ++ // Update position ++ if ( camera.movementflags & MOVE_FORWARD ) { ++ vector3_add( camera.origin, vector3_scaled( camera.forward, dtime * g_camwindow_globals_private.m_nMoveSpeed ) ); ++ } ++ if ( camera.movementflags & MOVE_BACK ) { ++ vector3_add( camera.origin, vector3_scaled( camera.forward, -dtime * g_camwindow_globals_private.m_nMoveSpeed ) ); ++ } ++ if ( camera.movementflags & MOVE_STRAFELEFT ) { ++ vector3_add( camera.origin, vector3_scaled( camera.right, -dtime * g_camwindow_globals_private.m_nMoveSpeed ) ); ++ } ++ if ( camera.movementflags & MOVE_STRAFERIGHT ) { ++ vector3_add( camera.origin, vector3_scaled( camera.right, dtime * g_camwindow_globals_private.m_nMoveSpeed ) ); ++ } ++ if ( camera.movementflags & MOVE_UP ) { ++ vector3_add( camera.origin, vector3_scaled( g_vector3_axis_z, dtime * g_camwindow_globals_private.m_nMoveSpeed ) ); ++ } ++ if ( camera.movementflags & MOVE_DOWN ) { ++ vector3_add( camera.origin, vector3_scaled( g_vector3_axis_z, -dtime * g_camwindow_globals_private.m_nMoveSpeed ) ); ++ } ++ ++ Camera_updateModelview( camera ); ++} ++ ++void Camera_keyMove( camera_t& camera ){ ++ camera.m_mouseMove.flush(); ++ ++ //globalOutputStream() << "keymove... "; ++ float time_seconds = camera.m_keycontrol_timer.elapsed_msec() / static_cast( msec_per_sec ); ++ camera.m_keycontrol_timer.start(); ++ if ( time_seconds > 0.05f ) { ++ time_seconds = 0.05f; // 20fps ++ } ++ Cam_KeyControl( camera, time_seconds * 5.0f ); ++ ++ camera.m_update(); ++ CameraMovedNotify(); ++} ++ ++gboolean camera_keymove( gpointer data ){ ++ Camera_keyMove( *reinterpret_cast( data ) ); ++ return TRUE; ++} ++ ++void Camera_setMovementFlags( camera_t& camera, unsigned int mask ){ ++ if ( ( ~camera.movementflags & mask ) != 0 && camera.movementflags == 0 ) { ++ camera.m_keymove_handler = g_idle_add( camera_keymove, &camera ); ++ } ++ camera.movementflags |= mask; ++} ++void Camera_clearMovementFlags( camera_t& camera, unsigned int mask ){ ++ if ( ( camera.movementflags & ~mask ) == 0 && camera.movementflags != 0 ) { ++ g_source_remove( camera.m_keymove_handler ); ++ camera.m_keymove_handler = 0; ++ } ++ camera.movementflags &= ~mask; ++} ++ ++void Camera_MoveForward_KeyDown( camera_t& camera ){ ++ Camera_setMovementFlags( camera, MOVE_FORWARD ); ++} ++void Camera_MoveForward_KeyUp( camera_t& camera ){ ++ Camera_clearMovementFlags( camera, MOVE_FORWARD ); ++} ++void Camera_MoveBack_KeyDown( camera_t& camera ){ ++ Camera_setMovementFlags( camera, MOVE_BACK ); ++} ++void Camera_MoveBack_KeyUp( camera_t& camera ){ ++ Camera_clearMovementFlags( camera, MOVE_BACK ); ++} ++ ++void Camera_MoveLeft_KeyDown( camera_t& camera ){ ++ Camera_setMovementFlags( camera, MOVE_STRAFELEFT ); ++} ++void Camera_MoveLeft_KeyUp( camera_t& camera ){ ++ Camera_clearMovementFlags( camera, MOVE_STRAFELEFT ); ++} ++void Camera_MoveRight_KeyDown( camera_t& camera ){ ++ Camera_setMovementFlags( camera, MOVE_STRAFERIGHT ); ++} ++void Camera_MoveRight_KeyUp( camera_t& camera ){ ++ Camera_clearMovementFlags( camera, MOVE_STRAFERIGHT ); ++} ++ ++void Camera_MoveUp_KeyDown( camera_t& camera ){ ++ Camera_setMovementFlags( camera, MOVE_UP ); ++} ++void Camera_MoveUp_KeyUp( camera_t& camera ){ ++ Camera_clearMovementFlags( camera, MOVE_UP ); ++} ++void Camera_MoveDown_KeyDown( camera_t& camera ){ ++ Camera_setMovementFlags( camera, MOVE_DOWN ); ++} ++void Camera_MoveDown_KeyUp( camera_t& camera ){ ++ Camera_clearMovementFlags( camera, MOVE_DOWN ); ++} ++ ++void Camera_RotateLeft_KeyDown( camera_t& camera ){ ++ Camera_setMovementFlags( camera, MOVE_ROTLEFT ); ++} ++void Camera_RotateLeft_KeyUp( camera_t& camera ){ ++ Camera_clearMovementFlags( camera, MOVE_ROTLEFT ); ++} ++void Camera_RotateRight_KeyDown( camera_t& camera ){ ++ Camera_setMovementFlags( camera, MOVE_ROTRIGHT ); ++} ++void Camera_RotateRight_KeyUp( camera_t& camera ){ ++ Camera_clearMovementFlags( camera, MOVE_ROTRIGHT ); ++} ++ ++void Camera_PitchUp_KeyDown( camera_t& camera ){ ++ Camera_setMovementFlags( camera, MOVE_PITCHUP ); ++} ++void Camera_PitchUp_KeyUp( camera_t& camera ){ ++ Camera_clearMovementFlags( camera, MOVE_PITCHUP ); ++} ++void Camera_PitchDown_KeyDown( camera_t& camera ){ ++ Camera_setMovementFlags( camera, MOVE_PITCHDOWN ); ++} ++void Camera_PitchDown_KeyUp( camera_t& camera ){ ++ Camera_clearMovementFlags( camera, MOVE_PITCHDOWN ); ++} ++ ++ ++typedef ReferenceCaller FreeMoveCameraMoveForwardKeyDownCaller; ++typedef ReferenceCaller FreeMoveCameraMoveForwardKeyUpCaller; ++typedef ReferenceCaller FreeMoveCameraMoveBackKeyDownCaller; ++typedef ReferenceCaller FreeMoveCameraMoveBackKeyUpCaller; ++typedef ReferenceCaller FreeMoveCameraMoveLeftKeyDownCaller; ++typedef ReferenceCaller FreeMoveCameraMoveLeftKeyUpCaller; ++typedef ReferenceCaller FreeMoveCameraMoveRightKeyDownCaller; ++typedef ReferenceCaller FreeMoveCameraMoveRightKeyUpCaller; ++typedef ReferenceCaller FreeMoveCameraMoveUpKeyDownCaller; ++typedef ReferenceCaller FreeMoveCameraMoveUpKeyUpCaller; ++typedef ReferenceCaller FreeMoveCameraMoveDownKeyDownCaller; ++typedef ReferenceCaller FreeMoveCameraMoveDownKeyUpCaller; ++ ++ ++const float SPEED_MOVE = 32; ++const float SPEED_TURN = 22.5; ++const float MIN_CAM_SPEED = 10; ++const float MAX_CAM_SPEED = 610; ++const float CAM_SPEED_STEP = 50; ++ ++void Camera_MoveForward_Discrete( camera_t& camera ){ ++ Camera_Move_updateAxes( camera ); ++ Camera_setOrigin( camera, vector3_added( Camera_getOrigin( camera ), vector3_scaled( camera.forward, SPEED_MOVE ) ) ); ++} ++void Camera_MoveBack_Discrete( camera_t& camera ){ ++ Camera_Move_updateAxes( camera ); ++ Camera_setOrigin( camera, vector3_added( Camera_getOrigin( camera ), vector3_scaled( camera.forward, -SPEED_MOVE ) ) ); ++} ++ ++void Camera_MoveUp_Discrete( camera_t& camera ){ ++ Vector3 origin( Camera_getOrigin( camera ) ); ++ origin[2] += SPEED_MOVE; ++ Camera_setOrigin( camera, origin ); ++} ++void Camera_MoveDown_Discrete( camera_t& camera ){ ++ Vector3 origin( Camera_getOrigin( camera ) ); ++ origin[2] -= SPEED_MOVE; ++ Camera_setOrigin( camera, origin ); ++} ++ ++void Camera_MoveLeft_Discrete( camera_t& camera ){ ++ Camera_Move_updateAxes( camera ); ++ Camera_setOrigin( camera, vector3_added( Camera_getOrigin( camera ), vector3_scaled( camera.right, -SPEED_MOVE ) ) ); ++} ++void Camera_MoveRight_Discrete( camera_t& camera ){ ++ Camera_Move_updateAxes( camera ); ++ Camera_setOrigin( camera, vector3_added( Camera_getOrigin( camera ), vector3_scaled( camera.right, SPEED_MOVE ) ) ); ++} ++ ++void Camera_RotateLeft_Discrete( camera_t& camera ){ ++ Vector3 angles( Camera_getAngles( camera ) ); ++ angles[CAMERA_YAW] += SPEED_TURN; ++ Camera_setAngles( camera, angles ); ++} ++void Camera_RotateRight_Discrete( camera_t& camera ){ ++ Vector3 angles( Camera_getAngles( camera ) ); ++ angles[CAMERA_YAW] -= SPEED_TURN; ++ Camera_setAngles( camera, angles ); ++} ++ ++void Camera_PitchUp_Discrete( camera_t& camera ){ ++ Vector3 angles( Camera_getAngles( camera ) ); ++ angles[CAMERA_PITCH] += SPEED_TURN; ++ if ( angles[CAMERA_PITCH] > 90 ) { ++ angles[CAMERA_PITCH] = 90; ++ } ++ Camera_setAngles( camera, angles ); ++} ++void Camera_PitchDown_Discrete( camera_t& camera ){ ++ Vector3 angles( Camera_getAngles( camera ) ); ++ angles[CAMERA_PITCH] -= SPEED_TURN; ++ if ( angles[CAMERA_PITCH] < -90 ) { ++ angles[CAMERA_PITCH] = -90; ++ } ++ Camera_setAngles( camera, angles ); ++} ++ ++ ++class RadiantCameraView : public CameraView ++{ ++camera_t& m_camera; ++View* m_view; ++Callback m_update; ++public: ++RadiantCameraView( camera_t& camera, View* view, const Callback& update ) : m_camera( camera ), m_view( view ), m_update( update ){ ++} ++void update(){ ++ m_view->Construct( m_camera.projection, m_camera.modelview, m_camera.width, m_camera.height ); ++ m_update(); ++} ++void setModelview( const Matrix4& modelview ){ ++ m_camera.modelview = modelview; ++ matrix4_multiply_by_matrix4( m_camera.modelview, g_radiant2opengl ); ++ matrix4_affine_invert( m_camera.modelview ); ++ Camera_updateVectors( m_camera ); ++ update(); ++} ++void setFieldOfView( float fieldOfView ){ ++ float farClip = Camera_getFarClipPlane( m_camera ); ++ m_camera.projection = projection_for_camera( farClip / 4096.0f, farClip, fieldOfView, m_camera.width, m_camera.height ); ++ update(); ++} ++}; ++ ++ ++void Camera_motionDelta( int x, int y, unsigned int state, void* data ){ ++ camera_t* cam = reinterpret_cast( data ); ++ ++ cam->m_mouseMove.motion_delta( x, y, state ); ++ ++ switch ( g_camwindow_globals_private.m_nStrafeMode ) ++ { ++ case 0: ++ cam->m_strafe = ( state & GDK_CONTROL_MASK ) != 0; ++ if ( cam->m_strafe ) { ++ cam->m_strafe_forward = ( state & GDK_SHIFT_MASK ) != 0; ++ } ++ else{ ++ cam->m_strafe_forward = false; ++ } ++ break; ++ case 1: ++ cam->m_strafe = ( state & GDK_CONTROL_MASK ) != 0 && ( state & GDK_SHIFT_MASK ) == 0; ++ cam->m_strafe_forward = false; ++ break; ++ case 2: ++ cam->m_strafe = ( state & GDK_CONTROL_MASK ) != 0 && ( state & GDK_SHIFT_MASK ) == 0; ++ cam->m_strafe_forward = cam->m_strafe; ++ break; ++ } ++} ++ ++class CamWnd ++{ ++View m_view; ++camera_t m_Camera; ++RadiantCameraView m_cameraview; ++#if 0 ++int m_PositionDragCursorX; ++int m_PositionDragCursorY; ++#endif ++ ++guint m_freemove_handle_focusout; ++ ++static Shader* m_state_select1; ++static Shader* m_state_select2; ++ ++FreezePointer m_freezePointer; ++ ++public: ++ui::GLArea m_gl_widget; ++ui::Window m_parent{ui::null}; ++ ++SelectionSystemWindowObserver* m_window_observer; ++XORRectangle m_XORRectangle; ++ ++DeferredDraw m_deferredDraw; ++DeferredMotion m_deferred_motion; ++ ++guint m_selection_button_press_handler; ++guint m_selection_button_release_handler; ++guint m_selection_motion_handler; ++ ++guint m_freelook_button_press_handler; ++ ++guint m_sizeHandler; ++guint m_exposeHandler; ++ ++CamWnd(); ++~CamWnd(); ++ ++bool m_drawing; ++void queue_draw(){ ++ //ASSERT_MESSAGE(!m_drawing, "CamWnd::queue_draw(): called while draw is already in progress"); ++ if ( m_drawing ) { ++ return; ++ } ++ //globalOutputStream() << "queue... "; ++ m_deferredDraw.draw(); ++} ++void draw(); ++ ++static void captureStates(){ ++ m_state_select1 = GlobalShaderCache().capture( "$CAM_HIGHLIGHT" ); ++ m_state_select2 = GlobalShaderCache().capture( "$CAM_OVERLAY" ); ++} ++static void releaseStates(){ ++ GlobalShaderCache().release( "$CAM_HIGHLIGHT" ); ++ GlobalShaderCache().release( "$CAM_OVERLAY" ); ++} ++ ++camera_t& getCamera(){ ++ return m_Camera; ++} ++ ++void BenchMark(); ++void Cam_ChangeFloor( bool up ); ++ ++void DisableFreeMove(); ++void EnableFreeMove(); ++bool m_bFreeMove; ++ ++CameraView& getCameraView(){ ++ return m_cameraview; ++} ++ ++private: ++void Cam_Draw(); ++}; ++ ++typedef MemberCaller CamWndQueueDraw; ++ ++Shader* CamWnd::m_state_select1 = 0; ++Shader* CamWnd::m_state_select2 = 0; ++ ++CamWnd* NewCamWnd(){ ++ return new CamWnd; ++} ++void DeleteCamWnd( CamWnd* camwnd ){ ++ delete camwnd; ++} ++ ++void CamWnd_constructStatic(){ ++ CamWnd::captureStates(); ++} ++ ++void CamWnd_destroyStatic(){ ++ CamWnd::releaseStates(); ++} ++ ++static CamWnd* g_camwnd = 0; ++ ++void GlobalCamera_setCamWnd( CamWnd& camwnd ){ ++ g_camwnd = &camwnd; ++} ++ ++ ++ui::GLArea CamWnd_getWidget( CamWnd& camwnd ){ ++ return camwnd.m_gl_widget; ++} ++ ++ui::Window CamWnd_getParent( CamWnd& camwnd ){ ++ return camwnd.m_parent; ++} ++ ++ToggleShown g_camera_shown( true ); ++ ++void CamWnd_setParent( CamWnd& camwnd, ui::Window parent ){ ++ camwnd.m_parent = parent; ++ g_camera_shown.connect( camwnd.m_parent ); ++} ++ ++void CamWnd_Update( CamWnd& camwnd ){ ++ camwnd.queue_draw(); ++} ++ ++ ++ ++camwindow_globals_t g_camwindow_globals; ++ ++const Vector3& Camera_getOrigin( CamWnd& camwnd ){ ++ return Camera_getOrigin( camwnd.getCamera() ); ++} ++ ++void Camera_setOrigin( CamWnd& camwnd, const Vector3& origin ){ ++ Camera_setOrigin( camwnd.getCamera(), origin ); ++} ++ ++const Vector3& Camera_getAngles( CamWnd& camwnd ){ ++ return Camera_getAngles( camwnd.getCamera() ); ++} ++ ++void Camera_setAngles( CamWnd& camwnd, const Vector3& angles ){ ++ Camera_setAngles( camwnd.getCamera(), angles ); ++} ++ ++ ++// ============================================================================= ++// CamWnd class ++ ++gboolean enable_freelook_button_press( ui::Widget widget, GdkEventButton* event, CamWnd* camwnd ){ ++ if ( event->type == GDK_BUTTON_PRESS && event->button == 3 && modifiers_for_state( event->state ) == c_modifierNone ) { ++ camwnd->EnableFreeMove(); ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++gboolean disable_freelook_button_press( ui::Widget widget, GdkEventButton* event, CamWnd* camwnd ){ ++ if ( event->type == GDK_BUTTON_PRESS && event->button == 3 && modifiers_for_state( event->state ) == c_modifierNone ) { ++ camwnd->DisableFreeMove(); ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++#if 0 ++gboolean mousecontrol_button_press( ui::Widget widget, GdkEventButton* event, CamWnd* camwnd ){ ++ if ( event->type == GDK_BUTTON_PRESS && event->button == 3 ) { ++ Cam_MouseControl( camwnd->getCamera(), event->x, widget->allocation.height - 1 - event->y ); ++ } ++ return FALSE; ++} ++#endif ++ ++void camwnd_update_xor_rectangle( CamWnd& self, rect_t area ){ ++ if ( self.m_gl_widget.visible() ) { ++ self.m_XORRectangle.set( rectangle_from_area( area.min, area.max, self.getCamera().width, self.getCamera().height ) ); ++ } ++} ++ ++ ++gboolean selection_button_press( ui::Widget widget, GdkEventButton* event, WindowObserver* observer ){ ++ if ( event->type == GDK_BUTTON_PRESS ) { ++ observer->onMouseDown( WindowVector_forDouble( event->x, event->y ), button_for_button( event->button ), modifiers_for_state( event->state ) ); ++ } ++ return FALSE; ++} ++ ++gboolean selection_button_release( ui::Widget widget, GdkEventButton* event, WindowObserver* observer ){ ++ if ( event->type == GDK_BUTTON_RELEASE ) { ++ observer->onMouseUp( WindowVector_forDouble( event->x, event->y ), button_for_button( event->button ), modifiers_for_state( event->state ) ); ++ } ++ return FALSE; ++} ++ ++void selection_motion( gdouble x, gdouble y, guint state, void* data ){ ++ //globalOutputStream() << "motion... "; ++ reinterpret_cast( data )->onMouseMotion( WindowVector_forDouble( x, y ), modifiers_for_state( state ) ); ++} ++ ++inline WindowVector windowvector_for_widget_centre( ui::Widget widget ){ ++ auto allocation = widget.dimensions(); ++ return WindowVector( static_cast( allocation.width / 2 ), static_cast(allocation.height / 2 ) ); ++} ++ ++gboolean selection_button_press_freemove( ui::Widget widget, GdkEventButton* event, WindowObserver* observer ){ ++ if ( event->type == GDK_BUTTON_PRESS ) { ++ observer->onMouseDown( windowvector_for_widget_centre( widget ), button_for_button( event->button ), modifiers_for_state( event->state ) ); ++ } ++ return FALSE; ++} ++ ++gboolean selection_button_release_freemove( ui::Widget widget, GdkEventButton* event, WindowObserver* observer ){ ++ if ( event->type == GDK_BUTTON_RELEASE ) { ++ observer->onMouseUp( windowvector_for_widget_centre( widget ), button_for_button( event->button ), modifiers_for_state( event->state ) ); ++ } ++ return FALSE; ++} ++ ++gboolean selection_motion_freemove( ui::Widget widget, GdkEventMotion *event, WindowObserver* observer ){ ++ observer->onMouseMotion( windowvector_for_widget_centre( widget ), modifiers_for_state( event->state ) ); ++ return FALSE; ++} ++ ++gboolean wheelmove_scroll( ui::Widget widget, GdkEventScroll* event, CamWnd* camwnd ){ ++ if ( event->direction == GDK_SCROLL_UP ) { ++ Camera_Freemove_updateAxes( camwnd->getCamera() ); ++ Camera_setOrigin( *camwnd, vector3_added( Camera_getOrigin( *camwnd ), vector3_scaled( camwnd->getCamera().forward, static_cast( g_camwindow_globals_private.m_nMoveSpeed ) ) ) ); ++ } ++ else if ( event->direction == GDK_SCROLL_DOWN ) { ++ Camera_Freemove_updateAxes( camwnd->getCamera() ); ++ Camera_setOrigin( *camwnd, vector3_added( Camera_getOrigin( *camwnd ), vector3_scaled( camwnd->getCamera().forward, -static_cast( g_camwindow_globals_private.m_nMoveSpeed ) ) ) ); ++ } ++ ++ return FALSE; ++} ++ ++gboolean camera_size_allocate( ui::Widget widget, GtkAllocation* allocation, CamWnd* camwnd ){ ++ camwnd->getCamera().width = allocation->width; ++ camwnd->getCamera().height = allocation->height; ++ Camera_updateProjection( camwnd->getCamera() ); ++ camwnd->m_window_observer->onSizeChanged( camwnd->getCamera().width, camwnd->getCamera().height ); ++ camwnd->queue_draw(); ++ return FALSE; ++} ++ ++gboolean camera_expose( ui::Widget widget, GdkEventExpose* event, gpointer data ){ ++ reinterpret_cast( data )->draw(); ++ return FALSE; ++} ++ ++void KeyEvent_connect( const char* name ){ ++ const KeyEvent& keyEvent = GlobalKeyEvents_find( name ); ++ keydown_accelerators_add( keyEvent.m_accelerator, keyEvent.m_keyDown ); ++ keyup_accelerators_add( keyEvent.m_accelerator, keyEvent.m_keyUp ); ++} ++ ++void KeyEvent_disconnect( const char* name ){ ++ const KeyEvent& keyEvent = GlobalKeyEvents_find( name ); ++ keydown_accelerators_remove( keyEvent.m_accelerator ); ++ keyup_accelerators_remove( keyEvent.m_accelerator ); ++} ++ ++void CamWnd_registerCommands( CamWnd& camwnd ){ ++<<<<<<< HEAD ++ GlobalKeyEvents_insert( "CameraForward", Accelerator( GDK_KEY_Up ), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraBack", Accelerator( GDK_KEY_Down ), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraLeft", Accelerator( GDK_KEY_Left ), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraRight", Accelerator( GDK_KEY_Right ), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraStrafeRight", Accelerator( GDK_KEY_period ), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraStrafeLeft", Accelerator( GDK_KEY_comma ), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraUp", Accelerator( 'D' ), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraDown", Accelerator( 'C' ), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraAngleDown", Accelerator( 'A' ), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraAngleUp", Accelerator( 'Z' ), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++======= ++ GlobalKeyEvents_insert( "CameraForward", accelerator_null(), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraBack", accelerator_null(), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraLeft", accelerator_null(), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraRight", accelerator_null(), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraStrafeRight", accelerator_null(), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraStrafeLeft", accelerator_null(), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraUp", accelerator_null(), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraDown", accelerator_null(), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraAngleUp", accelerator_null(), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84 ++ ); ++ GlobalKeyEvents_insert( "CameraAngleDown", accelerator_null(), ++ ReferenceCaller( camwnd.getCamera() ), ++ ReferenceCaller( camwnd.getCamera() ) ++ ); ++ ++<<<<<<< HEAD ++ GlobalKeyEvents_insert( "CameraFreeMoveForward", Accelerator( GDK_KEY_Up ), ++ FreeMoveCameraMoveForwardKeyDownCaller( camwnd.getCamera() ), ++ FreeMoveCameraMoveForwardKeyUpCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraFreeMoveBack", Accelerator( GDK_KEY_Down ), ++ FreeMoveCameraMoveBackKeyDownCaller( camwnd.getCamera() ), ++ FreeMoveCameraMoveBackKeyUpCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraFreeMoveLeft", Accelerator( GDK_KEY_Left ), ++ FreeMoveCameraMoveLeftKeyDownCaller( camwnd.getCamera() ), ++ FreeMoveCameraMoveLeftKeyUpCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraFreeMoveRight", Accelerator( GDK_KEY_Right ), ++======= ++ GlobalKeyEvents_insert( "CameraFreeMoveForward", accelerator_null(), ++ FreeMoveCameraMoveForwardKeyDownCaller( camwnd.getCamera() ), ++ FreeMoveCameraMoveForwardKeyUpCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraFreeMoveBack", accelerator_null(), ++ FreeMoveCameraMoveBackKeyDownCaller( camwnd.getCamera() ), ++ FreeMoveCameraMoveBackKeyUpCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraFreeMoveLeft", accelerator_null(), ++ FreeMoveCameraMoveLeftKeyDownCaller( camwnd.getCamera() ), ++ FreeMoveCameraMoveLeftKeyUpCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraFreeMoveRight", accelerator_null(), ++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84 ++ FreeMoveCameraMoveRightKeyDownCaller( camwnd.getCamera() ), ++ FreeMoveCameraMoveRightKeyUpCaller( camwnd.getCamera() ) ++ ); ++ ++ GlobalKeyEvents_insert( "CameraFreeMoveForward2", accelerator_null(), ++ FreeMoveCameraMoveForwardKeyDownCaller( camwnd.getCamera() ), ++ FreeMoveCameraMoveForwardKeyUpCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraFreeMoveBack2", accelerator_null(), ++ FreeMoveCameraMoveBackKeyDownCaller( camwnd.getCamera() ), ++ FreeMoveCameraMoveBackKeyUpCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraFreeMoveLeft2", accelerator_null(), ++ FreeMoveCameraMoveLeftKeyDownCaller( camwnd.getCamera() ), ++ FreeMoveCameraMoveLeftKeyUpCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraFreeMoveRight2", accelerator_null(), ++ FreeMoveCameraMoveRightKeyDownCaller( camwnd.getCamera() ), ++ FreeMoveCameraMoveRightKeyUpCaller( camwnd.getCamera() ) ++ ); ++ ++<<<<<<< HEAD ++ GlobalKeyEvents_insert( "CameraFreeMoveUp", Accelerator( 'D' ), ++ FreeMoveCameraMoveUpKeyDownCaller( camwnd.getCamera() ), ++ FreeMoveCameraMoveUpKeyUpCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraFreeMoveDown", Accelerator( 'C' ), ++======= ++ GlobalKeyEvents_insert( "CameraFreeMoveUp", accelerator_null(), ++ FreeMoveCameraMoveUpKeyDownCaller( camwnd.getCamera() ), ++ FreeMoveCameraMoveUpKeyUpCaller( camwnd.getCamera() ) ++ ); ++ GlobalKeyEvents_insert( "CameraFreeMoveDown", accelerator_null(), ++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84 ++ FreeMoveCameraMoveDownKeyDownCaller( camwnd.getCamera() ), ++ FreeMoveCameraMoveDownKeyUpCaller( camwnd.getCamera() ) ++ ); ++ ++<<<<<<< HEAD ++ GlobalCommands_insert( "CameraForward", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_Up ) ); ++ GlobalCommands_insert( "CameraBack", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_Down ) ); ++ GlobalCommands_insert( "CameraLeft", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_Left ) ); ++ GlobalCommands_insert( "CameraRight", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_Right ) ); ++ GlobalCommands_insert( "CameraStrafeRight", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_period ) ); ++ GlobalCommands_insert( "CameraStrafeLeft", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_comma ) ); ++ ++ GlobalCommands_insert( "CameraUp", ReferenceCaller( camwnd.getCamera() ), Accelerator( 'D' ) ); ++ GlobalCommands_insert( "CameraDown", ReferenceCaller( camwnd.getCamera() ), Accelerator( 'C' ) ); ++ GlobalCommands_insert( "CameraAngleUp", ReferenceCaller( camwnd.getCamera() ), Accelerator( 'A' ) ); ++ GlobalCommands_insert( "CameraAngleDown", ReferenceCaller( camwnd.getCamera() ), Accelerator( 'Z' ) ); ++======= ++ GlobalCommands_insert( "CameraForward", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraBack", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraLeft", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraRight", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraStrafeRight", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraStrafeLeft", ReferenceCaller( camwnd.getCamera() ) ); ++ ++ GlobalCommands_insert( "CameraUp", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraDown", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraAngleUp", ReferenceCaller( camwnd.getCamera() ) ); ++ GlobalCommands_insert( "CameraAngleDown", ReferenceCaller( camwnd.getCamera() ) ); ++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84 ++} ++ ++void CamWnd_Move_Enable( CamWnd& camwnd ){ ++ KeyEvent_connect( "CameraForward" ); ++ KeyEvent_connect( "CameraBack" ); ++ KeyEvent_connect( "CameraLeft" ); ++ KeyEvent_connect( "CameraRight" ); ++ KeyEvent_connect( "CameraStrafeRight" ); ++ KeyEvent_connect( "CameraStrafeLeft" ); ++ KeyEvent_connect( "CameraUp" ); ++ KeyEvent_connect( "CameraDown" ); ++ KeyEvent_connect( "CameraAngleUp" ); ++ KeyEvent_connect( "CameraAngleDown" ); ++} ++ ++void CamWnd_Move_Disable( CamWnd& camwnd ){ ++ KeyEvent_disconnect( "CameraForward" ); ++ KeyEvent_disconnect( "CameraBack" ); ++ KeyEvent_disconnect( "CameraLeft" ); ++ KeyEvent_disconnect( "CameraRight" ); ++ KeyEvent_disconnect( "CameraStrafeRight" ); ++ KeyEvent_disconnect( "CameraStrafeLeft" ); ++ KeyEvent_disconnect( "CameraUp" ); ++ KeyEvent_disconnect( "CameraDown" ); ++ KeyEvent_disconnect( "CameraAngleUp" ); ++ KeyEvent_disconnect( "CameraAngleDown" ); ++} ++ ++void CamWnd_Move_Discrete_Enable( CamWnd& camwnd ){ ++ command_connect_accelerator( "CameraForward" ); ++ command_connect_accelerator( "CameraBack" ); ++ command_connect_accelerator( "CameraLeft" ); ++ command_connect_accelerator( "CameraRight" ); ++ command_connect_accelerator( "CameraStrafeRight" ); ++ command_connect_accelerator( "CameraStrafeLeft" ); ++ command_connect_accelerator( "CameraUp" ); ++ command_connect_accelerator( "CameraDown" ); ++ command_connect_accelerator( "CameraAngleUp" ); ++ command_connect_accelerator( "CameraAngleDown" ); ++} ++ ++void CamWnd_Move_Discrete_Disable( CamWnd& camwnd ){ ++ command_disconnect_accelerator( "CameraForward" ); ++ command_disconnect_accelerator( "CameraBack" ); ++ command_disconnect_accelerator( "CameraLeft" ); ++ command_disconnect_accelerator( "CameraRight" ); ++ command_disconnect_accelerator( "CameraStrafeRight" ); ++ command_disconnect_accelerator( "CameraStrafeLeft" ); ++ command_disconnect_accelerator( "CameraUp" ); ++ command_disconnect_accelerator( "CameraDown" ); ++ command_disconnect_accelerator( "CameraAngleUp" ); ++ command_disconnect_accelerator( "CameraAngleDown" ); ++} ++ ++struct CamWnd_Move_Discrete { ++ static void Export(const Callback &returnz) { ++ returnz(g_camwindow_globals_private.m_bCamDiscrete); ++ } ++ ++ static void Import(bool value) { ++ if (g_camwnd) { ++ Import_(*g_camwnd, value); ++ } else { ++ g_camwindow_globals_private.m_bCamDiscrete = value; ++ } ++ } ++ ++ static void Import_(CamWnd &camwnd, bool value) { ++ if ( g_camwindow_globals_private.m_bCamDiscrete ) { ++ CamWnd_Move_Discrete_Disable( camwnd ); ++ } else { ++ CamWnd_Move_Disable( camwnd ); ++ } ++ ++ g_camwindow_globals_private.m_bCamDiscrete = value; ++ ++ if ( g_camwindow_globals_private.m_bCamDiscrete ) { ++ CamWnd_Move_Discrete_Enable( camwnd ); ++ } else { ++ CamWnd_Move_Enable( camwnd ); ++ } ++} ++}; ++ ++ ++void CamWnd_Add_Handlers_Move( CamWnd& camwnd ){ ++ camwnd.m_selection_button_press_handler = camwnd.m_gl_widget.connect( "button_press_event", G_CALLBACK( selection_button_press ), camwnd.m_window_observer ); ++ camwnd.m_selection_button_release_handler = camwnd.m_gl_widget.connect( "button_release_event", G_CALLBACK( selection_button_release ), camwnd.m_window_observer ); ++ camwnd.m_selection_motion_handler = camwnd.m_gl_widget.connect( "motion_notify_event", G_CALLBACK( DeferredMotion::gtk_motion ), &camwnd.m_deferred_motion ); ++ ++ camwnd.m_freelook_button_press_handler = camwnd.m_gl_widget.connect( "button_press_event", G_CALLBACK( enable_freelook_button_press ), &camwnd ); ++ ++ if ( g_camwindow_globals_private.m_bCamDiscrete ) { ++ CamWnd_Move_Discrete_Enable( camwnd ); ++ } ++ else ++ { ++ CamWnd_Move_Enable( camwnd ); ++ } ++} ++ ++void CamWnd_Remove_Handlers_Move( CamWnd& camwnd ){ ++ g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_selection_button_press_handler ); ++ g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_selection_button_release_handler ); ++ g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_selection_motion_handler ); ++ ++ g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_freelook_button_press_handler ); ++ ++ if ( g_camwindow_globals_private.m_bCamDiscrete ) { ++ CamWnd_Move_Discrete_Disable( camwnd ); ++ } ++ else ++ { ++ CamWnd_Move_Disable( camwnd ); ++ } ++} ++ ++void CamWnd_Add_Handlers_FreeMove( CamWnd& camwnd ){ ++ camwnd.m_selection_button_press_handler = camwnd.m_gl_widget.connect( "button_press_event", G_CALLBACK( selection_button_press_freemove ), camwnd.m_window_observer ); ++ camwnd.m_selection_button_release_handler = camwnd.m_gl_widget.connect( "button_release_event", G_CALLBACK( selection_button_release_freemove ), camwnd.m_window_observer ); ++ camwnd.m_selection_motion_handler = camwnd.m_gl_widget.connect( "motion_notify_event", G_CALLBACK( selection_motion_freemove ), camwnd.m_window_observer ); ++ ++ camwnd.m_freelook_button_press_handler = camwnd.m_gl_widget.connect( "button_press_event", G_CALLBACK( disable_freelook_button_press ), &camwnd ); ++ ++ KeyEvent_connect( "CameraFreeMoveForward" ); ++ KeyEvent_connect( "CameraFreeMoveBack" ); ++ KeyEvent_connect( "CameraFreeMoveLeft" ); ++ KeyEvent_connect( "CameraFreeMoveRight" ); ++ ++ KeyEvent_connect( "CameraFreeMoveForward2" ); ++ KeyEvent_connect( "CameraFreeMoveBack2" ); ++ KeyEvent_connect( "CameraFreeMoveLeft2" ); ++ KeyEvent_connect( "CameraFreeMoveRight2" ); ++ ++ KeyEvent_connect( "CameraFreeMoveUp" ); ++ KeyEvent_connect( "CameraFreeMoveDown" ); ++} ++ ++void CamWnd_Remove_Handlers_FreeMove( CamWnd& camwnd ){ ++ KeyEvent_disconnect( "CameraFreeMoveForward" ); ++ KeyEvent_disconnect( "CameraFreeMoveBack" ); ++ KeyEvent_disconnect( "CameraFreeMoveLeft" ); ++ KeyEvent_disconnect( "CameraFreeMoveRight" ); ++ ++ KeyEvent_disconnect( "CameraFreeMoveForward2" ); ++ KeyEvent_disconnect( "CameraFreeMoveBack2" ); ++ KeyEvent_disconnect( "CameraFreeMoveLeft2" ); ++ KeyEvent_disconnect( "CameraFreeMoveRight2" ); ++ ++ KeyEvent_disconnect( "CameraFreeMoveUp" ); ++ KeyEvent_disconnect( "CameraFreeMoveDown" ); ++ ++ g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_selection_button_press_handler ); ++ g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_selection_button_release_handler ); ++ g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_selection_motion_handler ); ++ ++ g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_freelook_button_press_handler ); ++} ++ ++CamWnd::CamWnd() : ++ m_view( true ), ++ m_Camera( &m_view, CamWndQueueDraw( *this ) ), ++ m_cameraview( m_Camera, &m_view, ReferenceCaller( *this ) ), ++ m_gl_widget( glwidget_new( TRUE ) ), ++ m_window_observer( NewWindowObserver() ), ++ m_XORRectangle( m_gl_widget ), ++ m_deferredDraw( WidgetQueueDrawCaller( m_gl_widget ) ), ++ m_deferred_motion( selection_motion, m_window_observer ), ++ m_selection_button_press_handler( 0 ), ++ m_selection_button_release_handler( 0 ), ++ m_selection_motion_handler( 0 ), ++ m_freelook_button_press_handler( 0 ), ++ m_drawing( false ){ ++ m_bFreeMove = false; ++ ++ GlobalWindowObservers_add( m_window_observer ); ++ GlobalWindowObservers_connectWidget( m_gl_widget ); ++ ++ m_window_observer->setRectangleDrawCallback( ReferenceCaller( *this ) ); ++ m_window_observer->setView( m_view ); ++ ++ g_object_ref( m_gl_widget._handle ); ++ ++ gtk_widget_set_events( m_gl_widget, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK ); ++ gtk_widget_set_can_focus( m_gl_widget, true ); ++ ++ m_sizeHandler = m_gl_widget.connect( "size_allocate", G_CALLBACK( camera_size_allocate ), this ); ++ m_exposeHandler = m_gl_widget.on_render( G_CALLBACK( camera_expose ), this ); ++ ++ Map_addValidCallback( g_map, DeferredDrawOnMapValidChangedCaller( m_deferredDraw ) ); ++ ++ CamWnd_registerCommands( *this ); ++ ++ CamWnd_Add_Handlers_Move( *this ); ++ ++ m_gl_widget.connect( "scroll_event", G_CALLBACK( wheelmove_scroll ), this ); ++ ++ AddSceneChangeCallback( ReferenceCaller( *this ) ); ++ ++ PressedButtons_connect( g_pressedButtons, m_gl_widget ); ++} ++ ++CamWnd::~CamWnd(){ ++ if ( m_bFreeMove ) { ++ DisableFreeMove(); ++ } ++ ++ CamWnd_Remove_Handlers_Move( *this ); ++ ++ g_signal_handler_disconnect( G_OBJECT( m_gl_widget ), m_sizeHandler ); ++ g_signal_handler_disconnect( G_OBJECT( m_gl_widget ), m_exposeHandler ); ++ ++ m_gl_widget.unref(); ++ ++ m_window_observer->release(); ++} ++ ++class FloorHeightWalker : public scene::Graph::Walker ++{ ++float m_current; ++float& m_bestUp; ++float& m_bestDown; ++public: ++FloorHeightWalker( float current, float& bestUp, float& bestDown ) : ++ m_current( current ), m_bestUp( bestUp ), m_bestDown( bestDown ){ ++ bestUp = g_MaxWorldCoord; ++ bestDown = -g_MaxWorldCoord; ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ if ( path.top().get().visible() ++ && Node_isBrush( path.top() ) ) { // this node is a floor ++ const AABB& aabb = instance.worldAABB(); ++ float floorHeight = aabb.origin.z() + aabb.extents.z(); ++ if ( floorHeight > m_current && floorHeight < m_bestUp ) { ++ m_bestUp = floorHeight; ++ } ++ if ( floorHeight < m_current && floorHeight > m_bestDown ) { ++ m_bestDown = floorHeight; ++ } ++ } ++ else if( !path.top().get().visible() ){ ++ return false; ++ } ++ return true; ++} ++}; ++ ++void CamWnd::Cam_ChangeFloor( bool up ){ ++ float current = m_Camera.origin[2] - 48; ++ float bestUp; ++ float bestDown; ++ GlobalSceneGraph().traverse( FloorHeightWalker( current, bestUp, bestDown ) ); ++ ++ if ( up && bestUp != g_MaxWorldCoord ) { ++ current = bestUp; ++ } ++ if ( !up && bestDown != -g_MaxWorldCoord ) { ++ current = bestDown; ++ } ++ ++ m_Camera.origin[2] = current + 48; ++ Camera_updateModelview( getCamera() ); ++ CamWnd_Update( *this ); ++ CameraMovedNotify(); ++} ++ ++ ++#if 0 ++ ++// button_press ++Sys_GetCursorPos( &m_PositionDragCursorX, &m_PositionDragCursorY ); ++ ++// motion ++if ( ( m_bFreeMove && ( buttons == ( RAD_CONTROL | RAD_SHIFT ) ) ) ++ || ( !m_bFreeMove && ( buttons == ( RAD_RBUTTON | RAD_CONTROL ) ) ) ) { ++ Cam_PositionDrag(); ++ CamWnd_Update( camwnd ); ++ CameraMovedNotify(); ++ return; ++} ++ ++void CamWnd::Cam_PositionDrag(){ ++ int x, y; ++ ++ Sys_GetCursorPos( m_gl_widget, &x, &y ); ++ if ( x != m_PositionDragCursorX || y != m_PositionDragCursorY ) { ++ x -= m_PositionDragCursorX; ++ vector3_add( m_Camera.origin, vector3_scaled( m_Camera.vright, x ) ); ++ y -= m_PositionDragCursorY; ++ m_Camera.origin[2] -= y; ++ Camera_updateModelview(); ++ CamWnd_Update( camwnd ); ++ CameraMovedNotify(); ++ ++ Sys_SetCursorPos( m_parent, m_PositionDragCursorX, m_PositionDragCursorY ); ++ } ++} ++#endif ++ ++ ++// NOTE TTimo if there's an OS-level focus out of the application ++// then we can release the camera cursor grab ++static gboolean camwindow_freemove_focusout( ui::Widget widget, GdkEventFocus* event, gpointer data ){ ++ reinterpret_cast( data )->DisableFreeMove(); ++ return FALSE; ++} ++ ++void CamWnd::EnableFreeMove(){ ++ //globalOutputStream() << "EnableFreeMove\n"; ++ ++ ASSERT_MESSAGE( !m_bFreeMove, "EnableFreeMove: free-move was already enabled" ); ++ m_bFreeMove = true; ++ Camera_clearMovementFlags( getCamera(), MOVE_ALL ); ++ ++ CamWnd_Remove_Handlers_Move( *this ); ++ CamWnd_Add_Handlers_FreeMove( *this ); ++ ++ gtk_window_set_focus( m_parent, m_gl_widget ); ++ m_freemove_handle_focusout = m_gl_widget.connect( "focus_out_event", G_CALLBACK( camwindow_freemove_focusout ), this ); ++ m_freezePointer.freeze_pointer( m_parent, m_gl_widget, Camera_motionDelta, &m_Camera ); ++ ++ CamWnd_Update( *this ); ++} ++ ++void CamWnd::DisableFreeMove(){ ++ //globalOutputStream() << "DisableFreeMove\n"; ++ ++ ASSERT_MESSAGE( m_bFreeMove, "DisableFreeMove: free-move was not enabled" ); ++ m_bFreeMove = false; ++ Camera_clearMovementFlags( getCamera(), MOVE_ALL ); ++ ++ CamWnd_Remove_Handlers_FreeMove( *this ); ++ CamWnd_Add_Handlers_Move( *this ); ++ ++ m_freezePointer.unfreeze_pointer( m_parent, true ); ++ g_signal_handler_disconnect( G_OBJECT( m_gl_widget ), m_freemove_handle_focusout ); ++ ++ CamWnd_Update( *this ); ++} ++ ++ ++#include "renderer.h" ++ ++class CamRenderer : public Renderer ++{ ++struct state_type ++{ ++ state_type() : m_highlight( 0 ), m_state( 0 ), m_lights( 0 ){ ++ } ++ unsigned int m_highlight; ++ Shader* m_state; ++ const LightList* m_lights; ++}; ++ ++std::vector m_state_stack; ++RenderStateFlags m_globalstate; ++Shader* m_state_select0; ++Shader* m_state_select1; ++const Vector3& m_viewer; ++ ++public: ++CamRenderer( RenderStateFlags globalstate, Shader* select0, Shader* select1, const Vector3& viewer ) : ++ m_globalstate( globalstate ), ++ m_state_select0( select0 ), ++ m_state_select1( select1 ), ++ m_viewer( viewer ){ ++ ASSERT_NOTNULL( select0 ); ++ ASSERT_NOTNULL( select1 ); ++ m_state_stack.push_back( state_type() ); ++} ++ ++void SetState( Shader* state, EStyle style ){ ++ ASSERT_NOTNULL( state ); ++ if ( style == eFullMaterials ) { ++ m_state_stack.back().m_state = state; ++ } ++} ++EStyle getStyle() const { ++ return eFullMaterials; ++} ++void PushState(){ ++ m_state_stack.push_back( m_state_stack.back() ); ++} ++void PopState(){ ++ ASSERT_MESSAGE( !m_state_stack.empty(), "popping empty stack" ); ++ m_state_stack.pop_back(); ++} ++void Highlight( EHighlightMode mode, bool bEnable = true ){ ++ ( bEnable ) ++ ? m_state_stack.back().m_highlight |= mode ++ : m_state_stack.back().m_highlight &= ~mode; ++} ++void setLights( const LightList& lights ){ ++ m_state_stack.back().m_lights = &lights; ++} ++void addRenderable( const OpenGLRenderable& renderable, const Matrix4& world ){ ++ if ( m_state_stack.back().m_highlight & ePrimitive ) { ++ m_state_select0->addRenderable( renderable, world, m_state_stack.back().m_lights ); ++ } ++ if ( m_state_stack.back().m_highlight & eFace ) { ++ m_state_select1->addRenderable( renderable, world, m_state_stack.back().m_lights ); ++ } ++ ++ m_state_stack.back().m_state->addRenderable( renderable, world, m_state_stack.back().m_lights ); ++} ++ ++void render( const Matrix4& modelview, const Matrix4& projection ){ ++ GlobalShaderCache().render( m_globalstate, modelview, projection, m_viewer ); ++} ++}; ++ ++/* ++ ============== ++ Cam_Draw ++ ============== ++ */ ++/* ++void ShowStatsToggle(){ ++ g_camwindow_globals_private.m_showStats ^= 1; ++} ++ ++void ShowStatsExport( const Callback &importer ){ ++ importer( g_camwindow_globals_private.m_showStats ); ++} ++ ++FreeCaller&), ShowStatsExport> g_show_stats_caller; ++Callback &)> g_show_stats_callback( g_show_stats_caller ); ++ToggleItem g_show_stats( g_show_stats_callback ); ++*/ ++ ++void ShowStatsToggle(){ ++ g_camwindow_globals_private.m_showStats ^= 1; ++// g_show_stats.update(); ++ UpdateAllWindows(); ++} ++typedef FreeCaller ShowStatsToggleCaller; ++void ShowStatsExport( const Callback & importer ){ ++ importer( g_camwindow_globals_private.m_showStats ); ++} ++typedef FreeCaller &), ShowStatsExport> ShowStatsExportCaller; ++ ++ShowStatsExportCaller g_show_stats_caller; ++Callback &)> g_show_stats_callback( g_show_stats_caller ); ++ToggleItem g_show_stats( g_show_stats_callback ); ++ ++void CamWnd::Cam_Draw(){ ++ glViewport( 0, 0, m_Camera.width, m_Camera.height ); ++#if 0 ++ GLint viewprt[4]; ++ glGetIntegerv( GL_VIEWPORT, viewprt ); ++#endif ++ ++ // enable depth buffer writes ++ glDepthMask( GL_TRUE ); ++ glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); ++ ++ Vector3 clearColour( 0, 0, 0 ); ++ if ( m_Camera.draw_mode != cd_lighting ) { ++ clearColour = g_camwindow_globals.color_cameraback; ++ } ++ ++ glClearColor( clearColour[0], clearColour[1], clearColour[2], 0 ); ++ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); ++ ++ extern void Renderer_ResetStats(); ++ Renderer_ResetStats(); ++ extern void Cull_ResetStats(); ++ Cull_ResetStats(); ++ ++ glMatrixMode( GL_PROJECTION ); ++ glLoadMatrixf( reinterpret_cast( &m_Camera.projection ) ); ++ ++ glMatrixMode( GL_MODELVIEW ); ++ glLoadMatrixf( reinterpret_cast( &m_Camera.modelview ) ); ++ ++ ++ // one directional light source directly behind the viewer ++ { ++ GLfloat inverse_cam_dir[4], ambient[4], diffuse[4]; //, material[4]; ++ ++ ambient[0] = ambient[1] = ambient[2] = 0.4f; ++ ambient[3] = 1.0f; ++ diffuse[0] = diffuse[1] = diffuse[2] = 0.4f; ++ diffuse[3] = 1.0f; ++ //material[0] = material[1] = material[2] = 0.8f; ++ //material[3] = 1.0f; ++ ++ inverse_cam_dir[0] = m_Camera.vpn[0]; ++ inverse_cam_dir[1] = m_Camera.vpn[1]; ++ inverse_cam_dir[2] = m_Camera.vpn[2]; ++ inverse_cam_dir[3] = 0; ++ ++ glLightfv( GL_LIGHT0, GL_POSITION, inverse_cam_dir ); ++ ++ glLightfv( GL_LIGHT0, GL_AMBIENT, ambient ); ++ glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse ); ++ ++ glEnable( GL_LIGHT0 ); ++ } ++ ++ ++ unsigned int globalstate = RENDER_DEPTHTEST | RENDER_COLOURWRITE | RENDER_DEPTHWRITE | RENDER_ALPHATEST | RENDER_BLEND | RENDER_CULLFACE | RENDER_COLOURARRAY | RENDER_OFFSETLINE | RENDER_POLYGONSMOOTH | RENDER_LINESMOOTH | RENDER_FOG | RENDER_COLOURCHANGE; ++ switch ( m_Camera.draw_mode ) ++ { ++ case cd_wire: ++ break; ++ case cd_solid: ++ globalstate |= RENDER_FILL ++ | RENDER_LIGHTING ++ | RENDER_SMOOTH ++ | RENDER_SCALED; ++ break; ++ case cd_texture: ++ globalstate |= RENDER_FILL ++ | RENDER_LIGHTING ++ | RENDER_TEXTURE ++ | RENDER_SMOOTH ++ | RENDER_SCALED; ++ break; ++ case cd_lighting: ++ globalstate |= RENDER_FILL ++ | RENDER_LIGHTING ++ | RENDER_TEXTURE ++ | RENDER_SMOOTH ++ | RENDER_SCALED ++ | RENDER_BUMP ++ | RENDER_PROGRAM ++ | RENDER_SCREEN; ++ break; ++ default: ++ globalstate = 0; ++ break; ++ } ++ ++// if ( !g_xywindow_globals.m_bNoStipple ) { ++ globalstate |= RENDER_LINESTIPPLE | RENDER_POLYGONSTIPPLE; ++// } ++ ++ { ++ CamRenderer renderer( globalstate, m_state_select2, m_state_select1, m_view.getViewer() ); ++ ++ Scene_Render( renderer, m_view ); ++ ++ renderer.render( m_Camera.modelview, m_Camera.projection ); ++ } ++ ++ // prepare for 2d stuff ++ glColor4f( 1, 1, 1, 1 ); ++ glDisable( GL_BLEND ); ++ glMatrixMode( GL_PROJECTION ); ++ glLoadIdentity(); ++ glOrtho( 0, (float)m_Camera.width, 0, (float)m_Camera.height, -100, 100 ); ++ glScalef( 1, -1, 1 ); ++ glTranslatef( 0, -(float)m_Camera.height, 0 ); ++ glMatrixMode( GL_MODELVIEW ); ++ glLoadIdentity(); ++ ++ if ( GlobalOpenGL().GL_1_3() ) { ++ glClientActiveTexture( GL_TEXTURE0 ); ++ glActiveTexture( GL_TEXTURE0 ); ++ } ++ ++ glDisableClientState( GL_TEXTURE_COORD_ARRAY ); ++ glDisableClientState( GL_NORMAL_ARRAY ); ++ glDisableClientState( GL_COLOR_ARRAY ); ++ ++ glDisable( GL_TEXTURE_2D ); ++ glDisable( GL_LIGHTING ); ++ glDisable( GL_COLOR_MATERIAL ); ++ glDisable( GL_DEPTH_TEST ); ++ glColor3f( 1.f, 1.f, 1.f ); ++ glLineWidth( 1 ); ++ ++ // draw the crosshair ++ if ( m_bFreeMove ) { ++ glBegin( GL_LINES ); ++ glVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f + 6 ); ++ glVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f + 2 ); ++ glVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f - 6 ); ++ glVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f - 2 ); ++ glVertex2f( (float)m_Camera.width / 2.f + 6, (float)m_Camera.height / 2.f ); ++ glVertex2f( (float)m_Camera.width / 2.f + 2, (float)m_Camera.height / 2.f ); ++ glVertex2f( (float)m_Camera.width / 2.f - 6, (float)m_Camera.height / 2.f ); ++ glVertex2f( (float)m_Camera.width / 2.f - 2, (float)m_Camera.height / 2.f ); ++ glEnd(); ++ } ++ ++ if ( g_camwindow_globals_private.m_showStats ) { ++ glRasterPos3f( 1.0f, static_cast( m_Camera.height ) - GlobalOpenGL().m_font->getPixelDescent(), 0.0f ); ++ extern const char* Renderer_GetStats(); ++ GlobalOpenGL().drawString( Renderer_GetStats() ); ++ ++ glRasterPos3f( 1.0f, static_cast( m_Camera.height ) - GlobalOpenGL().m_font->getPixelDescent() - GlobalOpenGL().m_font->getPixelHeight(), 0.0f ); ++ extern const char* Cull_GetStats(); ++ GlobalOpenGL().drawString( Cull_GetStats() ); ++ } ++ ++ // bind back to the default texture so that we don't have problems ++ // elsewhere using/modifying texture maps between contexts ++ glBindTexture( GL_TEXTURE_2D, 0 ); ++} ++ ++void CamWnd::draw(){ ++ m_drawing = true; ++ ++ //globalOutputStream() << "draw...\n"; ++ if ( glwidget_make_current( m_gl_widget ) != FALSE ) { ++ if ( Map_Valid( g_map ) && ScreenUpdates_Enabled() ) { ++ GlobalOpenGL_debugAssertNoErrors(); ++ Cam_Draw(); ++ GlobalOpenGL_debugAssertNoErrors(); ++ //qglFinish(); ++ ++ m_XORRectangle.set( rectangle_t() ); ++ } ++ ++ glwidget_swap_buffers( m_gl_widget ); ++ } ++ ++ m_drawing = false; ++} ++ ++void CamWnd::BenchMark(){ ++ double dStart = Sys_DoubleTime(); ++ for ( int i = 0 ; i < 100 ; i++ ) ++ { ++ Vector3 angles; ++ angles[CAMERA_ROLL] = 0; ++ angles[CAMERA_PITCH] = 0; ++ angles[CAMERA_YAW] = static_cast( i * ( 360.0 / 100.0 ) ); ++ Camera_setAngles( *this, angles ); ++ } ++ double dEnd = Sys_DoubleTime(); ++ globalOutputStream() << FloatFormat( dEnd - dStart, 5, 2 ) << " seconds\n"; ++} ++ ++ ++void GlobalCamera_ResetAngles(){ ++ CamWnd& camwnd = *g_camwnd; ++ Vector3 angles; ++ angles[CAMERA_ROLL] = angles[CAMERA_PITCH] = 0; ++ angles[CAMERA_YAW] = static_cast( 22.5 * floor( ( Camera_getAngles( camwnd )[CAMERA_YAW] + 11 ) / 22.5 ) ); ++ Camera_setAngles( camwnd, angles ); ++} ++ ++#include "select.h" ++ ++void GlobalCamera_FocusOnSelected(){ ++ CamWnd& camwnd = *g_camwnd; ++ ++ Vector3 angles( Camera_getAngles( camwnd ) ); ++ Vector3 radangles( degrees_to_radians( angles[0] ), degrees_to_radians( angles[1] ), degrees_to_radians( angles[2] ) ); ++ Vector3 viewvector; ++ viewvector[0] = cos( radangles[1] ) * cos( radangles[0] ); ++ viewvector[1] = sin( radangles[1] ) * cos( radangles[0] ); ++ viewvector[2] = sin( radangles[0] ); ++ ++ Vector3 camorigin( Camera_getOrigin( camwnd ) ); ++ ++ AABB aabb( aabb_for_minmax( Select_getWorkZone().d_work_min, Select_getWorkZone().d_work_max ) ); ++ ++ View& view = *( camwnd.getCamera().m_view ); ++ ++ Plane3 frustumPlanes[4]; ++ frustumPlanes[0] = plane3_translated( view.getFrustum().left, camorigin - aabb.origin ); ++ frustumPlanes[1] = plane3_translated( view.getFrustum().right, camorigin - aabb.origin ); ++ frustumPlanes[2] = plane3_translated( view.getFrustum().top, camorigin - aabb.origin ); ++ frustumPlanes[3] = plane3_translated( view.getFrustum().bottom, camorigin - aabb.origin ); ++ ++ float offset = 64.0f; ++ ++ Vector3 corners[8]; ++ aabb_corners( aabb, corners ); ++ ++ for ( size_t i = 0; i < 4; ++i ){ ++ for ( size_t j = 0; j < 8; ++j ){ ++ Ray ray( aabb.origin, -viewvector ); ++ //Plane3 newplane( frustumPlanes[i].normal(), vector3_dot( frustumPlanes[i].normal(), corners[j] - frustumPlanes[i].normal() * 16.0f ) ); ++ Plane3 newplane( frustumPlanes[i].normal(), vector3_dot( frustumPlanes[i].normal(), corners[j] ) ); ++ float d = vector3_dot( ray.direction, newplane.normal() ); ++ if( d != 0 ){ ++ float s = vector3_dot( newplane.normal() * newplane.dist() - ray.origin, newplane.normal() ) / d; ++ offset = std::max( offset, s ); ++ } ++ } ++ } ++ Camera_setOrigin( camwnd, aabb.origin - viewvector * offset ); ++} ++ ++void Camera_ChangeFloorUp(){ ++ CamWnd& camwnd = *g_camwnd; ++ camwnd.Cam_ChangeFloor( true ); ++} ++ ++void Camera_ChangeFloorDown(){ ++ CamWnd& camwnd = *g_camwnd; ++ camwnd.Cam_ChangeFloor( false ); ++} ++ ++void Camera_CubeIn(){ ++ CamWnd& camwnd = *g_camwnd; ++ g_camwindow_globals.m_nCubicScale--; ++ if ( g_camwindow_globals.m_nCubicScale < 1 ) { ++ g_camwindow_globals.m_nCubicScale = 1; ++ } ++ Camera_updateProjection( camwnd.getCamera() ); ++ CamWnd_Update( camwnd ); ++ g_pParentWnd->SetGridStatus(); ++} ++ ++void Camera_CubeOut(){ ++ CamWnd& camwnd = *g_camwnd; ++ g_camwindow_globals.m_nCubicScale++; ++ if ( g_camwindow_globals.m_nCubicScale > 23 ) { ++ g_camwindow_globals.m_nCubicScale = 23; ++ } ++ Camera_updateProjection( camwnd.getCamera() ); ++ CamWnd_Update( camwnd ); ++ g_pParentWnd->SetGridStatus(); ++} ++ ++bool Camera_GetFarClip(){ ++ return g_camwindow_globals_private.m_bCubicClipping; ++} ++ ++ConstReferenceCaller &), PropertyImpl::Export> g_getfarclip_caller( g_camwindow_globals_private.m_bCubicClipping ); ++ToggleItem g_getfarclip_item( g_getfarclip_caller ); ++ ++void Camera_SetFarClip( bool value ){ ++ CamWnd& camwnd = *g_camwnd; ++ g_camwindow_globals_private.m_bCubicClipping = value; ++ g_getfarclip_item.update(); ++ Camera_updateProjection( camwnd.getCamera() ); ++ CamWnd_Update( camwnd ); ++} ++ ++struct Camera_FarClip { ++ static void Export(const Callback &returnz) { ++ returnz(g_camwindow_globals_private.m_bCubicClipping); ++ } ++ ++ static void Import(bool value) { ++ Camera_SetFarClip(value); ++ } ++}; ++ ++void Camera_ToggleFarClip(){ ++ Camera_SetFarClip( !Camera_GetFarClip() ); ++} ++ ++ ++void CamWnd_constructToolbar( ui::Toolbar toolbar ){ ++ toolbar_append_toggle_button( toolbar, "Cubic clip the camera view (Ctrl + \\)", "view_cubicclipping.png", "ToggleCubicClip" ); ++} ++ ++void CamWnd_registerShortcuts(){ ++ toggle_add_accelerator( "ToggleCubicClip" ); ++ ++ if ( g_pGameDescription->mGameType == "doom3" ) { ++ command_connect_accelerator( "TogglePreview" ); ++ } ++ ++ command_connect_accelerator( "CameraSpeedInc" ); ++ command_connect_accelerator( "CameraSpeedDec" ); ++} ++ ++ ++void GlobalCamera_Benchmark(){ ++ CamWnd& camwnd = *g_camwnd; ++ camwnd.BenchMark(); ++} ++ ++void GlobalCamera_Update(){ ++ CamWnd& camwnd = *g_camwnd; ++ CamWnd_Update( camwnd ); ++} ++ ++camera_draw_mode CamWnd_GetMode(){ ++ return camera_t::draw_mode; ++} ++void CamWnd_SetMode( camera_draw_mode mode ){ ++ ShaderCache_setBumpEnabled( mode == cd_lighting ); ++ camera_t::draw_mode = mode; ++ if ( g_camwnd != 0 ) { ++ CamWnd_Update( *g_camwnd ); ++ } ++} ++ ++void CamWnd_TogglePreview( void ){ ++ // gametype must be doom3 for this function to work ++ // if the gametype is not doom3 something is wrong with the ++ // global command list or somebody else calls this function. ++ ASSERT_MESSAGE( g_pGameDescription->mGameType == "doom3", "CamWnd_TogglePreview called although mGameType is not doom3 compatible" ); ++ ++ // switch between textured and lighting mode ++ CamWnd_SetMode( ( CamWnd_GetMode() == cd_lighting ) ? cd_texture : cd_lighting ); ++} ++ ++ ++CameraModel* g_camera_model = 0; ++ ++void CamWnd_LookThroughCamera( CamWnd& camwnd ){ ++ if ( g_camera_model != 0 ) { ++ CamWnd_Add_Handlers_Move( camwnd ); ++ g_camera_model->setCameraView( 0, Callback() ); ++ g_camera_model = 0; ++ Camera_updateModelview( camwnd.getCamera() ); ++ Camera_updateProjection( camwnd.getCamera() ); ++ CamWnd_Update( camwnd ); ++ } ++} ++ ++inline CameraModel* Instance_getCameraModel( scene::Instance& instance ){ ++ return InstanceTypeCast::cast( instance ); ++} ++ ++void CamWnd_LookThroughSelected( CamWnd& camwnd ){ ++ if ( g_camera_model != 0 ) { ++ CamWnd_LookThroughCamera( camwnd ); ++ } ++ ++ if ( GlobalSelectionSystem().countSelected() != 0 ) { ++ scene::Instance& instance = GlobalSelectionSystem().ultimateSelected(); ++ CameraModel* cameraModel = Instance_getCameraModel( instance ); ++ if ( cameraModel != 0 ) { ++ CamWnd_Remove_Handlers_Move( camwnd ); ++ g_camera_model = cameraModel; ++ g_camera_model->setCameraView( &camwnd.getCameraView(), ReferenceCaller( camwnd ) ); ++ } ++ } ++} ++ ++void GlobalCamera_LookThroughSelected(){ ++ CamWnd_LookThroughSelected( *g_camwnd ); ++} ++ ++void GlobalCamera_LookThroughCamera(){ ++ CamWnd_LookThroughCamera( *g_camwnd ); ++} ++ ++struct RenderMode { ++ static void Export(const Callback &returnz) { ++ switch (CamWnd_GetMode()) { ++ case cd_wire: ++ returnz(0); ++ break; ++ case cd_solid: ++ returnz(1); ++ break; ++ case cd_texture: ++ returnz(2); ++ break; ++ case cd_lighting: ++ returnz(3); ++ break; ++ } ++ } ++ ++ static void Import(int value) { ++ switch (value) { ++ case 0: ++ CamWnd_SetMode( cd_wire ); ++ break; ++ case 1: ++ CamWnd_SetMode( cd_solid ); ++ break; ++ case 2: ++ CamWnd_SetMode( cd_texture ); ++ break; ++ case 3: ++ CamWnd_SetMode( cd_lighting ); ++ break; ++ default: ++ CamWnd_SetMode( cd_texture ); ++ } ++} ++}; ++ ++void Camera_constructPreferences( PreferencesPage& page ){ ++ page.appendSlider( "Movement Speed", g_camwindow_globals_private.m_nMoveSpeed, TRUE, 0, 0, 100, MIN_CAM_SPEED, MAX_CAM_SPEED, 1, 10 ); ++ page.appendCheckBox( "", "Link strafe speed to movement speed", g_camwindow_globals_private.m_bCamLinkSpeed ); ++ page.appendSlider( "Rotation Speed", g_camwindow_globals_private.m_nAngleSpeed, TRUE, 0, 0, 3, 1, 180, 1, 10 ); ++ page.appendCheckBox( "", "Invert mouse vertical axis", g_camwindow_globals_private.m_bCamInverseMouse ); ++ page.appendCheckBox( ++ "", "Discrete movement", ++ make_property() ++ ); ++ page.appendCheckBox( ++ "", "Enable far-clip plane", ++ make_property() ++ ); ++ ++ if ( g_pGameDescription->mGameType == "doom3" ) { ++ const char* render_mode[] = { "Wireframe", "Flatshade", "Textured", "Lighting" }; ++ ++ page.appendCombo( ++ "Render Mode", ++ STRING_ARRAY_RANGE( render_mode ), ++ make_property() ++ ); ++ } ++ else ++ { ++ const char* render_mode[] = { "Wireframe", "Flatshade", "Textured" }; ++ ++ page.appendCombo( ++ "Render Mode", ++ STRING_ARRAY_RANGE( render_mode ), ++ make_property() ++ ); ++ } ++ ++ const char* strafe_mode[] = { "Both", "Forward", "Up" }; ++ ++ page.appendCombo( ++ "Strafe Mode", ++ g_camwindow_globals_private.m_nStrafeMode, ++ STRING_ARRAY_RANGE( strafe_mode ) ++ ); ++} ++void Camera_constructPage( PreferenceGroup& group ){ ++ PreferencesPage page( group.createPage( "Camera", "Camera View Preferences" ) ); ++ Camera_constructPreferences( page ); ++} ++void Camera_registerPreferencesPage(){ ++ PreferencesDialog_addSettingsPage( makeCallbackF(Camera_constructPage) ); ++} ++ ++#include "preferencesystem.h" ++#include "stringio.h" ++#include "dialog.h" ++ ++void CameraSpeed_increase(){ ++ if ( g_camwindow_globals_private.m_nMoveSpeed <= ( MAX_CAM_SPEED - CAM_SPEED_STEP - 10 ) ) { ++ g_camwindow_globals_private.m_nMoveSpeed += CAM_SPEED_STEP; ++ } ++ else { ++ g_camwindow_globals_private.m_nMoveSpeed = MAX_CAM_SPEED - 10; ++ } ++} ++ ++void CameraSpeed_decrease(){ ++ if ( g_camwindow_globals_private.m_nMoveSpeed >= ( MIN_CAM_SPEED + CAM_SPEED_STEP ) ) { ++ g_camwindow_globals_private.m_nMoveSpeed -= CAM_SPEED_STEP; ++ } ++ else { ++ g_camwindow_globals_private.m_nMoveSpeed = MIN_CAM_SPEED; ++ } ++} ++ ++/// \brief Initialisation for things that have the same lifespan as this module. ++void CamWnd_Construct(){ ++ GlobalCommands_insert( "CenterView", makeCallbackF(GlobalCamera_ResetAngles), Accelerator( GDK_KEY_End ) ); ++ GlobalCommands_insert( "CameraFocusOnSelected", makeCallbackF( GlobalCamera_FocusOnSelected ), Accelerator( GDK_Tab ) ); ++ ++ GlobalToggles_insert( "ToggleCubicClip", makeCallbackF(Camera_ToggleFarClip), ToggleItem::AddCallbackCaller( g_getfarclip_item ), Accelerator( '\\', (GdkModifierType)GDK_CONTROL_MASK ) ); ++ GlobalCommands_insert( "CubicClipZoomIn", makeCallbackF(Camera_CubeIn), Accelerator( '[', (GdkModifierType)GDK_CONTROL_MASK ) ); ++ GlobalCommands_insert( "CubicClipZoomOut", makeCallbackF(Camera_CubeOut), Accelerator( ']', (GdkModifierType)GDK_CONTROL_MASK ) ); ++ ++ GlobalCommands_insert( "UpFloor", makeCallbackF(Camera_ChangeFloorUp), Accelerator( GDK_KEY_Prior ) ); ++ GlobalCommands_insert( "DownFloor", makeCallbackF(Camera_ChangeFloorDown), Accelerator( GDK_KEY_Next ) ); ++ ++ GlobalToggles_insert( "ToggleCamera", ToggleShown::ToggleCaller( g_camera_shown ), ToggleItem::AddCallbackCaller( g_camera_shown.m_item ), Accelerator( 'C', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) ); ++// GlobalCommands_insert( "LookThroughSelected", makeCallbackF(GlobalCamera_LookThroughSelected) ); ++// GlobalCommands_insert( "LookThroughCamera", makeCallbackF(GlobalCamera_LookThroughCamera) ); ++ ++ if ( g_pGameDescription->mGameType == "doom3" ) { ++ GlobalCommands_insert( "TogglePreview", makeCallbackF(CamWnd_TogglePreview), Accelerator( GDK_KEY_F3 ) ); ++ } ++ ++ GlobalCommands_insert( "CameraSpeedInc", makeCallbackF(CameraSpeed_increase), Accelerator( GDK_KEY_KP_Add, (GdkModifierType)GDK_SHIFT_MASK ) ); ++ GlobalCommands_insert( "CameraSpeedDec", makeCallbackF(CameraSpeed_decrease), Accelerator( GDK_KEY_KP_Subtract, (GdkModifierType)GDK_SHIFT_MASK ) ); ++ ++ GlobalShortcuts_insert( "CameraForward", Accelerator( GDK_KEY_Up ) ); ++ GlobalShortcuts_insert( "CameraBack", Accelerator( GDK_KEY_Down ) ); ++ GlobalShortcuts_insert( "CameraLeft", Accelerator( GDK_KEY_Left ) ); ++ GlobalShortcuts_insert( "CameraRight", Accelerator( GDK_KEY_Right ) ); ++ GlobalShortcuts_insert( "CameraStrafeRight", Accelerator( GDK_KEY_period ) ); ++ GlobalShortcuts_insert( "CameraStrafeLeft", Accelerator( GDK_KEY_comma ) ); ++ ++ GlobalShortcuts_insert( "CameraUp", accelerator_null() ); ++ GlobalShortcuts_insert( "CameraDown", accelerator_null() ); ++ GlobalShortcuts_insert( "CameraAngleUp", accelerator_null() ); ++ GlobalShortcuts_insert( "CameraAngleDown", accelerator_null() ); ++ ++ GlobalShortcuts_insert( "CameraFreeMoveForward", Accelerator( GDK_Up ) ); ++ GlobalShortcuts_insert( "CameraFreeMoveBack", Accelerator( GDK_Down ) ); ++ GlobalShortcuts_insert( "CameraFreeMoveLeft", Accelerator( GDK_Left ) ); ++ GlobalShortcuts_insert( "CameraFreeMoveRight", Accelerator( GDK_Right ) ); ++ ++ GlobalShortcuts_insert( "CameraFreeMoveForward2", Accelerator( GDK_Up ) ); ++ GlobalShortcuts_insert( "CameraFreeMoveBack2", Accelerator( GDK_Down ) ); ++ GlobalShortcuts_insert( "CameraFreeMoveLeft2", Accelerator( GDK_Left ) ); ++ GlobalShortcuts_insert( "CameraFreeMoveRight2", Accelerator( GDK_Right ) ); ++ ++<<<<<<< HEAD ++ GlobalToggles_insert( "ShowStats", makeCallbackF(ShowStatsToggle), ToggleItem::AddCallbackCaller( g_show_stats ) ); ++ ++ GlobalPreferenceSystem().registerPreference( "ShowStats", make_property_string( g_camwindow_globals_private.m_showStats ) ); ++ GlobalPreferenceSystem().registerPreference( "MoveSpeed", make_property_string( g_camwindow_globals_private.m_nMoveSpeed ) ); ++ GlobalPreferenceSystem().registerPreference( "CamLinkSpeed", make_property_string( g_camwindow_globals_private.m_bCamLinkSpeed ) ); ++ GlobalPreferenceSystem().registerPreference( "AngleSpeed", make_property_string( g_camwindow_globals_private.m_nAngleSpeed ) ); ++ GlobalPreferenceSystem().registerPreference( "CamInverseMouse", make_property_string( g_camwindow_globals_private.m_bCamInverseMouse ) ); ++ GlobalPreferenceSystem().registerPreference( "CamDiscrete", make_property_string()); ++ GlobalPreferenceSystem().registerPreference( "CubicClipping", make_property_string( g_camwindow_globals_private.m_bCubicClipping ) ); ++ GlobalPreferenceSystem().registerPreference( "CubicScale", make_property_string( g_camwindow_globals.m_nCubicScale ) ); ++ GlobalPreferenceSystem().registerPreference( "SI_Colors4", make_property_string( g_camwindow_globals.color_cameraback ) ); ++ GlobalPreferenceSystem().registerPreference( "SI_Colors12", make_property_string( g_camwindow_globals.color_selbrushes3d ) ); ++ GlobalPreferenceSystem().registerPreference( "CameraRenderMode", make_property_string() ); ++ GlobalPreferenceSystem().registerPreference( "StrafeMode", make_property_string( g_camwindow_globals_private.m_nStrafeMode ) ); ++======= ++ GlobalShortcuts_insert( "CameraFreeMoveUp", accelerator_null() ); ++ GlobalShortcuts_insert( "CameraFreeMoveDown", accelerator_null() ); ++ ++ GlobalToggles_insert( "ShowStats", FreeCaller(), ToggleItem::AddCallbackCaller( g_show_stats ) ); ++ ++ GlobalPreferenceSystem().registerPreference( "ShowStats", BoolImportStringCaller( g_camwindow_globals_private.m_showStats ), BoolExportStringCaller( g_camwindow_globals_private.m_showStats ) ); ++ GlobalPreferenceSystem().registerPreference( "MoveSpeed", IntImportStringCaller( g_camwindow_globals_private.m_nMoveSpeed ), IntExportStringCaller( g_camwindow_globals_private.m_nMoveSpeed ) ); ++ GlobalPreferenceSystem().registerPreference( "CamLinkSpeed", BoolImportStringCaller( g_camwindow_globals_private.m_bCamLinkSpeed ), BoolExportStringCaller( g_camwindow_globals_private.m_bCamLinkSpeed ) ); ++ GlobalPreferenceSystem().registerPreference( "AngleSpeed", IntImportStringCaller( g_camwindow_globals_private.m_nAngleSpeed ), IntExportStringCaller( g_camwindow_globals_private.m_nAngleSpeed ) ); ++ GlobalPreferenceSystem().registerPreference( "CamInverseMouse", BoolImportStringCaller( g_camwindow_globals_private.m_bCamInverseMouse ), BoolExportStringCaller( g_camwindow_globals_private.m_bCamInverseMouse ) ); ++ GlobalPreferenceSystem().registerPreference( "CamDiscrete", makeBoolStringImportCallback( CamWndMoveDiscreteImportCaller() ), BoolExportStringCaller( g_camwindow_globals_private.m_bCamDiscrete ) ); ++ GlobalPreferenceSystem().registerPreference( "CubicClipping", BoolImportStringCaller( g_camwindow_globals_private.m_bCubicClipping ), BoolExportStringCaller( g_camwindow_globals_private.m_bCubicClipping ) ); ++ GlobalPreferenceSystem().registerPreference( "CubicScale", IntImportStringCaller( g_camwindow_globals.m_nCubicScale ), IntExportStringCaller( g_camwindow_globals.m_nCubicScale ) ); ++ GlobalPreferenceSystem().registerPreference( "SI_Colors4", Vector3ImportStringCaller( g_camwindow_globals.color_cameraback ), Vector3ExportStringCaller( g_camwindow_globals.color_cameraback ) ); ++ GlobalPreferenceSystem().registerPreference( "SI_Colors12", Vector3ImportStringCaller( g_camwindow_globals.color_selbrushes3d ), Vector3ExportStringCaller( g_camwindow_globals.color_selbrushes3d ) ); ++ GlobalPreferenceSystem().registerPreference( "CameraRenderMode", makeIntStringImportCallback( RenderModeImportCaller() ), makeIntStringExportCallback( RenderModeExportCaller() ) ); ++ GlobalPreferenceSystem().registerPreference( "StrafeMode", IntImportStringCaller( g_camwindow_globals_private.m_nStrafeMode ), IntExportStringCaller( g_camwindow_globals_private.m_nStrafeMode ) ); ++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84 ++ ++ CamWnd_constructStatic(); ++ ++ Camera_registerPreferencesPage(); ++} ++void CamWnd_Destroy(){ ++ CamWnd_destroyStatic(); ++} diff --cc radiant/select.cpp index 22d0a963,240c05f4..8e749542 --- a/radiant/select.cpp +++ b/radiant/select.cpp @@@ -1133,61 -1134,73 +1133,61 @@@ static gboolean rotatedlg_delete( ui::W RotateDialog g_rotate_dialog; void DoRotateDlg(){ - if ( g_rotate_dialog.window == NULL ) { - g_rotate_dialog.window = create_dialog_window( MainFrame_getWindow(), "Arbitrary rotation", G_CALLBACK( rotatedlg_delete ), &g_rotate_dialog ); + if ( !g_rotate_dialog.window ) { + g_rotate_dialog.window = MainFrame_getWindow().create_dialog_window("Arbitrary rotation", G_CALLBACK(rotatedlg_delete ), &g_rotate_dialog ); - GtkAccelGroup* accel = gtk_accel_group_new(); - gtk_window_add_accel_group( g_rotate_dialog.window, accel ); + auto accel = ui::AccelGroup(ui::New); + g_rotate_dialog.window.add_accel_group( accel ); { - GtkHBox* hbox = create_dialog_hbox( 4, 4 ); - gtk_container_add( GTK_CONTAINER( g_rotate_dialog.window ), GTK_WIDGET( hbox ) ); + auto hbox = create_dialog_hbox( 4, 4 ); + g_rotate_dialog.window.add(hbox); { - GtkTable* table = create_dialog_table( 3, 2, 4, 4 ); - gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 ); + auto table = create_dialog_table( 3, 2, 4, 4 ); + hbox.pack_start( table, TRUE, TRUE, 0 ); { - GtkWidget* label = gtk_label_new( " X " ); - gtk_widget_show( label ); - gtk_table_attach( table, label, 0, 1, 0, 1, - (GtkAttachOptions) ( 0 ), - (GtkAttachOptions) ( 0 ), 0, 0 ); + ui::Widget label = ui::Label( " X " ); + label.show(); + table.attach(label, {0, 1, 0, 1}, {0, 0}); } { - GtkWidget* label = gtk_label_new( " Y " ); - gtk_widget_show( label ); - gtk_table_attach( table, label, 0, 1, 1, 2, - (GtkAttachOptions) ( 0 ), - (GtkAttachOptions) ( 0 ), 0, 0 ); + ui::Widget label = ui::Label( " Y " ); + label.show(); + table.attach(label, {0, 1, 1, 2}, {0, 0}); } { - GtkWidget* label = gtk_label_new( " Z " ); - gtk_widget_show( label ); - gtk_table_attach( table, label, 0, 1, 2, 3, - (GtkAttachOptions) ( 0 ), - (GtkAttachOptions) ( 0 ), 0, 0 ); + ui::Widget label = ui::Label( " Z " ); + label.show(); + table.attach(label, {0, 1, 2, 3}, {0, 0}); } { - GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( 0, -359, 359, 1, 10, 0 ) ); - GtkSpinButton* spin = GTK_SPIN_BUTTON( gtk_spin_button_new( adj, 1, 1 ) ); - gtk_widget_show( GTK_WIDGET( spin ) ); - gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 0, 1, - (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), - (GtkAttachOptions) ( 0 ), 0, 0 ); - gtk_widget_set_size_request( GTK_WIDGET( spin ), 64, -1 ); + auto adj = ui::Adjustment( 0, -359, 359, 1, 10, 0 ); - auto spin = ui::SpinButton( adj, 1, 0 ); ++ auto spin = ui::SpinButton( adj, 1, 1 ); + spin.show(); + table.attach(spin, {1, 2, 0, 1}, {GTK_EXPAND | GTK_FILL, 0}); + spin.dimensions(64, -1); gtk_spin_button_set_wrap( spin, TRUE ); - gtk_widget_grab_focus( GTK_WIDGET( spin ) ); + gtk_widget_grab_focus( spin ); g_rotate_dialog.x = spin; } { - GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( 0, -359, 359, 1, 10, 0 ) ); - GtkSpinButton* spin = GTK_SPIN_BUTTON( gtk_spin_button_new( adj, 1, 1 ) ); - gtk_widget_show( GTK_WIDGET( spin ) ); - gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 1, 2, - (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), - (GtkAttachOptions) ( 0 ), 0, 0 ); - gtk_widget_set_size_request( GTK_WIDGET( spin ), 64, -1 ); + auto adj = ui::Adjustment( 0, -359, 359, 1, 10, 0 ); - auto spin = ui::SpinButton( adj, 1, 0 ); ++ auto spin = ui::SpinButton( adj, 1, 1 ); + spin.show(); + table.attach(spin, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0}); + spin.dimensions(64, -1); gtk_spin_button_set_wrap( spin, TRUE ); g_rotate_dialog.y = spin; } { - GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( 0, -359, 359, 1, 10, 0 ) ); - GtkSpinButton* spin = GTK_SPIN_BUTTON( gtk_spin_button_new( adj, 1, 1 ) ); - gtk_widget_show( GTK_WIDGET( spin ) ); - gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 2, 3, - (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), - (GtkAttachOptions) ( 0 ), 0, 0 ); - gtk_widget_set_size_request( GTK_WIDGET( spin ), 64, -1 ); + auto adj = ui::Adjustment( 0, -359, 359, 1, 10, 0 ); - auto spin = ui::SpinButton( adj, 1, 0 ); ++ auto spin = ui::SpinButton( adj, 1, 1 ); + spin.show(); + table.attach(spin, {1, 2, 2, 3}, {GTK_EXPAND | GTK_FILL, 0}); + spin.dimensions(64, -1); gtk_spin_button_set_wrap( spin, TRUE ); g_rotate_dialog.z = spin; diff --cc radiant/select.cpp.orig index 00000000,00000000..a042a21c new file mode 100644 --- /dev/null +++ b/radiant/select.cpp.orig @@@ -1,0 -1,0 +1,1383 @@@ ++/* ++ Copyright (C) 1999-2006 Id Software, Inc. and contributors. ++ For a list of contributors, see the accompanying CONTRIBUTORS file. ++ ++ 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 "select.h" ++ ++#include ++ ++#include "debugging/debugging.h" ++ ++#include "ientity.h" ++#include "iselection.h" ++#include "iundo.h" ++ ++#include ++ ++#include "stream/stringstream.h" ++#include "signal/isignal.h" ++#include "shaderlib.h" ++#include "scenelib.h" ++ ++#include "gtkutil/idledraw.h" ++#include "gtkutil/dialog.h" ++#include "gtkutil/widget.h" ++#include "brushmanip.h" ++#include "brush.h" ++#include "patchmanip.h" ++#include "patchdialog.h" ++#include "selection.h" ++#include "texwindow.h" ++#include "gtkmisc.h" ++#include "mainframe.h" ++#include "grid.h" ++#include "map.h" ++#include "entityinspector.h" ++ ++ ++ ++select_workzone_t g_select_workzone; ++ ++ ++/** ++ Loops over all selected brushes and stores their ++ world AABBs in the specified array. ++ */ ++class CollectSelectedBrushesBounds : public SelectionSystem::Visitor ++{ ++AABB* m_bounds; // array of AABBs ++Unsigned m_max; // max AABB-elements in array ++Unsigned& m_count; // count of valid AABBs stored in array ++ ++public: ++CollectSelectedBrushesBounds( AABB* bounds, Unsigned max, Unsigned& count ) ++ : m_bounds( bounds ), ++ m_max( max ), ++ m_count( count ){ ++ m_count = 0; ++} ++ ++void visit( scene::Instance& instance ) const { ++ ASSERT_MESSAGE( m_count <= m_max, "Invalid m_count in CollectSelectedBrushesBounds" ); ++ ++ // stop if the array is already full ++ if ( m_count == m_max ) { ++ return; ++ } ++ ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( ( selectable != 0 ) ++ && instance.isSelected() ) { ++ // brushes only ++ if ( Instance_getBrush( instance ) != 0 ) { ++ m_bounds[m_count] = instance.worldAABB(); ++ ++m_count; ++ } ++ } ++} ++}; ++ ++/** ++ Selects all objects that intersect one of the bounding AABBs. ++ The exact intersection-method is specified through TSelectionPolicy ++ */ ++template ++class SelectByBounds : public scene::Graph::Walker ++{ ++AABB* m_aabbs; // selection aabbs ++Unsigned m_count; // number of aabbs in m_aabbs ++TSelectionPolicy policy; // type that contains a custom intersection method aabb<->aabb ++ ++public: ++SelectByBounds( AABB* aabbs, Unsigned count ) ++ : m_aabbs( aabbs ), ++ m_count( count ){ ++} ++ ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ if( path.top().get().visible() ){ ++ Selectable* selectable = Instance_getSelectable( instance ); ++ ++ // ignore worldspawn ++ Entity* entity = Node_getEntity( path.top() ); ++ if ( entity ) { ++ if ( string_equal( entity->getKeyValue( "classname" ), "worldspawn" ) ) { ++ return true; ++ } ++ } ++ ++ if ( ( path.size() > 1 ) && ++ ( !path.top().get().isRoot() ) && ++ ( selectable != 0 ) && ++ ( !node_is_group( path.top() ) ) ++ ) { ++ for ( Unsigned i = 0; i < m_count; ++i ) ++ { ++ if ( policy.Evaluate( m_aabbs[i], instance ) ) { ++ selectable->setSelected( true ); ++ } ++ } ++ } ++ } ++ else{ ++ return false; ++ } ++ ++ return true; ++} ++ ++/** ++ Performs selection operation on the global scenegraph. ++ If delete_bounds_src is true, then the objects which were ++ used as source for the selection aabbs will be deleted. ++ */ ++static void DoSelection( bool delete_bounds_src = true ){ ++ if ( GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) { ++ // we may not need all AABBs since not all selected objects have to be brushes ++ const Unsigned max = (Unsigned)GlobalSelectionSystem().countSelected(); ++ AABB* aabbs = new AABB[max]; ++ ++ Unsigned count; ++ CollectSelectedBrushesBounds collector( aabbs, max, count ); ++ GlobalSelectionSystem().foreachSelected( collector ); ++ ++ // nothing usable in selection ++ if ( !count ) { ++ delete[] aabbs; ++ return; ++ } ++ ++ // delete selected objects ++ if ( delete_bounds_src ) { // see deleteSelection ++ UndoableCommand undo( "deleteSelected" ); ++ Select_Delete(); ++ } ++ ++ // select objects with bounds ++ GlobalSceneGraph().traverse( SelectByBounds( aabbs, count ) ); ++ ++ SceneChangeNotify(); ++ delete[] aabbs; ++ } ++} ++}; ++ ++/** ++ SelectionPolicy for SelectByBounds ++ Returns true if box and the AABB of instance intersect ++ */ ++class SelectionPolicy_Touching ++{ ++public: ++bool Evaluate( const AABB& box, scene::Instance& instance ) const { ++ const AABB& other( instance.worldAABB() ); ++ for ( Unsigned i = 0; i < 3; ++i ) ++ { ++ if ( fabsf( box.origin[i] - other.origin[i] ) > ( box.extents[i] + other.extents[i] ) ) { ++ return false; ++ } ++ } ++ return true; ++} ++}; ++ ++/** ++ SelectionPolicy for SelectByBounds ++ Returns true if the AABB of instance is inside box ++ */ ++class SelectionPolicy_Inside ++{ ++public: ++bool Evaluate( const AABB& box, scene::Instance& instance ) const { ++ const AABB& other( instance.worldAABB() ); ++ for ( Unsigned i = 0; i < 3; ++i ) ++ { ++ if ( fabsf( box.origin[i] - other.origin[i] ) > ( box.extents[i] - other.extents[i] ) ) { ++ return false; ++ } ++ } ++ return true; ++} ++}; ++ ++class DeleteSelected : public scene::Graph::Walker ++{ ++mutable bool m_remove; ++mutable bool m_removedChild; ++public: ++DeleteSelected() ++ : m_remove( false ), m_removedChild( false ){ ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ m_removedChild = false; ++ ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( selectable != 0 ++ && selectable->isSelected() ++ && path.size() > 1 ++ && !path.top().get().isRoot() ) { ++ m_remove = true; ++ ++ return false; // dont traverse into child elements ++ } ++ return true; ++} ++void post( const scene::Path& path, scene::Instance& instance ) const { ++ ++ if ( m_removedChild ) { ++ m_removedChild = false; ++ ++ // delete empty entities ++ Entity* entity = Node_getEntity( path.top() ); ++ if ( entity != 0 ++ && path.top().get_pointer() != Map_FindWorldspawn( g_map ) ++ && Node_getTraversable( path.top() )->empty() ) { ++ Path_deleteTop( path ); ++ } ++ } ++ ++ // node should be removed ++ if ( m_remove ) { ++ if ( Node_isEntity( path.parent() ) != 0 ) { ++ m_removedChild = true; ++ } ++ ++ m_remove = false; ++ Path_deleteTop( path ); ++ } ++} ++}; ++ ++void Scene_DeleteSelected( scene::Graph& graph ){ ++ graph.traverse( DeleteSelected() ); ++ SceneChangeNotify(); ++} ++ ++void Select_Delete( void ){ ++ Scene_DeleteSelected( GlobalSceneGraph() ); ++} ++ ++class InvertSelectionWalker : public scene::Graph::Walker ++{ ++SelectionSystem::EMode m_mode; ++mutable Selectable* m_selectable; ++public: ++InvertSelectionWalker( SelectionSystem::EMode mode ) ++ : m_mode( mode ), m_selectable( 0 ){ ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ if( !path.top().get().visible() ){ ++ m_selectable = 0; ++ return false; ++ } ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( selectable ) { ++ switch ( m_mode ) ++ { ++ case SelectionSystem::eEntity: ++ if ( Node_isEntity( path.top() ) != 0 ) { ++ m_selectable = path.top().get().visible() ? selectable : 0; ++ } ++ break; ++ case SelectionSystem::ePrimitive: ++ m_selectable = path.top().get().visible() ? selectable : 0; ++ break; ++ case SelectionSystem::eComponent: ++ break; ++ } ++ } ++ return true; ++} ++void post( const scene::Path& path, scene::Instance& instance ) const { ++ if ( m_selectable != 0 ) { ++ m_selectable->setSelected( !m_selectable->isSelected() ); ++ m_selectable = 0; ++ } ++} ++}; ++ ++void Scene_Invert_Selection( scene::Graph& graph ){ ++ graph.traverse( InvertSelectionWalker( GlobalSelectionSystem().Mode() ) ); ++} ++ ++void Select_Invert(){ ++ Scene_Invert_Selection( GlobalSceneGraph() ); ++} ++ ++//interesting printings ++class ExpandSelectionToEntitiesWalker_dbg : public scene::Graph::Walker ++{ ++mutable std::size_t m_depth; ++NodeSmartReference worldspawn; ++public: ++ExpandSelectionToEntitiesWalker_dbg() : m_depth( 0 ), worldspawn( Map_FindOrInsertWorldspawn( g_map ) ){ ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ ++m_depth; ++ globalOutputStream() << "pre depth_" << m_depth; ++ globalOutputStream() << " path.size()_" << path.size(); ++ if ( path.top().get() == worldspawn ) ++ globalOutputStream() << " worldspawn"; ++ if( path.top().get().isRoot() ) ++ globalOutputStream() << " path.top().get().isRoot()"; ++ Entity* entity = Node_getEntity( path.top() ); ++ if ( entity != 0 ){ ++ globalOutputStream() << " entity!=0"; ++ if( entity->isContainer() ){ ++ globalOutputStream() << " entity->isContainer()"; ++ } ++ globalOutputStream() << " classname_" << entity->getKeyValue( "classname" ); ++ } ++ globalOutputStream() << "\n"; ++// globalOutputStream() << "" << ; ++// globalOutputStream() << "" << ; ++// globalOutputStream() << "" << ; ++// globalOutputStream() << "" << ; ++ return true; ++} ++void post( const scene::Path& path, scene::Instance& instance ) const { ++ globalOutputStream() << "post depth_" << m_depth; ++ globalOutputStream() << " path.size()_" << path.size(); ++ if ( path.top().get() == worldspawn ) ++ globalOutputStream() << " worldspawn"; ++ if( path.top().get().isRoot() ) ++ globalOutputStream() << " path.top().get().isRoot()"; ++ Entity* entity = Node_getEntity( path.top() ); ++ if ( entity != 0 ){ ++ globalOutputStream() << " entity!=0"; ++ if( entity->isContainer() ){ ++ globalOutputStream() << " entity->isContainer()"; ++ } ++ globalOutputStream() << " classname_" << entity->getKeyValue( "classname" ); ++ } ++ globalOutputStream() << "\n"; ++ --m_depth; ++} ++}; ++ ++class ExpandSelectionToEntitiesWalker : public scene::Graph::Walker ++{ ++mutable std::size_t m_depth; ++NodeSmartReference worldspawn; ++public: ++ExpandSelectionToEntitiesWalker() : m_depth( 0 ), worldspawn( Map_FindOrInsertWorldspawn( g_map ) ){ ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ ++m_depth; ++ ++ // ignore worldspawn ++// NodeSmartReference me( path.top().get() ); ++// if ( me == worldspawn ) { ++// return false; ++// } ++ ++ if ( m_depth == 2 ) { // entity depth ++ // traverse and select children if any one is selected ++ bool beselected = false; ++ if ( instance.childSelected() ) { ++ beselected = true; ++ if( path.top().get() != worldspawn ){ ++ Instance_setSelected( instance, true ); ++ } ++ } ++ return Node_getEntity( path.top() )->isContainer() && beselected; ++ } ++ else if ( m_depth == 3 ) { // primitive depth ++ Instance_setSelected( instance, true ); ++ return false; ++ } ++ return true; ++} ++void post( const scene::Path& path, scene::Instance& instance ) const { ++ --m_depth; ++} ++}; ++ ++void Scene_ExpandSelectionToEntities(){ ++ GlobalSceneGraph().traverse( ExpandSelectionToEntitiesWalker() ); ++} ++ ++ ++namespace ++{ ++void Selection_UpdateWorkzone(){ ++ if ( GlobalSelectionSystem().countSelected() != 0 ) { ++ Select_GetBounds( g_select_workzone.d_work_min, g_select_workzone.d_work_max ); ++ } ++} ++typedef FreeCaller SelectionUpdateWorkzoneCaller; ++ ++IdleDraw g_idleWorkzone = IdleDraw( SelectionUpdateWorkzoneCaller() ); ++} ++ ++const select_workzone_t& Select_getWorkZone(){ ++ g_idleWorkzone.flush(); ++ return g_select_workzone; ++} ++ ++void UpdateWorkzone_ForSelection(){ ++ g_idleWorkzone.queueDraw(); ++} ++ ++// update the workzone to the current selection ++void UpdateWorkzone_ForSelectionChanged( const Selectable& selectable ){ ++ if ( selectable.isSelected() ) { ++ UpdateWorkzone_ForSelection(); ++ } ++} ++ ++void Select_SetShader( const char* shader ){ ++ if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) { ++ Scene_BrushSetShader_Selected( GlobalSceneGraph(), shader ); ++ Scene_PatchSetShader_Selected( GlobalSceneGraph(), shader ); ++ } ++ Scene_BrushSetShader_Component_Selected( GlobalSceneGraph(), shader ); ++} ++ ++void Select_SetTexdef( const TextureProjection& projection ){ ++ if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) { ++ Scene_BrushSetTexdef_Selected( GlobalSceneGraph(), projection ); ++ } ++ Scene_BrushSetTexdef_Component_Selected( GlobalSceneGraph(), projection ); ++} ++ ++void Select_SetFlags( const ContentsFlagsValue& flags ){ ++ if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) { ++ Scene_BrushSetFlags_Selected( GlobalSceneGraph(), flags ); ++ } ++ Scene_BrushSetFlags_Component_Selected( GlobalSceneGraph(), flags ); ++} ++ ++void Select_GetBounds( Vector3& mins, Vector3& maxs ){ ++ AABB bounds; ++ Scene_BoundsSelected( GlobalSceneGraph(), bounds ); ++ maxs = vector3_added( bounds.origin, bounds.extents ); ++ mins = vector3_subtracted( bounds.origin, bounds.extents ); ++} ++ ++void Select_GetMid( Vector3& mid ){ ++ AABB bounds; ++ Scene_BoundsSelected( GlobalSceneGraph(), bounds ); ++ mid = vector3_snapped( bounds.origin ); ++} ++ ++ ++void Select_FlipAxis( int axis ){ ++ Vector3 flip( 1, 1, 1 ); ++ flip[axis] = -1; ++ GlobalSelectionSystem().scaleSelected( flip ); ++} ++ ++ ++void Select_Scale( float x, float y, float z ){ ++ GlobalSelectionSystem().scaleSelected( Vector3( x, y, z ) ); ++} ++ ++enum axis_t ++{ ++ eAxisX = 0, ++ eAxisY = 1, ++ eAxisZ = 2, ++}; ++ ++enum sign_t ++{ ++ eSignPositive = 1, ++ eSignNegative = -1, ++}; ++ ++inline Matrix4 matrix4_rotation_for_axis90( axis_t axis, sign_t sign ){ ++ switch ( axis ) ++ { ++ case eAxisX: ++ if ( sign == eSignPositive ) { ++ return matrix4_rotation_for_sincos_x( 1, 0 ); ++ } ++ else ++ { ++ return matrix4_rotation_for_sincos_x( -1, 0 ); ++ } ++ case eAxisY: ++ if ( sign == eSignPositive ) { ++ return matrix4_rotation_for_sincos_y( 1, 0 ); ++ } ++ else ++ { ++ return matrix4_rotation_for_sincos_y( -1, 0 ); ++ } ++ default: //case eAxisZ: ++ if ( sign == eSignPositive ) { ++ return matrix4_rotation_for_sincos_z( 1, 0 ); ++ } ++ else ++ { ++ return matrix4_rotation_for_sincos_z( -1, 0 ); ++ } ++ } ++} ++ ++inline void matrix4_rotate_by_axis90( Matrix4& matrix, axis_t axis, sign_t sign ){ ++ matrix4_multiply_by_matrix4( matrix, matrix4_rotation_for_axis90( axis, sign ) ); ++} ++ ++inline void matrix4_pivoted_rotate_by_axis90( Matrix4& matrix, axis_t axis, sign_t sign, const Vector3& pivotpoint ){ ++ matrix4_translate_by_vec3( matrix, pivotpoint ); ++ matrix4_rotate_by_axis90( matrix, axis, sign ); ++ matrix4_translate_by_vec3( matrix, vector3_negated( pivotpoint ) ); ++} ++ ++inline Quaternion quaternion_for_axis90( axis_t axis, sign_t sign ){ ++#if 1 ++ switch ( axis ) ++ { ++ case eAxisX: ++ if ( sign == eSignPositive ) { ++ return Quaternion( c_half_sqrt2f, 0, 0, c_half_sqrt2f ); ++ } ++ else ++ { ++ return Quaternion( -c_half_sqrt2f, 0, 0, -c_half_sqrt2f ); ++ } ++ case eAxisY: ++ if ( sign == eSignPositive ) { ++ return Quaternion( 0, c_half_sqrt2f, 0, c_half_sqrt2f ); ++ } ++ else ++ { ++ return Quaternion( 0, -c_half_sqrt2f, 0, -c_half_sqrt2f ); ++ } ++ default: //case eAxisZ: ++ if ( sign == eSignPositive ) { ++ return Quaternion( 0, 0, c_half_sqrt2f, c_half_sqrt2f ); ++ } ++ else ++ { ++ return Quaternion( 0, 0, -c_half_sqrt2f, -c_half_sqrt2f ); ++ } ++ } ++#else ++ quaternion_for_matrix4_rotation( matrix4_rotation_for_axis90( (axis_t)axis, ( deg > 0 ) ? eSignPositive : eSignNegative ) ); ++#endif ++} ++ ++void Select_RotateAxis( int axis, float deg ){ ++ if ( fabs( deg ) == 90.f ) { ++ GlobalSelectionSystem().rotateSelected( quaternion_for_axis90( (axis_t)axis, ( deg > 0 ) ? eSignPositive : eSignNegative ), true ); ++ } ++ else ++ { ++ switch ( axis ) ++ { ++ case 0: ++ GlobalSelectionSystem().rotateSelected( quaternion_for_matrix4_rotation( matrix4_rotation_for_x_degrees( deg ) ), false ); ++ break; ++ case 1: ++ GlobalSelectionSystem().rotateSelected( quaternion_for_matrix4_rotation( matrix4_rotation_for_y_degrees( deg ) ), false ); ++ break; ++ case 2: ++ GlobalSelectionSystem().rotateSelected( quaternion_for_matrix4_rotation( matrix4_rotation_for_z_degrees( deg ) ), false ); ++ break; ++ } ++ } ++} ++ ++ ++void Select_ShiftTexture( float x, float y ){ ++ if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) { ++ Scene_BrushShiftTexdef_Selected( GlobalSceneGraph(), x, y ); ++ Scene_PatchTranslateTexture_Selected( GlobalSceneGraph(), x, y ); ++ } ++ //globalOutputStream() << "shift selected face textures: s=" << x << " t=" << y << '\n'; ++ Scene_BrushShiftTexdef_Component_Selected( GlobalSceneGraph(), x, y ); ++} ++ ++void Select_ScaleTexture( float x, float y ){ ++ if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) { ++ Scene_BrushScaleTexdef_Selected( GlobalSceneGraph(), x, y ); ++ Scene_PatchScaleTexture_Selected( GlobalSceneGraph(), x, y ); ++ } ++ Scene_BrushScaleTexdef_Component_Selected( GlobalSceneGraph(), x, y ); ++} ++ ++void Select_RotateTexture( float amt ){ ++ if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) { ++ Scene_BrushRotateTexdef_Selected( GlobalSceneGraph(), amt ); ++ Scene_PatchRotateTexture_Selected( GlobalSceneGraph(), amt ); ++ } ++ Scene_BrushRotateTexdef_Component_Selected( GlobalSceneGraph(), amt ); ++} ++ ++// TTimo modified to handle shader architecture: ++// expects shader names at input, comparison relies on shader names .. texture names no longer relevant ++void FindReplaceTextures( const char* pFind, const char* pReplace, bool bSelected ){ ++ if ( !texdef_name_valid( pFind ) ) { ++ globalErrorStream() << "FindReplaceTextures: invalid texture name: '" << pFind << "', aborted\n"; ++ return; ++ } ++ if ( !texdef_name_valid( pReplace ) ) { ++ globalErrorStream() << "FindReplaceTextures: invalid texture name: '" << pReplace << "', aborted\n"; ++ return; ++ } ++ ++ StringOutputStream command; ++ command << "textureFindReplace -find " << pFind << " -replace " << pReplace; ++ UndoableCommand undo( command.c_str() ); ++ ++ if ( bSelected ) { ++ if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) { ++ Scene_BrushFindReplaceShader_Selected( GlobalSceneGraph(), pFind, pReplace ); ++ Scene_PatchFindReplaceShader_Selected( GlobalSceneGraph(), pFind, pReplace ); ++ } ++ Scene_BrushFindReplaceShader_Component_Selected( GlobalSceneGraph(), pFind, pReplace ); ++ } ++ else ++ { ++ Scene_BrushFindReplaceShader( GlobalSceneGraph(), pFind, pReplace ); ++ Scene_PatchFindReplaceShader( GlobalSceneGraph(), pFind, pReplace ); ++ } ++} ++ ++typedef std::vector PropertyValues; ++ ++bool propertyvalues_contain( const PropertyValues& propertyvalues, const char *str ){ ++ for ( PropertyValues::const_iterator i = propertyvalues.begin(); i != propertyvalues.end(); ++i ) ++ { ++ if ( string_equal( str, *i ) ) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++class EntityFindByPropertyValueWalker : public scene::Graph::Walker ++{ ++const PropertyValues& m_propertyvalues; ++const char *m_prop; ++const NodeSmartReference worldspawn; ++public: ++EntityFindByPropertyValueWalker( const char *prop, const PropertyValues& propertyvalues ) ++ : m_propertyvalues( propertyvalues ), m_prop( prop ), worldspawn( Map_FindOrInsertWorldspawn( g_map ) ){ ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ if( !path.top().get().visible() ){ ++ return false; ++ } ++ // ignore worldspawn ++ if ( path.top().get() == worldspawn ) { ++ return false; ++ } ++ ++ Entity* entity = Node_getEntity( path.top() ); ++ if ( entity != 0 ){ ++ if( propertyvalues_contain( m_propertyvalues, entity->getKeyValue( m_prop ) ) ) { ++ Instance_getSelectable( instance )->setSelected( true ); ++ return true; ++ } ++ return false; ++ } ++ else if( path.size() > 2 && !path.top().get().isRoot() ){ ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if( selectable != 0 ) ++ selectable->setSelected( true ); ++ } ++ return true; ++} ++}; ++ ++void Scene_EntitySelectByPropertyValues( scene::Graph& graph, const char *prop, const PropertyValues& propertyvalues ){ ++ graph.traverse( EntityFindByPropertyValueWalker( prop, propertyvalues ) ); ++} ++ ++class EntityGetSelectedPropertyValuesWalker : public scene::Graph::Walker ++{ ++PropertyValues& m_propertyvalues; ++const char *m_prop; ++const NodeSmartReference worldspawn; ++public: ++EntityGetSelectedPropertyValuesWalker( const char *prop, PropertyValues& propertyvalues ) ++ : m_propertyvalues( propertyvalues ), m_prop( prop ), worldspawn( Map_FindOrInsertWorldspawn( g_map ) ){ ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ Entity* entity = Node_getEntity( path.top() ); ++ if ( entity != 0 ){ ++ if( path.top().get() != worldspawn ){ ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( ( selectable != 0 && selectable->isSelected() ) || instance.childSelected() ) { ++ if ( !propertyvalues_contain( m_propertyvalues, entity->getKeyValue( m_prop ) ) ) { ++ m_propertyvalues.push_back( entity->getKeyValue( m_prop ) ); ++ } ++ } ++ } ++ return false; ++ } ++ return true; ++} ++}; ++/* ++class EntityGetSelectedPropertyValuesWalker : public scene::Graph::Walker ++{ ++PropertyValues& m_propertyvalues; ++const char *m_prop; ++mutable bool m_selected_children; ++const NodeSmartReference worldspawn; ++public: ++EntityGetSelectedPropertyValuesWalker( const char *prop, PropertyValues& propertyvalues ) ++ : m_propertyvalues( propertyvalues ), m_prop( prop ), m_selected_children( false ), worldspawn( Map_FindOrInsertWorldspawn( g_map ) ){ ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( selectable != 0 ++ && selectable->isSelected() ) { ++ Entity* entity = Node_getEntity( path.top() ); ++ if ( entity != 0 ) { ++ if ( !propertyvalues_contain( m_propertyvalues, entity->getKeyValue( m_prop ) ) ) { ++ m_propertyvalues.push_back( entity->getKeyValue( m_prop ) ); ++ } ++ return false; ++ } ++ else{ ++ m_selected_children = true; ++ } ++ } ++ return true; ++} ++void post( const scene::Path& path, scene::Instance& instance ) const { ++ Entity* entity = Node_getEntity( path.top() ); ++ if( entity != 0 && m_selected_children ){ ++ m_selected_children = false; ++ if( path.top().get() == worldspawn ) ++ return; ++ if ( !propertyvalues_contain( m_propertyvalues, entity->getKeyValue( m_prop ) ) ) { ++ m_propertyvalues.push_back( entity->getKeyValue( m_prop ) ); ++ } ++ } ++} ++}; ++*/ ++void Scene_EntityGetPropertyValues( scene::Graph& graph, const char *prop, PropertyValues& propertyvalues ){ ++ graph.traverse( EntityGetSelectedPropertyValuesWalker( prop, propertyvalues ) ); ++} ++ ++void Select_AllOfType(){ ++ if ( GlobalSelectionSystem().Mode() == SelectionSystem::eComponent ) { ++ if ( GlobalSelectionSystem().ComponentMode() == SelectionSystem::eFace ) { ++ GlobalSelectionSystem().setSelectedAllComponents( false ); ++ Scene_BrushSelectByShader_Component( GlobalSceneGraph(), TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) ); ++ } ++ } ++ else ++ { ++ PropertyValues propertyvalues; ++ const char *prop = EntityInspector_getCurrentKey(); ++ if ( !prop || !*prop ) { ++ prop = "classname"; ++ } ++ Scene_EntityGetPropertyValues( GlobalSceneGraph(), prop, propertyvalues ); ++ GlobalSelectionSystem().setSelectedAll( false ); ++ if ( !propertyvalues.empty() ) { ++ Scene_EntitySelectByPropertyValues( GlobalSceneGraph(), prop, propertyvalues ); ++ } ++ else ++ { ++ Scene_BrushSelectByShader( GlobalSceneGraph(), TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) ); ++ Scene_PatchSelectByShader( GlobalSceneGraph(), TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) ); ++ } ++ } ++} ++ ++void Select_Inside( void ){ ++ SelectByBounds::DoSelection(); ++} ++ ++void Select_Touching( void ){ ++ SelectByBounds::DoSelection( false ); ++} ++ ++void Select_FitTexture( float horizontal, float vertical ){ ++ if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) { ++ Scene_BrushFitTexture_Selected( GlobalSceneGraph(), horizontal, vertical ); ++ } ++ Scene_BrushFitTexture_Component_Selected( GlobalSceneGraph(), horizontal, vertical ); ++ ++ SceneChangeNotify(); ++} ++ ++ ++#include "commands.h" ++#include "dialog.h" ++ ++inline void hide_node( scene::Node& node, bool hide ){ ++ hide ++ ? node.enable( scene::Node::eHidden ) ++ : node.disable( scene::Node::eHidden ); ++} ++ ++bool g_nodes_be_hidden = false; ++ ++ConstReferenceCaller &), PropertyImpl::Export> g_hidden_caller( g_nodes_be_hidden ); ++ ++ToggleItem g_hidden_item( g_hidden_caller ); ++ ++class HideSelectedWalker : public scene::Graph::Walker ++{ ++bool m_hide; ++public: ++HideSelectedWalker( bool hide ) ++ : m_hide( hide ){ ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( selectable != 0 ++ && selectable->isSelected() ) { ++ g_nodes_be_hidden = m_hide; ++ hide_node( path.top(), m_hide ); ++ } ++ return true; ++} ++}; ++ ++void Scene_Hide_Selected( bool hide ){ ++ GlobalSceneGraph().traverse( HideSelectedWalker( hide ) ); ++} ++ ++void Select_Hide(){ ++ Scene_Hide_Selected( true ); ++ SceneChangeNotify(); ++} ++ ++void HideSelected(){ ++ Select_Hide(); ++ GlobalSelectionSystem().setSelectedAll( false ); ++ g_hidden_item.update(); ++} ++ ++ ++class HideAllWalker : public scene::Graph::Walker ++{ ++bool m_hide; ++public: ++HideAllWalker( bool hide ) ++ : m_hide( hide ){ ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ hide_node( path.top(), m_hide ); ++ return true; ++} ++}; ++ ++void Scene_Hide_All( bool hide ){ ++ GlobalSceneGraph().traverse( HideAllWalker( hide ) ); ++} ++ ++void Select_ShowAllHidden(){ ++ Scene_Hide_All( false ); ++ SceneChangeNotify(); ++ g_nodes_be_hidden = false; ++ g_hidden_item.update(); ++} ++ ++ ++void Selection_Flipx(){ ++ UndoableCommand undo( "mirrorSelected -axis x" ); ++ Select_FlipAxis( 0 ); ++} ++ ++void Selection_Flipy(){ ++ UndoableCommand undo( "mirrorSelected -axis y" ); ++ Select_FlipAxis( 1 ); ++} ++ ++void Selection_Flipz(){ ++ UndoableCommand undo( "mirrorSelected -axis z" ); ++ Select_FlipAxis( 2 ); ++} ++ ++void Selection_Rotatex(){ ++ UndoableCommand undo( "rotateSelected -axis x -angle -90" ); ++ Select_RotateAxis( 0,-90 ); ++} ++ ++void Selection_Rotatey(){ ++ UndoableCommand undo( "rotateSelected -axis y -angle 90" ); ++ Select_RotateAxis( 1, 90 ); ++} ++ ++void Selection_Rotatez(){ ++ UndoableCommand undo( "rotateSelected -axis z -angle -90" ); ++ Select_RotateAxis( 2,-90 ); ++} ++#include "xywindow.h" ++void Selection_FlipHorizontally(){ ++ VIEWTYPE viewtype = GlobalXYWnd_getCurrentViewType(); ++ switch ( viewtype ) ++ { ++ case XY: ++ case XZ: ++ Selection_Flipx(); ++ break; ++ default: ++ Selection_Flipy(); ++ break; ++ } ++} ++ ++void Selection_FlipVertically(){ ++ VIEWTYPE viewtype = GlobalXYWnd_getCurrentViewType(); ++ switch ( viewtype ) ++ { ++ case XZ: ++ case YZ: ++ Selection_Flipz(); ++ break; ++ default: ++ Selection_Flipy(); ++ break; ++ } ++} ++ ++void Selection_RotateClockwise(){ ++ UndoableCommand undo( "rotateSelected Clockwise 90" ); ++ VIEWTYPE viewtype = GlobalXYWnd_getCurrentViewType(); ++ switch ( viewtype ) ++ { ++ case XY: ++ Select_RotateAxis( 2, -90 ); ++ break; ++ case XZ: ++ Select_RotateAxis( 1, 90 ); ++ break; ++ default: ++ Select_RotateAxis( 0, -90 ); ++ break; ++ } ++} ++ ++void Selection_RotateAnticlockwise(){ ++ UndoableCommand undo( "rotateSelected Anticlockwise 90" ); ++ VIEWTYPE viewtype = GlobalXYWnd_getCurrentViewType(); ++ switch ( viewtype ) ++ { ++ case XY: ++ Select_RotateAxis( 2, 90 ); ++ break; ++ case XZ: ++ Select_RotateAxis( 1, -90 ); ++ break; ++ default: ++ Select_RotateAxis( 0, 90 ); ++ break; ++ } ++ ++} ++ ++ ++ ++void Select_registerCommands(){ ++ GlobalCommands_insert( "ShowHidden", makeCallbackF( Select_ShowAllHidden ), Accelerator( 'H', (GdkModifierType)GDK_SHIFT_MASK ) ); ++ GlobalToggles_insert( "HideSelected", makeCallbackF( HideSelected ), ToggleItem::AddCallbackCaller( g_hidden_item ), Accelerator( 'H' ) ); ++ ++ GlobalCommands_insert( "MirrorSelectionX", makeCallbackF( Selection_Flipx ) ); ++ GlobalCommands_insert( "RotateSelectionX", makeCallbackF( Selection_Rotatex ) ); ++ GlobalCommands_insert( "MirrorSelectionY", makeCallbackF( Selection_Flipy ) ); ++ GlobalCommands_insert( "RotateSelectionY", makeCallbackF( Selection_Rotatey ) ); ++ GlobalCommands_insert( "MirrorSelectionZ", makeCallbackF( Selection_Flipz ) ); ++ GlobalCommands_insert( "RotateSelectionZ", makeCallbackF( Selection_Rotatez ) ); ++ ++ GlobalCommands_insert( "MirrorSelectionHorizontally", makeCallbackF( Selection_FlipHorizontally ) ); ++ GlobalCommands_insert( "MirrorSelectionVertically", makeCallbackF( Selection_FlipVertically ) ); ++ ++ GlobalCommands_insert( "RotateSelectionClockwise", makeCallbackF( Selection_RotateClockwise ) ); ++ GlobalCommands_insert( "RotateSelectionAnticlockwise", makeCallbackF( Selection_RotateAnticlockwise ) ); ++} ++ ++ ++void Nudge( int nDim, float fNudge ){ ++ Vector3 translate( 0, 0, 0 ); ++ translate[nDim] = fNudge; ++ ++ GlobalSelectionSystem().translateSelected( translate ); ++} ++ ++void Selection_NudgeZ( float amount ){ ++ StringOutputStream command; ++ command << "nudgeSelected -axis z -amount " << amount; ++ UndoableCommand undo( command.c_str() ); ++ ++ Nudge( 2, amount ); ++} ++ ++void Selection_MoveDown(){ ++ Selection_NudgeZ( -GetGridSize() ); ++} ++ ++void Selection_MoveUp(){ ++ Selection_NudgeZ( GetGridSize() ); ++} ++ ++void SceneSelectionChange( const Selectable& selectable ){ ++ SceneChangeNotify(); ++} ++ ++SignalHandlerId Selection_boundsChanged; ++ ++void Selection_construct(){ ++ typedef FreeCaller SceneSelectionChangeCaller; ++ GlobalSelectionSystem().addSelectionChangeCallback( SceneSelectionChangeCaller() ); ++ typedef FreeCaller UpdateWorkzoneForSelectionChangedCaller; ++ GlobalSelectionSystem().addSelectionChangeCallback( UpdateWorkzoneForSelectionChangedCaller() ); ++ typedef FreeCaller UpdateWorkzoneForSelectionCaller; ++ Selection_boundsChanged = GlobalSceneGraph().addBoundsChangedCallback( UpdateWorkzoneForSelectionCaller() ); ++} ++ ++void Selection_destroy(){ ++ GlobalSceneGraph().removeBoundsChangedCallback( Selection_boundsChanged ); ++} ++ ++ ++#include "gtkdlgs.h" ++#include ++ ++ ++inline Quaternion quaternion_for_euler_xyz_degrees( const Vector3& eulerXYZ ){ ++#if 0 ++ return quaternion_for_matrix4_rotation( matrix4_rotation_for_euler_xyz_degrees( eulerXYZ ) ); ++#elif 0 ++ return quaternion_multiplied_by_quaternion( ++ quaternion_multiplied_by_quaternion( ++ quaternion_for_z( degrees_to_radians( eulerXYZ[2] ) ), ++ quaternion_for_y( degrees_to_radians( eulerXYZ[1] ) ) ++ ), ++ quaternion_for_x( degrees_to_radians( eulerXYZ[0] ) ) ++ ); ++#elif 1 ++ double cx = cos( degrees_to_radians( eulerXYZ[0] * 0.5 ) ); ++ double sx = sin( degrees_to_radians( eulerXYZ[0] * 0.5 ) ); ++ double cy = cos( degrees_to_radians( eulerXYZ[1] * 0.5 ) ); ++ double sy = sin( degrees_to_radians( eulerXYZ[1] * 0.5 ) ); ++ double cz = cos( degrees_to_radians( eulerXYZ[2] * 0.5 ) ); ++ double sz = sin( degrees_to_radians( eulerXYZ[2] * 0.5 ) ); ++ ++ return Quaternion( ++ cz * cy * sx - sz * sy * cx, ++ cz * sy * cx + sz * cy * sx, ++ sz * cy * cx - cz * sy * sx, ++ cz * cy * cx + sz * sy * sx ++ ); ++#endif ++} ++ ++struct RotateDialog ++{ ++ ui::SpinButton x{ui::null}; ++ ui::SpinButton y{ui::null}; ++ ui::SpinButton z{ui::null}; ++ ui::Window window{ui::null}; ++}; ++ ++static gboolean rotatedlg_apply( ui::Widget widget, RotateDialog* rotateDialog ){ ++ Vector3 eulerXYZ; ++ ++ gtk_spin_button_update ( rotateDialog->x ); ++ gtk_spin_button_update ( rotateDialog->y ); ++ gtk_spin_button_update ( rotateDialog->z ); ++ eulerXYZ[0] = static_cast( gtk_spin_button_get_value( rotateDialog->x ) ); ++ eulerXYZ[1] = static_cast( gtk_spin_button_get_value( rotateDialog->y ) ); ++ eulerXYZ[2] = static_cast( gtk_spin_button_get_value( rotateDialog->z ) ); ++ ++ StringOutputStream command; ++ command << "rotateSelectedEulerXYZ -x " << eulerXYZ[0] << " -y " << eulerXYZ[1] << " -z " << eulerXYZ[2]; ++ UndoableCommand undo( command.c_str() ); ++ ++ GlobalSelectionSystem().rotateSelected( quaternion_for_euler_xyz_degrees( eulerXYZ ), false ); ++ return TRUE; ++} ++ ++static gboolean rotatedlg_cancel( ui::Widget widget, RotateDialog* rotateDialog ){ ++ rotateDialog->window.hide(); ++ ++ gtk_spin_button_set_value( rotateDialog->x, 0.0f ); // reset to 0 on close ++ gtk_spin_button_set_value( rotateDialog->y, 0.0f ); ++ gtk_spin_button_set_value( rotateDialog->z, 0.0f ); ++ ++ return TRUE; ++} ++ ++static gboolean rotatedlg_ok( ui::Widget widget, RotateDialog* rotateDialog ){ ++ rotatedlg_apply( widget, rotateDialog ); ++// rotatedlg_cancel( widget, rotateDialog ); ++ rotateDialog->window.hide(); ++ return TRUE; ++} ++ ++static gboolean rotatedlg_delete( ui::Widget widget, GdkEventAny *event, RotateDialog* rotateDialog ){ ++ rotatedlg_cancel( widget, rotateDialog ); ++ return TRUE; ++} ++ ++RotateDialog g_rotate_dialog; ++void DoRotateDlg(){ ++ if ( !g_rotate_dialog.window ) { ++ g_rotate_dialog.window = MainFrame_getWindow().create_dialog_window("Arbitrary rotation", G_CALLBACK(rotatedlg_delete ), &g_rotate_dialog ); ++ ++ auto accel = ui::AccelGroup(ui::New); ++ g_rotate_dialog.window.add_accel_group( accel ); ++ ++ { ++ auto hbox = create_dialog_hbox( 4, 4 ); ++ g_rotate_dialog.window.add(hbox); ++ { ++ auto table = create_dialog_table( 3, 2, 4, 4 ); ++ hbox.pack_start( table, TRUE, TRUE, 0 ); ++ { ++ ui::Widget label = ui::Label( " X " ); ++ label.show(); ++ table.attach(label, {0, 1, 0, 1}, {0, 0}); ++ } ++ { ++ ui::Widget label = ui::Label( " Y " ); ++ label.show(); ++ table.attach(label, {0, 1, 1, 2}, {0, 0}); ++ } ++ { ++ ui::Widget label = ui::Label( " Z " ); ++ label.show(); ++ table.attach(label, {0, 1, 2, 3}, {0, 0}); ++ } ++ { ++<<<<<<< HEAD ++ auto adj = ui::Adjustment( 0, -359, 359, 1, 10, 0 ); ++ auto spin = ui::SpinButton( adj, 1, 0 ); ++ spin.show(); ++ table.attach(spin, {1, 2, 0, 1}, {GTK_EXPAND | GTK_FILL, 0}); ++ spin.dimensions(64, -1); ++======= ++ GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( 0, -359, 359, 1, 10, 0 ) ); ++ GtkSpinButton* spin = GTK_SPIN_BUTTON( gtk_spin_button_new( adj, 1, 1 ) ); ++ gtk_widget_show( GTK_WIDGET( spin ) ); ++ gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 0, 1, ++ (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), ++ (GtkAttachOptions) ( 0 ), 0, 0 ); ++ gtk_widget_set_size_request( GTK_WIDGET( spin ), 64, -1 ); ++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84 ++ gtk_spin_button_set_wrap( spin, TRUE ); ++ ++ gtk_widget_grab_focus( spin ); ++ ++ g_rotate_dialog.x = spin; ++ } ++ { ++<<<<<<< HEAD ++ auto adj = ui::Adjustment( 0, -359, 359, 1, 10, 0 ); ++ auto spin = ui::SpinButton( adj, 1, 0 ); ++ spin.show(); ++ table.attach(spin, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0}); ++ spin.dimensions(64, -1); ++======= ++ GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( 0, -359, 359, 1, 10, 0 ) ); ++ GtkSpinButton* spin = GTK_SPIN_BUTTON( gtk_spin_button_new( adj, 1, 1 ) ); ++ gtk_widget_show( GTK_WIDGET( spin ) ); ++ gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 1, 2, ++ (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), ++ (GtkAttachOptions) ( 0 ), 0, 0 ); ++ gtk_widget_set_size_request( GTK_WIDGET( spin ), 64, -1 ); ++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84 ++ gtk_spin_button_set_wrap( spin, TRUE ); ++ ++ g_rotate_dialog.y = spin; ++ } ++ { ++<<<<<<< HEAD ++ auto adj = ui::Adjustment( 0, -359, 359, 1, 10, 0 ); ++ auto spin = ui::SpinButton( adj, 1, 0 ); ++ spin.show(); ++ table.attach(spin, {1, 2, 2, 3}, {GTK_EXPAND | GTK_FILL, 0}); ++ spin.dimensions(64, -1); ++======= ++ GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( 0, -359, 359, 1, 10, 0 ) ); ++ GtkSpinButton* spin = GTK_SPIN_BUTTON( gtk_spin_button_new( adj, 1, 1 ) ); ++ gtk_widget_show( GTK_WIDGET( spin ) ); ++ gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 2, 3, ++ (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), ++ (GtkAttachOptions) ( 0 ), 0, 0 ); ++ gtk_widget_set_size_request( GTK_WIDGET( spin ), 64, -1 ); ++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84 ++ gtk_spin_button_set_wrap( spin, TRUE ); ++ ++ g_rotate_dialog.z = spin; ++ } ++ } ++ { ++ auto vbox = create_dialog_vbox( 4 ); ++ hbox.pack_start( vbox, TRUE, TRUE, 0 ); ++ { ++ auto button = create_dialog_button( "OK", G_CALLBACK( rotatedlg_ok ), &g_rotate_dialog ); ++ vbox.pack_start( button, FALSE, FALSE, 0 ); ++ widget_make_default( button ); ++ gtk_widget_add_accelerator( button , "clicked", accel, GDK_KEY_Return, (GdkModifierType)0, (GtkAccelFlags)0 ); ++ } ++ { ++ auto button = create_dialog_button( "Cancel", G_CALLBACK( rotatedlg_cancel ), &g_rotate_dialog ); ++ vbox.pack_start( button, FALSE, FALSE, 0 ); ++ gtk_widget_add_accelerator( button , "clicked", accel, GDK_KEY_Escape, (GdkModifierType)0, (GtkAccelFlags)0 ); ++ } ++ { ++ auto button = create_dialog_button( "Apply", G_CALLBACK( rotatedlg_apply ), &g_rotate_dialog ); ++ vbox.pack_start( button, FALSE, FALSE, 0 ); ++ } ++ } ++ } ++ } ++ ++ g_rotate_dialog.window.show(); ++} ++ ++ ++ ++ ++ ++ ++ ++ ++ ++struct ScaleDialog ++{ ++ ui::Entry x{ui::null}; ++ ui::Entry y{ui::null}; ++ ui::Entry z{ui::null}; ++ ui::Window window{ui::null}; ++}; ++ ++static gboolean scaledlg_apply( ui::Widget widget, ScaleDialog* scaleDialog ){ ++ float sx, sy, sz; ++ ++ sx = static_cast( atof( gtk_entry_get_text( GTK_ENTRY( scaleDialog->x ) ) ) ); ++ sy = static_cast( atof( gtk_entry_get_text( GTK_ENTRY( scaleDialog->y ) ) ) ); ++ sz = static_cast( atof( gtk_entry_get_text( GTK_ENTRY( scaleDialog->z ) ) ) ); ++ ++ StringOutputStream command; ++ command << "scaleSelected -x " << sx << " -y " << sy << " -z " << sz; ++ UndoableCommand undo( command.c_str() ); ++ ++ Select_Scale( sx, sy, sz ); ++ ++ return TRUE; ++} ++ ++static gboolean scaledlg_cancel( ui::Widget widget, ScaleDialog* scaleDialog ){ ++ scaleDialog->window.hide(); ++ ++ scaleDialog->x.text("1.0"); ++ scaleDialog->y.text("1.0"); ++ scaleDialog->z.text("1.0"); ++ ++ return TRUE; ++} ++ ++static gboolean scaledlg_ok( ui::Widget widget, ScaleDialog* scaleDialog ){ ++ scaledlg_apply( widget, scaleDialog ); ++ //scaledlg_cancel( widget, scaleDialog ); ++ scaleDialog->window.hide(); ++ return TRUE; ++} ++ ++static gboolean scaledlg_delete( ui::Widget widget, GdkEventAny *event, ScaleDialog* scaleDialog ){ ++ scaledlg_cancel( widget, scaleDialog ); ++ return TRUE; ++} ++ ++ScaleDialog g_scale_dialog; ++ ++void DoScaleDlg(){ ++ if ( !g_scale_dialog.window ) { ++ g_scale_dialog.window = MainFrame_getWindow().create_dialog_window("Arbitrary scale", G_CALLBACK(scaledlg_delete ), &g_scale_dialog ); ++ ++ auto accel = ui::AccelGroup(ui::New); ++ g_scale_dialog.window.add_accel_group( accel ); ++ ++ { ++ auto hbox = create_dialog_hbox( 4, 4 ); ++ g_scale_dialog.window.add(hbox); ++ { ++ auto table = create_dialog_table( 3, 2, 4, 4 ); ++ hbox.pack_start( table, TRUE, TRUE, 0 ); ++ { ++ ui::Widget label = ui::Label( " X " ); ++ label.show(); ++ table.attach(label, {0, 1, 0, 1}, {0, 0}); ++ } ++ { ++ ui::Widget label = ui::Label( " Y " ); ++ label.show(); ++ table.attach(label, {0, 1, 1, 2}, {0, 0}); ++ } ++ { ++ ui::Widget label = ui::Label( " Z " ); ++ label.show(); ++ table.attach(label, {0, 1, 2, 3}, {0, 0}); ++ } ++ { ++ auto entry = ui::Entry(ui::New); ++ entry.text("1.0"); ++ entry.show(); ++ table.attach(entry, {1, 2, 0, 1}, {GTK_EXPAND | GTK_FILL, 0}); ++ ++ g_scale_dialog.x = entry; ++ } ++ { ++ auto entry = ui::Entry(ui::New); ++ entry.text("1.0"); ++ entry.show(); ++ table.attach(entry, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0}); ++ ++ g_scale_dialog.y = entry; ++ } ++ { ++ auto entry = ui::Entry(ui::New); ++ entry.text("1.0"); ++ entry.show(); ++ table.attach(entry, {1, 2, 2, 3}, {GTK_EXPAND | GTK_FILL, 0}); ++ ++ g_scale_dialog.z = entry; ++ } ++ } ++ { ++ auto vbox = create_dialog_vbox( 4 ); ++ hbox.pack_start( vbox, TRUE, TRUE, 0 ); ++ { ++ auto button = create_dialog_button( "OK", G_CALLBACK( scaledlg_ok ), &g_scale_dialog ); ++ vbox.pack_start( button, FALSE, FALSE, 0 ); ++ widget_make_default( button ); ++ gtk_widget_add_accelerator( button , "clicked", accel, GDK_KEY_Return, (GdkModifierType)0, (GtkAccelFlags)0 ); ++ } ++ { ++ auto button = create_dialog_button( "Cancel", G_CALLBACK( scaledlg_cancel ), &g_scale_dialog ); ++ vbox.pack_start( button, FALSE, FALSE, 0 ); ++ gtk_widget_add_accelerator( button , "clicked", accel, GDK_KEY_Escape, (GdkModifierType)0, (GtkAccelFlags)0 ); ++ } ++ { ++ auto button = create_dialog_button( "Apply", G_CALLBACK( scaledlg_apply ), &g_scale_dialog ); ++ vbox.pack_start( button, FALSE, FALSE, 0 ); ++ } ++ } ++ } ++ } ++ ++ g_scale_dialog.window.show(); ++} diff --cc radiant/selection.cpp index e8d16c95,13255cfc..11e2337e --- a/radiant/selection.cpp +++ b/radiant/selection.cpp @@@ -3989,16 -4043,31 +4047,31 @@@ bool mouseDown( DeviceVector position ) } void mouseMoved( DeviceVector position ){ - getSelectionSystem().MoveSelected( *m_view, &position[0] ); + getSelectionSystem().MoveSelected( *m_view, &position[0], ( m_state & c_modifierShift ) == c_modifierShift ); } -typedef MemberCaller1 MouseMovedCaller; +typedef MemberCaller MouseMovedCaller; void mouseUp( DeviceVector position ){ getSelectionSystem().endMove(); g_mouseMovedCallback.clear(); g_mouseUpCallback.clear(); } -typedef MemberCaller1 MouseUpCaller; +typedef MemberCaller MouseUpCaller; + + void setState( ModifierFlags state ){ + m_state = state; + } + + ModifierFlags getState() const { + return m_state; + } + + void modifierEnable( ModifierFlags type ){ + setState( bitfield_enable( getState(), type ) ); + } + void modifierDisable( ModifierFlags type ){ + setState( bitfield_disable( getState(), type ) ); + } }; void Scene_copyClosestTexture( SelectionTest& test ); diff --cc radiant/selection.cpp.orig index 00000000,00000000..fc01a71e new file mode 100644 --- /dev/null +++ b/radiant/selection.cpp.orig @@@ -1,0 -1,0 +1,4231 @@@ ++/* ++ 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.h" ++#include "globaldefs.h" ++ ++#include "debugging/debugging.h" ++ ++#include ++#include ++#include ++ ++#include "windowobserver.h" ++#include "iundo.h" ++#include "ientity.h" ++#include "cullable.h" ++#include "renderable.h" ++#include "selectable.h" ++#include "editable.h" ++ ++#include "math/frustum.h" ++#include "signal/signal.h" ++#include "generic/object.h" ++#include "selectionlib.h" ++#include "render.h" ++#include "view.h" ++#include "renderer.h" ++#include "stream/stringstream.h" ++#include "eclasslib.h" ++#include "generic/bitfield.h" ++#include "generic/static.h" ++#include "pivot.h" ++#include "stringio.h" ++#include "container/container.h" ++ ++#include "grid.h" ++ ++TextOutputStream& ostream_write( TextOutputStream& t, const Vector4& v ){ ++ return t << "[ " << v.x() << " " << v.y() << " " << v.z() << " " << v.w() << " ]"; ++} ++ ++TextOutputStream& ostream_write( TextOutputStream& t, const Matrix4& m ){ ++ return t << "[ " << m.x() << " " << m.y() << " " << m.z() << " " << m.t() << " ]"; ++} ++ ++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 ); ++ } ++}; ++ ++ ++void point_for_device_point( Vector3& point, const Matrix4& device2object, const float x, const float y, const float z ){ ++ // transform from normalised device coords to object coords ++ point = vector4_projected( matrix4_transformed_vector4( device2object, Vector4( x, y, z, 1 ) ) ); ++} ++ ++void ray_for_device_point( Ray& ray, const Matrix4& device2object, const float x, const float y ){ ++ // point at x, y, zNear ++ point_for_device_point( ray.origin, device2object, x, y, -1 ); ++ ++ // point at x, y, zFar ++ point_for_device_point( ray.direction, device2object, x, y, 1 ); ++ ++ // construct ray ++ vector3_subtract( ray.direction, ray.origin ); ++ vector3_normalise( ray.direction ); ++} ++ ++bool sphere_intersect_ray( const Vector3& origin, float radius, const Ray& ray, Vector3& intersection ){ ++ 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 ) { ++ intersection = vector3_added( ray.origin, vector3_scaled( ray.direction, a - sqrt( d ) ) ); ++ return true; ++ } ++ else ++ { ++ intersection = vector3_added( ray.origin, vector3_scaled( ray.direction, a ) ); ++ return false; ++ } ++} ++ ++void ray_intersect_ray( const Ray& ray, const Ray& other, Vector3& intersection ){ ++ intersection = vector3_subtracted( ray.origin, other.origin ); ++ //float a = 1;//vector3_dot(ray.direction, ray.direction); // always >= 0 ++ double dot = vector3_dot( ray.direction, other.direction ); ++ //float c = 1;//vector3_dot(other.direction, other.direction); // always >= 0 ++ double d = vector3_dot( ray.direction, intersection ); ++ double e = vector3_dot( other.direction, intersection ); ++ double D = 1 - dot * dot; //a*c - dot*dot; // always >= 0 ++ ++ if ( D < 0.000001 ) { ++ // the lines are almost parallel ++ intersection = vector3_added( other.origin, vector3_scaled( other.direction, e ) ); ++ } ++ else ++ { ++ intersection = vector3_added( other.origin, vector3_scaled( other.direction, ( e - dot * d ) / D ) ); ++ } ++} ++ ++const Vector3 g_origin( 0, 0, 0 ); ++const float g_radius = 64; ++ ++void point_on_sphere( Vector3& point, const Matrix4& device2object, const float x, const float y ){ ++ Ray ray; ++ ray_for_device_point( ray, device2object, x, y ); ++ sphere_intersect_ray( g_origin, g_radius, ray, point ); ++} ++ ++void point_on_axis( Vector3& point, const Vector3& axis, const Matrix4& device2object, const float x, const float y ){ ++ Ray ray; ++ ray_for_device_point( ray, device2object, x, y ); ++ ray_intersect_ray( ray, Ray( Vector3( 0, 0, 0 ), axis ), point ); ++} ++ ++void point_on_plane( Vector3& point, const Matrix4& device2object, const float x, const float y ){ ++ Matrix4 object2device( matrix4_full_inverse( device2object ) ); ++ point = vector4_projected( matrix4_transformed_vector4( device2object, Vector4( x, y, object2device[14] / object2device[15], 1 ) ) ); ++} ++ ++//! a and b are unit vectors .. returns angle in radians ++inline float angle_between( const Vector3& a, const Vector3& b ){ ++ return static_cast( 2.0 * atan2( ++ vector3_length( vector3_subtracted( a, b ) ), ++ vector3_length( vector3_added( a, b ) ) ++ ) ); ++} ++ ++ ++#if GDEF_DEBUG ++class test_quat ++{ ++public: ++test_quat( const Vector3& from, const Vector3& to ){ ++ Vector4 quaternion( quaternion_for_unit_vectors( from, to ) ); ++ Matrix4 matrix( matrix4_rotation_for_quaternion( quaternion_multiplied_by_quaternion( quaternion, c_quaternion_identity ) ) ); ++} ++private: ++}; ++ ++static test_quat bleh( g_vector3_axis_x, g_vector3_axis_y ); ++#endif ++ ++//! 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 ++float angle_for_axis( const Vector3& a, const Vector3& b, const Vector3& axis ){ ++ if ( vector3_dot( axis, vector3_cross( a, b ) ) > 0.0 ) { ++ return angle_between( a, b ); ++ } ++ else{ ++ return -angle_between( a, b ); ++ } ++} ++ ++float distance_for_axis( const Vector3& a, const Vector3& b, const Vector3& axis ){ ++ return static_cast( vector3_dot( b, axis ) - vector3_dot( a, axis ) ); ++} ++ ++class Manipulatable ++{ ++public: ++virtual void Construct( const Matrix4& device2manip, const float x, const float y ) = 0; ++virtual void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap ) = 0; ++}; ++ ++void transform_local2object( Matrix4& object, const Matrix4& local, const Matrix4& local2object ){ ++ object = matrix4_multiplied_by_matrix4( ++ matrix4_multiplied_by_matrix4( local2object, local ), ++ matrix4_full_inverse( local2object ) ++ ); ++} ++ ++class Rotatable ++{ ++public: ++virtual ~Rotatable() = default; ++virtual void rotate( const Quaternion& rotation ) = 0; ++}; ++ ++class RotateFree : public Manipulatable ++{ ++Vector3 m_start; ++Rotatable& m_rotatable; ++public: ++RotateFree( Rotatable& rotatable ) ++ : m_rotatable( rotatable ){ ++} ++void Construct( const Matrix4& device2manip, const float x, const float y ){ ++ point_on_sphere( m_start, device2manip, x, y ); ++ vector3_normalise( m_start ); ++} ++void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap ){ ++ Vector3 current; ++ point_on_sphere( current, device2manip, x, y ); ++ ++ if( snap ){ ++ Vector3 axis( 0, 0, 0 ); ++ for( std::size_t i = 0; i < 3; ++i ){ ++ if( current[i] == 0.0f ){ ++ axis[i] = 1.0f; ++ break; ++ } ++ } ++ if( vector3_length_squared( axis ) != 0 ){ ++ constrain_to_axis( current, axis ); ++ m_rotatable.rotate( quaternion_for_axisangle( axis, float_snapped( angle_for_axis( m_start, current, axis ), static_cast( c_pi / 12.0 ) ) ) ); ++ return; ++ } ++ } ++ ++ vector3_normalise( current ); ++ m_rotatable.rotate( quaternion_for_unit_vectors( m_start, current ) ); ++} ++}; ++ ++class RotateAxis : public Manipulatable ++{ ++Vector3 m_axis; ++Vector3 m_start; ++Rotatable& m_rotatable; ++public: ++RotateAxis( Rotatable& rotatable ) ++ : m_rotatable( rotatable ){ ++} ++void Construct( const Matrix4& device2manip, const float x, const float y ){ ++ point_on_sphere( m_start, device2manip, x, y ); ++ 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 float x, const float y, const bool snap ){ ++ Vector3 current; ++ point_on_sphere( current, device2manip, x, y ); ++ constrain_to_axis( current, m_axis ); ++ ++ if( snap ){ ++ m_rotatable.rotate( quaternion_for_axisangle( m_axis, float_snapped( angle_for_axis( m_start, current, m_axis ), static_cast( 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 translation_local2object( Vector3& object, const Vector3& local, const Matrix4& local2object ){ ++ object = matrix4_get_translation_vec3( ++ matrix4_multiplied_by_matrix4( ++ matrix4_translated_by_vec3( local2object, local ), ++ matrix4_full_inverse( local2object ) ++ ) ++ ); ++} ++ ++class Translatable ++{ ++public: ++virtual ~Translatable() = default; ++virtual void translate( const Vector3& translation ) = 0; ++}; ++ ++class TranslateAxis : public Manipulatable ++{ ++Vector3 m_start; ++Vector3 m_axis; ++Translatable& m_translatable; ++public: ++TranslateAxis( Translatable& translatable ) ++ : m_translatable( translatable ){ ++} ++void Construct( const Matrix4& device2manip, const float x, const float y ){ ++ point_on_axis( m_start, m_axis, device2manip, x, y ); ++} ++void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap ){ ++ Vector3 current; ++ point_on_axis( current, m_axis, device2manip, x, y ); ++ current = vector3_scaled( m_axis, distance_for_axis( m_start, current, m_axis ) ); ++ ++ translation_local2object( current, current, manip2object ); ++ vector3_snap( current, GetSnapGridSize() ); ++ ++ m_translatable.translate( current ); ++} ++ ++void SetAxis( const Vector3& axis ){ ++ m_axis = axis; ++} ++}; ++ ++class TranslateFree : public Manipulatable ++{ ++private: ++Vector3 m_start; ++Translatable& m_translatable; ++public: ++TranslateFree( Translatable& translatable ) ++ : m_translatable( translatable ){ ++} ++void Construct( const Matrix4& device2manip, const float x, const float y ){ ++ point_on_plane( m_start, device2manip, x, y ); ++} ++void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap ){ ++ Vector3 current; ++ point_on_plane( current, device2manip, x, y ); ++ current = vector3_subtracted( current, m_start ); ++ ++ if( snap ){ ++ for ( std::size_t i = 0; i < 3 ; ++i ){ ++ if( fabs( current[i] ) >= fabs( current[(i + 1) % 3] ) ){ ++ current[(i + 1) % 3] = 0.0f; ++ } ++ else{ ++ current[i] = 0.0f; ++ } ++ } ++ } ++ ++ translation_local2object( current, current, manip2object ); ++ vector3_snap( current, GetSnapGridSize() ); ++ ++ m_translatable.translate( current ); ++} ++}; ++ ++void GetSelectionAABB( AABB& bounds ); ++const Matrix4& SelectionSystem_GetPivot2World(); ++ ++class Scalable ++{ ++public: ++virtual ~Scalable() = default; ++virtual void scale( const Vector3& scaling ) = 0; ++}; ++ ++ ++class ScaleAxis : public Manipulatable ++{ ++private: ++Vector3 m_start; ++Vector3 m_axis; ++Scalable& m_scalable; ++ ++Vector3 m_choosen_extent; ++ ++public: ++ScaleAxis( Scalable& scalable ) ++ : m_scalable( scalable ){ ++} ++void Construct( const Matrix4& device2manip, const float x, const float y ){ ++ point_on_axis( m_start, m_axis, device2manip, x, y ); ++ ++ AABB aabb; ++ GetSelectionAABB( aabb ); ++ Vector3 transform_origin = vector4_to_vector3( SelectionSystem_GetPivot2World().t() ); ++ m_choosen_extent = Vector3( ++ std::max( aabb.origin[0] + aabb.extents[0] - transform_origin[0], - aabb.origin[0] + aabb.extents[0] + transform_origin[0] ), ++ std::max( aabb.origin[1] + aabb.extents[1] - transform_origin[1], - aabb.origin[1] + aabb.extents[1] + transform_origin[1] ), ++ std::max( aabb.origin[2] + aabb.extents[2] - transform_origin[2], - aabb.origin[2] + aabb.extents[2] + transform_origin[2] ) ++ ); ++ ++} ++void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap ){ ++ //globalOutputStream() << "manip2object: " << manip2object << " device2manip: " << device2manip << " x: " << x << " y:" << y <<"\n"; ++ Vector3 current; ++ point_on_axis( current, m_axis, device2manip, x, y ); ++ Vector3 delta = vector3_subtracted( current, m_start ); ++ ++ translation_local2object( delta, delta, manip2object ); ++ vector3_snap( delta, GetSnapGridSize() ); ++ ++ Vector3 start( vector3_snapped( m_start, GetSnapGridSize() != 0.0f ? GetSnapGridSize() : 0.001f ) ); ++ for ( std::size_t i = 0; i < 3 ; ++i ){ //prevent snapping to 0 with big gridsize ++ if( float_snapped( m_start[i], 0.001f ) != 0.0f && start[i] == 0.0f ){ ++ start[i] = GetSnapGridSize(); ++ } ++ } ++ //globalOutputStream() << "m_start: " << m_start << " start: " << start << " delta: " << delta <<"\n"; ++ 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] ++ ); ++ ++ for( std::size_t i = 0; i < 3; i++ ){ ++ if( m_choosen_extent[i] > 0.0625f ){ //epsilon to prevent super high scale for set of models, having really small extent, formed by origins ++ scale[i] = ( m_choosen_extent[i] + delta[i] ) / m_choosen_extent[i]; ++ } ++ } ++ if( snap ){ ++ for( std::size_t i = 0; i < 3; i++ ){ ++ if( scale[i] == 1.0f ){ ++ scale[i] = vector3_dot( scale, m_axis ); ++ } ++ } ++ } ++ //globalOutputStream() << "scale: " << scale <<"\n"; ++ m_scalable.scale( scale ); ++} ++ ++void SetAxis( const Vector3& axis ){ ++ m_axis = axis; ++} ++}; ++ ++class ScaleFree : public Manipulatable ++{ ++private: ++Vector3 m_start; ++Scalable& m_scalable; ++ ++Vector3 m_choosen_extent; ++ ++public: ++ScaleFree( Scalable& scalable ) ++ : m_scalable( scalable ){ ++} ++void Construct( const Matrix4& device2manip, const float x, const float y ){ ++ point_on_plane( m_start, device2manip, x, y ); ++ ++ AABB aabb; ++ GetSelectionAABB( aabb ); ++ Vector3 transform_origin = vector4_to_vector3( SelectionSystem_GetPivot2World().t() ); ++ m_choosen_extent = Vector3( ++ std::max( aabb.origin[0] + aabb.extents[0] - transform_origin[0], - aabb.origin[0] + aabb.extents[0] + transform_origin[0] ), ++ std::max( aabb.origin[1] + aabb.extents[1] - transform_origin[1], - aabb.origin[1] + aabb.extents[1] + transform_origin[1] ), ++ std::max( aabb.origin[2] + aabb.extents[2] - transform_origin[2], - aabb.origin[2] + aabb.extents[2] + transform_origin[2] ) ++ ); ++} ++void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap ){ ++ Vector3 current; ++ point_on_plane( current, device2manip, x, y ); ++ Vector3 delta = vector3_subtracted( current, m_start ); ++ ++ translation_local2object( delta, delta, manip2object ); ++ vector3_snap( delta, GetSnapGridSize() ); ++ ++ Vector3 start( vector3_snapped( m_start, GetSnapGridSize() != 0.0f ? GetSnapGridSize() : 0.001f ) ); ++ for ( std::size_t i = 0; i < 3 ; ++i ){ //prevent snapping to 0 with big gridsize ++ if( float_snapped( m_start[i], 0.001f ) != 0.0f && start[i] == 0.0f ){ ++ start[i] = GetSnapGridSize(); ++ } ++ } ++ 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_choosen_extent[i] > 0.0625f ){ ++ scale[i] = ( m_choosen_extent[i] + delta[i] ) / m_choosen_extent[i]; ++ } ++ } ++ //globalOutputStream() << "pre snap scale: " << scale <<"\n"; ++ if( snap ){ ++ float bestscale = scale[0]; ++ for( std::size_t i = 1; i < 3; i++ ){ ++ //if( fabs( 1.0f - fabs( scale[i] ) ) > fabs( 1.0f - fabs( bestscale ) ) ){ ++ if( fabs( scale[i] ) > fabs( bestscale ) && scale[i] != 1.0f ){ //harder to scale down with this, but glitchier with upper one ++ bestscale = scale[i]; ++ } ++ //globalOutputStream() << "bestscale: " << bestscale <<"\n"; ++ } ++ for( std::size_t i = 0; i < 3; i++ ){ ++ if( start[i] != 0.0f ){ // !!!!check grid == 0 case ++ scale[i] = ( scale[i] < 0.0f ) ? -fabs( bestscale ) : fabs( bestscale ); ++ } ++ } ++ } ++ //globalOutputStream() << "scale: " << scale <<"\n"; ++ m_scalable.scale( scale ); ++} ++}; ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++class RenderableClippedPrimitive : public OpenGLRenderable ++{ ++struct primitive_t ++{ ++ PointVertex m_points[9]; ++ std::size_t m_count; ++}; ++Matrix4 m_inverse; ++std::vector m_primitives; ++public: ++Matrix4 m_world; ++ ++void render( RenderStateFlags state ) const { ++ for ( std::size_t i = 0; i < m_primitives.size(); ++i ) ++ { ++ glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_primitives[i].m_points[0].colour ); ++ glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_primitives[i].m_points[0].vertex ); ++ switch ( m_primitives[i].m_count ) ++ { ++ case 1: break; ++ case 2: glDrawArrays( GL_LINES, 0, GLsizei( m_primitives[i].m_count ) ); break; ++ default: 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; ++} ++}; ++ ++#if GDEF_DEBUG ++#define DEBUG_SELECTION ++#endif ++ ++#if defined( DEBUG_SELECTION ) ++Shader* g_state_clipped; ++RenderableClippedPrimitive g_render_clipped; ++#endif ++ ++ ++#if 0 ++// dist_Point_to_Line(): get the distance of a point to a line. ++// Input: a Point P and a Line L (in any dimension) ++// Return: the shortest distance from P to L ++float ++dist_Point_to_Line( Point P, Line L ){ ++ Vector v = L.P1 - L.P0; ++ Vector w = P - L.P0; ++ ++ double c1 = dot( w,v ); ++ double c2 = dot( v,v ); ++ double b = c1 / c2; ++ ++ Point Pb = L.P0 + b * v; ++ return d( P, Pb ); ++} ++#endif ++ ++class Segment3D ++{ ++typedef Vector3 point_type; ++public: ++Segment3D( const point_type& _p0, const point_type& _p1 ) ++ : p0( _p0 ), p1( _p1 ){ ++} ++ ++point_type p0, p1; ++}; ++ ++typedef Vector3 Point3D; ++ ++inline double vector3_distance_squared( const Point3D& a, const Point3D& b ){ ++ return vector3_length_squared( b - a ); ++} ++ ++// get the distance of a point to a segment. ++Point3D segment_closest_point_to_point( const Segment3D& segment, const Point3D& point ){ ++ Vector3 v = segment.p1 - segment.p0; ++ Vector3 w = point - segment.p0; ++ ++ double c1 = vector3_dot( w,v ); ++ if ( c1 <= 0 ) { ++ return segment.p0; ++ } ++ ++ double c2 = vector3_dot( v,v ); ++ if ( c2 <= c1 ) { ++ return segment.p1; ++ } ++ ++ return Point3D( segment.p0 + v * ( c1 / c2 ) ); ++} ++ ++double segment_dist_to_point_3d( const Segment3D& segment, const Point3D& point ){ ++ return vector3_distance_squared( point, segment_closest_point_to_point( segment, point ) ); ++} ++ ++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] ++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 ++ float vt = (float)( 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] ) ); ++} ++ ++enum clipcull_t ++{ ++ eClipCullNone, ++ eClipCullCW, ++ eClipCullCCW, ++}; ++ ++ ++inline SelectionIntersection select_point_from_clipped( Vector4& clipped ){ ++ return SelectionIntersection( clipped[2] / clipped[3], static_cast( vector3_length_squared( Vector3( clipped[0] / clipped[3], clipped[1] / clipped[3], 0 ) ) ) ); ++} ++ ++void BestPoint( std::size_t count, Vector4 clipped[9], SelectionIntersection& best, clipcull_t cull ){ ++ 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 != eClipCullNone && count > 2 ) { ++ double signed_area = triangle_signed_area_XY( normalised[0], normalised[1], normalised[2] ); ++ ++ if ( ( cull == eClipCullCW && signed_area > 0 ) ++ || ( cull == eClipCullCCW && signed_area < 0 ) ) { ++ return; ++ } ++ } ++ ++ if ( count == 2 ) { ++ Segment3D segment( normalised[0], normalised[1] ); ++ Point3D point = segment_closest_point_to_point( segment, Vector3( 0, 0, 0 ) ); ++ assign_if_closer( best, SelectionIntersection( point.z(), 0 ) ); ++ } ++ else if ( count > 2 && !point_test_polygon_2d( Vector3( 0, 0, 0 ), normalised, normalised + count ) ) { ++ point_iterator_t end = normalised + count; ++ for ( point_iterator_t previous = end - 1, current = normalised; current != end; previous = current, ++current ) ++ { ++ Segment3D segment( *previous, *current ); ++ Point3D point = segment_closest_point_to_point( segment, Vector3( 0, 0, 0 ) ); ++ float depth = point.z(); ++ point.z() = 0; ++ float distance = static_cast( vector3_length_squared( point ) ); ++ ++ assign_if_closer( best, SelectionIntersection( depth, distance ) ); ++ } ++ } ++ else if ( count > 2 ) { ++ assign_if_closer( ++ best, ++ SelectionIntersection( ++ static_cast( ray_distance_to_plane( ++ Ray( Vector3( 0, 0, 0 ), Vector3( 0, 0, 1 ) ), ++ plane3_for_points( normalised[0], normalised[1], normalised[2] ) ++ ) ), ++ 0 ++ ) ++ ); ++ } ++ ++#if defined( DEBUG_SELECTION ) ++ if ( count >= 2 ) { ++ g_render_clipped.insert( clipped, count ); ++ } ++#endif ++} ++ ++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, eClipCullNone ); ++ } ++} ++ ++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, eClipCullNone ); ++ } ++} ++ ++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, eClipCullNone ); ++} ++ ++void Circle_BestPoint( const Matrix4& local2view, clipcull_t 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 ); ++ } ++} ++ ++void Quad_BestPoint( const Matrix4& local2view, clipcull_t 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 ); ++ } ++} ++ ++struct FlatShadedVertex ++{ ++ Vertex3f vertex; ++ Colour4b colour; ++ Normal3f normal; ++ ++ FlatShadedVertex(){ ++ } ++}; ++ ++ ++typedef FlatShadedVertex* FlatShadedVertexIterator; ++void Triangles_BestPoint( const Matrix4& local2view, clipcull_t 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( ( *x ).vertex ), ++ reinterpret_cast( ( *y ).vertex ), ++ reinterpret_cast( ( *z ).vertex ), ++ clipped ++ ), ++ clipped, ++ best, ++ cull ++ ); ++ } ++} ++ ++ ++typedef std::multimap SelectableSortedSet; ++ ++class SelectionPool : public Selector ++{ ++SelectableSortedSet m_pool; ++SelectionIntersection m_intersection; ++Selectable* m_selectable; ++ ++public: ++void pushSelectable( Selectable& selectable ){ ++ m_intersection = SelectionIntersection(); ++ m_selectable = &selectable; ++} ++void popSelectable(){ ++ addSelectable( m_intersection, m_selectable ); ++ m_intersection = SelectionIntersection(); ++} ++void addIntersection( const SelectionIntersection& intersection ){ ++ 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(); ++} ++}; ++ ++ ++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; ++} ++ ++template ++inline 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 = static_cast( radius * cos( theta ) ); ++ y = static_cast( 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 ++ } ++} ++ ++class Manipulator ++{ ++public: ++virtual Manipulatable* GetManipulatable() = 0; ++virtual void testSelect( const View& view, const Matrix4& pivot2world ){ ++} ++virtual void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ){ ++} ++virtual void setSelected( bool select ) = 0; ++virtual bool isSelected() const = 0; ++}; ++ ++ ++inline Vector3 normalised_safe( const Vector3& self ){ ++ if ( vector3_equal( self, g_vector3_identity ) ) { ++ return g_vector3_identity; ++ } ++ return vector3_normalised( self ); ++} ++ ++ ++class RotateManipulator : public Manipulator ++{ ++struct RenderableCircle : public OpenGLRenderable ++{ ++ Array m_vertices; ++ ++ RenderableCircle( std::size_t size ) : m_vertices( size ){ ++ } ++ void render( RenderStateFlags state ) const { ++ glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_vertices.data()->colour ); ++ glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_vertices.data()->vertex ); ++ glDrawArrays( GL_LINE_LOOP, 0, GLsizei( m_vertices.size() ) ); ++ } ++ void setColour( const Colour4b& colour ){ ++ for ( Array::iterator i = m_vertices.begin(); i != m_vertices.end(); ++i ) ++ { ++ ( *i ).colour = colour; ++ } ++ } ++}; ++ ++struct RenderableSemiCircle : public OpenGLRenderable ++{ ++ Array m_vertices; ++ ++ RenderableSemiCircle( std::size_t size ) : m_vertices( size ){ ++ } ++ void render( RenderStateFlags state ) const { ++ glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_vertices.data()->colour ); ++ glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_vertices.data()->vertex ); ++ glDrawArrays( GL_LINE_STRIP, 0, GLsizei( m_vertices.size() ) ); ++ } ++ void setColour( const Colour4b& colour ){ ++ for ( Array::iterator i = m_vertices.begin(); i != m_vertices.end(); ++i ) ++ { ++ ( *i ).colour = colour; ++ } ++ } ++}; ++ ++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: ++static Shader* m_state_outer; ++ ++RotateManipulator( 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() ); ++ ++ m_selectable_sphere.setSelected( true ); ++} ++ ++ ++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 ), vector4_to_vector3( m_pivot.m_viewpointSpace.z() ) ) ); ++ ++ 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; ++ vector4_to_vector3( m_local2world_x.y() ) = normalised_safe( ++ vector3_cross( g_vector3_axis_x, localViewpoint ) ++ ); ++ vector4_to_vector3( m_local2world_x.z() ) = normalised_safe( ++ vector3_cross( vector4_to_vector3( m_local2world_x.x() ), vector4_to_vector3( m_local2world_x.y() ) ) ++ ); ++ 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; ++ vector4_to_vector3( m_local2world_y.z() ) = normalised_safe( ++ vector3_cross( g_vector3_axis_y, localViewpoint ) ++ ); ++ vector4_to_vector3( m_local2world_y.x() ) = normalised_safe( ++ vector3_cross( vector4_to_vector3( m_local2world_y.y() ), vector4_to_vector3( m_local2world_y.z() ) ) ++ ); ++ 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; ++ vector4_to_vector3( m_local2world_z.x() ) = normalised_safe( ++ vector3_cross( g_vector3_axis_z, localViewpoint ) ++ ); ++ vector4_to_vector3( m_local2world_z.y() ) = normalised_safe( ++ vector3_cross( vector4_to_vector3( m_local2world_z.z() ), vector4_to_vector3( m_local2world_z.x() ) ) ++ ); ++ matrix4_premultiply_by_matrix4( m_local2world_z, m_pivot.m_worldSpace ); ++ } ++} ++ ++void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ){ ++ 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 ){ ++ m_pivot.update( pivot2world, view.GetModelview(), view.GetProjection(), view.GetViewport() ); ++ updateCircleTransforms(); ++ ++ SelectionPool selector; ++ ++ { ++ { ++ 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 ); ++ } ++ ++ { ++ 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 ); ++ } ++ ++ { ++ 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 ); ++ } ++ } ++ ++ { ++ 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, eClipCullCW, 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.begin() ).second->setSelected( true ); ++ } ++} ++ ++Manipulatable* GetManipulatable(){ ++ 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 ){ ++ m_selectable_x.setSelected( select ); ++ m_selectable_y.setSelected( select ); ++ m_selectable_z.setSelected( select ); ++ m_selectable_screen.setSelected( select ); ++} ++bool isSelected() const { ++ return m_selectable_x.isSelected() ++ | m_selectable_y.isSelected() ++ | m_selectable_z.isSelected() ++ | m_selectable_screen.isSelected() ++ | m_selectable_sphere.isSelected(); ++} ++}; ++ ++Shader* RotateManipulator::m_state_outer; ++ ++ ++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 ++inline 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 * static_cast( cos( i * head_segment ) ); ++ VertexRemap::z( point.vertex ) = arrowhead_radius * static_cast( sin( i * head_segment ) ); ++ NormalRemap::x( point.normal ) = arrowhead_radius / arrowhead_length; ++ NormalRemap::y( point.normal ) = static_cast( cos( i * head_segment ) ); ++ NormalRemap::z( point.normal ) = static_cast( 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 ) = static_cast( cos( ( i + 0.5 ) * head_segment ) ); ++ NormalRemap::z( point.normal ) = static_cast( 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 * static_cast( cos( ( i + 1 ) * head_segment ) ); ++ VertexRemap::z( point.vertex ) = arrowhead_radius * static_cast( sin( ( i + 1 ) * head_segment ) ); ++ NormalRemap::x( point.normal ) = arrowhead_radius / arrowhead_length; ++ NormalRemap::y( point.normal ) = static_cast( cos( ( i + 1 ) * head_segment ) ); ++ NormalRemap::z( point.normal ) = static_cast( 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 * static_cast( cos( i * head_segment ) ); ++ VertexRemap::z( point.vertex ) = arrowhead_radius * static_cast( 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 * static_cast( cos( ( i + 1 ) * head_segment ) ); ++ VertexRemap::z( point.vertex ) = arrowhead_radius * static_cast( sin( ( i + 1 ) * head_segment ) ); ++ NormalRemap::x( point.normal ) = -1; ++ NormalRemap::y( point.normal ) = 0; ++ NormalRemap::z( point.normal ) = 0; ++ } ++ } ++} ++ ++template ++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 ++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 ++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(); ++} ++}; ++ ++void vector3_print( const Vector3& v ){ ++ globalOutputStream() << "( " << v.x() << " " << v.y() << " " << v.z() << " )"; ++} ++ ++class TranslateManipulator : public Manipulator ++{ ++struct RenderableArrowLine : public OpenGLRenderable ++{ ++ PointVertex m_line[2]; ++ ++ RenderableArrowLine(){ ++ } ++ void render( RenderStateFlags state ) const { ++ glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_line[0].colour ); ++ glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_line[0].vertex ); ++ glDrawArrays( GL_LINES, 0, 2 ); ++ } ++ void setColour( const Colour4b& colour ){ ++ m_line[0].colour = colour; ++ m_line[1].colour = colour; ++ } ++}; ++struct RenderableArrowHead : public OpenGLRenderable ++{ ++ Array m_vertices; ++ ++ RenderableArrowHead( std::size_t size ) ++ : m_vertices( size ){ ++ } ++ void render( RenderStateFlags state ) const { ++ glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( FlatShadedVertex ), &m_vertices.data()->colour ); ++ glVertexPointer( 3, GL_FLOAT, sizeof( FlatShadedVertex ), &m_vertices.data()->vertex ); ++ glNormalPointer( GL_FLOAT, sizeof( FlatShadedVertex ), &m_vertices.data()->normal ); ++ glDrawArrays( GL_TRIANGLES, 0, GLsizei( m_vertices.size() ) ); ++ } ++ void setColour( const Colour4b& colour ){ ++ for ( Array::iterator i = m_vertices.begin(); i != m_vertices.end(); ++i ) ++ { ++ ( *i ).colour = colour; ++ } ++ } ++}; ++struct RenderableQuad : public OpenGLRenderable ++{ ++ PointVertex m_quad[4]; ++ void render( RenderStateFlags state ) const { ++ glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_quad[0].colour ); ++ glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_quad[0].vertex ); ++ 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; ++ } ++}; ++ ++TranslateFree m_free; ++TranslateAxis m_axis; ++RenderableArrowLine m_arrow_x; ++RenderableArrowLine m_arrow_y; ++RenderableArrowLine 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: ++static Shader* m_state_wire; ++static Shader* m_state_fill; ++ ++TranslateManipulator( 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(), TripleRemapXYZ() ); ++ draw_arrowline( length, m_arrow_y.m_line, 1 ); ++ draw_arrowhead( segments, length, m_arrow_head_y.m_vertices.data(), TripleRemapYZX(), TripleRemapYZX() ); ++ draw_arrowline( length, m_arrow_z.m_line, 2 ); ++ draw_arrowhead( segments, length, m_arrow_head_z.m_vertices.data(), TripleRemapZXY(), TripleRemapZXY() ); ++ ++ 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 fabs( vector3_dot( pivot.m_axis_screen, axis ) ) < 0.95; ++} ++ ++void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ){ ++ m_pivot.update( pivot2world, volume.GetModelview(), volume.GetProjection(), volume.GetViewport() ); ++ ++ // temp hack ++ UpdateColours(); ++ ++ Vector3 x = vector3_normalised( vector4_to_vector3( m_pivot.m_worldSpace.x() ) ); ++ bool show_x = manipulator_show_axis( m_pivot, x ); ++ ++ Vector3 y = vector3_normalised( vector4_to_vector3( m_pivot.m_worldSpace.y() ) ); ++ bool show_y = manipulator_show_axis( m_pivot, y ); ++ ++ Vector3 z = vector3_normalised( vector4_to_vector3( m_pivot.m_worldSpace.z() ) ); ++ 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 ){ ++ m_pivot.update( pivot2world, view.GetModelview(), view.GetProjection(), view.GetViewport() ); ++ ++ SelectionPool selector; ++ ++ Vector3 x = vector3_normalised( vector4_to_vector3( m_pivot.m_worldSpace.x() ) ); ++ bool show_x = manipulator_show_axis( m_pivot, x ); ++ ++ Vector3 y = vector3_normalised( vector4_to_vector3( m_pivot.m_worldSpace.y() ) ); ++ bool show_y = manipulator_show_axis( m_pivot, y ); ++ ++ Vector3 z = vector3_normalised( vector4_to_vector3( m_pivot.m_worldSpace.z() ) ); ++ bool show_z = manipulator_show_axis( m_pivot, z ); ++ ++ { ++ Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_pivot.m_viewpointSpace ) ); ++ ++ { ++ SelectionIntersection best; ++ Quad_BestPoint( local2view, eClipCullCW, m_quad_screen.m_quad, best ); ++ if ( best.valid() ) { ++ best = SelectionIntersection( 0, 0 ); ++ selector.addSelectable( best, &m_selectable_screen ); ++ } ++ } ++ } ++ ++ { ++ 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, eClipCullCW, 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, eClipCullCW, 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, eClipCullCW, m_arrow_head_z.m_vertices.begin(), m_arrow_head_z.m_vertices.end(), best ); ++ selector.addSelectable( best, &m_selectable_z ); ++ } ++ } ++ ++ if ( !selector.failed() ) { ++ ( *selector.begin() ).second->setSelected( true ); ++ } ++} ++ ++Manipulatable* GetManipulatable(){ ++ 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 ){ ++ m_selectable_x.setSelected( select ); ++ m_selectable_y.setSelected( select ); ++ m_selectable_z.setSelected( select ); ++ m_selectable_screen.setSelected( select ); ++} ++bool isSelected() const { ++ return m_selectable_x.isSelected() ++ | m_selectable_y.isSelected() ++ | m_selectable_z.isSelected() ++ | m_selectable_screen.isSelected(); ++} ++}; ++ ++Shader* TranslateManipulator::m_state_wire; ++Shader* TranslateManipulator::m_state_fill; ++ ++class ScaleManipulator : public Manipulator ++{ ++struct RenderableArrow : public OpenGLRenderable ++{ ++ PointVertex m_line[2]; ++ ++ void render( RenderStateFlags state ) const { ++ glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_line[0].colour ); ++ glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_line[0].vertex ); ++ 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 { ++ glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_quad[0].colour ); ++ glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_quad[0].vertex ); ++ glDrawArrays( GL_QUADS, 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; ++ } ++}; ++ ++ScaleFree m_free; ++ScaleAxis m_axis; ++RenderableArrow m_arrow_x; ++RenderableArrow m_arrow_y; ++RenderableArrow 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: ++ScaleManipulator( 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 ); ++} ++ ++Pivot2World& getPivot(){ ++ return m_pivot; ++} ++ ++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 ){ ++ 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 ){ ++ m_pivot.update( pivot2world, view.GetModelview(), view.GetProjection(), view.GetViewport() ); ++ ++ SelectionPool selector; ++ ++ { ++ 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 ); ++ } ++ } ++ ++ { ++ Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_pivot.m_viewpointSpace ) ); ++ ++ { ++ SelectionIntersection best; ++ Quad_BestPoint( local2view, eClipCullCW, m_quad_screen.m_quad, best ); ++ selector.addSelectable( best, &m_selectable_screen ); ++ } ++ } ++ ++ if ( !selector.failed() ) { ++ ( *selector.begin() ).second->setSelected( true ); ++ } ++} ++ ++Manipulatable* GetManipulatable(){ ++ 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 ){ ++ m_selectable_x.setSelected( select ); ++ m_selectable_y.setSelected( select ); ++ m_selectable_z.setSelected( select ); ++ m_selectable_screen.setSelected( select ); ++} ++bool isSelected() const { ++ return m_selectable_x.isSelected() ++ | m_selectable_y.isSelected() ++ | m_selectable_z.isSelected() ++ | m_selectable_screen.isSelected(); ++} ++}; ++ ++ ++inline PlaneSelectable* Instance_getPlaneSelectable( scene::Instance& instance ){ ++ return InstanceTypeCast::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 { ++ if ( path.top().get().visible() ) { ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( selectable != 0 && selectable->isSelected() ) { ++ 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 { ++ if ( path.top().get().visible() ) { ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( selectable != 0 && selectable->isSelected() ) { ++ 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 { ++ if ( plane.a < other.a ) { ++ return true; ++ } ++ if ( other.a < plane.a ) { ++ return false; ++ } ++ ++ if ( plane.b < other.b ) { ++ return true; ++ } ++ if ( other.b < plane.b ) { ++ return false; ++ } ++ ++ if ( plane.c < other.c ) { ++ return true; ++ } ++ if ( other.c < plane.c ) { ++ return false; ++ } ++ ++ if ( plane.d < other.d ) { ++ return true; ++ } ++ if ( other.d < plane.d ) { ++ return false; ++ } ++ ++ return false; ++} ++}; ++ ++typedef std::set PlaneSet; ++ ++inline void PlaneSet_insert( PlaneSet& self, const Plane3& plane ){ ++ self.insert( plane ); ++} ++ ++inline bool PlaneSet_contains( const PlaneSet& self, const Plane3& plane ){ ++ return self.find( plane ) != self.end(); ++} ++ ++ ++class SelectedPlaneSet : public SelectedPlanes ++{ ++PlaneSet m_selectedPlanes; ++public: ++bool empty() const { ++ return m_selectedPlanes.empty(); ++} ++ ++void insert( const Plane3& plane ){ ++ PlaneSet_insert( m_selectedPlanes, plane ); ++} ++bool contains( const Plane3& plane ) const { ++ return PlaneSet_contains( m_selectedPlanes, plane ); ++} ++typedef MemberCaller 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(); ++} ++ ++void Scene_Translate_Component_Selected( scene::Graph& graph, const Vector3& translation ); ++void Scene_Translate_Selected( scene::Graph& graph, const Vector3& translation ); ++void Scene_TestSelect_Primitive( Selector& selector, SelectionTest& test, const VolumeTest& volume ); ++void Scene_TestSelect_Component( Selector& selector, SelectionTest& test, const VolumeTest& volume, SelectionSystem::EComponentMode componentMode ); ++void Scene_TestSelect_Component_Selected( Selector& selector, SelectionTest& test, const VolumeTest& volume, SelectionSystem::EComponentMode componentMode ); ++void Scene_SelectAll_Component( bool select, SelectionSystem::EComponentMode componentMode ); ++ ++class ResizeTranslatable : public Translatable ++{ ++void translate( const Vector3& translation ){ ++ Scene_Translate_Component_Selected( GlobalSceneGraph(), translation ); ++} ++}; ++ ++class DragTranslatable : public Translatable ++{ ++void translate( const Vector3& translation ){ ++ if ( GlobalSelectionSystem().Mode() == SelectionSystem::eComponent ) { ++ Scene_Translate_Component_Selected( GlobalSceneGraph(), translation ); ++ } ++ else ++ { ++ Scene_Translate_Selected( GlobalSceneGraph(), translation ); ++ } ++} ++}; ++ ++class SelectionVolume : public SelectionTest ++{ ++Matrix4 m_local2view; ++const View& m_view; ++clipcull_t m_cull; ++Vector3 m_near; ++Vector3 m_far; ++public: ++SelectionVolume( const View& view ) ++ : m_view( view ){ ++} ++ ++const VolumeTest& getVolume() const { ++ return m_view; ++} ++ ++const Vector3& getNear() const { ++ return m_near; ++} ++const Vector3& getFar() const { ++ return m_far; ++} ++ ++void BeginMesh( const Matrix4& localToWorld, bool twoSided ){ ++ 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() ? eClipCullNone : ( matrix4_handedness( localToWorld ) == MATRIX4_RIGHTHANDED ) ? eClipCullCW : eClipCullCCW; ++ ++ { ++ Matrix4 screen2world( matrix4_full_inverse( m_local2view ) ); ++ ++ m_near = vector4_projected( ++ matrix4_transformed_vector4( ++ screen2world, ++ Vector4( 0, 0, -1, 1 ) ++ ) ++ ); ++ ++ m_far = vector4_projected( ++ matrix4_transformed_vector4( ++ screen2world, ++ Vector4( 0, 0, 1, 1 ) ++ ) ++ ); ++ } ++ ++#if defined( DEBUG_SELECTION ) ++ g_render_clipped.construct( m_view.GetViewMatrix() ); ++#endif ++} ++void TestPoint( const Vector3& point, SelectionIntersection& best ){ ++ 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 ){ ++ Vector4 clipped[9]; ++ for ( std::size_t i = 0; i + 2 < count; ++i ) ++ { ++ BestPoint( ++ matrix4_clip_triangle( ++ m_local2view, ++ reinterpret_cast( vertices[0] ), ++ reinterpret_cast( vertices[i + 1] ), ++ reinterpret_cast( vertices[i + 2] ), ++ clipped ++ ), ++ clipped, ++ best, ++ m_cull ++ ); ++ } ++} ++void TestLineLoop( const VertexPointer& vertices, std::size_t count, SelectionIntersection& best ){ ++ 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( ( *prev ) ), ++ reinterpret_cast( ( *i ) ), ++ clipped ++ ), ++ clipped, ++ best, ++ m_cull ++ ); ++ } ++} ++void TestLineStrip( const VertexPointer& vertices, std::size_t count, SelectionIntersection& best ){ ++ 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( ( *i ) ), ++ reinterpret_cast( ( *next ) ), ++ clipped ++ ), ++ clipped, ++ best, ++ m_cull ++ ); ++ } ++} ++void TestLines( const VertexPointer& vertices, std::size_t count, SelectionIntersection& best ){ ++ 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( ( *i ) ), ++ reinterpret_cast( ( *( i + 1 ) ) ), ++ clipped ++ ), ++ clipped, ++ best, ++ m_cull ++ ); ++ } ++} ++void TestTriangles( const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best ){ ++ Vector4 clipped[9]; ++ for ( IndexPointer::iterator i( indices.begin() ); i != indices.end(); i += 3 ) ++ { ++ BestPoint( ++ matrix4_clip_triangle( ++ m_local2view, ++ reinterpret_cast( vertices[*i] ), ++ reinterpret_cast( vertices[*( i + 1 )] ), ++ reinterpret_cast( vertices[*( i + 2 )] ), ++ clipped ++ ), ++ clipped, ++ best, ++ m_cull ++ ); ++ } ++} ++void TestQuads( const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best ){ ++ Vector4 clipped[9]; ++ for ( IndexPointer::iterator i( indices.begin() ); i != indices.end(); i += 4 ) ++ { ++ BestPoint( ++ matrix4_clip_triangle( ++ m_local2view, ++ reinterpret_cast( vertices[*i] ), ++ reinterpret_cast( vertices[*( i + 1 )] ), ++ reinterpret_cast( vertices[*( i + 3 )] ), ++ clipped ++ ), ++ clipped, ++ best, ++ m_cull ++ ); ++ BestPoint( ++ matrix4_clip_triangle( ++ m_local2view, ++ reinterpret_cast( vertices[*( i + 1 )] ), ++ reinterpret_cast( vertices[*( i + 2 )] ), ++ reinterpret_cast( vertices[*( i + 3 )] ), ++ clipped ++ ), ++ clipped, ++ best, ++ m_cull ++ ); ++ } ++} ++void TestQuadStrip( const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best ){ ++ Vector4 clipped[9]; ++ for ( IndexPointer::iterator i( indices.begin() ); i + 2 != indices.end(); i += 2 ) ++ { ++ BestPoint( ++ matrix4_clip_triangle( ++ m_local2view, ++ reinterpret_cast( vertices[*i] ), ++ reinterpret_cast( vertices[*( i + 1 )] ), ++ reinterpret_cast( vertices[*( i + 2 )] ), ++ clipped ++ ), ++ clipped, ++ best, ++ m_cull ++ ); ++ BestPoint( ++ matrix4_clip_triangle( ++ m_local2view, ++ reinterpret_cast( vertices[*( i + 2 )] ), ++ reinterpret_cast( vertices[*( i + 1 )] ), ++ reinterpret_cast( vertices[*( i + 3 )] ), ++ clipped ++ ), ++ clipped, ++ best, ++ m_cull ++ ); ++ } ++} ++}; ++ ++class SelectionCounter ++{ ++public: ++using func = void(const Selectable &); ++ ++SelectionCounter( const SelectionChangeCallback& onchanged ) ++ : m_count( 0 ), m_onchanged( onchanged ){ ++} ++void operator()( const Selectable& selectable ){ ++ if ( selectable.isSelected() ) { ++ ++m_count; ++ } ++ else ++ { ++ ASSERT_MESSAGE( m_count != 0, "selection counter underflow" ); ++ --m_count; ++ } ++ ++ m_onchanged( selectable ); ++} ++bool empty() const { ++ return m_count == 0; ++} ++std::size_t size() const { ++ return m_count; ++} ++private: ++std::size_t m_count; ++SelectionChangeCallback m_onchanged; ++}; ++ ++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 float device_point[2], const float device_epsilon[2] ){ ++ 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 float device_point[2], const float device_delta[2] ){ ++ rect_t selection_box; ++ selection_box.min[0] = ( device_delta[0] < 0 ) ? ( device_point[0] + device_delta[0] ) : ( device_point[0] ); ++ selection_box.min[1] = ( device_delta[1] < 0 ) ? ( device_point[1] + device_delta[1] ) : ( device_point[1] ); ++ selection_box.max[0] = ( device_delta[0] > 0 ) ? ( device_point[0] + device_delta[0] ) : ( device_point[0] ); ++ selection_box.max[1] = ( device_delta[1] > 0 ) ? ( device_point[1] + device_delta[1] ) : ( device_point[1] ); ++ return selection_box; ++} ++ ++Quaternion construct_local_rotation( const Quaternion& world, const Quaternion& localToWorld ){ ++ return quaternion_normalised( quaternion_multiplied_by_quaternion( ++ quaternion_normalised( quaternion_multiplied_by_quaternion( ++ quaternion_inverse( localToWorld ), ++ world ++ ) ), ++ localToWorld ++ ) ); ++} ++ ++inline void matrix4_assign_rotation( Matrix4& matrix, const Matrix4& other ){ ++ matrix[0] = other[0]; ++ matrix[1] = other[1]; ++ matrix[2] = other[2]; ++ matrix[4] = other[4]; ++ matrix[5] = other[5]; ++ matrix[6] = other[6]; ++ matrix[8] = other[8]; ++ matrix[9] = other[9]; ++ matrix[10] = other[10]; ++} ++ ++void matrix4_assign_rotation_for_pivot( Matrix4& matrix, scene::Instance& instance ){ ++ Editable* editable = Node_getEditable( instance.path().top() ); ++ if ( editable != 0 ) { ++ matrix4_assign_rotation( matrix, matrix4_multiplied_by_matrix4( instance.localToWorld(), editable->getLocalPivot() ) ); ++ } ++ else ++ { ++ matrix4_assign_rotation( matrix, instance.localToWorld() ); ++ } ++} ++ ++inline bool Instance_isSelectedComponents( scene::Instance& instance ){ ++ ComponentSelectionTestable* componentSelectionTestable = Instance_getComponentSelectionTestable( instance ); ++ return componentSelectionTestable != 0 ++ && componentSelectionTestable->isSelectedComponents(); ++} ++ ++class TranslateSelected : public SelectionSystem::Visitor ++{ ++const Vector3& m_translate; ++public: ++TranslateSelected( const Vector3& translate ) ++ : m_translate( translate ){ ++} ++void visit( scene::Instance& instance ) const { ++ Transformable* transform = Instance_getTransformable( instance ); ++ if ( transform != 0 ) { ++ transform->setType( TRANSFORM_PRIMITIVE ); ++ transform->setTranslation( m_translate ); ++ } ++} ++}; ++ ++void Scene_Translate_Selected( scene::Graph& graph, const Vector3& translation ){ ++ if ( GlobalSelectionSystem().countSelected() != 0 ) { ++ GlobalSelectionSystem().foreachSelected( TranslateSelected( translation ) ); ++ } ++} ++ ++Vector3 get_local_pivot( const Vector3& world_pivot, const Matrix4& localToWorld ){ ++ return Vector3( ++ matrix4_transformed_point( ++ matrix4_full_inverse( localToWorld ), ++ world_pivot ++ ) ++ ); ++} ++ ++void translation_for_pivoted_matrix_transform( Vector3& parent_translation, const Matrix4& local_transform, const Vector3& world_pivot, const Matrix4& localToWorld, const Matrix4& localToParent ){ ++ // we need a translation inside the parent system to move the origin of this object to the right place ++ ++ // mathematically, it must fulfill: ++ // ++ // local_translation local_transform local_pivot = local_pivot ++ // local_translation = local_pivot - local_transform local_pivot ++ // ++ // or maybe? ++ // local_transform local_translation local_pivot = local_pivot ++ // local_translation local_pivot = local_transform^-1 local_pivot ++ // local_translation + local_pivot = local_transform^-1 local_pivot ++ // local_translation = local_transform^-1 local_pivot - local_pivot ++ ++ Vector3 local_pivot( get_local_pivot( world_pivot, localToWorld ) ); ++ ++ Vector3 local_translation( ++ vector3_subtracted( ++ local_pivot, ++ matrix4_transformed_point( ++ local_transform, ++ local_pivot ++ ) ++ /* ++ matrix4_transformed_point( ++ matrix4_full_inverse(local_transform), ++ local_pivot ++ ), ++ local_pivot ++ */ ++ ) ++ ); ++ ++ translation_local2object( parent_translation, local_translation, localToParent ); ++ ++ /* ++ // verify it! ++ globalOutputStream() << "World pivot is at " << world_pivot << "\n"; ++ globalOutputStream() << "Local pivot is at " << local_pivot << "\n"; ++ globalOutputStream() << "Transformation " << local_transform << " moves it to: " << matrix4_transformed_point(local_transform, local_pivot) << "\n"; ++ globalOutputStream() << "Must move by " << local_translation << " in the local system" << "\n"; ++ globalOutputStream() << "Must move by " << parent_translation << " in the parent system" << "\n"; ++ */ ++} ++ ++void translation_for_pivoted_rotation( Vector3& parent_translation, const Quaternion& local_rotation, const Vector3& world_pivot, const Matrix4& localToWorld, const Matrix4& localToParent ){ ++ translation_for_pivoted_matrix_transform( parent_translation, matrix4_rotation_for_quaternion_quantised( local_rotation ), world_pivot, localToWorld, localToParent ); ++} ++ ++void translation_for_pivoted_scale( Vector3& parent_translation, const Vector3& world_scale, const Vector3& world_pivot, const Matrix4& localToWorld, const Matrix4& localToParent ){ ++ Matrix4 local_transform( ++ matrix4_multiplied_by_matrix4( ++ matrix4_full_inverse( localToWorld ), ++ matrix4_multiplied_by_matrix4( ++ matrix4_scale_for_vec3( world_scale ), ++ localToWorld ++ ) ++ ) ++ ); ++ local_transform.tx() = local_transform.ty() = local_transform.tz() = 0; // cancel translation parts ++ translation_for_pivoted_matrix_transform( parent_translation, local_transform, world_pivot, localToWorld, localToParent ); ++} ++ ++class rotate_selected : public SelectionSystem::Visitor ++{ ++const Quaternion& m_rotate; ++const Vector3& m_world_pivot; ++public: ++rotate_selected( const Quaternion& rotation, const Vector3& world_pivot ) ++ : m_rotate( rotation ), m_world_pivot( world_pivot ){ ++} ++void visit( scene::Instance& instance ) const { ++ TransformNode* transformNode = Node_getTransformNode( instance.path().top() ); ++ if ( transformNode != 0 ) { ++ Transformable* transform = Instance_getTransformable( instance ); ++ if ( transform != 0 ) { ++ transform->setType( TRANSFORM_PRIMITIVE ); ++ transform->setScale( c_scale_identity ); ++ transform->setTranslation( c_translation_identity ); ++ ++ transform->setType( TRANSFORM_PRIMITIVE ); ++ transform->setRotation( m_rotate ); ++ ++ { ++ Editable* editable = Node_getEditable( instance.path().top() ); ++ const Matrix4& localPivot = editable != 0 ? editable->getLocalPivot() : g_matrix4_identity; ++ ++ Vector3 parent_translation; ++ translation_for_pivoted_rotation( ++ parent_translation, ++ m_rotate, ++ m_world_pivot, ++ matrix4_multiplied_by_matrix4( instance.localToWorld(), localPivot ), ++ matrix4_multiplied_by_matrix4( transformNode->localToParent(), localPivot ) ++ ); ++ ++ transform->setTranslation( parent_translation ); ++ } ++ } ++ } ++} ++}; ++ ++void Scene_Rotate_Selected( scene::Graph& graph, const Quaternion& rotation, const Vector3& world_pivot ){ ++ if ( GlobalSelectionSystem().countSelected() != 0 ) { ++ GlobalSelectionSystem().foreachSelected( rotate_selected( rotation, world_pivot ) ); ++ } ++} ++ ++class scale_selected : public SelectionSystem::Visitor ++{ ++const Vector3& m_scale; ++const Vector3& m_world_pivot; ++public: ++scale_selected( const Vector3& scaling, const Vector3& world_pivot ) ++ : m_scale( scaling ), m_world_pivot( world_pivot ){ ++} ++void visit( scene::Instance& instance ) const { ++ TransformNode* transformNode = Node_getTransformNode( instance.path().top() ); ++ if ( transformNode != 0 ) { ++ Transformable* transform = Instance_getTransformable( instance ); ++ if ( transform != 0 ) { ++ transform->setType( TRANSFORM_PRIMITIVE ); ++ transform->setScale( c_scale_identity ); ++ transform->setTranslation( c_translation_identity ); ++ ++ transform->setType( TRANSFORM_PRIMITIVE ); ++ transform->setScale( m_scale ); ++ { ++ Editable* editable = Node_getEditable( instance.path().top() ); ++ const Matrix4& localPivot = editable != 0 ? editable->getLocalPivot() : g_matrix4_identity; ++ ++ Vector3 parent_translation; ++ translation_for_pivoted_scale( ++ parent_translation, ++ m_scale, ++ m_world_pivot, ++ matrix4_multiplied_by_matrix4( instance.localToWorld(), localPivot ), ++ matrix4_multiplied_by_matrix4( transformNode->localToParent(), localPivot ) ++ ); ++ ++ transform->setTranslation( parent_translation ); ++ } ++ } ++ } ++} ++}; ++ ++void Scene_Scale_Selected( scene::Graph& graph, const Vector3& scaling, const Vector3& world_pivot ){ ++ if ( GlobalSelectionSystem().countSelected() != 0 ) { ++ GlobalSelectionSystem().foreachSelected( scale_selected( scaling, world_pivot ) ); ++ } ++} ++ ++ ++class translate_component_selected : public SelectionSystem::Visitor ++{ ++const Vector3& m_translate; ++public: ++translate_component_selected( const Vector3& translate ) ++ : m_translate( translate ){ ++} ++void visit( scene::Instance& instance ) const { ++ Transformable* transform = Instance_getTransformable( instance ); ++ if ( transform != 0 ) { ++ transform->setType( TRANSFORM_COMPONENT ); ++ transform->setTranslation( m_translate ); ++ } ++} ++}; ++ ++void Scene_Translate_Component_Selected( scene::Graph& graph, const Vector3& translation ){ ++ if ( GlobalSelectionSystem().countSelected() != 0 ) { ++ GlobalSelectionSystem().foreachSelectedComponent( translate_component_selected( translation ) ); ++ } ++} ++ ++class rotate_component_selected : public SelectionSystem::Visitor ++{ ++const Quaternion& m_rotate; ++const Vector3& m_world_pivot; ++public: ++rotate_component_selected( const Quaternion& rotation, const Vector3& world_pivot ) ++ : m_rotate( rotation ), m_world_pivot( world_pivot ){ ++} ++void visit( scene::Instance& instance ) const { ++ Transformable* transform = Instance_getTransformable( instance ); ++ if ( transform != 0 ) { ++ Vector3 parent_translation; ++ translation_for_pivoted_rotation( parent_translation, m_rotate, m_world_pivot, instance.localToWorld(), Node_getTransformNode( instance.path().top() )->localToParent() ); ++ ++ transform->setType( TRANSFORM_COMPONENT ); ++ transform->setRotation( m_rotate ); ++ transform->setTranslation( parent_translation ); ++ } ++} ++}; ++ ++void Scene_Rotate_Component_Selected( scene::Graph& graph, const Quaternion& rotation, const Vector3& world_pivot ){ ++ if ( GlobalSelectionSystem().countSelectedComponents() != 0 ) { ++ GlobalSelectionSystem().foreachSelectedComponent( rotate_component_selected( rotation, world_pivot ) ); ++ } ++} ++ ++class scale_component_selected : public SelectionSystem::Visitor ++{ ++const Vector3& m_scale; ++const Vector3& m_world_pivot; ++public: ++scale_component_selected( const Vector3& scaling, const Vector3& world_pivot ) ++ : m_scale( scaling ), m_world_pivot( world_pivot ){ ++} ++void visit( scene::Instance& instance ) const { ++ Transformable* transform = Instance_getTransformable( instance ); ++ if ( transform != 0 ) { ++ Vector3 parent_translation; ++ translation_for_pivoted_scale( parent_translation, m_scale, m_world_pivot, instance.localToWorld(), Node_getTransformNode( instance.path().top() )->localToParent() ); ++ ++ transform->setType( TRANSFORM_COMPONENT ); ++ transform->setScale( m_scale ); ++ transform->setTranslation( parent_translation ); ++ } ++} ++}; ++ ++void Scene_Scale_Component_Selected( scene::Graph& graph, const Vector3& scaling, const Vector3& world_pivot ){ ++ if ( GlobalSelectionSystem().countSelectedComponents() != 0 ) { ++ GlobalSelectionSystem().foreachSelectedComponent( scale_component_selected( scaling, world_pivot ) ); ++ } ++} ++ ++ ++class BooleanSelector : public Selector ++{ ++bool m_selected; ++SelectionIntersection m_intersection; ++Selectable* m_selectable; ++public: ++BooleanSelector() : m_selected( false ){ ++} ++ ++void pushSelectable( Selectable& selectable ){ ++ m_intersection = SelectionIntersection(); ++ m_selectable = &selectable; ++} ++void popSelectable(){ ++ if ( m_intersection.valid() ) { ++ m_selected = true; ++ } ++ m_intersection = SelectionIntersection(); ++} ++void addIntersection( const SelectionIntersection& intersection ){ ++ if ( m_selectable->isSelected() ) { ++ assign_if_closer( m_intersection, intersection ); ++ } ++} ++ ++bool isSelected(){ ++ return m_selected; ++} ++}; ++ ++class BestSelector : public Selector ++{ ++SelectionIntersection m_intersection; ++Selectable* m_selectable; ++SelectionIntersection m_bestIntersection; ++std::list m_bestSelectable; ++public: ++BestSelector() : m_bestIntersection( SelectionIntersection() ), m_bestSelectable( 0 ){ ++} ++ ++void pushSelectable( Selectable& selectable ){ ++ m_intersection = SelectionIntersection(); ++ m_selectable = &selectable; ++} ++void popSelectable(){ ++ if ( m_intersection.equalEpsilon( m_bestIntersection, 0.25f, 0.001f ) ) { ++ 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 ){ ++ assign_if_closer( m_intersection, intersection ); ++} ++ ++std::list& best(){ ++ return m_bestSelectable; ++} ++}; ++ ++class DragManipulator : public Manipulator ++{ ++TranslateFree m_freeResize; ++TranslateFree m_freeDrag; ++ResizeTranslatable m_resize; ++DragTranslatable m_drag; ++SelectableBool m_dragSelectable; ++public: ++ ++bool m_selected; ++ ++DragManipulator() : m_freeResize( m_resize ), m_freeDrag( m_drag ), m_selected( false ){ ++} ++ ++Manipulatable* GetManipulatable(){ ++ return m_dragSelectable.isSelected() ? &m_freeDrag : &m_freeResize; ++} ++ ++void testSelect( const View& view, const Matrix4& pivot2world ){ ++ SelectionPool selector; ++ ++ SelectionVolume test( view ); ++ ++ if ( GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) { ++ BooleanSelector booleanSelector; ++ ++ Scene_TestSelect_Primitive( booleanSelector, test, view ); ++ ++ if ( booleanSelector.isSelected() ) { ++ selector.addSelectable( SelectionIntersection( 0, 0 ), &m_dragSelectable ); ++ m_selected = false; ++ } ++ else ++ { ++ m_selected = Scene_forEachPlaneSelectable_selectPlanes( GlobalSceneGraph(), selector, test ); ++ } ++ } ++ else ++ { ++ BestSelector bestSelector; ++ Scene_TestSelect_Component_Selected( bestSelector, test, view, GlobalSelectionSystem().ComponentMode() ); ++ for ( std::list::iterator i = bestSelector.best().begin(); i != bestSelector.best().end(); ++i ) ++ { ++ if ( !( *i )->isSelected() ) { ++ GlobalSelectionSystem().setSelectedAllComponents( false ); ++ } ++ m_selected = false; ++ selector.addSelectable( SelectionIntersection( 0, 0 ), ( *i ) ); ++ m_dragSelectable.setSelected( true ); ++ } ++ } ++ ++ for ( SelectionPool::iterator i = selector.begin(); i != selector.end(); ++i ) ++ { ++ ( *i ).second->setSelected( true ); ++ } ++} ++ ++void setSelected( bool select ){ ++ m_selected = select; ++ m_dragSelectable.setSelected( select ); ++} ++bool isSelected() const { ++ return m_selected || m_dragSelectable.isSelected(); ++} ++}; ++ ++class ClipManipulator : public Manipulator ++{ ++public: ++ ++Manipulatable* GetManipulatable(){ ++ ERROR_MESSAGE( "clipper is not manipulatable" ); ++ return 0; ++} ++ ++void setSelected( bool select ){ ++} ++bool isSelected() const { ++ return false; ++} ++}; ++ ++class select_all : public scene::Graph::Walker ++{ ++bool m_select; ++public: ++select_all( bool select ) ++ : m_select( select ){ ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( selectable != 0 ) { ++ selectable->setSelected( m_select ); ++ } ++ return true; ++} ++}; ++ ++class select_all_component : public scene::Graph::Walker ++{ ++bool m_select; ++SelectionSystem::EComponentMode m_mode; ++public: ++select_all_component( bool select, SelectionSystem::EComponentMode mode ) ++ : m_select( select ), m_mode( mode ){ ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ ComponentSelectionTestable* componentSelectionTestable = Instance_getComponentSelectionTestable( instance ); ++ if ( componentSelectionTestable ) { ++ componentSelectionTestable->setSelectedComponents( m_select, m_mode ); ++ } ++ return true; ++} ++}; ++ ++void Scene_SelectAll_Component( bool select, SelectionSystem::EComponentMode componentMode ){ ++ GlobalSceneGraph().traverse( select_all_component( select, componentMode ) ); ++} ++ ++ ++// RadiantSelectionSystem ++class RadiantSelectionSystem : ++ public SelectionSystem, ++ public Translatable, ++ public Rotatable, ++ public Scalable, ++ public Renderable ++{ ++mutable Matrix4 m_pivot2world; ++Matrix4 m_pivot2world_start; ++Matrix4 m_manip2pivot_start; ++Translation m_translation; ++Rotation m_rotation; ++Scale m_scale; ++public: ++static Shader* m_state; ++bool m_bPreferPointEntsIn2D; ++private: ++EManipulatorMode m_manipulator_mode; ++Manipulator* m_manipulator; ++ ++// state ++bool m_undo_begun; ++EMode m_mode; ++EComponentMode m_componentmode; ++ ++SelectionCounter m_count_primitive; ++SelectionCounter m_count_component; ++ ++TranslateManipulator m_translate_manipulator; ++RotateManipulator m_rotate_manipulator; ++ScaleManipulator m_scale_manipulator; ++DragManipulator m_drag_manipulator; ++ClipManipulator m_clip_manipulator; ++ ++typedef SelectionList selection_t; ++selection_t m_selection; ++selection_t m_component_selection; ++ ++Signal1 m_selectionChanged_callbacks; ++ ++void ConstructPivot() const; ++void setCustomPivotOrigin( Vector3& point ) const; ++public: ++void getSelectionAABB( AABB& bounds ) const; ++private: ++mutable bool m_pivotChanged; ++bool m_pivot_moving; ++mutable bool m_pivotIsCustom; ++ ++void Scene_TestSelect( Selector& selector, SelectionTest& test, const View& view, SelectionSystem::EMode mode, SelectionSystem::EComponentMode componentMode ); ++ ++bool nothingSelected() const { ++ return ( Mode() == eComponent && m_count_component.empty() ) ++ || ( Mode() == ePrimitive && m_count_primitive.empty() ); ++} ++ ++ ++public: ++enum EModifier ++{ ++ eManipulator, ++ eToggle, ++ eReplace, ++ eCycle, ++ eSelect, ++ eDeselect, ++}; ++ ++RadiantSelectionSystem() : ++ m_bPreferPointEntsIn2D( true ), ++ m_undo_begun( false ), ++ m_mode( ePrimitive ), ++ m_componentmode( eDefault ), ++ m_count_primitive( SelectionChangedCaller( *this ) ), ++ m_count_component( SelectionChangedCaller( *this ) ), ++ m_translate_manipulator( *this, 2, 64 ), ++ m_rotate_manipulator( *this, 8, 64 ), ++ m_scale_manipulator( *this, 0, 64 ), ++ m_pivotChanged( false ), ++ m_pivot_moving( false ), ++ m_pivotIsCustom( false ){ ++ SetManipulatorMode( eTranslate ); ++ pivotChanged(); ++ addSelectionChangeCallback( PivotChangedSelectionCaller( *this ) ); ++ AddGridChangeCallback( PivotChangedCaller( *this ) ); ++} ++void pivotChanged() const { ++ m_pivotChanged = true; ++ SceneChangeNotify(); ++} ++typedef ConstMemberCaller PivotChangedCaller; ++void pivotChangedSelection( const Selectable& selectable ){ ++ pivotChanged(); ++} ++typedef MemberCaller PivotChangedSelectionCaller; ++ ++void SetMode( EMode mode ){ ++ if ( m_mode != mode ) { ++ m_mode = mode; ++ pivotChanged(); ++ } ++} ++EMode Mode() const { ++ return m_mode; ++} ++void SetComponentMode( EComponentMode mode ){ ++ m_componentmode = mode; ++} ++EComponentMode ComponentMode() const { ++ return m_componentmode; ++} ++void SetManipulatorMode( EManipulatorMode mode ){ ++ m_pivotIsCustom = false; ++ m_manipulator_mode = mode; ++ switch ( m_manipulator_mode ) ++ { ++ case eTranslate: m_manipulator = &m_translate_manipulator; break; ++ case eRotate: m_manipulator = &m_rotate_manipulator; break; ++ case eScale: m_manipulator = &m_scale_manipulator; break; ++ case eDrag: m_manipulator = &m_drag_manipulator; break; ++ case eClip: m_manipulator = &m_clip_manipulator; break; ++ } ++ pivotChanged(); ++} ++EManipulatorMode ManipulatorMode() const { ++ return m_manipulator_mode; ++} ++ ++SelectionChangeCallback getObserver( EMode mode ){ ++ if ( mode == ePrimitive ) { ++ return makeCallback( m_count_primitive ); ++ } ++ else ++ { ++ return makeCallback( m_count_component ); ++ } ++} ++std::size_t countSelected() const { ++ return m_count_primitive.size(); ++} ++std::size_t countSelectedComponents() const { ++ return m_count_component.size(); ++} ++void onSelectedChanged( scene::Instance& instance, const Selectable& selectable ){ ++ if ( selectable.isSelected() ) { ++ m_selection.append( instance ); ++ } ++ else ++ { ++ m_selection.erase( instance ); ++ } ++ ++ ASSERT_MESSAGE( m_selection.size() == m_count_primitive.size(), "selection-tracking error" ); ++} ++void onComponentSelection( scene::Instance& instance, const Selectable& selectable ){ ++ if ( selectable.isSelected() ) { ++ m_component_selection.append( instance ); ++ } ++ else ++ { ++ m_component_selection.erase( instance ); ++ } ++ ++ ASSERT_MESSAGE( m_component_selection.size() == m_count_component.size(), "selection-tracking error" ); ++} ++scene::Instance& ultimateSelected() const { ++ ASSERT_MESSAGE( m_selection.size() > 0, "no instance selected" ); ++ return m_selection.back(); ++} ++scene::Instance& penultimateSelected() const { ++ ASSERT_MESSAGE( m_selection.size() > 1, "only one instance selected" ); ++ return *( *( --( --m_selection.end() ) ) ); ++} ++void setSelectedAll( bool selected ){ ++ GlobalSceneGraph().traverse( select_all( selected ) ); ++ ++ m_manipulator->setSelected( selected ); ++} ++void setSelectedAllComponents( bool selected ){ ++ Scene_SelectAll_Component( selected, SelectionSystem::eVertex ); ++ Scene_SelectAll_Component( selected, SelectionSystem::eEdge ); ++ Scene_SelectAll_Component( selected, SelectionSystem::eFace ); ++ ++ m_manipulator->setSelected( selected ); ++} ++ ++void foreachSelected( const Visitor& visitor ) const { ++ selection_t::const_iterator i = m_selection.begin(); ++ while ( i != m_selection.end() ) ++ { ++ visitor.visit( *( *( i++ ) ) ); ++ } ++} ++void foreachSelectedComponent( const Visitor& visitor ) const { ++ selection_t::const_iterator i = m_component_selection.begin(); ++ while ( i != m_component_selection.end() ) ++ { ++ visitor.visit( *( *( i++ ) ) ); ++ } ++} ++ ++void addSelectionChangeCallback( const SelectionChangeHandler& handler ){ ++ m_selectionChanged_callbacks.connectLast( handler ); ++} ++void selectionChanged( const Selectable& selectable ){ ++ m_selectionChanged_callbacks( selectable ); ++} ++typedef MemberCaller SelectionChangedCaller; ++ ++ ++void startMove(){ ++ m_pivot2world_start = GetPivot2World(); ++} ++ ++bool SelectManipulator( const View& view, const float device_point[2], const float device_epsilon[2] ){ ++ if ( !nothingSelected() || ( ManipulatorMode() == eDrag && Mode() == eComponent ) ) { ++#if defined ( DEBUG_SELECTION ) ++ g_render_clipped.destroy(); ++#endif ++ ++ m_manipulator->setSelected( false ); ++ ++ if ( !nothingSelected() || ( ManipulatorMode() == eDrag && Mode() == eComponent ) ) { ++ View scissored( view ); ++ ConstructSelectionTest( scissored, SelectionBoxForPoint( device_point, device_epsilon ) ); ++ m_manipulator->testSelect( scissored, GetPivot2World() ); ++ } ++ ++ startMove(); ++ ++ m_pivot_moving = m_manipulator->isSelected(); ++ ++ if ( m_pivot_moving ) { ++ Pivot2World pivot; ++ pivot.update( GetPivot2World(), view.GetModelview(), view.GetProjection(), view.GetViewport() ); ++ ++ m_manip2pivot_start = matrix4_multiplied_by_matrix4( matrix4_full_inverse( m_pivot2world_start ), pivot.m_worldSpace ); ++ ++ Matrix4 device2manip; ++ ConstructDevice2Manip( device2manip, m_pivot2world_start, view.GetModelview(), view.GetProjection(), view.GetViewport() ); ++ m_manipulator->GetManipulatable()->Construct( device2manip, device_point[0], device_point[1] ); ++ ++ m_undo_begun = false; ++ } ++ ++ SceneChangeNotify(); ++ } ++ ++ return m_pivot_moving; ++} ++ ++void deselectAll(){ ++ if ( Mode() == eComponent ) { ++ setSelectedAllComponents( false ); ++ } ++ else ++ { ++ setSelectedAll( false ); ++ } ++} ++ ++void deselectComponentsOrAll( bool components ){ ++ if ( components ) { ++ setSelectedAllComponents( false ); ++ } ++ else ++ { ++ deselectAll(); ++ } ++} ++ ++void SelectPoint( const View& view, const float device_point[2], const float device_epsilon[2], RadiantSelectionSystem::EModifier modifier, bool face ){ ++ //globalOutputStream() << device_point[0] << " " << device_point[1] << "\n"; ++ ASSERT_MESSAGE( fabs( device_point[0] ) <= 1.0f && fabs( device_point[1] ) <= 1.0f, "point-selection error" ); ++ ++ if ( modifier == eReplace ) { ++ deselectComponentsOrAll( face ); ++ } ++/* ++//nothingSelected() doesn't consider faces, selected in non-component mode, m ++ if ( modifier == eCycle && nothingSelected() ){ ++ modifier = eReplace; ++ } ++*/ ++ #if defined ( DEBUG_SELECTION ) ++ g_render_clipped.destroy(); ++ #endif ++ ++ { ++ View scissored( view ); ++ ConstructSelectionTest( scissored, SelectionBoxForPoint( device_point, device_epsilon ) ); ++ ++ SelectionVolume volume( scissored ); ++ SelectionPool selector; ++ SelectionPool selector_point_ents; ++ const bool prefer_point_ents = m_bPreferPointEntsIn2D && Mode() == ePrimitive && !view.fill() && !face ++ && ( modifier == RadiantSelectionSystem::eReplace || modifier == RadiantSelectionSystem::eSelect || modifier == RadiantSelectionSystem::eDeselect ); ++ ++ if( prefer_point_ents ){ ++ Scene_TestSelect( selector_point_ents, volume, scissored, eEntity, ComponentMode() ); ++ } ++ if( prefer_point_ents && !selector_point_ents.failed() ){ ++ switch ( modifier ) ++ { ++ // if cycle mode not enabled, enable it ++ case RadiantSelectionSystem::eReplace: ++ { ++ // select closest ++ ( *selector_point_ents.begin() ).second->setSelected( true ); ++ } ++ break; ++ case RadiantSelectionSystem::eSelect: ++ { ++ SelectionPool::iterator best = selector_point_ents.begin(); ++ if( !( *best ).second->isSelected() ){ ++ ( *best ).second->setSelected( true ); ++ } ++ SelectionPool::iterator i = best; ++ ++i; ++ while ( i != selector_point_ents.end() ) ++ { ++ if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){ ++ if( !( *i ).second->isSelected() ){ ++ ( *i ).second->setSelected( true ); ++ } ++ } ++ else{ ++ break; ++ } ++ ++i; ++ } ++ } ++ break; ++ case RadiantSelectionSystem::eDeselect: ++ { ++ SelectionPool::iterator best = selector_point_ents.begin(); ++ if( ( *best ).second->isSelected() ){ ++ ( *best ).second->setSelected( false ); ++ } ++ SelectionPool::iterator i = best; ++ ++i; ++ while ( i != selector_point_ents.end() ) ++ { ++ if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){ ++ if( ( *i ).second->isSelected() ){ ++ ( *i ).second->setSelected( false ); ++ } ++ } ++ else{ ++ break; ++ } ++ ++i; ++ } ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ else{ ++ if ( face ){ ++ Scene_TestSelect_Component( selector, volume, scissored, eFace ); ++ } ++ else{ ++ Scene_TestSelect( selector, volume, scissored, Mode(), ComponentMode() ); ++ } ++ ++ if ( !selector.failed() ) { ++ switch ( modifier ) ++ { ++ case RadiantSelectionSystem::eToggle: ++ { ++ SelectableSortedSet::iterator best = selector.begin(); ++ // toggle selection of the object with least depth ++ if ( ( *best ).second->isSelected() ) { ++ ( *best ).second->setSelected( false ); ++ } ++ else{ ++ ( *best ).second->setSelected( true ); ++ } ++ } ++ break; ++ // if cycle mode not enabled, enable it ++ case RadiantSelectionSystem::eReplace: ++ { ++ // select closest ++ ( *selector.begin() ).second->setSelected( true ); ++ } ++ break; ++ // select the next object in the list from the one already selected ++ case RadiantSelectionSystem::eCycle: ++ { ++ bool CycleSelectionOccured = false; ++ SelectionPool::iterator i = selector.begin(); ++ while ( i != selector.end() ) ++ { ++ if ( ( *i ).second->isSelected() ) { ++ deselectComponentsOrAll( face ); ++ ++i; ++ if ( i != selector.end() ) { ++ i->second->setSelected( true ); ++ } ++ else ++ { ++ selector.begin()->second->setSelected( true ); ++ } ++ CycleSelectionOccured = true; ++ break; ++ } ++ ++i; ++ } ++ if( !CycleSelectionOccured ){ ++ deselectComponentsOrAll( face ); ++ ( *selector.begin() ).second->setSelected( true ); ++ } ++ } ++ break; ++ case RadiantSelectionSystem::eSelect: ++ { ++ SelectionPool::iterator best = selector.begin(); ++ if( !( *best ).second->isSelected() ){ ++ ( *best ).second->setSelected( true ); ++ } ++ SelectionPool::iterator i = best; ++ ++i; ++ while ( i != selector.end() ) ++ { ++ if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){ ++ if( !( *i ).second->isSelected() ){ ++ ( *i ).second->setSelected( true ); ++ } ++ } ++ else{ ++ break; ++ } ++ ++i; ++ } ++ } ++ break; ++ case RadiantSelectionSystem::eDeselect: ++ { ++ SelectionPool::iterator best = selector.begin(); ++ if( ( *best ).second->isSelected() ){ ++ ( *best ).second->setSelected( false ); ++ } ++ SelectionPool::iterator i = best; ++ ++i; ++ while ( i != selector.end() ) ++ { ++ if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){ ++ if( ( *i ).second->isSelected() ){ ++ ( *i ).second->setSelected( false ); ++ } ++ } ++ else{ ++ break; ++ } ++ ++i; ++ } ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ else if( modifier == eCycle ){ ++ deselectComponentsOrAll( face ); ++ } ++ } ++ } ++} ++ ++bool SelectPoint_InitPaint( const View& view, const float device_point[2], const float device_epsilon[2], bool face ){ ++ ASSERT_MESSAGE( fabs( device_point[0] ) <= 1.0f && fabs( device_point[1] ) <= 1.0f, "point-selection error" ); ++ #if defined ( DEBUG_SELECTION ) ++ g_render_clipped.destroy(); ++ #endif ++ ++ { ++ View scissored( view ); ++ ConstructSelectionTest( scissored, SelectionBoxForPoint( device_point, device_epsilon ) ); ++ ++ SelectionVolume volume( scissored ); ++ SelectionPool selector; ++ SelectionPool selector_point_ents; ++ const bool prefer_point_ents = m_bPreferPointEntsIn2D && Mode() == ePrimitive && !view.fill() && !face; ++ ++ if( prefer_point_ents ){ ++ Scene_TestSelect( selector_point_ents, volume, scissored, eEntity, ComponentMode() ); ++ } ++ if( prefer_point_ents && !selector_point_ents.failed() ){ ++ SelectableSortedSet::iterator best = selector_point_ents.begin(); ++ const bool wasSelected = ( *best ).second->isSelected(); ++ ( *best ).second->setSelected( !wasSelected ); ++ SelectableSortedSet::iterator i = best; ++ ++i; ++ while ( i != selector_point_ents.end() ) ++ { ++ if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){ ++ ( *i ).second->setSelected( !wasSelected ); ++ } ++ else{ ++ break; ++ } ++ ++i; ++ } ++ return !wasSelected; ++ } ++ else{//do primitives, if ents failed ++ if ( face ){ ++ Scene_TestSelect_Component( selector, volume, scissored, eFace ); ++ } ++ else{ ++ Scene_TestSelect( selector, volume, scissored, Mode(), ComponentMode() ); ++ } ++ if ( !selector.failed() ){ ++ SelectableSortedSet::iterator best = selector.begin(); ++ const bool wasSelected = ( *best ).second->isSelected(); ++ ( *best ).second->setSelected( !wasSelected ); ++ SelectableSortedSet::iterator i = best; ++ ++i; ++ while ( i != selector.end() ) ++ { ++ if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){ ++ ( *i ).second->setSelected( !wasSelected ); ++ } ++ else{ ++ break; ++ } ++ ++i; ++ } ++ return !wasSelected; ++ } ++ else{ ++ return true; ++ } ++ } ++ } ++} ++ ++void SelectArea( const View& view, const float device_point[2], const float device_delta[2], RadiantSelectionSystem::EModifier modifier, bool face ){ ++ if ( modifier == eReplace ) { ++ deselectComponentsOrAll( face ); ++ } ++ ++ #if defined ( DEBUG_SELECTION ) ++ g_render_clipped.destroy(); ++ #endif ++ ++ { ++ View scissored( view ); ++ ConstructSelectionTest( scissored, SelectionBoxForArea( device_point, device_delta ) ); ++ ++ SelectionVolume volume( scissored ); ++ SelectionPool pool; ++ if ( face ) { ++ Scene_TestSelect_Component( pool, volume, scissored, eFace ); ++ } ++ else ++ { ++ Scene_TestSelect( pool, volume, scissored, Mode(), ComponentMode() ); ++ } ++ ++ for ( SelectionPool::iterator i = pool.begin(); i != pool.end(); ++i ) ++ { ++ ( *i ).second->setSelected( !( modifier == RadiantSelectionSystem::eToggle && ( *i ).second->isSelected() ) ); ++ } ++ } ++} ++ ++ ++void translate( const Vector3& translation ){ ++ if ( !nothingSelected() ) { ++ //ASSERT_MESSAGE(!m_pivotChanged, "pivot is invalid"); ++ ++ m_translation = translation; ++ ++ m_pivot2world = m_pivot2world_start; ++ matrix4_translate_by_vec3( m_pivot2world, translation ); ++ ++ if ( Mode() == eComponent ) { ++ Scene_Translate_Component_Selected( GlobalSceneGraph(), m_translation ); ++ } ++ else ++ { ++ Scene_Translate_Selected( GlobalSceneGraph(), m_translation ); ++ } ++ ++ SceneChangeNotify(); ++ } ++} ++void outputTranslation( TextOutputStream& ostream ){ ++ ostream << " -xyz " << m_translation.x() << " " << m_translation.y() << " " << m_translation.z(); ++} ++void rotate( const Quaternion& rotation ){ ++ if ( !nothingSelected() ) { ++ //ASSERT_MESSAGE(!m_pivotChanged, "pivot is invalid"); ++ ++ m_rotation = rotation; ++ ++ if ( Mode() == eComponent ) { ++ Scene_Rotate_Component_Selected( GlobalSceneGraph(), m_rotation, vector4_to_vector3( m_pivot2world.t() ) ); ++ ++ matrix4_assign_rotation_for_pivot( m_pivot2world, m_component_selection.back() ); ++ } ++ else ++ { ++ Scene_Rotate_Selected( GlobalSceneGraph(), m_rotation, vector4_to_vector3( m_pivot2world.t() ) ); ++ ++ matrix4_assign_rotation_for_pivot( m_pivot2world, m_selection.back() ); ++ } ++ ++ SceneChangeNotify(); ++ } ++} ++void outputRotation( TextOutputStream& ostream ){ ++ ostream << " -eulerXYZ " << m_rotation.x() << " " << m_rotation.y() << " " << m_rotation.z(); ++} ++void scale( const Vector3& scaling ){ ++ if ( !nothingSelected() ) { ++ m_scale = scaling; ++ ++ if ( Mode() == eComponent ) { ++ Scene_Scale_Component_Selected( GlobalSceneGraph(), m_scale, vector4_to_vector3( m_pivot2world.t() ) ); ++ } ++ else ++ { ++ Scene_Scale_Selected( GlobalSceneGraph(), m_scale, vector4_to_vector3( m_pivot2world.t() ) ); ++ } ++ ++ SceneChangeNotify(); ++ } ++} ++void outputScale( TextOutputStream& ostream ){ ++ ostream << " -scale " << m_scale.x() << " " << m_scale.y() << " " << m_scale.z(); ++} ++ ++void rotateSelected( const Quaternion& rotation, bool snapOrigin ){ ++ if( snapOrigin && !m_pivotIsCustom ){ ++ m_pivot2world.tx() = float_snapped( m_pivot2world.tx(), GetSnapGridSize() ); ++ m_pivot2world.ty() = float_snapped( m_pivot2world.ty(), GetSnapGridSize() ); ++ m_pivot2world.tz() = float_snapped( m_pivot2world.tz(), GetSnapGridSize() ); ++ } ++ startMove(); ++ rotate( rotation ); ++ freezeTransforms(); ++} ++void translateSelected( const Vector3& translation ){ ++ startMove(); ++ translate( translation ); ++ freezeTransforms(); ++} ++void scaleSelected( const Vector3& scaling ){ ++ startMove(); ++ scale( scaling ); ++ freezeTransforms(); ++} ++ ++void MoveSelected( const View& view, const float device_point[2], bool snap ){ ++ if ( m_manipulator->isSelected() ) { ++ if ( !m_undo_begun ) { ++ m_undo_begun = true; ++ GlobalUndoSystem().start(); ++ } ++ ++ Matrix4 device2manip; ++ ConstructDevice2Manip( device2manip, m_pivot2world_start, view.GetModelview(), view.GetProjection(), view.GetViewport() ); ++ m_manipulator->GetManipulatable()->Transform( m_manip2pivot_start, device2manip, device_point[0], device_point[1], snap ); ++ } ++} ++ ++/// \todo Support view-dependent nudge. ++void NudgeManipulator( const Vector3& nudge, const Vector3& view ){ ++ if ( ManipulatorMode() == eTranslate || ManipulatorMode() == eDrag ) { ++ translateSelected( nudge ); ++ } ++} ++ ++void endMove(); ++void freezeTransforms(); ++ ++void renderSolid( Renderer& renderer, const VolumeTest& volume ) const; ++void renderWireframe( Renderer& renderer, const VolumeTest& volume ) const { ++ renderSolid( renderer, volume ); ++} ++ ++const Matrix4& GetPivot2World() const { ++ ConstructPivot(); ++ return m_pivot2world; ++} ++ ++static void constructStatic(){ ++ m_state = GlobalShaderCache().capture( "$POINT" ); ++ #if defined( DEBUG_SELECTION ) ++ g_state_clipped = GlobalShaderCache().capture( "$DEBUG_CLIPPED" ); ++ #endif ++ TranslateManipulator::m_state_wire = GlobalShaderCache().capture( "$WIRE_OVERLAY" ); ++ TranslateManipulator::m_state_fill = GlobalShaderCache().capture( "$FLATSHADE_OVERLAY" ); ++ RotateManipulator::m_state_outer = GlobalShaderCache().capture( "$WIRE_OVERLAY" ); ++} ++ ++static void destroyStatic(){ ++ #if defined( DEBUG_SELECTION ) ++ GlobalShaderCache().release( "$DEBUG_CLIPPED" ); ++ #endif ++ GlobalShaderCache().release( "$WIRE_OVERLAY" ); ++ GlobalShaderCache().release( "$FLATSHADE_OVERLAY" ); ++ GlobalShaderCache().release( "$WIRE_OVERLAY" ); ++ GlobalShaderCache().release( "$POINT" ); ++} ++}; ++ ++Shader* RadiantSelectionSystem::m_state = 0; ++ ++ ++namespace ++{ ++RadiantSelectionSystem* g_RadiantSelectionSystem; ++ ++inline RadiantSelectionSystem& getSelectionSystem(){ ++ return *g_RadiantSelectionSystem; ++} ++} ++ ++#include "map.h" ++ ++class testselect_entity_visible : public scene::Graph::Walker ++{ ++Selector& m_selector; ++SelectionTest& m_test; ++public: ++testselect_entity_visible( Selector& selector, SelectionTest& test ) ++ : m_selector( selector ), m_test( test ){ ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ if( path.top().get_pointer() == Map_GetWorldspawn( g_map ) || ++ node_is_group( path.top().get() ) ){ ++ return false; ++ } ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( selectable != 0 ++ && Node_isEntity( path.top() ) ) { ++ m_selector.pushSelectable( *selectable ); ++ } ++ ++ SelectionTestable* selectionTestable = Instance_getSelectionTestable( instance ); ++ if ( selectionTestable ) { ++ selectionTestable->testSelect( m_selector, m_test ); ++ } ++ ++ return true; ++} ++void post( const scene::Path& path, scene::Instance& instance ) const { ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( selectable != 0 ++ && Node_isEntity( path.top() ) ) { ++ m_selector.popSelectable(); ++ } ++} ++}; ++ ++class testselect_primitive_visible : public scene::Graph::Walker ++{ ++Selector& m_selector; ++SelectionTest& m_test; ++public: ++testselect_primitive_visible( Selector& selector, SelectionTest& test ) ++ : m_selector( selector ), m_test( test ){ ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( selectable != 0 ) { ++ m_selector.pushSelectable( *selectable ); ++ } ++ ++ SelectionTestable* selectionTestable = Instance_getSelectionTestable( instance ); ++ if ( selectionTestable ) { ++ selectionTestable->testSelect( m_selector, m_test ); ++ } ++ ++ return true; ++} ++void post( const scene::Path& path, scene::Instance& instance ) const { ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( selectable != 0 ) { ++ m_selector.popSelectable(); ++ } ++} ++}; ++ ++class testselect_component_visible : public scene::Graph::Walker ++{ ++Selector& m_selector; ++SelectionTest& m_test; ++SelectionSystem::EComponentMode m_mode; ++public: ++testselect_component_visible( Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode ) ++ : m_selector( selector ), m_test( test ), m_mode( mode ){ ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ ComponentSelectionTestable* componentSelectionTestable = Instance_getComponentSelectionTestable( instance ); ++ if ( componentSelectionTestable ) { ++ componentSelectionTestable->testSelectComponents( m_selector, m_test, m_mode ); ++ } ++ ++ return true; ++} ++}; ++ ++ ++class testselect_component_visible_selected : public scene::Graph::Walker ++{ ++Selector& m_selector; ++SelectionTest& m_test; ++SelectionSystem::EComponentMode m_mode; ++public: ++testselect_component_visible_selected( Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode ) ++ : m_selector( selector ), m_test( test ), m_mode( mode ){ ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( selectable != 0 && selectable->isSelected() ) { ++ ComponentSelectionTestable* componentSelectionTestable = Instance_getComponentSelectionTestable( instance ); ++ if ( componentSelectionTestable ) { ++ componentSelectionTestable->testSelectComponents( m_selector, m_test, m_mode ); ++ } ++ } ++ ++ return true; ++} ++}; ++ ++void Scene_TestSelect_Primitive( Selector& selector, SelectionTest& test, const VolumeTest& volume ){ ++ Scene_forEachVisible( GlobalSceneGraph(), volume, testselect_primitive_visible( selector, test ) ); ++} ++ ++void Scene_TestSelect_Component_Selected( Selector& selector, SelectionTest& test, const VolumeTest& volume, SelectionSystem::EComponentMode componentMode ){ ++ Scene_forEachVisible( GlobalSceneGraph(), volume, testselect_component_visible_selected( selector, test, componentMode ) ); ++} ++ ++void Scene_TestSelect_Component( Selector& selector, SelectionTest& test, const VolumeTest& volume, SelectionSystem::EComponentMode componentMode ){ ++ Scene_forEachVisible( GlobalSceneGraph(), volume, testselect_component_visible( selector, test, componentMode ) ); ++} ++ ++void RadiantSelectionSystem::Scene_TestSelect( Selector& selector, SelectionTest& test, const View& view, SelectionSystem::EMode mode, SelectionSystem::EComponentMode componentMode ){ ++ switch ( mode ) ++ { ++ case eEntity: ++ { ++ Scene_forEachVisible( GlobalSceneGraph(), view, testselect_entity_visible( selector, test ) ); ++ } ++ break; ++ case ePrimitive: ++ Scene_TestSelect_Primitive( selector, test, view ); ++ break; ++ case eComponent: ++ Scene_TestSelect_Component_Selected( selector, test, view, componentMode ); ++ break; ++ } ++} ++ ++class FreezeTransforms : public scene::Graph::Walker ++{ ++public: ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ TransformNode* transformNode = Node_getTransformNode( path.top() ); ++ if ( transformNode != 0 ) { ++ Transformable* transform = Instance_getTransformable( instance ); ++ if ( transform != 0 ) { ++ transform->freezeTransform(); ++ } ++ } ++ return true; ++} ++}; ++ ++void RadiantSelectionSystem::freezeTransforms(){ ++ GlobalSceneGraph().traverse( FreezeTransforms() ); ++} ++ ++ ++void RadiantSelectionSystem::endMove(){ ++ freezeTransforms(); ++ ++ if ( Mode() == ePrimitive ) { ++ if ( ManipulatorMode() == eDrag ) { ++ Scene_SelectAll_Component( false, SelectionSystem::eFace ); ++ } ++ } ++ ++ m_pivot_moving = false; ++ pivotChanged(); ++ ++ SceneChangeNotify(); ++ ++ if ( m_undo_begun ) { ++ StringOutputStream command; ++ ++ if ( ManipulatorMode() == eTranslate ) { ++ command << "translateTool"; ++ outputTranslation( command ); ++ } ++ else if ( ManipulatorMode() == eRotate ) { ++ command << "rotateTool"; ++ outputRotation( command ); ++ } ++ else if ( ManipulatorMode() == eScale ) { ++ command << "scaleTool"; ++ outputScale( command ); ++ } ++ else if ( ManipulatorMode() == eDrag ) { ++ command << "dragTool"; ++ } ++ ++ GlobalUndoSystem().finish( command.c_str() ); ++ } ++ ++} ++ ++inline AABB Instance_getPivotBounds( scene::Instance& instance ){ ++ Entity* entity = Node_getEntity( instance.path().top() ); ++ if ( entity != 0 ++ && ( entity->getEntityClass().fixedsize ++ || !node_is_group( instance.path().top() ) ) ) { ++ Editable* editable = Node_getEditable( instance.path().top() ); ++ if ( editable != 0 ) { ++ return AABB( vector4_to_vector3( matrix4_multiplied_by_matrix4( instance.localToWorld(), editable->getLocalPivot() ).t() ), Vector3( 0, 0, 0 ) ); ++ } ++ else ++ { ++ return AABB( vector4_to_vector3( instance.localToWorld().t() ), Vector3( 0, 0, 0 ) ); ++ } ++ } ++ ++ return instance.worldAABB(); ++} ++ ++class bounds_selected : public scene::Graph::Walker ++{ ++AABB& m_bounds; ++public: ++bounds_selected( AABB& bounds ) ++ : m_bounds( bounds ){ ++ m_bounds = AABB(); ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( selectable != 0 ++ && selectable->isSelected() ) { ++ aabb_extend_by_aabb_safe( m_bounds, Instance_getPivotBounds( instance ) ); ++ } ++ return true; ++} ++}; ++ ++class bounds_selected_component : public scene::Graph::Walker ++{ ++AABB& m_bounds; ++public: ++bounds_selected_component( AABB& bounds ) ++ : m_bounds( bounds ){ ++ m_bounds = AABB(); ++} ++bool pre( const scene::Path& path, scene::Instance& instance ) const { ++ Selectable* selectable = Instance_getSelectable( instance ); ++ if ( selectable != 0 ++ && selectable->isSelected() ) { ++ ComponentEditable* componentEditable = Instance_getComponentEditable( instance ); ++ if ( componentEditable ) { ++ aabb_extend_by_aabb_safe( m_bounds, aabb_for_oriented_aabb_safe( componentEditable->getSelectedComponentsBounds(), instance.localToWorld() ) ); ++ } ++ } ++ return true; ++} ++}; ++ ++void Scene_BoundsSelected( scene::Graph& graph, AABB& bounds ){ ++ graph.traverse( bounds_selected( bounds ) ); ++} ++ ++void Scene_BoundsSelectedComponent( scene::Graph& graph, AABB& bounds ){ ++ graph.traverse( bounds_selected_component( bounds ) ); ++} ++ ++#if 0 ++inline void pivot_for_node( Matrix4& pivot, scene::Node& node, scene::Instance& instance ){ ++ ComponentEditable* componentEditable = Instance_getComponentEditable( instance ); ++ if ( GlobalSelectionSystem().Mode() == SelectionSystem::eComponent ++ && componentEditable != 0 ) { ++ pivot = matrix4_translation_for_vec3( componentEditable->getSelectedComponentsBounds().origin ); ++ } ++ else ++ { ++ Bounded* bounded = Instance_getBounded( instance ); ++ if ( bounded != 0 ) { ++ pivot = matrix4_translation_for_vec3( bounded->localAABB().origin ); ++ } ++ else ++ { ++ pivot = g_matrix4_identity; ++ } ++ } ++} ++#endif ++ ++void RadiantSelectionSystem::ConstructPivot() const { ++ if ( !m_pivotChanged || m_pivot_moving || m_pivotIsCustom ) { ++ return; ++ } ++ m_pivotChanged = false; ++ ++ Vector3 m_object_pivot; ++ ++ if ( !nothingSelected() ) { ++ { ++ AABB bounds; ++ if ( Mode() == eComponent ) { ++ Scene_BoundsSelectedComponent( GlobalSceneGraph(), bounds ); ++ } ++ else ++ { ++ Scene_BoundsSelected( GlobalSceneGraph(), bounds ); ++ } ++ m_object_pivot = bounds.origin; ++ } ++ ++ //vector3_snap( m_object_pivot, GetSnapGridSize() ); ++ //globalOutputStream() << m_object_pivot << "\n"; ++ m_pivot2world = matrix4_translation_for_vec3( m_object_pivot ); ++ ++ switch ( m_manipulator_mode ) ++ { ++ case eTranslate: ++ break; ++ case eRotate: ++ if ( Mode() == eComponent ) { ++ matrix4_assign_rotation_for_pivot( m_pivot2world, m_component_selection.back() ); ++ } ++ else ++ { ++ matrix4_assign_rotation_for_pivot( m_pivot2world, m_selection.back() ); ++ } ++ break; ++ case eScale: ++ if ( Mode() == eComponent ) { ++ matrix4_assign_rotation_for_pivot( m_pivot2world, m_component_selection.back() ); ++ } ++ else ++ { ++ matrix4_assign_rotation_for_pivot( m_pivot2world, m_selection.back() ); ++ } ++ break; ++ default: ++ break; ++ } ++ } ++} ++ ++void RadiantSelectionSystem::setCustomPivotOrigin( Vector3& point ) const { ++ if ( !nothingSelected() && ( m_manipulator_mode == eTranslate || m_manipulator_mode == eRotate || m_manipulator_mode == eScale ) ) { ++ AABB bounds; ++ if ( Mode() == eComponent ) { ++ Scene_BoundsSelectedComponent( GlobalSceneGraph(), bounds ); ++ } ++ else ++ { ++ Scene_BoundsSelected( GlobalSceneGraph(), bounds ); ++ } ++ //globalOutputStream() << point << "\n"; ++ for( std::size_t i = 0; i < 3; i++ ){ ++ if( point[i] < 900000.0f ){ ++ float bestsnapDist = fabs( bounds.origin[i] - point[i] ); ++ float bestsnapTo = bounds.origin[i]; ++ float othersnapDist = fabs( bounds.origin[i] + bounds.extents[i] - point[i] ); ++ if( othersnapDist < bestsnapDist ){ ++ bestsnapDist = othersnapDist; ++ bestsnapTo = bounds.origin[i] + bounds.extents[i]; ++ } ++ othersnapDist = fabs( bounds.origin[i] - bounds.extents[i] - point[i] ); ++ if( othersnapDist < bestsnapDist ){ ++ bestsnapDist = othersnapDist; ++ bestsnapTo = bounds.origin[i] - bounds.extents[i]; ++ } ++ othersnapDist = fabs( float_snapped( point[i], GetSnapGridSize() ) - point[i] ); ++ if( othersnapDist < bestsnapDist ){ ++ bestsnapDist = othersnapDist; ++ bestsnapTo = float_snapped( point[i], GetSnapGridSize() ); ++ } ++ point[i] = bestsnapTo; ++ ++ m_pivot2world[i + 12] = point[i]; //m_pivot2world.tx() .ty() .tz() ++ } ++ } ++ ++ switch ( m_manipulator_mode ) ++ { ++ case eTranslate: ++ break; ++ case eRotate: ++ if ( Mode() == eComponent ) { ++ matrix4_assign_rotation_for_pivot( m_pivot2world, m_component_selection.back() ); ++ } ++ else ++ { ++ matrix4_assign_rotation_for_pivot( m_pivot2world, m_selection.back() ); ++ } ++ break; ++ case eScale: ++ if ( Mode() == eComponent ) { ++ matrix4_assign_rotation_for_pivot( m_pivot2world, m_component_selection.back() ); ++ } ++ else ++ { ++ matrix4_assign_rotation_for_pivot( m_pivot2world, m_selection.back() ); ++ } ++ break; ++ default: ++ break; ++ } ++ ++ m_pivotIsCustom = true; ++ } ++} ++ ++void RadiantSelectionSystem::getSelectionAABB( AABB& bounds ) const { ++ if ( !nothingSelected() ) { ++ if ( Mode() == eComponent ) { ++ Scene_BoundsSelectedComponent( GlobalSceneGraph(), bounds ); ++ } ++ else ++ { ++ Scene_BoundsSelected( GlobalSceneGraph(), bounds ); ++ } ++ } ++} ++ ++void GetSelectionAABB( AABB& bounds ){ ++ getSelectionSystem().getSelectionAABB( bounds ); ++} ++ ++const Matrix4& SelectionSystem_GetPivot2World(){ ++ return getSelectionSystem().GetPivot2World(); ++} ++ ++void RadiantSelectionSystem::renderSolid( Renderer& renderer, const VolumeTest& volume ) const { ++ //if(view->TestPoint(m_object_pivot)) ++ if ( !nothingSelected() ) { ++ renderer.Highlight( Renderer::ePrimitive, false ); ++ renderer.Highlight( Renderer::eFace, false ); ++ ++ renderer.SetState( m_state, Renderer::eWireframeOnly ); ++ renderer.SetState( m_state, Renderer::eFullMaterials ); ++ ++ m_manipulator->render( renderer, volume, GetPivot2World() ); ++ } ++ ++#if defined( DEBUG_SELECTION ) ++ renderer.SetState( g_state_clipped, Renderer::eWireframeOnly ); ++ renderer.SetState( g_state_clipped, Renderer::eFullMaterials ); ++ renderer.addRenderable( g_render_clipped, g_render_clipped.m_world ); ++#endif ++} ++ ++#include "preferencesystem.h" ++#include "preferences.h" ++ ++void SelectionSystem_constructPreferences( PreferencesPage& page ){ ++ page.appendCheckBox( "", "Prefer point entities in 2D", getSelectionSystem().m_bPreferPointEntsIn2D ); ++} ++void SelectionSystem_constructPage( PreferenceGroup& group ){ ++ PreferencesPage page( group.createPage( "Selection", "Selection System Settings" ) ); ++ SelectionSystem_constructPreferences( page ); ++} ++void SelectionSystem_registerPreferencesPage(){ ++ PreferencesDialog_addSettingsPage( FreeCaller() ); ++} ++ ++ ++ ++void SelectionSystem_OnBoundsChanged(){ ++ getSelectionSystem().pivotChanged(); ++} ++ ++SignalHandlerId SelectionSystem_boundsChanged; ++ ++void SelectionSystem_Construct(){ ++ RadiantSelectionSystem::constructStatic(); ++ ++ g_RadiantSelectionSystem = new RadiantSelectionSystem; ++ ++ SelectionSystem_boundsChanged = GlobalSceneGraph().addBoundsChangedCallback( FreeCaller() ); ++ ++ GlobalShaderCache().attachRenderable( getSelectionSystem() ); ++ ++ GlobalPreferenceSystem().registerPreference( "PreferPointEntsIn2D", make_property_string( getSelectionSystem().m_bPreferPointEntsIn2D ) ); ++ SelectionSystem_registerPreferencesPage(); ++} ++ ++void SelectionSystem_Destroy(){ ++ GlobalShaderCache().detachRenderable( getSelectionSystem() ); ++ ++ GlobalSceneGraph().removeBoundsChangedCallback( SelectionSystem_boundsChanged ); ++ ++ delete g_RadiantSelectionSystem; ++ ++ RadiantSelectionSystem::destroyStatic(); ++} ++ ++ ++ ++ ++inline float screen_normalised( float pos, std::size_t size ){ ++ return ( ( 2.0f * pos ) / size ) - 1.0f; ++} ++ ++typedef Vector2 DeviceVector; ++ ++inline DeviceVector window_to_normalised_device( WindowVector window, std::size_t width, std::size_t height ){ ++ return DeviceVector( screen_normalised( window.x(), width ), screen_normalised( height - 1 - window.y(), height ) ); ++} ++ ++inline float device_constrained( float pos ){ ++ return std::min( 1.0f, std::max( -1.0f, pos ) ); ++} ++ ++inline DeviceVector device_constrained( DeviceVector device ){ ++ return DeviceVector( device_constrained( device.x() ), device_constrained( device.y() ) ); ++} ++ ++inline float window_constrained( float pos, std::size_t origin, std::size_t size ){ ++ return std::min( static_cast( origin + size ), std::max( static_cast( origin ), pos ) ); ++} ++ ++inline WindowVector window_constrained( WindowVector window, std::size_t x, std::size_t y, std::size_t width, std::size_t height ){ ++ return WindowVector( window_constrained( window.x(), x, width ), window_constrained( window.y(), y, height ) ); ++} ++ ++typedef Callback MouseEventCallback; ++ ++Single g_mouseMovedCallback; ++Single g_mouseUpCallback; ++ ++#if 1 ++const ButtonIdentifier c_button_select = c_buttonLeft; ++const ButtonIdentifier c_button_select2 = c_buttonRight; ++const ModifierFlags c_modifier_manipulator = c_modifierNone; ++const ModifierFlags c_modifier_toggle = c_modifierShift; ++const ModifierFlags c_modifier_replace = c_modifierShift | c_modifierAlt; ++const ModifierFlags c_modifier_face = c_modifierControl; ++#else ++const ButtonIdentifier c_button_select = c_buttonLeft; ++const ModifierFlags c_modifier_manipulator = c_modifierNone; ++const ModifierFlags c_modifier_toggle = c_modifierControl; ++const ModifierFlags c_modifier_replace = c_modifierNone; ++const ModifierFlags c_modifier_face = c_modifierShift; ++#endif ++const ModifierFlags c_modifier_toggle_face = c_modifier_toggle | c_modifier_face; ++const ModifierFlags c_modifier_replace_face = c_modifier_replace | c_modifier_face; ++ ++const ButtonIdentifier c_button_texture = c_buttonMiddle; ++const ModifierFlags c_modifier_apply_texture1 = c_modifierControl | c_modifierShift; ++const ModifierFlags c_modifier_apply_texture2 = c_modifierControl; ++const ModifierFlags c_modifier_apply_texture3 = c_modifierShift; ++const ModifierFlags c_modifier_copy_texture = c_modifierNone; ++ ++class Selector_ ++{ ++RadiantSelectionSystem::EModifier modifier_for_state( ModifierFlags state ){ ++ if ( ( state == c_modifier_toggle || state == c_modifier_toggle_face || state == c_modifier_face ) ) { ++ if( m_mouse2 ){ ++ return RadiantSelectionSystem::eReplace; ++ } ++ else{ ++ return RadiantSelectionSystem::eToggle; ++ } ++ } ++ return RadiantSelectionSystem::eManipulator; ++} ++ ++rect_t getDeviceArea() const { ++ DeviceVector delta( m_current - m_start ); ++ if ( selecting() && fabs( delta.x() ) > m_epsilon.x() && fabs( delta.y() ) > m_epsilon.y() ) { ++ return SelectionBoxForArea( &m_start[0], &delta[0] ); ++ } ++ else ++ { ++ rect_t default_area = { { 0, 0, }, { 0, 0, }, }; ++ return default_area; ++ } ++} ++ ++public: ++DeviceVector m_start; ++DeviceVector m_current; ++DeviceVector m_epsilon; ++ModifierFlags m_state; ++bool m_mouse2; ++bool m_mouseMoved; ++bool m_mouseMovedWhilePressed; ++bool m_paintSelect; ++const View* m_view; ++RectangleCallback m_window_update; ++ ++Selector_() : m_start( 0.0f, 0.0f ), m_current( 0.0f, 0.0f ), m_state( c_modifierNone ), m_mouse2( false ), m_mouseMoved( false ), m_mouseMovedWhilePressed( false ){ ++} ++ ++void draw_area(){ ++ m_window_update( getDeviceArea() ); ++} ++ ++void testSelect( DeviceVector position ){ ++ RadiantSelectionSystem::EModifier modifier = modifier_for_state( m_state ); ++ if ( modifier != RadiantSelectionSystem::eManipulator ) { ++ DeviceVector delta( position - m_start ); ++ if ( fabs( delta.x() ) > m_epsilon.x() && fabs( delta.y() ) > m_epsilon.y() ) { ++ DeviceVector delta( position - m_start ); ++ //getSelectionSystem().SelectArea( *m_view, &m_start[0], &delta[0], modifier, ( m_state & c_modifier_face ) != c_modifierNone ); ++ getSelectionSystem().SelectArea( *m_view, &m_start[0], &delta[0], RadiantSelectionSystem::eToggle, ( m_state & c_modifier_face ) != c_modifierNone ); ++ } ++ else if( !m_mouseMovedWhilePressed ){ ++ if ( modifier == RadiantSelectionSystem::eReplace && !m_mouseMoved ) { ++ modifier = RadiantSelectionSystem::eCycle; ++ } ++ getSelectionSystem().SelectPoint( *m_view, &position[0], &m_epsilon[0], modifier, ( m_state & c_modifier_face ) != c_modifierNone ); ++ } ++ } ++ ++ m_start = m_current = DeviceVector( 0.0f, 0.0f ); ++ draw_area(); ++} ++ ++void testSelect_simpleM1( DeviceVector position ){ ++ /*RadiantSelectionSystem::EModifier modifier = RadiantSelectionSystem::eReplace; ++ DeviceVector delta( position - m_start ); ++ if ( fabs( delta.x() ) < m_epsilon.x() && fabs( delta.y() ) < m_epsilon.y() ) { ++ modifier = RadiantSelectionSystem::eCycle; ++ } ++ getSelectionSystem().SelectPoint( *m_view, &position[0], &m_epsilon[0], modifier, false );*/ ++ getSelectionSystem().SelectPoint( *m_view, &position[0], &m_epsilon[0], m_mouseMoved ? RadiantSelectionSystem::eReplace : RadiantSelectionSystem::eCycle, false ); ++ m_start = m_current = device_constrained( position ); ++} ++ ++ ++bool selecting() const { ++ return m_state != c_modifier_manipulator && m_mouse2; ++} ++ ++void setState( ModifierFlags state ){ ++ bool was_selecting = selecting(); ++ m_state = state; ++ if ( was_selecting ^ selecting() ) { ++ draw_area(); ++ } ++} ++ ++ModifierFlags getState() const { ++ return m_state; ++} ++ ++void modifierEnable( ModifierFlags type ){ ++ setState( bitfield_enable( getState(), type ) ); ++} ++void modifierDisable( ModifierFlags type ){ ++ setState( bitfield_disable( getState(), type ) ); ++} ++ ++void mouseDown( DeviceVector position ){ ++ m_start = m_current = device_constrained( position ); ++ if( !m_mouse2 && m_state != c_modifierNone ){ ++ m_paintSelect = getSelectionSystem().SelectPoint_InitPaint( *m_view, &position[0], &m_epsilon[0], ( m_state & c_modifier_face ) != c_modifierNone ); ++ } ++} ++ ++void mouseMoved( DeviceVector position ){ ++ m_current = device_constrained( position ); ++ m_mouseMovedWhilePressed = true; ++ if( m_mouse2 ){ ++ draw_area(); ++ } ++ else if( m_state != c_modifier_manipulator ){ ++ getSelectionSystem().SelectPoint( *m_view, &m_current[0], &m_epsilon[0], ++ m_paintSelect ? RadiantSelectionSystem::eSelect : RadiantSelectionSystem::eDeselect, ++ ( m_state & c_modifier_face ) != c_modifierNone ); ++ } ++} ++typedef MemberCaller MouseMovedCaller; ++ ++void mouseUp( DeviceVector position ){ ++ if( m_mouse2 ){ ++ testSelect( device_constrained( position ) ); ++ } ++ else{ ++ m_start = m_current = DeviceVector( 0.0f, 0.0f ); ++ } ++ ++ g_mouseMovedCallback.clear(); ++ g_mouseUpCallback.clear(); ++} ++typedef MemberCaller MouseUpCaller; ++}; ++ ++ ++class Manipulator_ ++{ ++public: ++DeviceVector m_epsilon; ++const View* m_view; ++ModifierFlags m_state; ++ ++Manipulator_() : m_state( c_modifierNone ){ ++} ++ ++bool mouseDown( DeviceVector position ){ ++ return getSelectionSystem().SelectManipulator( *m_view, &position[0], &m_epsilon[0] ); ++} ++ ++void mouseMoved( DeviceVector position ){ ++ getSelectionSystem().MoveSelected( *m_view, &position[0], ( m_state & c_modifierShift ) == c_modifierShift ); ++} ++typedef MemberCaller MouseMovedCaller; ++ ++void mouseUp( DeviceVector position ){ ++ getSelectionSystem().endMove(); ++ g_mouseMovedCallback.clear(); ++ g_mouseUpCallback.clear(); ++} ++<<<<<<< HEAD ++typedef MemberCaller MouseUpCaller; ++======= ++typedef MemberCaller1 MouseUpCaller; ++ ++void setState( ModifierFlags state ){ ++ m_state = state; ++} ++ ++ModifierFlags getState() const { ++ return m_state; ++} ++ ++void modifierEnable( ModifierFlags type ){ ++ setState( bitfield_enable( getState(), type ) ); ++} ++void modifierDisable( ModifierFlags type ){ ++ setState( bitfield_disable( getState(), type ) ); ++} ++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84 ++}; ++ ++void Scene_copyClosestTexture( SelectionTest& test ); ++void Scene_applyClosestTexture( SelectionTest& test ); ++ ++class RadiantWindowObserver : public SelectionSystemWindowObserver ++{ ++enum ++{ ++ SELECT_EPSILON = 8, ++}; ++ ++int m_width; ++int m_height; ++ ++bool m_mouse_down; ++ ++public: ++Selector_ m_selector; ++Manipulator_ m_manipulator; ++ ++RadiantWindowObserver() : m_mouse_down( false ){ ++} ++void release(){ ++ delete this; ++} ++void setView( const View& view ){ ++ m_selector.m_view = &view; ++ m_manipulator.m_view = &view; ++} ++void setRectangleDrawCallback( const RectangleCallback& callback ){ ++ m_selector.m_window_update = callback; ++} ++void onSizeChanged( int width, int height ){ ++ m_width = width; ++ m_height = height; ++ DeviceVector epsilon( SELECT_EPSILON / static_cast( m_width ), SELECT_EPSILON / static_cast( m_height ) ); ++ m_selector.m_epsilon = m_manipulator.m_epsilon = epsilon; ++} ++void onMouseDown( const WindowVector& position, ButtonIdentifier button, ModifierFlags modifiers ){ ++ if ( button == c_button_select || ( button == c_button_select2 && modifiers != c_modifierNone ) ) { ++ m_mouse_down = true; ++ //m_selector.m_mouseMoved = false; ++ ++ DeviceVector devicePosition( window_to_normalised_device( position, m_width, m_height ) ); ++ if ( modifiers == c_modifier_manipulator && m_manipulator.mouseDown( devicePosition ) ) { ++ g_mouseMovedCallback.insert( MouseEventCallback( Manipulator_::MouseMovedCaller( m_manipulator ) ) ); ++ g_mouseUpCallback.insert( MouseEventCallback( Manipulator_::MouseUpCaller( m_manipulator ) ) ); ++ } ++ else ++ { ++ if ( button == c_button_select ) { ++ m_selector.m_mouse2 = false; ++ } ++ else{ ++ m_selector.m_mouse2 = true; ++ } ++ m_selector.mouseDown( devicePosition ); ++ g_mouseMovedCallback.insert( MouseEventCallback( Selector_::MouseMovedCaller( m_selector ) ) ); ++ g_mouseUpCallback.insert( MouseEventCallback( Selector_::MouseUpCaller( m_selector ) ) ); ++ } ++ } ++ else if ( button == c_button_texture ) { ++ DeviceVector devicePosition( device_constrained( window_to_normalised_device( position, m_width, m_height ) ) ); ++ ++ View scissored( *m_selector.m_view ); ++ ConstructSelectionTest( scissored, SelectionBoxForPoint( &devicePosition[0], &m_selector.m_epsilon[0] ) ); ++ SelectionVolume volume( scissored ); ++ ++ if ( modifiers == c_modifier_apply_texture1 || modifiers == c_modifier_apply_texture2 || modifiers == c_modifier_apply_texture3 ) { ++ Scene_applyClosestTexture( volume ); ++ } ++ else if ( modifiers == c_modifier_copy_texture ) { ++ Scene_copyClosestTexture( volume ); ++ } ++ } ++} ++void onMouseMotion( const WindowVector& position, ModifierFlags modifiers ){ ++ m_selector.m_mouseMoved = true; ++ if ( m_mouse_down && !g_mouseMovedCallback.empty() ) { ++ m_selector.m_mouseMovedWhilePressed = true; ++ g_mouseMovedCallback.get() ( window_to_normalised_device( position, m_width, m_height ) ); ++ } ++} ++void onMouseUp( const WindowVector& position, ButtonIdentifier button, ModifierFlags modifiers ){ ++ if ( ( button == c_button_select || button == c_button_select2 ) && !g_mouseUpCallback.empty() ) { ++ m_mouse_down = false; ++ ++ g_mouseUpCallback.get() ( window_to_normalised_device( position, m_width, m_height ) ); ++ } ++ //L button w/o scene changed = tunnel selection ++ if( // !getSelectionSystem().m_undo_begun && ++ modifiers == c_modifierNone && button == c_button_select && ++ //( !m_selector.m_mouseMoved || !m_mouse_down ) && ++ !m_selector.m_mouseMovedWhilePressed && ++ ( getSelectionSystem().Mode() != SelectionSystem::eComponent || getSelectionSystem().ManipulatorMode() != SelectionSystem::eDrag ) ){ ++ m_selector.testSelect_simpleM1( device_constrained( window_to_normalised_device( position, m_width, m_height ) ) ); ++ } ++ //getSelectionSystem().m_undo_begun = false; ++ m_selector.m_mouseMoved = false; ++ m_selector.m_mouseMovedWhilePressed = false; ++} ++void onModifierDown( ModifierFlags type ){ ++ m_selector.modifierEnable( type ); ++ m_manipulator.modifierEnable( type ); ++} ++void onModifierUp( ModifierFlags type ){ ++ m_selector.modifierDisable( type ); ++ m_manipulator.modifierDisable( type ); ++} ++}; ++ ++ ++ ++SelectionSystemWindowObserver* NewWindowObserver(){ ++ return new RadiantWindowObserver; ++} ++ ++ ++ ++#include "modulesystem/singletonmodule.h" ++#include "modulesystem/moduleregistry.h" ++ ++class SelectionDependencies : ++ public GlobalSceneGraphModuleRef, ++ public GlobalShaderCacheModuleRef, ++ public GlobalOpenGLModuleRef ++{ ++}; ++ ++class SelectionAPI : public TypeSystemRef ++{ ++SelectionSystem* m_selection; ++public: ++typedef SelectionSystem Type; ++STRING_CONSTANT( Name, "*" ); ++ ++SelectionAPI(){ ++ SelectionSystem_Construct(); ++ ++ m_selection = &getSelectionSystem(); ++} ++~SelectionAPI(){ ++ SelectionSystem_Destroy(); ++} ++SelectionSystem* getTable(){ ++ return m_selection; ++} ++}; ++ ++typedef SingletonModule SelectionModule; ++typedef Static StaticSelectionModule; ++StaticRegisterModule staticRegisterSelection( StaticSelectionModule::instance() ); diff --cc radiant/texwindow.cpp index 24090155,819d4bc3..23174f1c --- a/radiant/texwindow.cpp +++ b/radiant/texwindow.cpp @@@ -531,23 -475,14 +533,23 @@@ bool TextureSearch_IsShown( const char } } -CopiedString g_notex; -CopiedString g_shadernotex; - // if texture_showinuse jump over non in-use textures - bool Texture_IsShown( IShader* shader, bool show_shaders, bool show_textures, bool hideUnused ){ + bool Texture_IsShown( IShader* shader, bool show_shaders, bool show_textures, bool hideUnused, bool hideNonShadersInCommon ){ - // filter notex / shadernotex images - if ( g_TextureBrowser_filterNotex && ( string_equal( g_notex.c_str(), shader->getTexture()->name ) || string_equal( g_shadernotex.c_str(), shader->getTexture()->name ) ) ) { - return false; + // filter missing shaders + // ugly: filter on built-in fallback name after substitution + if ( g_TextureBrowser_filterMissing ) { + if ( isMissing( shader->getTexture()->name ) ) { + return false; + } + } + // filter the fallback (notex/shadernotex) for missing shaders or editor image + if ( g_TextureBrowser_filterFallback ) { + if ( isNotex( shader->getName() ) ) { + return false; + } + if ( isNotex( shader->getTexture()->name ) ) { + return false; + } } if ( g_TextureBrowser_currentDirectory == "Untagged" ) { @@@ -918,62 -836,43 +925,56 @@@ void TextureBrowser_ShowTagSearchResult bool TextureBrowser_hideUnused(); -void TextureBrowser_hideUnusedExport( const BoolImportCallback& importer ){ +void TextureBrowser_hideUnusedExport( const Callback & importer ){ importer( TextureBrowser_hideUnused() ); } -typedef FreeCaller1 TextureBrowserHideUnusedExport; -void TextureBrowser_showShadersExport( const BoolImportCallback& importer ){ +typedef FreeCaller &), TextureBrowser_hideUnusedExport> TextureBrowserHideUnusedExport; + +void TextureBrowser_showShadersExport( const Callback & importer ){ importer( GlobalTextureBrowser().m_showShaders ); } -typedef FreeCaller1 TextureBrowserShowShadersExport; -void TextureBrowser_showTexturesExport( const BoolImportCallback& importer ){ +typedef FreeCaller &), TextureBrowser_showShadersExport> TextureBrowserShowShadersExport; + +void TextureBrowser_showTexturesExport( const Callback & importer ){ importer( GlobalTextureBrowser().m_showTextures ); } -typedef FreeCaller1 TextureBrowserShowTexturesExport; -void TextureBrowser_showShaderlistOnly( const BoolImportCallback& importer ){ +typedef FreeCaller &), TextureBrowser_showTexturesExport> TextureBrowserShowTexturesExport; + +void TextureBrowser_showShaderlistOnly( const Callback & importer ){ importer( g_TextureBrowser_shaderlistOnly ); } -typedef FreeCaller1 TextureBrowserShowShaderlistOnlyExport; -void TextureBrowser_fixedSize( const BoolImportCallback& importer ){ +typedef FreeCaller &), TextureBrowser_showShaderlistOnly> TextureBrowserShowShaderlistOnlyExport; + +void TextureBrowser_fixedSize( const Callback & importer ){ importer( g_TextureBrowser_fixedSize ); } -typedef FreeCaller1 TextureBrowser_FixedSizeExport; -void TextureBrowser_filterNotex( const BoolImportCallback& importer ){ - importer( g_TextureBrowser_filterNotex ); +typedef FreeCaller &), TextureBrowser_fixedSize> TextureBrowser_FixedSizeExport; + +void TextureBrowser_filterMissing( const Callback & importer ){ + importer( g_TextureBrowser_filterMissing ); } -typedef FreeCaller1 TextureBrowser_filterNotexExport; -void TextureBrowser_enableAlpha( const BoolImportCallback& importer ){ +typedef FreeCaller &), TextureBrowser_filterMissing> TextureBrowser_filterMissingExport; + +void TextureBrowser_filterFallback( const Callback & importer ){ + importer( g_TextureBrowser_filterFallback ); +} + +typedef FreeCaller &), TextureBrowser_filterFallback> TextureBrowser_filterFallbackExport; + +void TextureBrowser_enableAlpha( const Callback & importer ){ importer( g_TextureBrowser_enableAlpha ); } -typedef FreeCaller1 TextureBrowser_enableAlphaExport; + +typedef FreeCaller &), TextureBrowser_enableAlpha> TextureBrowser_enableAlphaExport; void TextureBrowser_SetHideUnused( TextureBrowser& textureBrowser, bool hideUnused ){ - if ( hideUnused ) { - textureBrowser.m_hideUnused = true; - } - else - { - textureBrowser.m_hideUnused = false; - } + textureBrowser.m_hideUnused = hideUnused; textureBrowser.m_hideunused_item.update(); @@@ -2815,8 -2719,15 +2816,16 @@@ void TextureBrowser_constructPreference const char* startup_shaders[] = { "None", TextureBrowser_getComonShadersName() }; page.appendCombo( "Load Shaders at Startup", reinterpret_cast( GlobalTextureBrowser().m_startupShaders ), STRING_ARRAY_RANGE( startup_shaders ) ); } + { + StringOutputStream sstream( 256 ); + sstream << "Hide nonShaders in " << TextureBrowser_getComonShadersDir() << " folder"; + page.appendCheckBox( + "", sstream.c_str(), + GlobalTextureBrowser().m_hideNonShadersInCommon + ); + } } + void TextureBrowser_constructPage( PreferenceGroup& group ){ PreferencesPage page( group.createPage( "Texture Browser", "Texture Browser Preferences" ) ); TextureBrowser_constructPreferences( page ); @@@ -2834,39 -2747,48 +2843,40 @@@ void TextureBrowser_registerPreferences void TextureClipboard_textureSelected( const char* shader ); void TextureBrowser_Construct(){ - GlobalCommands_insert( "ShaderInfo", FreeCaller() ); - GlobalCommands_insert( "ShowUntagged", FreeCaller() ); - GlobalCommands_insert( "AddTag", FreeCaller() ); - GlobalCommands_insert( "RenameTag", FreeCaller() ); - GlobalCommands_insert( "DeleteTag", FreeCaller() ); - GlobalCommands_insert( "CopyTag", FreeCaller() ); - GlobalCommands_insert( "PasteTag", FreeCaller() ); - GlobalCommands_insert( "RefreshShaders", FreeCaller() ); - GlobalToggles_insert( "ShowInUse", FreeCaller(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_hideunused_item ), Accelerator( 'U' ) ); - GlobalCommands_insert( "ShowAllTextures", FreeCaller(), Accelerator( 'A', (GdkModifierType)GDK_CONTROL_MASK ) ); - GlobalCommands_insert( "ToggleTextures", FreeCaller(), Accelerator( 'T' ) ); - GlobalToggles_insert( "ToggleShowShaders", FreeCaller(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showshaders_item ) ); - GlobalToggles_insert( "ToggleShowTextures", FreeCaller(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showtextures_item ) ); - GlobalToggles_insert( "ToggleShowShaderlistOnly", FreeCaller(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showshaderlistonly_item ) ); - GlobalToggles_insert( "FixedSize", FreeCaller(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_fixedsize_item ) ); - GlobalToggles_insert( "FilterNotex", FreeCaller(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_filternotex_item ) ); - GlobalToggles_insert( "EnableAlpha", FreeCaller(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_enablealpha_item ) ); - - GlobalPreferenceSystem().registerPreference( "TextureScale", - makeSizeStringImportCallback( TextureBrowserSetScaleCaller( g_TextureBrowser ) ), - SizeExportStringCaller( g_TextureBrowser.m_textureScale ) - ); - GlobalPreferenceSystem().registerPreference( "UniformTextureSize", - makeIntStringImportCallback(UniformTextureSizeImportCaller(g_TextureBrowser)), - IntExportStringCaller(g_TextureBrowser.m_uniformTextureSize) ); - GlobalPreferenceSystem().registerPreference( "UniformTextureMinSize", - makeIntStringImportCallback(UniformTextureMinSizeImportCaller(g_TextureBrowser)), - IntExportStringCaller(g_TextureBrowser.m_uniformTextureMinSize) ); - GlobalPreferenceSystem().registerPreference( "TextureScrollbar", - makeBoolStringImportCallback( TextureBrowserImportShowScrollbarCaller( g_TextureBrowser ) ), - BoolExportStringCaller( GlobalTextureBrowser().m_showTextureScrollbar ) - ); - GlobalPreferenceSystem().registerPreference( "ShowShaders", BoolImportStringCaller( GlobalTextureBrowser().m_showShaders ), BoolExportStringCaller( GlobalTextureBrowser().m_showShaders ) ); - GlobalPreferenceSystem().registerPreference( "ShowTextures", BoolImportStringCaller( GlobalTextureBrowser().m_showTextures ), BoolExportStringCaller( GlobalTextureBrowser().m_showTextures ) ); - GlobalPreferenceSystem().registerPreference( "ShowShaderlistOnly", BoolImportStringCaller( g_TextureBrowser_shaderlistOnly ), BoolExportStringCaller( g_TextureBrowser_shaderlistOnly ) ); - GlobalPreferenceSystem().registerPreference( "FixedSize", BoolImportStringCaller( g_TextureBrowser_fixedSize ), BoolExportStringCaller( g_TextureBrowser_fixedSize ) ); - GlobalPreferenceSystem().registerPreference( "FilterNotex", BoolImportStringCaller( g_TextureBrowser_filterNotex ), BoolExportStringCaller( g_TextureBrowser_filterNotex ) ); - GlobalPreferenceSystem().registerPreference( "EnableAlpha", BoolImportStringCaller( g_TextureBrowser_enableAlpha ), BoolExportStringCaller( g_TextureBrowser_enableAlpha ) ); - GlobalPreferenceSystem().registerPreference( "LoadShaders", IntImportStringCaller( reinterpret_cast( GlobalTextureBrowser().m_startupShaders ) ), IntExportStringCaller( reinterpret_cast( GlobalTextureBrowser().m_startupShaders ) ) ); - GlobalPreferenceSystem().registerPreference( "WheelMouseInc", SizeImportStringCaller( GlobalTextureBrowser().m_mouseWheelScrollIncrement ), SizeExportStringCaller( GlobalTextureBrowser().m_mouseWheelScrollIncrement ) ); - GlobalPreferenceSystem().registerPreference( "SI_Colors0", Vector3ImportStringCaller( GlobalTextureBrowser().color_textureback ), Vector3ExportStringCaller( GlobalTextureBrowser().color_textureback ) ); - GlobalPreferenceSystem().registerPreference( "HideNonShadersInCommon", BoolImportStringCaller( GlobalTextureBrowser().m_hideNonShadersInCommon ), BoolExportStringCaller( GlobalTextureBrowser().m_hideNonShadersInCommon ) ); + GlobalCommands_insert( "ShaderInfo", makeCallbackF(TextureBrowser_shaderInfo) ); + GlobalCommands_insert( "ShowUntagged", makeCallbackF(TextureBrowser_showUntagged) ); + GlobalCommands_insert( "AddTag", makeCallbackF(TextureBrowser_addTag) ); + GlobalCommands_insert( "RenameTag", makeCallbackF(TextureBrowser_renameTag) ); + GlobalCommands_insert( "DeleteTag", makeCallbackF(TextureBrowser_deleteTag) ); + GlobalCommands_insert( "CopyTag", makeCallbackF(TextureBrowser_copyTag) ); + GlobalCommands_insert( "PasteTag", makeCallbackF(TextureBrowser_pasteTag) ); + GlobalCommands_insert( "RefreshShaders", makeCallbackF(VFS_Refresh) ); + GlobalToggles_insert( "ShowInUse", makeCallbackF(TextureBrowser_ToggleHideUnused), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_hideunused_item ), Accelerator( 'U' ) ); + GlobalCommands_insert( "ShowAllTextures", makeCallbackF(TextureBrowser_showAll), Accelerator( 'A', (GdkModifierType)GDK_CONTROL_MASK ) ); + GlobalCommands_insert( "ToggleTextures", makeCallbackF(TextureBrowser_toggleShow), Accelerator( 'T' ) ); + GlobalToggles_insert( "ToggleShowShaders", makeCallbackF(TextureBrowser_ToggleShowShaders), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showshaders_item ) ); + GlobalToggles_insert( "ToggleShowTextures", makeCallbackF(TextureBrowser_ToggleShowTextures), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showtextures_item ) ); + GlobalToggles_insert( "ToggleShowShaderlistOnly", makeCallbackF(TextureBrowser_ToggleShowShaderListOnly), + ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showshaderlistonly_item ) ); + GlobalToggles_insert( "FixedSize", makeCallbackF(TextureBrowser_FixedSize), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_fixedsize_item ) ); + GlobalToggles_insert( "FilterMissing", makeCallbackF(TextureBrowser_FilterMissing), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_filternotex_item ) ); + GlobalToggles_insert( "FilterFallback", makeCallbackF(TextureBrowser_FilterFallback), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_hidenotex_item ) ); + GlobalToggles_insert( "EnableAlpha", makeCallbackF(TextureBrowser_EnableAlpha), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_enablealpha_item ) ); + + GlobalPreferenceSystem().registerPreference( "TextureScale", make_property_string(g_TextureBrowser) ); + GlobalPreferenceSystem().registerPreference( "UniformTextureSize", make_property_string(g_TextureBrowser) ); + GlobalPreferenceSystem().registerPreference( "UniformTextureMinSize", make_property_string(g_TextureBrowser) ); + GlobalPreferenceSystem().registerPreference( "TextureScrollbar", make_property_string(GlobalTextureBrowser())); + GlobalPreferenceSystem().registerPreference( "ShowShaders", make_property_string( GlobalTextureBrowser().m_showShaders ) ); + GlobalPreferenceSystem().registerPreference( "ShowTextures", make_property_string( GlobalTextureBrowser().m_showTextures ) ); + GlobalPreferenceSystem().registerPreference( "ShowShaderlistOnly", make_property_string( g_TextureBrowser_shaderlistOnly ) ); + GlobalPreferenceSystem().registerPreference( "FixedSize", make_property_string( g_TextureBrowser_fixedSize ) ); + GlobalPreferenceSystem().registerPreference( "FilterMissing", make_property_string( g_TextureBrowser_filterMissing ) ); + GlobalPreferenceSystem().registerPreference( "EnableAlpha", make_property_string( g_TextureBrowser_enableAlpha ) ); + GlobalPreferenceSystem().registerPreference( "LoadShaders", make_property_string( reinterpret_cast( GlobalTextureBrowser().m_startupShaders ) ) ); + GlobalPreferenceSystem().registerPreference( "WheelMouseInc", make_property_string( GlobalTextureBrowser().m_mouseWheelScrollIncrement ) ); + GlobalPreferenceSystem().registerPreference( "SI_Colors0", make_property_string( GlobalTextureBrowser().color_textureback ) ); ++ GlobalPreferenceSystem().registerPreference( "HideNonShadersInCommon", make_property_string( GlobalTextureBrowser().m_hideNonShadersInCommon ) ); g_TextureBrowser.shader = texdef_name_default(); diff --cc radiant/texwindow.cpp.orig index 00000000,00000000..a24c46dd new file mode 100644 --- /dev/null +++ b/radiant/texwindow.cpp.orig @@@ -1,0 -1,0 +1,2954 @@@ ++/* ++ Copyright (C) 1999-2006 Id Software, Inc. and contributors. ++ For a list of contributors, see the accompanying CONTRIBUTORS file. ++ ++ 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 ++ */ ++ ++// ++// Texture Window ++// ++// Leonardo Zide (leo@lokigames.com) ++// ++ ++#include "texwindow.h" ++ ++#include ++ ++#include "debugging/debugging.h" ++#include "warnings.h" ++ ++#include "defaults.h" ++#include "ifilesystem.h" ++#include "iundo.h" ++#include "igl.h" ++#include "iarchive.h" ++#include "moduleobserver.h" ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "signal/signal.h" ++#include "math/vector.h" ++#include "texturelib.h" ++#include "string/string.h" ++#include "shaderlib.h" ++#include "os/file.h" ++#include "os/path.h" ++#include "stream/memstream.h" ++#include "stream/textfilestream.h" ++#include "stream/stringstream.h" ++#include "cmdlib.h" ++#include "texmanip.h" ++#include "textures.h" ++#include "convert.h" ++ ++#include "gtkutil/menu.h" ++#include "gtkutil/nonmodal.h" ++#include "gtkutil/cursor.h" ++#include "gtkutil/widget.h" ++#include "gtkutil/glwidget.h" ++#include "gtkutil/messagebox.h" ++ ++#include "error.h" ++#include "map.h" ++#include "qgl.h" ++#include "select.h" ++#include "brush_primit.h" ++#include "brushmanip.h" ++#include "patchmanip.h" ++#include "plugin.h" ++#include "qe3.h" ++#include "gtkdlgs.h" ++#include "gtkmisc.h" ++#include "mainframe.h" ++#include "findtexturedialog.h" ++#include "surfacedialog.h" ++#include "patchdialog.h" ++#include "groupdialog.h" ++#include "preferences.h" ++#include "shaders.h" ++#include "commands.h" ++ ++bool TextureBrowser_showWads(){ ++ return !string_empty( g_pGameDescription->getKeyValue( "show_wads" ) ); ++} ++ ++void TextureBrowser_queueDraw( TextureBrowser& textureBrowser ); ++ ++bool string_equal_start( const char* string, StringRange start ){ ++ return string_equal_n( string, start.first, start.last - start.first ); ++} ++ ++typedef std::set TextureGroups; ++ ++void TextureGroups_addWad( TextureGroups& groups, const char* archive ){ ++ if ( extension_equal( path_get_extension( archive ), "wad" ) ) { ++#if 1 ++ groups.insert( archive ); ++#else ++ CopiedString archiveBaseName( path_get_filename_start( archive ), path_get_filename_base_end( archive ) ); ++ groups.insert( archiveBaseName ); ++#endif ++ } ++} ++ ++typedef ReferenceCaller TextureGroupsAddWadCaller; ++ ++namespace ++{ ++bool g_TextureBrowser_shaderlistOnly = false; ++bool g_TextureBrowser_fixedSize = true; ++bool g_TextureBrowser_filterMissing = false; ++bool g_TextureBrowser_filterFallback = true; ++bool g_TextureBrowser_enableAlpha = false; ++} ++ ++CopiedString g_notex; ++CopiedString g_shadernotex; ++ ++bool isMissing(const char* name); ++ ++bool isNotex(const char* name); ++ ++bool isMissing(const char* name){ ++ if ( string_equal( g_notex.c_str(), name ) ) { ++ return true; ++ } ++ if ( string_equal( g_shadernotex.c_str(), name ) ) { ++ return true; ++ } ++ return false; ++} ++ ++bool isNotex(const char* name){ ++ if ( string_equal_suffix( name, "/" DEFAULT_NOTEX_BASENAME ) ) { ++ return true; ++ } ++ if ( string_equal_suffix( name, "/" DEFAULT_SHADERNOTEX_BASENAME ) ) { ++ return true; ++ } ++ return false; ++} ++ ++void TextureGroups_addShader( TextureGroups& groups, const char* shaderName ){ ++ const char* texture = path_make_relative( shaderName, "textures/" ); ++ ++ // hide notex / shadernotex images ++ if ( g_TextureBrowser_filterFallback ) { ++ if ( isNotex( shaderName ) ) { ++ return; ++ } ++ if ( isNotex( texture ) ) { ++ return; ++ } ++ } ++ ++ if ( texture != shaderName ) { ++ const char* last = path_remove_directory( texture ); ++ if ( !string_empty( last ) ) { ++ groups.insert( CopiedString( StringRange( texture, --last ) ) ); ++ } ++ } ++} ++ ++typedef ReferenceCaller TextureGroupsAddShaderCaller; ++ ++void TextureGroups_addDirectory( TextureGroups& groups, const char* directory ){ ++ groups.insert( directory ); ++} ++ ++typedef ReferenceCaller TextureGroupsAddDirectoryCaller; ++ ++class DeferredAdjustment ++{ ++gdouble m_value; ++guint m_handler; ++ ++typedef void ( *ValueChangedFunction )( void* data, gdouble value ); ++ ++ValueChangedFunction m_function; ++void* m_data; ++ ++static gboolean deferred_value_changed( gpointer data ){ ++ reinterpret_cast( data )->m_function( ++ reinterpret_cast( data )->m_data, ++ reinterpret_cast( data )->m_value ++ ); ++ reinterpret_cast( data )->m_handler = 0; ++ reinterpret_cast( data )->m_value = 0; ++ return FALSE; ++} ++ ++public: ++DeferredAdjustment( ValueChangedFunction function, void* data ) : m_value( 0 ), m_handler( 0 ), m_function( function ), m_data( data ){ ++} ++ ++void flush(){ ++ if ( m_handler != 0 ) { ++ g_source_remove( m_handler ); ++ deferred_value_changed( this ); ++ } ++} ++ ++void value_changed( gdouble value ){ ++ m_value = value; ++ if ( m_handler == 0 ) { ++ m_handler = g_idle_add( deferred_value_changed, this ); ++ } ++} ++ ++static void adjustment_value_changed(ui::Adjustment adjustment, DeferredAdjustment* self ){ ++ self->value_changed( gtk_adjustment_get_value(adjustment) ); ++} ++}; ++ ++ ++class TextureBrowser; ++ ++typedef ReferenceCaller TextureBrowserQueueDrawCaller; ++ ++void TextureBrowser_scrollChanged( void* data, gdouble value ); ++ ++ ++enum StartupShaders ++{ ++ STARTUPSHADERS_NONE = 0, ++ STARTUPSHADERS_COMMON, ++}; ++ ++void TextureBrowser_hideUnusedExport( const Callback & importer ); ++ ++typedef FreeCaller &), TextureBrowser_hideUnusedExport> TextureBrowserHideUnusedExport; ++ ++void TextureBrowser_showShadersExport( const Callback & importer ); ++ ++typedef FreeCaller &), TextureBrowser_showShadersExport> TextureBrowserShowShadersExport; ++ ++void TextureBrowser_showTexturesExport( const Callback & importer ); ++ ++typedef FreeCaller &), TextureBrowser_showTexturesExport> TextureBrowserShowTexturesExport; ++ ++void TextureBrowser_showShaderlistOnly( const Callback & importer ); ++ ++typedef FreeCaller &), TextureBrowser_showShaderlistOnly> TextureBrowserShowShaderlistOnlyExport; ++ ++void TextureBrowser_fixedSize( const Callback & importer ); ++ ++typedef FreeCaller &), TextureBrowser_fixedSize> TextureBrowserFixedSizeExport; ++ ++void TextureBrowser_filterMissing( const Callback & importer ); ++ ++typedef FreeCaller &), TextureBrowser_filterMissing> TextureBrowserFilterMissingExport; ++ ++void TextureBrowser_filterFallback( const Callback & importer ); ++ ++typedef FreeCaller &), TextureBrowser_filterFallback> TextureBrowserFilterFallbackExport; ++ ++void TextureBrowser_enableAlpha( const Callback & importer ); ++ ++typedef FreeCaller &), TextureBrowser_enableAlpha> TextureBrowserEnableAlphaExport; ++ ++class TextureBrowser ++{ ++public: ++int width, height; ++int originy; ++int m_nTotalHeight; ++ ++CopiedString shader; ++ ++ui::Window m_parent{ui::null}; ++ui::GLArea m_gl_widget{ui::null}; ++ui::Widget m_texture_scroll{ui::null}; ++ui::TreeView m_treeViewTree{ui::New}; ++ui::TreeView m_treeViewTags{ui::null}; ++ui::Frame m_tag_frame{ui::null}; ++ui::ListStore m_assigned_store{ui::null}; ++ui::ListStore m_available_store{ui::null}; ++ui::TreeView m_assigned_tree{ui::null}; ++ui::TreeView m_available_tree{ui::null}; ++ui::Widget m_scr_win_tree{ui::null}; ++ui::Widget m_scr_win_tags{ui::null}; ++ui::Widget m_tag_notebook{ui::null}; ++ui::Button m_search_button{ui::null}; ++ui::Widget m_shader_info_item{ui::null}; ++ ++std::set m_all_tags; ++ui::ListStore m_all_tags_list{ui::null}; ++std::vector m_copied_tags; ++std::set m_found_shaders; ++ ++ToggleItem m_hideunused_item; ++ToggleItem m_hidenotex_item; ++ToggleItem m_showshaders_item; ++ToggleItem m_showtextures_item; ++ToggleItem m_showshaderlistonly_item; ++ToggleItem m_fixedsize_item; ++ToggleItem m_filternotex_item; ++ToggleItem m_enablealpha_item; ++ ++guint m_sizeHandler; ++guint m_exposeHandler; ++ ++bool m_heightChanged; ++bool m_originInvalid; ++ ++DeferredAdjustment m_scrollAdjustment; ++FreezePointer m_freezePointer; ++ ++Vector3 color_textureback; ++// the increment step we use against the wheel mouse ++std::size_t m_mouseWheelScrollIncrement; ++std::size_t m_textureScale; ++// make the texture increments match the grid changes ++bool m_showShaders; ++bool m_showTextures; ++bool m_showTextureScrollbar; ++StartupShaders m_startupShaders; ++// if true, the texture window will only display in-use shaders ++// if false, all the shaders in memory are displayed ++bool m_hideUnused; ++bool m_rmbSelected; ++bool m_searchedTags; ++bool m_tags; ++bool m_move_started; ++// The uniform size (in pixels) that textures are resized to when m_resizeTextures is true. ++int m_uniformTextureSize; ++int m_uniformTextureMinSize; ++ ++<<<<<<< HEAD ++======= ++bool m_hideNonShadersInCommon; ++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84 ++// Return the display width of a texture in the texture browser ++void getTextureWH( qtexture_t* tex, int &W, int &H ){ ++ // Don't use uniform size ++ W = (int)( tex->width * ( (float)m_textureScale / 100 ) ); ++ H = (int)( tex->height * ( (float)m_textureScale / 100 ) ); ++ if ( W < 1 ) W = 1; ++ if ( H < 1 ) H = 1; ++ ++ if ( g_TextureBrowser_fixedSize ){ ++ if ( W >= H ) { ++ // Texture is square, or wider than it is tall ++ if ( W >= m_uniformTextureSize ){ ++ H = m_uniformTextureSize * H / W; ++ W = m_uniformTextureSize; ++ } ++ else if ( W <= m_uniformTextureMinSize ){ ++ H = m_uniformTextureMinSize * H / W; ++ W = m_uniformTextureMinSize; ++ } ++ } ++ else { ++ // Texture taller than it is wide ++ if ( H >= m_uniformTextureSize ){ ++ W = m_uniformTextureSize * W / H; ++ H = m_uniformTextureSize; ++ } ++ else if ( H <= m_uniformTextureMinSize ){ ++ W = m_uniformTextureMinSize * W / H; ++ H = m_uniformTextureMinSize; ++ } ++ } ++ } ++} ++ ++TextureBrowser() : ++ m_texture_scroll( ui::null ), ++ m_hideunused_item( TextureBrowserHideUnusedExport() ), ++ m_hidenotex_item( TextureBrowserFilterFallbackExport() ), ++ m_showshaders_item( TextureBrowserShowShadersExport() ), ++ m_showtextures_item( TextureBrowserShowTexturesExport() ), ++ m_showshaderlistonly_item( TextureBrowserShowShaderlistOnlyExport() ), ++ m_fixedsize_item( TextureBrowserFixedSizeExport() ), ++ m_filternotex_item( TextureBrowserFilterMissingExport() ), ++ m_enablealpha_item( TextureBrowserEnableAlphaExport() ), ++ m_heightChanged( true ), ++ m_originInvalid( true ), ++ m_scrollAdjustment( TextureBrowser_scrollChanged, this ), ++ color_textureback( 0.25f, 0.25f, 0.25f ), ++ m_mouseWheelScrollIncrement( 64 ), ++ m_textureScale( 50 ), ++ m_showShaders( true ), ++ m_showTextures( true ), ++ m_showTextureScrollbar( true ), ++ m_startupShaders( STARTUPSHADERS_NONE ), ++ m_hideUnused( false ), ++ m_rmbSelected( false ), ++ m_searchedTags( false ), ++ m_tags( false ), ++ m_uniformTextureSize( 160 ), ++ m_uniformTextureMinSize( 48 ), ++ m_hideNonShadersInCommon( true ), ++ m_move_started( false ){ ++} ++}; ++ ++void ( *TextureBrowser_textureSelected )( const char* shader ); ++ ++ ++void TextureBrowser_updateScroll( TextureBrowser& textureBrowser ); ++ ++ ++const char* TextureBrowser_getComonShadersName(){ ++ const char* value = g_pGameDescription->getKeyValue( "common_shaders_name" ); ++ if ( !string_empty( value ) ) { ++ return value; ++ } ++ return "Common"; ++} ++ ++const char* TextureBrowser_getComonShadersDir(){ ++ const char* value = g_pGameDescription->getKeyValue( "common_shaders_dir" ); ++ if ( !string_empty( value ) ) { ++ return value; ++ } ++ return "common/"; ++} ++ ++inline int TextureBrowser_fontHeight( TextureBrowser& textureBrowser ){ ++ return GlobalOpenGL().m_font->getPixelHeight(); ++} ++ ++const char* TextureBrowser_GetSelectedShader( TextureBrowser& textureBrowser ){ ++ return textureBrowser.shader.c_str(); ++} ++ ++void TextureBrowser_SetStatus( TextureBrowser& textureBrowser, const char* name ){ ++ IShader* shader = QERApp_Shader_ForName( name ); ++ qtexture_t* q = shader->getTexture(); ++ StringOutputStream strTex( 256 ); ++ strTex << name << " W: " << Unsigned( q->width ) << " H: " << Unsigned( q->height ); ++ shader->DecRef(); ++ g_pParentWnd->SetStatusText( g_pParentWnd->m_texture_status, strTex.c_str() ); ++} ++ ++void TextureBrowser_Focus( TextureBrowser& textureBrowser, const char* name ); ++ ++void TextureBrowser_SetSelectedShader( TextureBrowser& textureBrowser, const char* shader ){ ++ textureBrowser.shader = shader; ++ TextureBrowser_SetStatus( textureBrowser, shader ); ++ TextureBrowser_Focus( textureBrowser, shader ); ++ ++ if ( FindTextureDialog_isOpen() ) { ++ FindTextureDialog_selectTexture( shader ); ++ } ++ ++ // disable the menu item "shader info" if no shader was selected ++ IShader* ishader = QERApp_Shader_ForName( shader ); ++ CopiedString filename = ishader->getShaderFileName(); ++ ++ if ( filename.empty() ) { ++ if ( textureBrowser.m_shader_info_item != NULL ) { ++ gtk_widget_set_sensitive( textureBrowser.m_shader_info_item, FALSE ); ++ } ++ } ++ else { ++ gtk_widget_set_sensitive( textureBrowser.m_shader_info_item, TRUE ); ++ } ++ ++ ishader->DecRef(); ++} ++ ++ ++CopiedString g_TextureBrowser_currentDirectory; ++ ++/* ++ ============================================================================ ++ ++ TEXTURE LAYOUT ++ ++ TTimo: now based on a rundown through all the shaders ++ NOTE: we expect the Active shaders count doesn't change during a Texture_StartPos .. Texture_NextPos cycle ++ otherwise we may need to rely on a list instead of an array storage ++ ============================================================================ ++ */ ++ ++class TextureLayout ++{ ++public: ++// texture layout functions ++// TTimo: now based on shaders ++int current_x, current_y, current_row; ++}; ++ ++void Texture_StartPos( TextureLayout& layout ){ ++ layout.current_x = 8; ++ layout.current_y = -8; ++ layout.current_row = 0; ++} ++ ++void Texture_NextPos( TextureBrowser& textureBrowser, TextureLayout& layout, qtexture_t* current_texture, int *x, int *y ){ ++ qtexture_t* q = current_texture; ++ ++ int nWidth, nHeight; ++ textureBrowser.getTextureWH( q, nWidth, nHeight ); ++ if ( layout.current_x + nWidth > textureBrowser.width - 8 && layout.current_row ) { // go to the next row unless the texture is the first on the row ++ layout.current_x = 8; ++ layout.current_y -= layout.current_row + TextureBrowser_fontHeight( textureBrowser ) + 4;//+4 ++ layout.current_row = 0; ++ } ++ ++ *x = layout.current_x; ++ *y = layout.current_y; ++ ++ // Is our texture larger than the row? If so, grow the ++ // row height to match it ++ ++ if ( layout.current_row < nHeight ) { ++ layout.current_row = nHeight; ++ } ++ ++ // never go less than 96, or the names get all crunched up ++ layout.current_x += nWidth < 96 ? 96 : nWidth; ++ layout.current_x += 8; ++} ++ ++bool TextureSearch_IsShown( const char* name ){ ++ std::set::iterator iter; ++ ++ iter = GlobalTextureBrowser().m_found_shaders.find( name ); ++ ++ if ( iter == GlobalTextureBrowser().m_found_shaders.end() ) { ++ return false; ++ } ++ else { ++ return true; ++ } ++} ++ ++// if texture_showinuse jump over non in-use textures ++<<<<<<< HEAD ++bool Texture_IsShown( IShader* shader, bool show_shaders, bool show_textures, bool hideUnused ){ ++ // filter missing shaders ++ // ugly: filter on built-in fallback name after substitution ++ if ( g_TextureBrowser_filterMissing ) { ++ if ( isMissing( shader->getTexture()->name ) ) { ++ return false; ++ } ++ } ++ // filter the fallback (notex/shadernotex) for missing shaders or editor image ++ if ( g_TextureBrowser_filterFallback ) { ++ if ( isNotex( shader->getName() ) ) { ++ return false; ++ } ++ if ( isNotex( shader->getTexture()->name ) ) { ++ return false; ++ } ++======= ++bool Texture_IsShown( IShader* shader, bool show_shaders, bool show_textures, bool hideUnused, bool hideNonShadersInCommon ){ ++ // filter notex / shadernotex images ++ if ( g_TextureBrowser_filterNotex && ( string_equal( g_notex.c_str(), shader->getTexture()->name ) || string_equal( g_shadernotex.c_str(), shader->getTexture()->name ) ) ) { ++ return false; ++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84 ++ } ++ ++ if ( g_TextureBrowser_currentDirectory == "Untagged" ) { ++ std::set::iterator iter; ++ ++ iter = GlobalTextureBrowser().m_found_shaders.find( shader->getName() ); ++ ++ if ( iter == GlobalTextureBrowser().m_found_shaders.end() ) { ++ return false; ++ } ++ else { ++ return true; ++ } ++ } ++ ++ if ( !shader_equal_prefix( shader->getName(), "textures/" ) ) { ++ return false; ++ } ++ ++ if ( !show_shaders && !shader->IsDefault() ) { ++ return false; ++ } ++ ++ if ( !show_textures && shader->IsDefault() ) { ++ return false; ++ } ++ ++ if ( hideUnused && !shader->IsInUse() ) { ++ return false; ++ } ++ ++ if( hideNonShadersInCommon && shader->IsDefault() && !shader->IsInUse() //&& g_TextureBrowser_currentDirectory != "" ++ && shader_equal_prefix( shader_get_textureName( shader->getName() ), TextureBrowser_getComonShadersDir() ) ){ ++ return false; ++ } ++ ++ if ( GlobalTextureBrowser().m_searchedTags ) { ++ if ( !TextureSearch_IsShown( shader->getName() ) ) { ++ return false; ++ } ++ else { ++ return true; ++ } ++ } ++ else { ++ if ( !shader_equal_prefix( shader_get_textureName( shader->getName() ), g_TextureBrowser_currentDirectory.c_str() ) ) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++void TextureBrowser_heightChanged( TextureBrowser& textureBrowser ){ ++ textureBrowser.m_heightChanged = true; ++ ++ TextureBrowser_updateScroll( textureBrowser ); ++ TextureBrowser_queueDraw( textureBrowser ); ++} ++ ++void TextureBrowser_evaluateHeight( TextureBrowser& textureBrowser ){ ++ if ( textureBrowser.m_heightChanged ) { ++ textureBrowser.m_heightChanged = false; ++ ++ textureBrowser.m_nTotalHeight = 0; ++ ++ TextureLayout layout; ++ Texture_StartPos( layout ); ++ for ( QERApp_ActiveShaders_IteratorBegin(); !QERApp_ActiveShaders_IteratorAtEnd(); QERApp_ActiveShaders_IteratorIncrement() ) ++ { ++ IShader* shader = QERApp_ActiveShaders_IteratorCurrent(); ++ ++ if ( !Texture_IsShown( shader, textureBrowser.m_showShaders, textureBrowser.m_showTextures, textureBrowser.m_hideUnused, textureBrowser.m_hideNonShadersInCommon ) ) { ++ continue; ++ } ++ ++ int x, y; ++ Texture_NextPos( textureBrowser, layout, shader->getTexture(), &x, &y ); ++ int nWidth, nHeight; ++ textureBrowser.getTextureWH( shader->getTexture(), nWidth, nHeight ); ++ textureBrowser.m_nTotalHeight = std::max( textureBrowser.m_nTotalHeight, abs( layout.current_y ) + TextureBrowser_fontHeight( textureBrowser ) + nHeight + 4 ); ++ } ++ } ++} ++ ++int TextureBrowser_TotalHeight( TextureBrowser& textureBrowser ){ ++ TextureBrowser_evaluateHeight( textureBrowser ); ++ return textureBrowser.m_nTotalHeight; ++} ++ ++inline const int& min_int( const int& left, const int& right ){ ++ return std::min( left, right ); ++} ++ ++void TextureBrowser_clampOriginY( TextureBrowser& textureBrowser ){ ++ if ( textureBrowser.originy > 0 ) { ++ textureBrowser.originy = 0; ++ } ++ int lower = min_int( textureBrowser.height - TextureBrowser_TotalHeight( textureBrowser ), 0 ); ++ if ( textureBrowser.originy < lower ) { ++ textureBrowser.originy = lower; ++ } ++} ++ ++int TextureBrowser_getOriginY( TextureBrowser& textureBrowser ){ ++ if ( textureBrowser.m_originInvalid ) { ++ textureBrowser.m_originInvalid = false; ++ TextureBrowser_clampOriginY( textureBrowser ); ++ TextureBrowser_updateScroll( textureBrowser ); ++ } ++ return textureBrowser.originy; ++} ++ ++void TextureBrowser_setOriginY( TextureBrowser& textureBrowser, int originy ){ ++ textureBrowser.originy = originy; ++ TextureBrowser_clampOriginY( textureBrowser ); ++ TextureBrowser_updateScroll( textureBrowser ); ++ TextureBrowser_queueDraw( textureBrowser ); ++} ++ ++ ++Signal0 g_activeShadersChangedCallbacks; ++ ++void TextureBrowser_addActiveShadersChangedCallback( const SignalHandler& handler ){ ++ g_activeShadersChangedCallbacks.connectLast( handler ); ++} ++ ++void TextureBrowser_constructTreeStore(); ++ ++class ShadersObserver : public ModuleObserver ++{ ++Signal0 m_realiseCallbacks; ++public: ++void realise(){ ++ m_realiseCallbacks(); ++ /* texturebrowser tree update on vfs restart */ ++// TextureBrowser_constructTreeStore(); ++} ++ ++void unrealise(){ ++} ++ ++void insert( const SignalHandler& handler ){ ++ m_realiseCallbacks.connectLast( handler ); ++} ++}; ++ ++namespace ++{ ++ShadersObserver g_ShadersObserver; ++} ++ ++void TextureBrowser_addShadersRealiseCallback( const SignalHandler& handler ){ ++ g_ShadersObserver.insert( handler ); ++} ++ ++void TextureBrowser_activeShadersChanged( TextureBrowser& textureBrowser ){ ++ TextureBrowser_heightChanged( textureBrowser ); ++ textureBrowser.m_originInvalid = true; ++ ++ g_activeShadersChangedCallbacks(); ++} ++ ++struct TextureBrowser_ShowScrollbar { ++ static void Export(const TextureBrowser &self, const Callback &returnz) { ++ returnz(self.m_showTextureScrollbar); ++ } ++ ++ static void Import(TextureBrowser &self, bool value) { ++ self.m_showTextureScrollbar = value; ++ if (self.m_texture_scroll) { ++ self.m_texture_scroll.visible(self.m_showTextureScrollbar); ++ TextureBrowser_updateScroll(self); ++ } ++ } ++}; ++ ++ ++/* ++ ============== ++ TextureBrowser_ShowDirectory ++ relies on texture_directory global for the directory to use ++ 1) Load the shaders for the given directory ++ 2) Scan the remaining texture, load them and assign them a default shader (the "noshader" shader) ++ NOTE: when writing a texture plugin, or some texture extensions, this function may need to be overriden, and made ++ available through the IShaders interface ++ NOTE: for texture window layout: ++ all shaders are stored with alphabetical order after load ++ previously loaded and displayed stuff is hidden, only in-use and newly loaded is shown ++ ( the GL textures are not flushed though) ++ ============== ++ */ ++ ++bool endswith( const char *haystack, const char *needle ){ ++ size_t lh = strlen( haystack ); ++ size_t ln = strlen( needle ); ++ if ( lh < ln ) { ++ return false; ++ } ++ return !memcmp( haystack + ( lh - ln ), needle, ln ); ++} ++ ++bool texture_name_ignore( const char* name ){ ++ StringOutputStream strTemp( string_length( name ) ); ++ strTemp << LowerCase( name ); ++ ++ return ++ endswith( strTemp.c_str(), ".specular" ) || ++ endswith( strTemp.c_str(), ".glow" ) || ++ endswith( strTemp.c_str(), ".bump" ) || ++ endswith( strTemp.c_str(), ".diffuse" ) || ++ endswith( strTemp.c_str(), ".blend" ) || ++ endswith( strTemp.c_str(), ".alpha" ) || ++ endswith( strTemp.c_str(), "_alpha" ) || ++ /* Quetoo */ ++ endswith( strTemp.c_str(), "_h" ) || ++ endswith( strTemp.c_str(), "_local" ) || ++ endswith( strTemp.c_str(), "_nm" ) || ++ endswith( strTemp.c_str(), "_s" ) || ++ /* DarkPlaces */ ++ endswith( strTemp.c_str(), "_bump" ) || ++ endswith( strTemp.c_str(), "_glow" ) || ++ endswith( strTemp.c_str(), "_gloss" ) || ++ endswith( strTemp.c_str(), "_luma" ) || ++ endswith( strTemp.c_str(), "_norm" ) || ++ endswith( strTemp.c_str(), "_pants" ) || ++ endswith( strTemp.c_str(), "_shirt" ) || ++ endswith( strTemp.c_str(), "_reflect" ) || ++ /* Unvanquished */ ++ endswith( strTemp.c_str(), "_d" ) || ++ endswith( strTemp.c_str(), "_n" ) || ++ endswith( strTemp.c_str(), "_p" ) || ++ endswith( strTemp.c_str(), "_g" ) || ++ endswith( strTemp.c_str(), "_a" ) || ++ 0; ++} ++ ++class LoadShaderVisitor : public Archive::Visitor ++{ ++public: ++void visit( const char* name ){ ++ IShader* shader = QERApp_Shader_ForName( CopiedString( StringRange( name, path_get_filename_base_end( name ) ) ).c_str() ); ++ shader->DecRef(); ++} ++}; ++ ++void TextureBrowser_SetHideUnused( TextureBrowser& textureBrowser, bool hideUnused ); ++ ++ui::Widget g_page_textures{ui::null}; ++ ++void TextureBrowser_toggleShow(){ ++ GroupDialog_showPage( g_page_textures ); ++} ++ ++ ++void TextureBrowser_updateTitle(){ ++ GroupDialog_updatePageTitle( g_page_textures ); ++} ++ ++ ++class TextureCategoryLoadShader ++{ ++const char* m_directory; ++std::size_t& m_count; ++public: ++using func = void(const char *); ++ ++TextureCategoryLoadShader( const char* directory, std::size_t& count ) ++ : m_directory( directory ), m_count( count ){ ++ m_count = 0; ++} ++ ++void operator()( const char* name ) const { ++ if ( shader_equal_prefix( name, "textures/" ) ++ && shader_equal_prefix( name + string_length( "textures/" ), m_directory ) ) { ++ ++m_count; ++ // request the shader, this will load the texture if needed ++ // this Shader_ForName call is a kind of hack ++ IShader *pFoo = QERApp_Shader_ForName( name ); ++ pFoo->DecRef(); ++ } ++} ++}; ++ ++void TextureDirectory_loadTexture( const char* directory, const char* texture ){ ++ StringOutputStream name( 256 ); ++ name << directory << StringRange( texture, path_get_filename_base_end( texture ) ); ++ ++ if ( texture_name_ignore( name.c_str() ) ) { ++ return; ++ } ++ ++ if ( !shader_valid( name.c_str() ) ) { ++ globalOutputStream() << "Skipping invalid texture name: [" << name.c_str() << "]\n"; ++ return; ++ } ++ ++ // if a texture is already in use to represent a shader, ignore it ++ IShader* shader = QERApp_Shader_ForName( name.c_str() ); ++ shader->DecRef(); ++} ++ ++typedef ConstPointerCaller TextureDirectoryLoadTextureCaller; ++ ++class LoadTexturesByTypeVisitor : public ImageModules::Visitor ++{ ++const char* m_dirstring; ++public: ++LoadTexturesByTypeVisitor( const char* dirstring ) ++ : m_dirstring( dirstring ){ ++} ++ ++void visit( const char* minor, const _QERPlugImageTable& table ) const { ++ GlobalFileSystem().forEachFile( m_dirstring, minor, TextureDirectoryLoadTextureCaller( m_dirstring ) ); ++} ++}; ++ ++void TextureBrowser_ShowDirectory( TextureBrowser& textureBrowser, const char* directory ){ ++ if ( TextureBrowser_showWads() ) { ++ Archive* archive = GlobalFileSystem().getArchive( directory ); ++ ASSERT_NOTNULL( archive ); ++ LoadShaderVisitor visitor; ++ archive->forEachFile( Archive::VisitorFunc( visitor, Archive::eFiles, 0 ), "textures/" ); ++ } ++ else ++ { ++ g_TextureBrowser_currentDirectory = directory; ++ TextureBrowser_heightChanged( textureBrowser ); ++ ++ std::size_t shaders_count; ++ GlobalShaderSystem().foreachShaderName(makeCallback( TextureCategoryLoadShader( directory, shaders_count ) ) ); ++ globalOutputStream() << "Showing " << Unsigned( shaders_count ) << " shaders.\n"; ++ ++ if ( g_pGameDescription->mGameType != "doom3" ) { ++ // load remaining texture files ++ ++ StringOutputStream dirstring( 64 ); ++ dirstring << "textures/" << directory; ++ ++ Radiant_getImageModules().foreachModule( LoadTexturesByTypeVisitor( dirstring.c_str() ) ); ++ } ++ } ++ ++ // we'll display the newly loaded textures + all the ones already in use ++ TextureBrowser_SetHideUnused( textureBrowser, false ); ++ ++ TextureBrowser_updateTitle(); ++} ++ ++void TextureBrowser_ShowTagSearchResult( TextureBrowser& textureBrowser, const char* directory ){ ++ g_TextureBrowser_currentDirectory = directory; ++ TextureBrowser_heightChanged( textureBrowser ); ++ ++ std::size_t shaders_count; ++ GlobalShaderSystem().foreachShaderName(makeCallback( TextureCategoryLoadShader( directory, shaders_count ) ) ); ++ globalOutputStream() << "Showing " << Unsigned( shaders_count ) << " shaders.\n"; ++ ++ if ( g_pGameDescription->mGameType != "doom3" ) { ++ // load remaining texture files ++ StringOutputStream dirstring( 64 ); ++ dirstring << "textures/" << directory; ++ ++ { ++ LoadTexturesByTypeVisitor visitor( dirstring.c_str() ); ++ Radiant_getImageModules().foreachModule( visitor ); ++ } ++ } ++ ++ // we'll display the newly loaded textures + all the ones already in use ++ TextureBrowser_SetHideUnused( textureBrowser, false ); ++} ++ ++ ++bool TextureBrowser_hideUnused(); ++ ++void TextureBrowser_hideUnusedExport( const Callback & importer ){ ++ importer( TextureBrowser_hideUnused() ); ++} ++ ++typedef FreeCaller &), TextureBrowser_hideUnusedExport> TextureBrowserHideUnusedExport; ++ ++void TextureBrowser_showShadersExport( const Callback & importer ){ ++ importer( GlobalTextureBrowser().m_showShaders ); ++} ++ ++typedef FreeCaller &), TextureBrowser_showShadersExport> TextureBrowserShowShadersExport; ++ ++void TextureBrowser_showTexturesExport( const Callback & importer ){ ++ importer( GlobalTextureBrowser().m_showTextures ); ++} ++ ++typedef FreeCaller &), TextureBrowser_showTexturesExport> TextureBrowserShowTexturesExport; ++ ++void TextureBrowser_showShaderlistOnly( const Callback & importer ){ ++ importer( g_TextureBrowser_shaderlistOnly ); ++} ++ ++typedef FreeCaller &), TextureBrowser_showShaderlistOnly> TextureBrowserShowShaderlistOnlyExport; ++ ++void TextureBrowser_fixedSize( const Callback & importer ){ ++ importer( g_TextureBrowser_fixedSize ); ++} ++ ++typedef FreeCaller &), TextureBrowser_fixedSize> TextureBrowser_FixedSizeExport; ++ ++void TextureBrowser_filterMissing( const Callback & importer ){ ++ importer( g_TextureBrowser_filterMissing ); ++} ++ ++typedef FreeCaller &), TextureBrowser_filterMissing> TextureBrowser_filterMissingExport; ++ ++void TextureBrowser_filterFallback( const Callback & importer ){ ++ importer( g_TextureBrowser_filterFallback ); ++} ++ ++typedef FreeCaller &), TextureBrowser_filterFallback> TextureBrowser_filterFallbackExport; ++ ++void TextureBrowser_enableAlpha( const Callback & importer ){ ++ importer( g_TextureBrowser_enableAlpha ); ++} ++ ++typedef FreeCaller &), TextureBrowser_enableAlpha> TextureBrowser_enableAlphaExport; ++ ++void TextureBrowser_SetHideUnused( TextureBrowser& textureBrowser, bool hideUnused ){ ++ textureBrowser.m_hideUnused = hideUnused; ++ ++ textureBrowser.m_hideunused_item.update(); ++ ++ TextureBrowser_heightChanged( textureBrowser ); ++ textureBrowser.m_originInvalid = true; ++} ++ ++void TextureBrowser_ShowStartupShaders( TextureBrowser& textureBrowser ){ ++ if ( textureBrowser.m_startupShaders == STARTUPSHADERS_COMMON ) { ++ TextureBrowser_ShowDirectory( textureBrowser, TextureBrowser_getComonShadersDir() ); ++ } ++} ++ ++ ++//++timo NOTE: this is a mix of Shader module stuff and texture explorer ++// it might need to be split in parts or moved out .. dunno ++// scroll origin so the specified texture is completely on screen ++// if current texture is not displayed, nothing is changed ++void TextureBrowser_Focus( TextureBrowser& textureBrowser, const char* name ){ ++ TextureLayout layout; ++ // scroll origin so the texture is completely on screen ++ Texture_StartPos( layout ); ++ ++ for ( QERApp_ActiveShaders_IteratorBegin(); !QERApp_ActiveShaders_IteratorAtEnd(); QERApp_ActiveShaders_IteratorIncrement() ) ++ { ++ IShader* shader = QERApp_ActiveShaders_IteratorCurrent(); ++ ++ if ( !Texture_IsShown( shader, textureBrowser.m_showShaders, textureBrowser.m_showTextures, textureBrowser.m_hideUnused, textureBrowser.m_hideNonShadersInCommon ) ) { ++ continue; ++ } ++ ++ int x, y; ++ Texture_NextPos( textureBrowser, layout, shader->getTexture(), &x, &y ); ++ qtexture_t* q = shader->getTexture(); ++ if ( !q ) { ++ break; ++ } ++ ++ // we have found when texdef->name and the shader name match ++ // NOTE: as everywhere else for our comparisons, we are not case sensitive ++ if ( shader_equal( name, shader->getName() ) ) { ++ //int textureHeight = (int)( q->height * ( (float)textureBrowser.m_textureScale / 100 ) ) + 2 * TextureBrowser_fontHeight( textureBrowser ); ++ int textureWidth, textureHeight; ++ textureBrowser.getTextureWH( q, textureWidth, textureHeight ); ++ textureHeight += 2 * TextureBrowser_fontHeight( textureBrowser ); ++ ++ ++ int originy = TextureBrowser_getOriginY( textureBrowser ); ++ if ( y > originy ) { ++ originy = y + 4; ++ } ++ ++ if ( y - textureHeight < originy - textureBrowser.height ) { ++ originy = ( y - textureHeight ) + textureBrowser.height; ++ } ++ ++ TextureBrowser_setOriginY( textureBrowser, originy ); ++ return; ++ } ++ } ++} ++ ++IShader* Texture_At( TextureBrowser& textureBrowser, int mx, int my ){ ++ my += TextureBrowser_getOriginY( textureBrowser ) - textureBrowser.height; ++ ++ TextureLayout layout; ++ Texture_StartPos( layout ); ++ for ( QERApp_ActiveShaders_IteratorBegin(); !QERApp_ActiveShaders_IteratorAtEnd(); QERApp_ActiveShaders_IteratorIncrement() ) ++ { ++ IShader* shader = QERApp_ActiveShaders_IteratorCurrent(); ++ ++ if ( !Texture_IsShown( shader, textureBrowser.m_showShaders, textureBrowser.m_showTextures, textureBrowser.m_hideUnused, textureBrowser.m_hideNonShadersInCommon ) ) { ++ continue; ++ } ++ ++ int x, y; ++ Texture_NextPos( textureBrowser, layout, shader->getTexture(), &x, &y ); ++ qtexture_t *q = shader->getTexture(); ++ if ( !q ) { ++ break; ++ } ++ ++ int nWidth, nHeight; ++ textureBrowser.getTextureWH( q, nWidth, nHeight ); ++ if ( mx > x && mx - x < nWidth ++ && my < y && y - my < nHeight + TextureBrowser_fontHeight( textureBrowser ) ) { ++ return shader; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ ============== ++ SelectTexture ++ ++ By mouse click ++ ============== ++ */ ++void SelectTexture( TextureBrowser& textureBrowser, int mx, int my, bool bShift ){ ++ IShader* shader = Texture_At( textureBrowser, mx, my ); ++ if ( shader != 0 ) { ++ if ( bShift ) { ++ if ( shader->IsDefault() ) { ++ globalOutputStream() << "ERROR: " << shader->getName() << " is not a shader, it's a texture.\n"; ++ } ++ else{ ++ ViewShader( shader->getShaderFileName(), shader->getName() ); ++ } ++ } ++ else ++ { ++ TextureBrowser_SetSelectedShader( textureBrowser, shader->getName() ); ++ TextureBrowser_textureSelected( shader->getName() ); ++ ++ if ( !FindTextureDialog_isOpen() && !textureBrowser.m_rmbSelected ) { ++ UndoableCommand undo( "textureNameSetSelected" ); ++ Select_SetShader( shader->getName() ); ++ } ++ } ++ } ++} ++ ++/* ++ ============================================================================ ++ ++ MOUSE ACTIONS ++ ++ ============================================================================ ++ */ ++ ++void TextureBrowser_trackingDelta( int x, int y, unsigned int state, void* data ){ ++ TextureBrowser& textureBrowser = *reinterpret_cast( data ); ++ if ( y != 0 ) { ++ int scale = 1; ++ ++ if ( state & GDK_SHIFT_MASK ) { ++ scale = 4; ++ } ++ ++ int originy = TextureBrowser_getOriginY( textureBrowser ); ++ originy += y * scale; ++ TextureBrowser_setOriginY( textureBrowser, originy ); ++ } ++} ++ ++void TextureBrowser_Tracking_MouseUp( TextureBrowser& textureBrowser ){ ++ textureBrowser.m_move_started = false; ++ textureBrowser.m_freezePointer.unfreeze_pointer( textureBrowser.m_parent, false ); ++} ++ ++void TextureBrowser_Tracking_MouseDown( TextureBrowser& textureBrowser ){ ++ if( textureBrowser.m_move_started ){ ++ TextureBrowser_Tracking_MouseUp( textureBrowser ); ++ } ++ textureBrowser.m_move_started = true; ++ textureBrowser.m_freezePointer.freeze_pointer( textureBrowser.m_parent, textureBrowser.m_gl_widget, TextureBrowser_trackingDelta, &textureBrowser ); ++} ++ ++void TextureBrowser_Selection_MouseDown( TextureBrowser& textureBrowser, guint32 flags, int pointx, int pointy ){ ++ SelectTexture( textureBrowser, pointx, textureBrowser.height - 1 - pointy, ( flags & GDK_SHIFT_MASK ) != 0 ); ++} ++ ++/* ++ ============================================================================ ++ ++ DRAWING ++ ++ ============================================================================ ++ */ ++ ++/* ++ ============ ++ Texture_Draw ++ TTimo: relying on the shaders list to display the textures ++ we must query all qtexture_t* to manage and display through the IShaders interface ++ this allows a plugin to completely override the texture system ++ ============ ++ */ ++void Texture_Draw( TextureBrowser& textureBrowser ){ ++ int originy = TextureBrowser_getOriginY( textureBrowser ); ++ ++ glClearColor( textureBrowser.color_textureback[0], ++ textureBrowser.color_textureback[1], ++ textureBrowser.color_textureback[2], ++ 0 ); ++ glViewport( 0, 0, textureBrowser.width, textureBrowser.height ); ++ glMatrixMode( GL_PROJECTION ); ++ glLoadIdentity(); ++ ++ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); ++ glDisable( GL_DEPTH_TEST ); ++ ++ //glDisable( GL_BLEND ); ++ if ( g_TextureBrowser_enableAlpha ) { ++ glEnable( GL_BLEND ); ++ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); ++ } ++ else { ++ glDisable( GL_BLEND ); ++ } ++ ++ glOrtho( 0, textureBrowser.width, originy - textureBrowser.height, originy, -100, 100 ); ++ glEnable( GL_TEXTURE_2D ); ++ ++ glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); ++ ++ int last_y = 0, last_height = 0; ++ ++ TextureLayout layout; ++ Texture_StartPos( layout ); ++ for ( QERApp_ActiveShaders_IteratorBegin(); !QERApp_ActiveShaders_IteratorAtEnd(); QERApp_ActiveShaders_IteratorIncrement() ) ++ { ++ IShader* shader = QERApp_ActiveShaders_IteratorCurrent(); ++ ++ if ( !Texture_IsShown( shader, textureBrowser.m_showShaders, textureBrowser.m_showTextures, textureBrowser.m_hideUnused, textureBrowser.m_hideNonShadersInCommon ) ) { ++ continue; ++ } ++ ++ int x, y; ++ Texture_NextPos( textureBrowser, layout, shader->getTexture(), &x, &y ); ++ qtexture_t *q = shader->getTexture(); ++ if ( !q ) { ++ break; ++ } ++ ++ int nWidth, nHeight; ++ textureBrowser.getTextureWH( q, nWidth, nHeight ); ++ ++ if ( y != last_y ) { ++ last_y = y; ++ last_height = 0; ++ } ++ last_height = std::max( nHeight, last_height ); ++ ++ // Is this texture visible? ++ if ( ( y - nHeight - TextureBrowser_fontHeight( textureBrowser ) < originy ) ++ && ( y > originy - textureBrowser.height ) ) { ++ // borders rules: ++ // if it's the current texture, draw a thick red line, else: ++ // shaders have a white border, simple textures don't ++ // if !texture_showinuse: (some textures displayed may not be in use) ++ // draw an additional square around with 0.5 1 0.5 color ++ glLineWidth( 1 ); ++ const float xf = (float)x; ++ const float yf = (float)( y - TextureBrowser_fontHeight( textureBrowser ) ); ++ float xfMax = xf + 1.5 + nWidth; ++ float xfMin = xf - 1.5; ++ float yfMax = yf + 1.5; ++ float yfMin = yf - nHeight - 1.5; ++ ++ //selected texture ++ if ( shader_equal( TextureBrowser_GetSelectedShader( textureBrowser ), shader->getName() ) ) { ++ glLineWidth( 2 ); ++ if ( textureBrowser.m_rmbSelected ) { ++ glColor3f( 0,0,1 ); ++ } ++ else { ++ glColor3f( 1,0,0 ); ++ } ++ xfMax += .5; ++ xfMin -= .5; ++ yfMax += .5; ++ yfMin -= .5; ++ glDisable( GL_TEXTURE_2D ); ++ glBegin( GL_LINE_LOOP ); ++ glVertex2f( xfMin ,yfMax ); ++ glVertex2f( xfMin ,yfMin ); ++ glVertex2f( xfMax ,yfMin ); ++ glVertex2f( xfMax ,yfMax ); ++ glEnd(); ++ glEnable( GL_TEXTURE_2D ); ++ } ++ // highlight in-use textures ++ else if ( !textureBrowser.m_hideUnused && shader->IsInUse() ) { ++ glColor3f( 0.5,1,0.5 ); ++ glDisable( GL_TEXTURE_2D ); ++ glBegin( GL_LINE_LOOP ); ++ glVertex2f( xfMin ,yfMax ); ++ glVertex2f( xfMin ,yfMin ); ++ glVertex2f( xfMax ,yfMin ); ++ glVertex2f( xfMax ,yfMax ); ++ glEnd(); ++ glEnable( GL_TEXTURE_2D ); ++ } ++ // shader white border: ++ else if ( !shader->IsDefault() ) { ++ glColor3f( 1, 1, 1 ); ++ glDisable( GL_TEXTURE_2D ); ++ glBegin( GL_LINE_LOOP ); ++ glVertex2f( xfMin ,yfMax ); ++ glVertex2f( xfMin ,yfMin ); ++ glVertex2f( xfMax ,yfMin ); ++ glVertex2f( xfMax ,yfMax ); ++ glEnd(); ++ glEnable( GL_TEXTURE_2D ); ++ } ++ ++ // shader stipple: ++ if ( !shader->IsDefault() ) { ++ glEnable( GL_LINE_STIPPLE ); ++ glLineStipple( 1, 0xF000 ); ++ glDisable( GL_TEXTURE_2D ); ++ glBegin( GL_LINE_LOOP ); ++ glColor3f( 0, 0, 0 ); ++ glVertex2f( xfMin ,yfMax ); ++ glVertex2f( xfMin ,yfMin ); ++ glVertex2f( xfMax ,yfMin ); ++ glVertex2f( xfMax ,yfMax ); ++ glEnd(); ++ glDisable( GL_LINE_STIPPLE ); ++ glEnable( GL_TEXTURE_2D ); ++ } ++ ++ // draw checkerboard for transparent textures ++ if ( g_TextureBrowser_enableAlpha ) ++ { ++ glDisable( GL_TEXTURE_2D ); ++ glBegin( GL_QUADS ); ++ int font_height = TextureBrowser_fontHeight( textureBrowser ); ++ for ( int i = 0; i < nHeight; i += 8 ) ++ for ( int j = 0; j < nWidth; j += 8 ) ++ { ++ unsigned char color = (i + j) / 8 % 2 ? 0x66 : 0x99; ++ glColor3ub( color, color, color ); ++ int left = j; ++ int right = std::min(j+8, nWidth); ++ int top = i; ++ int bottom = std::min(i+8, nHeight); ++ glVertex2i(x + right, y - nHeight - font_height + top); ++ glVertex2i(x + left, y - nHeight - font_height + top); ++ glVertex2i(x + left, y - nHeight - font_height + bottom); ++ glVertex2i(x + right, y - nHeight - font_height + bottom); ++ } ++ glEnd(); ++ glEnable( GL_TEXTURE_2D ); ++ } ++ ++ // Draw the texture ++ glBindTexture( GL_TEXTURE_2D, q->texture_number ); ++ GlobalOpenGL_debugAssertNoErrors(); ++ glColor3f( 1,1,1 ); ++ glBegin( GL_QUADS ); ++ glTexCoord2i( 0,0 ); ++ glVertex2i( x,y - TextureBrowser_fontHeight( textureBrowser ) ); ++ glTexCoord2i( 1,0 ); ++ glVertex2i( x + nWidth,y - TextureBrowser_fontHeight( textureBrowser ) ); ++ glTexCoord2i( 1,1 ); ++ glVertex2i( x + nWidth,y - TextureBrowser_fontHeight( textureBrowser ) - nHeight ); ++ glTexCoord2i( 0,1 ); ++ glVertex2i( x,y - TextureBrowser_fontHeight( textureBrowser ) - nHeight ); ++ glEnd(); ++ ++ // draw the texture name ++ glDisable( GL_TEXTURE_2D ); ++ glColor3f( 1,1,1 ); ++ ++ glRasterPos2i( x, y - TextureBrowser_fontHeight( textureBrowser ) + 2 );//+5 ++ ++ // don't draw the directory name ++ const char* name = shader->getName(); ++ name += strlen( name ); ++ while ( name != shader->getName() && *( name - 1 ) != '/' && *( name - 1 ) != '\\' ) ++ name--; ++ ++ GlobalOpenGL().drawString( name ); ++ glEnable( GL_TEXTURE_2D ); ++ } ++ ++ //int totalHeight = abs(y) + last_height + TextureBrowser_fontHeight(textureBrowser) + 4; ++ } ++ ++ ++ // reset the current texture ++ glBindTexture( GL_TEXTURE_2D, 0 ); ++ //qglFinish(); ++} ++ ++void TextureBrowser_queueDraw( TextureBrowser& textureBrowser ){ ++ if ( textureBrowser.m_gl_widget ) { ++ gtk_widget_queue_draw( textureBrowser.m_gl_widget ); ++ } ++} ++ ++ ++void TextureBrowser_setScale( TextureBrowser& textureBrowser, std::size_t scale ){ ++ textureBrowser.m_textureScale = scale; ++ ++ textureBrowser.m_heightChanged = true; ++ textureBrowser.m_originInvalid = true; ++ g_activeShadersChangedCallbacks(); ++ ++ TextureBrowser_queueDraw( textureBrowser ); ++} ++ ++void TextureBrowser_setUniformSize( TextureBrowser& textureBrowser, std::size_t scale ){ ++ textureBrowser.m_uniformTextureSize = scale; ++ ++ textureBrowser.m_heightChanged = true; ++ textureBrowser.m_originInvalid = true; ++ g_activeShadersChangedCallbacks(); ++ ++ TextureBrowser_queueDraw( textureBrowser ); ++} ++ ++void TextureBrowser_setUniformMinSize( TextureBrowser& textureBrowser, std::size_t scale ){ ++ textureBrowser.m_uniformTextureMinSize = scale; ++ ++ textureBrowser.m_heightChanged = true; ++ textureBrowser.m_originInvalid = true; ++ g_activeShadersChangedCallbacks(); ++ ++ TextureBrowser_queueDraw( textureBrowser ); ++} ++ ++void TextureBrowser_MouseWheel( TextureBrowser& textureBrowser, bool bUp ){ ++ int originy = TextureBrowser_getOriginY( textureBrowser ); ++ ++ if ( bUp ) { ++ originy += int(textureBrowser.m_mouseWheelScrollIncrement); ++ } ++ else ++ { ++ originy -= int(textureBrowser.m_mouseWheelScrollIncrement); ++ } ++ ++ TextureBrowser_setOriginY( textureBrowser, originy ); ++} ++ ++XmlTagBuilder TagBuilder; ++ ++enum ++{ ++ TAG_COLUMN, ++ N_COLUMNS ++}; ++ ++void BuildStoreAssignedTags( ui::ListStore store, const char* shader, TextureBrowser* textureBrowser ){ ++ GtkTreeIter iter; ++ ++ store.clear(); ++ ++ std::vector assigned_tags; ++ TagBuilder.GetShaderTags( shader, assigned_tags ); ++ ++ for ( size_t i = 0; i < assigned_tags.size(); i++ ) ++ { ++ store.append(TAG_COLUMN, assigned_tags[i].c_str()); ++ } ++} ++ ++void BuildStoreAvailableTags( ui::ListStore storeAvailable, ++ ui::ListStore storeAssigned, ++ const std::set& allTags, ++ TextureBrowser* textureBrowser ){ ++ GtkTreeIter iterAssigned; ++ GtkTreeIter iterAvailable; ++ std::set::const_iterator iterAll; ++ gchar* tag_assigned; ++ ++ storeAvailable.clear(); ++ ++ bool row = gtk_tree_model_get_iter_first(storeAssigned, &iterAssigned ) != 0; ++ ++ if ( !row ) { // does the shader have tags assigned? ++ for ( iterAll = allTags.begin(); iterAll != allTags.end(); ++iterAll ) ++ { ++ storeAvailable.append(TAG_COLUMN, (*iterAll).c_str()); ++ } ++ } ++ else ++ { ++ while ( row ) // available tags = all tags - assigned tags ++ { ++ gtk_tree_model_get(storeAssigned, &iterAssigned, TAG_COLUMN, &tag_assigned, -1 ); ++ ++ for ( iterAll = allTags.begin(); iterAll != allTags.end(); ++iterAll ) ++ { ++ if ( strcmp( (char*)tag_assigned, ( *iterAll ).c_str() ) != 0 ) { ++ storeAvailable.append(TAG_COLUMN, (*iterAll).c_str()); ++ } ++ else ++ { ++ row = gtk_tree_model_iter_next(storeAssigned, &iterAssigned ) != 0; ++ ++ if ( row ) { ++ gtk_tree_model_get(storeAssigned, &iterAssigned, TAG_COLUMN, &tag_assigned, -1 ); ++ } ++ } ++ } ++ } ++ } ++} ++ ++gboolean TextureBrowser_button_press( ui::Widget widget, GdkEventButton* event, TextureBrowser* textureBrowser ){ ++ if ( event->type == GDK_BUTTON_PRESS ) { ++ if ( event->button == 3 ) { ++ if ( GlobalTextureBrowser().m_tags ) { ++ textureBrowser->m_rmbSelected = true; ++ TextureBrowser_Selection_MouseDown( *textureBrowser, event->state, static_cast( event->x ), static_cast( event->y ) ); ++ ++ BuildStoreAssignedTags( textureBrowser->m_assigned_store, textureBrowser->shader.c_str(), textureBrowser ); ++ BuildStoreAvailableTags( textureBrowser->m_available_store, textureBrowser->m_assigned_store, textureBrowser->m_all_tags, textureBrowser ); ++ textureBrowser->m_heightChanged = true; ++ textureBrowser->m_tag_frame.show(); ++ ++ ui::process(); ++ ++ TextureBrowser_Focus( *textureBrowser, textureBrowser->shader.c_str() ); ++ } ++ else ++ { ++ TextureBrowser_Tracking_MouseDown( *textureBrowser ); ++ } ++ } ++ else if ( event->button == 1 ) { ++ TextureBrowser_Selection_MouseDown( *textureBrowser, event->state, static_cast( event->x ), static_cast( event->y ) ); ++ ++ if ( GlobalTextureBrowser().m_tags ) { ++ textureBrowser->m_rmbSelected = false; ++ textureBrowser->m_tag_frame.hide(); ++ } ++ } ++ } ++ else if ( event->type == GDK_2BUTTON_PRESS && event->button == 1 ) { ++ #define GARUX_DISABLE_2BUTTON ++ #ifndef GARUX_DISABLE_2BUTTON ++ CopiedString texName = textureBrowser->shader; ++ //const char* sh = texName.c_str(); ++ char* sh = const_cast( texName.c_str() ); ++ char* dir = strrchr( sh, '/' ); ++ if( dir != NULL ){ ++ *(dir + 1) = '\0'; ++ dir = strchr( sh, '/' ); ++ if( dir != NULL ){ ++ dir++; ++ if( *dir != '\0'){ ++ ScopeDisableScreenUpdates disableScreenUpdates( dir, "Loading Textures" ); ++ TextureBrowser_ShowDirectory( *textureBrowser, dir ); ++ TextureBrowser_Focus( *textureBrowser, textureBrowser->shader.c_str() ); ++ TextureBrowser_queueDraw( *textureBrowser ); ++ } ++ } ++ } ++ #endif ++ } ++ else if ( event->type == GDK_2BUTTON_PRESS && event->button == 3 ) { ++ ScopeDisableScreenUpdates disableScreenUpdates( TextureBrowser_getComonShadersDir(), "Loading Textures" ); ++ TextureBrowser_ShowDirectory( *textureBrowser, TextureBrowser_getComonShadersDir() ); ++ TextureBrowser_queueDraw( *textureBrowser ); ++ } ++ return FALSE; ++} ++ ++gboolean TextureBrowser_button_release( ui::Widget widget, GdkEventButton* event, TextureBrowser* textureBrowser ){ ++ if ( event->type == GDK_BUTTON_RELEASE ) { ++ if ( event->button == 3 ) { ++ if ( !GlobalTextureBrowser().m_tags ) { ++ TextureBrowser_Tracking_MouseUp( *textureBrowser ); ++ } ++ } ++ } ++ return FALSE; ++} ++ ++gboolean TextureBrowser_motion( ui::Widget widget, GdkEventMotion *event, TextureBrowser* textureBrowser ){ ++ return FALSE; ++} ++ ++gboolean TextureBrowser_scroll( ui::Widget widget, GdkEventScroll* event, TextureBrowser* textureBrowser ){ ++ if ( event->direction == GDK_SCROLL_UP ) { ++ TextureBrowser_MouseWheel( *textureBrowser, true ); ++ } ++ else if ( event->direction == GDK_SCROLL_DOWN ) { ++ TextureBrowser_MouseWheel( *textureBrowser, false ); ++ } ++ return FALSE; ++} ++ ++void TextureBrowser_scrollChanged( void* data, gdouble value ){ ++ //globalOutputStream() << "vertical scroll\n"; ++ TextureBrowser_setOriginY( *reinterpret_cast( data ), -(int)value ); ++} ++ ++static void TextureBrowser_verticalScroll(ui::Adjustment adjustment, TextureBrowser* textureBrowser ){ ++ textureBrowser->m_scrollAdjustment.value_changed( gtk_adjustment_get_value(adjustment) ); ++} ++ ++void TextureBrowser_updateScroll( TextureBrowser& textureBrowser ){ ++ if ( textureBrowser.m_showTextureScrollbar ) { ++ int totalHeight = TextureBrowser_TotalHeight( textureBrowser ); ++ ++ totalHeight = std::max( totalHeight, textureBrowser.height ); ++ ++ auto vadjustment = gtk_range_get_adjustment( GTK_RANGE( textureBrowser.m_texture_scroll ) ); ++ ++ gtk_adjustment_set_value(vadjustment, -TextureBrowser_getOriginY( textureBrowser )); ++ gtk_adjustment_set_page_size(vadjustment, textureBrowser.height); ++ gtk_adjustment_set_page_increment(vadjustment, textureBrowser.height / 2); ++ gtk_adjustment_set_step_increment(vadjustment, 20); ++ gtk_adjustment_set_lower(vadjustment, 0); ++ gtk_adjustment_set_upper(vadjustment, totalHeight); ++ ++ g_signal_emit_by_name( G_OBJECT( vadjustment ), "changed" ); ++ } ++} ++ ++gboolean TextureBrowser_size_allocate( ui::Widget widget, GtkAllocation* allocation, TextureBrowser* textureBrowser ){ ++ textureBrowser->width = allocation->width; ++ textureBrowser->height = allocation->height; ++ TextureBrowser_heightChanged( *textureBrowser ); ++ textureBrowser->m_originInvalid = true; ++ TextureBrowser_queueDraw( *textureBrowser ); ++ return FALSE; ++} ++ ++gboolean TextureBrowser_expose( ui::Widget widget, GdkEventExpose* event, TextureBrowser* textureBrowser ){ ++ if ( glwidget_make_current( textureBrowser->m_gl_widget ) != FALSE ) { ++ GlobalOpenGL_debugAssertNoErrors(); ++ TextureBrowser_evaluateHeight( *textureBrowser ); ++ Texture_Draw( *textureBrowser ); ++ GlobalOpenGL_debugAssertNoErrors(); ++ glwidget_swap_buffers( textureBrowser->m_gl_widget ); ++ } ++ return FALSE; ++} ++ ++ ++TextureBrowser g_TextureBrowser; ++ ++TextureBrowser& GlobalTextureBrowser(){ ++ return g_TextureBrowser; ++} ++ ++bool TextureBrowser_hideUnused(){ ++ return g_TextureBrowser.m_hideUnused; ++} ++ ++void TextureBrowser_ToggleHideUnused(){ ++ if ( g_TextureBrowser.m_hideUnused ) { ++ TextureBrowser_SetHideUnused( g_TextureBrowser, false ); ++ } ++ else ++ { ++ TextureBrowser_SetHideUnused( g_TextureBrowser, true ); ++ } ++} ++ ++void TextureGroups_constructTreeModel( TextureGroups groups, ui::TreeStore store ){ ++ // put the information from the old textures menu into a treeview ++ GtkTreeIter iter, child; ++ ++ TextureGroups::const_iterator i = groups.begin(); ++ while ( i != groups.end() ) ++ { ++ const char* dirName = ( *i ).c_str(); ++ const char* firstUnderscore = strchr( dirName, '_' ); ++ StringRange dirRoot( dirName, ( firstUnderscore == 0 ) ? dirName : firstUnderscore + 1 ); ++ ++ TextureGroups::const_iterator next = i; ++ ++next; ++ if ( firstUnderscore != 0 ++ && next != groups.end() ++ && string_equal_start( ( *next ).c_str(), dirRoot ) ) { ++ gtk_tree_store_append( store, &iter, NULL ); ++ gtk_tree_store_set( store, &iter, 0, CopiedString( StringRange( dirName, firstUnderscore ) ).c_str(), -1 ); ++ ++ // keep going... ++ while ( i != groups.end() && string_equal_start( ( *i ).c_str(), dirRoot ) ) ++ { ++ gtk_tree_store_append( store, &child, &iter ); ++ gtk_tree_store_set( store, &child, 0, ( *i ).c_str(), -1 ); ++ ++i; ++ } ++ } ++ else ++ { ++ gtk_tree_store_append( store, &iter, NULL ); ++ gtk_tree_store_set( store, &iter, 0, dirName, -1 ); ++ ++i; ++ } ++ } ++} ++ ++TextureGroups TextureGroups_constructTreeView(){ ++ TextureGroups groups; ++ ++ if ( TextureBrowser_showWads() ) { ++ GlobalFileSystem().forEachArchive( TextureGroupsAddWadCaller( groups ) ); ++ } ++ else ++ { ++ // scan texture dirs and pak files only if not restricting to shaderlist ++ if ( g_pGameDescription->mGameType != "doom3" && !g_TextureBrowser_shaderlistOnly ) { ++ GlobalFileSystem().forEachDirectory( "textures/", TextureGroupsAddDirectoryCaller( groups ) ); ++ } ++ ++ GlobalShaderSystem().foreachShaderName( TextureGroupsAddShaderCaller( groups ) ); ++ } ++ ++ return groups; ++} ++ ++void TextureBrowser_constructTreeStore(){ ++ TextureGroups groups = TextureGroups_constructTreeView(); ++ auto store = ui::TreeStore::from(gtk_tree_store_new( 1, G_TYPE_STRING )); ++ TextureGroups_constructTreeModel( groups, store ); ++ std::set::iterator iter; ++ ++ gtk_tree_view_set_model(g_TextureBrowser.m_treeViewTree, store); ++ ++ g_object_unref( G_OBJECT( store ) ); ++} ++ ++void TextureBrowser_constructTreeStoreTags(){ ++ //TextureGroups groups; ++ auto store = ui::TreeStore::from(gtk_tree_store_new( 1, G_TYPE_STRING )); ++ auto model = g_TextureBrowser.m_all_tags_list; ++ ++ gtk_tree_view_set_model(g_TextureBrowser.m_treeViewTags, model ); ++ ++ g_object_unref( G_OBJECT( store ) ); ++} ++ ++void TreeView_onRowActivated( ui::TreeView treeview, ui::TreePath path, ui::TreeViewColumn col, gpointer userdata ){ ++ GtkTreeIter iter; ++ ++ auto model = gtk_tree_view_get_model(treeview ); ++ ++ if ( gtk_tree_model_get_iter( model, &iter, path ) ) { ++ gchar dirName[1024]; ++ ++ gchar* buffer; ++ gtk_tree_model_get( model, &iter, 0, &buffer, -1 ); ++ strcpy( dirName, buffer ); ++ g_free( buffer ); ++ ++ g_TextureBrowser.m_searchedTags = false; ++ ++ if ( !TextureBrowser_showWads() ) { ++ strcat( dirName, "/" ); ++ } ++ ++ ScopeDisableScreenUpdates disableScreenUpdates( dirName, "Loading Textures" ); ++ TextureBrowser_ShowDirectory( GlobalTextureBrowser(), dirName ); ++ TextureBrowser_queueDraw( GlobalTextureBrowser() ); ++ //deactivate, so SPACE and RETURN wont be broken for 2d ++ gtk_window_set_focus( GTK_WINDOW( gtk_widget_get_toplevel( GTK_WIDGET( treeview ) ) ), NULL ); ++ } ++} ++ ++void TextureBrowser_createTreeViewTree(){ ++ gtk_tree_view_set_enable_search(g_TextureBrowser.m_treeViewTree, FALSE ); ++ ++ gtk_tree_view_set_headers_visible(g_TextureBrowser.m_treeViewTree, FALSE ); ++ g_TextureBrowser.m_treeViewTree.connect( "row-activated", (GCallback) TreeView_onRowActivated, NULL ); ++ ++ auto renderer = ui::CellRendererText(ui::New); ++ gtk_tree_view_insert_column_with_attributes(g_TextureBrowser.m_treeViewTree, -1, "", renderer, "text", 0, NULL ); ++ ++ TextureBrowser_constructTreeStore(); ++} ++ ++void TextureBrowser_addTag(); ++ ++void TextureBrowser_renameTag(); ++ ++void TextureBrowser_deleteTag(); ++ ++void TextureBrowser_createContextMenu( ui::Widget treeview, GdkEventButton *event ){ ++ ui::Widget menu = ui::Menu(ui::New); ++ ++ ui::Widget menuitem = ui::MenuItem( "Add tag" ); ++ menuitem.connect( "activate", (GCallback)TextureBrowser_addTag, treeview ); ++ gtk_menu_shell_append( GTK_MENU_SHELL( menu ), menuitem ); ++ ++ menuitem = ui::MenuItem( "Rename tag" ); ++ menuitem.connect( "activate", (GCallback)TextureBrowser_renameTag, treeview ); ++ gtk_menu_shell_append( GTK_MENU_SHELL( menu ), menuitem ); ++ ++ menuitem = ui::MenuItem( "Delete tag" ); ++ menuitem.connect( "activate", (GCallback)TextureBrowser_deleteTag, treeview ); ++ gtk_menu_shell_append( GTK_MENU_SHELL( menu ), menuitem ); ++ ++ gtk_widget_show_all( menu ); ++ ++ gtk_menu_popup( GTK_MENU( menu ), NULL, NULL, NULL, NULL, ++ ( event != NULL ) ? event->button : 0, ++ gdk_event_get_time( (GdkEvent*)event ) ); ++} ++ ++gboolean TreeViewTags_onButtonPressed( ui::TreeView treeview, GdkEventButton *event ){ ++ if ( event->type == GDK_BUTTON_PRESS && event->button == 3 ) { ++ GtkTreePath *path; ++ auto selection = gtk_tree_view_get_selection(treeview ); ++ ++ if ( gtk_tree_view_get_path_at_pos(treeview, event->x, event->y, &path, NULL, NULL, NULL ) ) { ++ gtk_tree_selection_unselect_all( selection ); ++ gtk_tree_selection_select_path( selection, path ); ++ gtk_tree_path_free( path ); ++ } ++ ++ TextureBrowser_createContextMenu( treeview, event ); ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++void TextureBrowser_createTreeViewTags(){ ++ g_TextureBrowser.m_treeViewTags = ui::TreeView(ui::New); ++ gtk_tree_view_set_enable_search(g_TextureBrowser.m_treeViewTags, FALSE ); ++ ++ g_TextureBrowser.m_treeViewTags.connect( "button-press-event", (GCallback)TreeViewTags_onButtonPressed, NULL ); ++ ++ gtk_tree_view_set_headers_visible(g_TextureBrowser.m_treeViewTags, FALSE ); ++ ++ auto renderer = ui::CellRendererText(ui::New); ++ gtk_tree_view_insert_column_with_attributes(g_TextureBrowser.m_treeViewTags, -1, "", renderer, "text", 0, NULL ); ++ ++ TextureBrowser_constructTreeStoreTags(); ++} ++ ++ui::MenuItem TextureBrowser_constructViewMenu( ui::Menu menu ){ ++ ui::MenuItem textures_menu_item = ui::MenuItem(new_sub_menu_item_with_mnemonic( "_View" )); ++ ++ if ( g_Layout_enableDetachableMenus.m_value ) { ++ menu_tearoff( menu ); ++ } ++ ++ create_check_menu_item_with_mnemonic( menu, "Hide _Unused", "ShowInUse" ); ++ if ( string_empty( g_pGameDescription->getKeyValue( "show_wads" ) ) ) { ++ create_check_menu_item_with_mnemonic( menu, "Hide Image Missing", "FilterMissing" ); ++ } ++ ++ // hide notex and shadernotex on texture browser: no one wants to apply them ++ create_check_menu_item_with_mnemonic( menu, "Hide Fallback", "FilterFallback" ); ++ ++ menu_separator( menu ); ++ ++ ++ // we always want to show shaders but don't want a "Show Shaders" menu for doom3 and .wad file games ++ if ( g_pGameDescription->mGameType == "doom3" || !string_empty( g_pGameDescription->getKeyValue( "show_wads" ) ) ) { ++ g_TextureBrowser.m_showShaders = true; ++ } ++ else ++ { ++ create_check_menu_item_with_mnemonic( menu, "Show shaders", "ToggleShowShaders" ); ++ create_check_menu_item_with_mnemonic( menu, "Show textures", "ToggleShowTextures" ); ++ menu_separator( menu ); ++ } ++ ++ if ( g_TextureBrowser.m_tags ) { ++ create_menu_item_with_mnemonic( menu, "Show Untagged", "ShowUntagged" ); ++ } ++ if ( g_pGameDescription->mGameType != "doom3" && string_empty( g_pGameDescription->getKeyValue( "show_wads" ) ) ) { ++ create_check_menu_item_with_mnemonic( menu, "ShaderList Only", "ToggleShowShaderlistOnly" ); ++ } ++ ++ menu_separator( menu ); ++ create_check_menu_item_with_mnemonic( menu, "Fixed Size", "FixedSize" ); ++ create_check_menu_item_with_mnemonic( menu, "Transparency", "EnableAlpha" ); ++ ++ if ( string_empty( g_pGameDescription->getKeyValue( "show_wads" ) ) ) { ++ menu_separator( menu ); ++ g_TextureBrowser.m_shader_info_item = ui::Widget(create_menu_item_with_mnemonic( menu, "Shader Info", "ShaderInfo" )); ++ gtk_widget_set_sensitive( g_TextureBrowser.m_shader_info_item, FALSE ); ++ } ++ ++ ++ return textures_menu_item; ++} ++ ++void Popup_View_Menu( GtkWidget *widget, GtkMenu *menu ){ ++ gtk_menu_popup( menu, NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time() ); ++} ++ ++ui::MenuItem TextureBrowser_constructToolsMenu( ui::Menu menu ){ ++ ui::MenuItem textures_menu_item = ui::MenuItem(new_sub_menu_item_with_mnemonic( "_Tools" )); ++ ++ if ( g_Layout_enableDetachableMenus.m_value ) { ++ menu_tearoff( menu ); ++ } ++ ++ create_menu_item_with_mnemonic( menu, "Flush & Reload Shaders", "RefreshShaders" ); ++ create_menu_item_with_mnemonic( menu, "Find / Replace...", "FindReplaceTextures" ); ++ ++ return textures_menu_item; ++} ++ ++ui::MenuItem TextureBrowser_constructTagsMenu( ui::Menu menu ){ ++ ui::MenuItem textures_menu_item = ui::MenuItem(new_sub_menu_item_with_mnemonic( "T_ags" )); ++ ++ if ( g_Layout_enableDetachableMenus.m_value ) { ++ menu_tearoff( menu ); ++ } ++ ++ create_menu_item_with_mnemonic( menu, "Add tag", "AddTag" ); ++ create_menu_item_with_mnemonic( menu, "Rename tag", "RenameTag" ); ++ create_menu_item_with_mnemonic( menu, "Delete tag", "DeleteTag" ); ++ menu_separator( menu ); ++ create_menu_item_with_mnemonic( menu, "Copy tags from selected", "CopyTag" ); ++ create_menu_item_with_mnemonic( menu, "Paste tags to selected", "PasteTag" ); ++ ++ return textures_menu_item; ++} ++ ++gboolean TextureBrowser_tagMoveHelper( ui::TreeModel model, ui::TreePath path, GtkTreeIter iter, GSList** selected ){ ++ g_assert( selected != NULL ); ++ ++ auto rowref = gtk_tree_row_reference_new( model, path ); ++ *selected = g_slist_append( *selected, rowref ); ++ ++ return FALSE; ++} ++ ++void TextureBrowser_assignTags(){ ++ GSList* selected = NULL; ++ GSList* node; ++ gchar* tag_assigned; ++ ++ auto selection = gtk_tree_view_get_selection(g_TextureBrowser.m_available_tree ); ++ ++ gtk_tree_selection_selected_foreach( selection, (GtkTreeSelectionForeachFunc)TextureBrowser_tagMoveHelper, &selected ); ++ ++ if ( selected != NULL ) { ++ for ( node = selected; node != NULL; node = node->next ) ++ { ++ auto path = gtk_tree_row_reference_get_path( (GtkTreeRowReference*)node->data ); ++ ++ if ( path ) { ++ GtkTreeIter iter; ++ ++ if ( gtk_tree_model_get_iter(g_TextureBrowser.m_available_store, &iter, path ) ) { ++ gtk_tree_model_get(g_TextureBrowser.m_available_store, &iter, TAG_COLUMN, &tag_assigned, -1 ); ++ if ( !TagBuilder.CheckShaderTag( g_TextureBrowser.shader.c_str() ) ) { ++ // create a custom shader/texture entry ++ IShader* ishader = QERApp_Shader_ForName( g_TextureBrowser.shader.c_str() ); ++ CopiedString filename = ishader->getShaderFileName(); ++ ++ if ( filename.empty() ) { ++ // it's a texture ++ TagBuilder.AddShaderNode( g_TextureBrowser.shader.c_str(), CUSTOM, TEXTURE ); ++ } ++ else { ++ // it's a shader ++ TagBuilder.AddShaderNode( g_TextureBrowser.shader.c_str(), CUSTOM, SHADER ); ++ } ++ ishader->DecRef(); ++ } ++ TagBuilder.AddShaderTag( g_TextureBrowser.shader.c_str(), (char*)tag_assigned, TAG ); ++ ++ gtk_list_store_remove( g_TextureBrowser.m_available_store, &iter ); ++ g_TextureBrowser.m_assigned_store.append(TAG_COLUMN, tag_assigned); ++ } ++ } ++ } ++ ++ g_slist_foreach( selected, (GFunc)gtk_tree_row_reference_free, NULL ); ++ ++ // Save changes ++ TagBuilder.SaveXmlDoc(); ++ } ++ g_slist_free( selected ); ++} ++ ++void TextureBrowser_removeTags(){ ++ GSList* selected = NULL; ++ GSList* node; ++ gchar* tag; ++ ++ auto selection = gtk_tree_view_get_selection(g_TextureBrowser.m_assigned_tree ); ++ ++ gtk_tree_selection_selected_foreach( selection, (GtkTreeSelectionForeachFunc)TextureBrowser_tagMoveHelper, &selected ); ++ ++ if ( selected != NULL ) { ++ for ( node = selected; node != NULL; node = node->next ) ++ { ++ auto path = gtk_tree_row_reference_get_path( (GtkTreeRowReference*)node->data ); ++ ++ if ( path ) { ++ GtkTreeIter iter; ++ ++ if ( gtk_tree_model_get_iter(g_TextureBrowser.m_assigned_store, &iter, path ) ) { ++ gtk_tree_model_get(g_TextureBrowser.m_assigned_store, &iter, TAG_COLUMN, &tag, -1 ); ++ TagBuilder.DeleteShaderTag( g_TextureBrowser.shader.c_str(), tag ); ++ gtk_list_store_remove( g_TextureBrowser.m_assigned_store, &iter ); ++ } ++ } ++ } ++ ++ g_slist_foreach( selected, (GFunc)gtk_tree_row_reference_free, NULL ); ++ ++ // Update the "available tags list" ++ BuildStoreAvailableTags( g_TextureBrowser.m_available_store, g_TextureBrowser.m_assigned_store, g_TextureBrowser.m_all_tags, &g_TextureBrowser ); ++ ++ // Save changes ++ TagBuilder.SaveXmlDoc(); ++ } ++ g_slist_free( selected ); ++} ++ ++void TextureBrowser_buildTagList(){ ++ g_TextureBrowser.m_all_tags_list.clear(); ++ ++ std::set::iterator iter; ++ ++ for ( iter = g_TextureBrowser.m_all_tags.begin(); iter != g_TextureBrowser.m_all_tags.end(); ++iter ) ++ { ++ g_TextureBrowser.m_all_tags_list.append(TAG_COLUMN, (*iter).c_str()); ++ } ++} ++ ++void TextureBrowser_searchTags(){ ++ GSList* selected = NULL; ++ GSList* node; ++ gchar* tag; ++ char buffer[256]; ++ char tags_searched[256]; ++ ++ auto selection = gtk_tree_view_get_selection(g_TextureBrowser.m_treeViewTags ); ++ ++ gtk_tree_selection_selected_foreach( selection, (GtkTreeSelectionForeachFunc)TextureBrowser_tagMoveHelper, &selected ); ++ ++ if ( selected != NULL ) { ++ strcpy( buffer, "/root/*/*[tag='" ); ++ strcpy( tags_searched, "[TAGS] " ); ++ ++ for ( node = selected; node != NULL; node = node->next ) ++ { ++ auto path = gtk_tree_row_reference_get_path( (GtkTreeRowReference*)node->data ); ++ ++ if ( path ) { ++ GtkTreeIter iter; ++ ++ if ( gtk_tree_model_get_iter(g_TextureBrowser.m_all_tags_list, &iter, path ) ) { ++ gtk_tree_model_get(g_TextureBrowser.m_all_tags_list, &iter, TAG_COLUMN, &tag, -1 ); ++ ++ strcat( buffer, tag ); ++ strcat( tags_searched, tag ); ++ if ( node != g_slist_last( node ) ) { ++ strcat( buffer, "' and tag='" ); ++ strcat( tags_searched, ", " ); ++ } ++ } ++ } ++ } ++ ++ strcat( buffer, "']" ); ++ ++ g_slist_foreach( selected, (GFunc)gtk_tree_row_reference_free, NULL ); ++ ++ g_TextureBrowser.m_found_shaders.clear(); // delete old list ++ TagBuilder.TagSearch( buffer, g_TextureBrowser.m_found_shaders ); ++ ++ if ( !g_TextureBrowser.m_found_shaders.empty() ) { // found something ++ size_t shaders_found = g_TextureBrowser.m_found_shaders.size(); ++ ++ globalOutputStream() << "Found " << (unsigned int)shaders_found << " textures and shaders with " << tags_searched << "\n"; ++ ScopeDisableScreenUpdates disableScreenUpdates( "Searching...", "Loading Textures" ); ++ ++ std::set::iterator iter; ++ ++ for ( iter = g_TextureBrowser.m_found_shaders.begin(); iter != g_TextureBrowser.m_found_shaders.end(); iter++ ) ++ { ++ std::string path = ( *iter ).c_str(); ++ size_t pos = path.find_last_of( "/", path.size() ); ++ std::string name = path.substr( pos + 1, path.size() ); ++ path = path.substr( 0, pos + 1 ); ++ TextureDirectory_loadTexture( path.c_str(), name.c_str() ); ++ } ++ } ++ g_TextureBrowser.m_searchedTags = true; ++ g_TextureBrowser_currentDirectory = tags_searched; ++ ++ g_TextureBrowser.m_nTotalHeight = 0; ++ TextureBrowser_setOriginY( g_TextureBrowser, 0 ); ++ TextureBrowser_heightChanged( g_TextureBrowser ); ++ TextureBrowser_updateTitle(); ++ } ++ g_slist_free( selected ); ++} ++ ++void TextureBrowser_toggleSearchButton(){ ++ gint page = gtk_notebook_get_current_page( GTK_NOTEBOOK( g_TextureBrowser.m_tag_notebook ) ); ++ ++ if ( page == 0 ) { // tag page ++ gtk_widget_show_all( g_TextureBrowser.m_search_button ); ++ } ++ else { ++ g_TextureBrowser.m_search_button.hide(); ++ } ++} ++ ++void TextureBrowser_constructTagNotebook(){ ++ g_TextureBrowser.m_tag_notebook = ui::Widget::from(gtk_notebook_new()); ++ ui::Widget labelTags = ui::Label( "Tags" ); ++ ui::Widget labelTextures = ui::Label( "Textures" ); ++ ++ gtk_notebook_append_page( GTK_NOTEBOOK( g_TextureBrowser.m_tag_notebook ), g_TextureBrowser.m_scr_win_tree, labelTextures ); ++ gtk_notebook_append_page( GTK_NOTEBOOK( g_TextureBrowser.m_tag_notebook ), g_TextureBrowser.m_scr_win_tags, labelTags ); ++ ++ g_TextureBrowser.m_tag_notebook.connect( "switch-page", G_CALLBACK( TextureBrowser_toggleSearchButton ), NULL ); ++ ++ gtk_widget_show_all( g_TextureBrowser.m_tag_notebook ); ++} ++ ++void TextureBrowser_constructSearchButton(){ ++ auto image = ui::Widget::from(gtk_image_new_from_stock( GTK_STOCK_FIND, GTK_ICON_SIZE_SMALL_TOOLBAR )); ++ g_TextureBrowser.m_search_button = ui::Button(ui::New); ++ g_TextureBrowser.m_search_button.connect( "clicked", G_CALLBACK( TextureBrowser_searchTags ), NULL ); ++ gtk_widget_set_tooltip_text(g_TextureBrowser.m_search_button, "Search with selected tags"); ++ g_TextureBrowser.m_search_button.add(image); ++} ++ ++void TextureBrowser_checkTagFile(){ ++ const char SHADERTAG_FILE[] = "shadertags.xml"; ++ CopiedString default_filename, rc_filename; ++ StringOutputStream stream( 256 ); ++ ++ stream << LocalRcPath_get(); ++ stream << SHADERTAG_FILE; ++ rc_filename = stream.c_str(); ++ ++ if ( file_exists( rc_filename.c_str() ) ) { ++ g_TextureBrowser.m_tags = TagBuilder.OpenXmlDoc( rc_filename.c_str() ); ++ ++ if ( g_TextureBrowser.m_tags ) { ++ globalOutputStream() << "Loading tag file " << rc_filename.c_str() << ".\n"; ++ } ++ } ++ else ++ { ++ // load default tagfile ++ stream.clear(); ++ stream << g_pGameDescription->mGameToolsPath.c_str(); ++ stream << SHADERTAG_FILE; ++ default_filename = stream.c_str(); ++ ++ if ( file_exists( default_filename.c_str() ) ) { ++ g_TextureBrowser.m_tags = TagBuilder.OpenXmlDoc( default_filename.c_str(), rc_filename.c_str() ); ++ ++ if ( g_TextureBrowser.m_tags ) { ++ globalOutputStream() << "Loading default tag file " << default_filename.c_str() << ".\n"; ++ } ++ } ++ else ++ { ++ globalOutputStream() << "Unable to find default tag file " << default_filename.c_str() << ". No tag support. Plugins -> ShaderPlug -> Create tag file: to start using tags\n"; ++ } ++ } ++} ++ ++void TextureBrowser_SetNotex(){ ++ IShader* notex = QERApp_Shader_ForName( DEFAULT_NOTEX_NAME ); ++ IShader* shadernotex = QERApp_Shader_ForName( DEFAULT_SHADERNOTEX_NAME ); ++ ++ g_notex = notex->getTexture()->name; ++ g_shadernotex = shadernotex->getTexture()->name; ++ ++ notex->DecRef(); ++ shadernotex->DecRef(); ++} ++ ++ui::Widget TextureBrowser_constructWindow( ui::Window toplevel ){ ++ // The gl_widget and the tag assignment frame should be packed into a GtkVPaned with the slider ++ // position stored in local.pref. gtk_paned_get_position() and gtk_paned_set_position() don't ++ // seem to work in gtk 2.4 and the arrow buttons don't handle GTK_FILL, so here's another thing ++ // for the "once-the-gtk-libs-are-updated-TODO-list" :x ++ ++ TextureBrowser_checkTagFile(); ++ TextureBrowser_SetNotex(); ++ ++ GlobalShaderSystem().setActiveShadersChangedNotify( ReferenceCaller( g_TextureBrowser ) ); ++ ++ g_TextureBrowser.m_parent = toplevel; ++ ++ auto table = ui::Table(3, 3, FALSE); ++ auto vbox = ui::VBox(FALSE, 0); ++ table.attach(vbox, {0, 1, 1, 3}, {GTK_FILL, GTK_FILL}); ++ vbox.show(); ++ ++ // ui::Widget menu_bar{ui::null}; ++ auto toolbar = ui::Toolbar::from( gtk_toolbar_new() ); ++ ++ { // menu bar ++ // menu_bar = ui::Widget::from(gtk_menu_bar_new()); ++ auto menu_view = ui::Menu(ui::New); ++ // auto view_item = TextureBrowser_constructViewMenu( menu_view ); ++ TextureBrowser_constructViewMenu( menu_view ); ++ gtk_menu_set_title( menu_view, "View" ); ++ // gtk_menu_item_set_submenu( GTK_MENU_ITEM( view_item ), menu_view ); ++ // gtk_menu_shell_append( GTK_MENU_SHELL( menu_bar ), view_item ); ++ ++ //gtk_table_attach( GTK_TABLE( table ), GTK_WIDGET( toolbar ), 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0 ); ++ gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( toolbar ), FALSE, FALSE, 0 ); ++ ++ //view menu button ++ { ++ auto button = toolbar_append_button( toolbar, "View", "texbro_view.png" ); ++ button.dimensions( 22, 22 ); ++ button.connect( "clicked", G_CALLBACK( Popup_View_Menu ), menu_view ); ++ ++ //to show detached menu over floating tex bro ++ gtk_menu_attach_to_widget( GTK_MENU( menu_view ), GTK_WIDGET( button ), NULL ); ++ } ++ { ++ auto button = toolbar_append_button( toolbar, "Find / Replace...", "texbro_gtk-find-and-replace.png", "FindReplaceTextures" ); ++ button.dimensions( 22, 22 ); ++ } ++ { ++ auto button = toolbar_append_button( toolbar, "Flush & Reload Shaders", "texbro_refresh.png", "RefreshShaders" ); ++ button.dimensions( 22, 22 ); ++ } ++ toolbar.show(); ++ ++/* ++ auto menu_tools = ui::Menu(ui::New); ++ auto tools_item = TextureBrowser_constructToolsMenu( menu_tools ); ++ gtk_menu_item_set_submenu( GTK_MENU_ITEM( tools_item ), menu_tools ); ++ gtk_menu_shell_append( GTK_MENU_SHELL( menu_bar ), tools_item ); ++*/ ++ // table.attach(menu_bar, {0, 3, 0, 1}, {GTK_FILL, GTK_SHRINK}); ++ // menu_bar.show(); ++ } ++ { // Texture TreeView ++ g_TextureBrowser.m_scr_win_tree = ui::ScrolledWindow(ui::New); ++ gtk_container_set_border_width( GTK_CONTAINER( g_TextureBrowser.m_scr_win_tree ), 0 ); ++ ++ // vertical only scrolling for treeview ++ gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( g_TextureBrowser.m_scr_win_tree ), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS ); ++ ++ g_TextureBrowser.m_scr_win_tree.show(); ++ ++ TextureBrowser_createTreeViewTree(); ++ ++ gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW( g_TextureBrowser.m_scr_win_tree ), g_TextureBrowser.m_treeViewTree ); ++ g_TextureBrowser.m_treeViewTree.show(); ++ } ++ { // gl_widget scrollbar ++ auto w = ui::Widget::from(gtk_vscrollbar_new( ui::Adjustment( 0,0,0,1,1,0 ) )); ++ table.attach(w, {2, 3, 1, 2}, {GTK_SHRINK, GTK_FILL}); ++ w.show(); ++ g_TextureBrowser.m_texture_scroll = w; ++ ++ auto vadjustment = ui::Adjustment::from(gtk_range_get_adjustment( GTK_RANGE( g_TextureBrowser.m_texture_scroll ) )); ++ vadjustment.connect( "value_changed", G_CALLBACK( TextureBrowser_verticalScroll ), &g_TextureBrowser ); ++ ++ g_TextureBrowser.m_texture_scroll.visible(g_TextureBrowser.m_showTextureScrollbar); ++ } ++ { // gl_widget ++ g_TextureBrowser.m_gl_widget = glwidget_new( FALSE ); ++ g_object_ref( g_TextureBrowser.m_gl_widget._handle ); ++ ++ gtk_widget_set_events( g_TextureBrowser.m_gl_widget, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK ); ++ gtk_widget_set_can_focus( g_TextureBrowser.m_gl_widget, true ); ++ ++ table.attach(g_TextureBrowser.m_gl_widget, {1, 2, 1, 2}); ++ g_TextureBrowser.m_gl_widget.show(); ++ ++ g_TextureBrowser.m_sizeHandler = g_TextureBrowser.m_gl_widget.connect( "size_allocate", G_CALLBACK( TextureBrowser_size_allocate ), &g_TextureBrowser ); ++ g_TextureBrowser.m_exposeHandler = g_TextureBrowser.m_gl_widget.on_render( G_CALLBACK( TextureBrowser_expose ), &g_TextureBrowser ); ++ ++ g_TextureBrowser.m_gl_widget.connect( "button_press_event", G_CALLBACK( TextureBrowser_button_press ), &g_TextureBrowser ); ++ g_TextureBrowser.m_gl_widget.connect( "button_release_event", G_CALLBACK( TextureBrowser_button_release ), &g_TextureBrowser ); ++ g_TextureBrowser.m_gl_widget.connect( "motion_notify_event", G_CALLBACK( TextureBrowser_motion ), &g_TextureBrowser ); ++ g_TextureBrowser.m_gl_widget.connect( "scroll_event", G_CALLBACK( TextureBrowser_scroll ), &g_TextureBrowser ); ++ } ++ ++ // tag stuff ++ if ( g_TextureBrowser.m_tags ) { ++ { // fill tag GtkListStore ++ g_TextureBrowser.m_all_tags_list = ui::ListStore::from(gtk_list_store_new( N_COLUMNS, G_TYPE_STRING )); ++ auto sortable = GTK_TREE_SORTABLE( g_TextureBrowser.m_all_tags_list ); ++ gtk_tree_sortable_set_sort_column_id( sortable, TAG_COLUMN, GTK_SORT_ASCENDING ); ++ ++ TagBuilder.GetAllTags( g_TextureBrowser.m_all_tags ); ++ TextureBrowser_buildTagList(); ++ } ++ { // tag menu bar ++ auto menu_tags = ui::Menu(ui::New); ++ // auto tags_item = TextureBrowser_constructTagsMenu( menu_tags ); ++ TextureBrowser_constructTagsMenu( menu_tags ); ++ // gtk_menu_item_set_submenu( GTK_MENU_ITEM( tags_item ), menu_tags ); ++ // gtk_menu_shell_append( GTK_MENU_SHELL( menu_bar ), tags_item ); ++ ++ auto button = toolbar_append_button( toolbar, "Tags", "texbro_tags.png" ); ++ button.dimensions( 22, 22 ); ++ button.connect( "clicked", G_CALLBACK( Popup_View_Menu ), menu_tags ); ++ } ++ { // Tag TreeView ++ g_TextureBrowser.m_scr_win_tags = ui::ScrolledWindow(ui::New); ++ gtk_container_set_border_width( GTK_CONTAINER( g_TextureBrowser.m_scr_win_tags ), 0 ); ++ ++ // vertical only scrolling for treeview ++ gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( g_TextureBrowser.m_scr_win_tags ), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS ); ++ ++ TextureBrowser_createTreeViewTags(); ++ ++ auto selection = gtk_tree_view_get_selection(g_TextureBrowser.m_treeViewTags ); ++ gtk_tree_selection_set_mode( selection, GTK_SELECTION_MULTIPLE ); ++ ++ gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW( g_TextureBrowser.m_scr_win_tags ), g_TextureBrowser.m_treeViewTags ); ++ g_TextureBrowser.m_treeViewTags.show(); ++ } ++ { // Texture/Tag notebook ++ TextureBrowser_constructTagNotebook(); ++ vbox.pack_start( g_TextureBrowser.m_tag_notebook, TRUE, TRUE, 0 ); ++ } ++ { // Tag search button ++ TextureBrowser_constructSearchButton(); ++ vbox.pack_end(g_TextureBrowser.m_search_button, FALSE, FALSE, 0); ++ } ++ auto frame_table = ui::Table(3, 3, FALSE); ++ { // Tag frame ++ ++ g_TextureBrowser.m_tag_frame = ui::Frame( "Tag assignment" ); ++ gtk_frame_set_label_align( GTK_FRAME( g_TextureBrowser.m_tag_frame ), 0.5, 0.5 ); ++ gtk_frame_set_shadow_type( GTK_FRAME( g_TextureBrowser.m_tag_frame ), GTK_SHADOW_NONE ); ++ ++ table.attach(g_TextureBrowser.m_tag_frame, {1, 3, 2, 3}, {GTK_FILL, GTK_SHRINK}); ++ ++ frame_table.show(); ++ ++ g_TextureBrowser.m_tag_frame.add(frame_table); ++ } ++ { // assigned tag list ++ ui::Widget scrolled_win = ui::ScrolledWindow(ui::New); ++ gtk_container_set_border_width( GTK_CONTAINER( scrolled_win ), 0 ); ++ gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_win ), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS ); ++ ++ g_TextureBrowser.m_assigned_store = ui::ListStore::from(gtk_list_store_new( N_COLUMNS, G_TYPE_STRING )); ++ ++ auto sortable = GTK_TREE_SORTABLE( g_TextureBrowser.m_assigned_store ); ++ gtk_tree_sortable_set_sort_column_id( sortable, TAG_COLUMN, GTK_SORT_ASCENDING ); ++ ++ auto renderer = ui::CellRendererText(ui::New); ++ ++ g_TextureBrowser.m_assigned_tree = ui::TreeView(ui::TreeModel::from(g_TextureBrowser.m_assigned_store._handle)); ++ g_TextureBrowser.m_assigned_store.unref(); ++ g_TextureBrowser.m_assigned_tree.connect( "row-activated", (GCallback) TextureBrowser_removeTags, NULL ); ++ gtk_tree_view_set_headers_visible(g_TextureBrowser.m_assigned_tree, FALSE ); ++ ++ auto selection = gtk_tree_view_get_selection(g_TextureBrowser.m_assigned_tree ); ++ gtk_tree_selection_set_mode( selection, GTK_SELECTION_MULTIPLE ); ++ ++ auto column = ui::TreeViewColumn( "", renderer, {{"text", TAG_COLUMN}} ); ++ gtk_tree_view_append_column(g_TextureBrowser.m_assigned_tree, column ); ++ g_TextureBrowser.m_assigned_tree.show(); ++ ++ scrolled_win.show(); ++ gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW( scrolled_win ), g_TextureBrowser.m_assigned_tree ); ++ ++ frame_table.attach(scrolled_win, {0, 1, 1, 3}, {GTK_FILL, GTK_FILL}); ++ } ++ { // available tag list ++ ui::Widget scrolled_win = ui::ScrolledWindow(ui::New); ++ gtk_container_set_border_width( GTK_CONTAINER( scrolled_win ), 0 ); ++ gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_win ), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS ); ++ ++ g_TextureBrowser.m_available_store = ui::ListStore::from(gtk_list_store_new( N_COLUMNS, G_TYPE_STRING )); ++ auto sortable = GTK_TREE_SORTABLE( g_TextureBrowser.m_available_store ); ++ gtk_tree_sortable_set_sort_column_id( sortable, TAG_COLUMN, GTK_SORT_ASCENDING ); ++ ++ auto renderer = ui::CellRendererText(ui::New); ++ ++ g_TextureBrowser.m_available_tree = ui::TreeView(ui::TreeModel::from(g_TextureBrowser.m_available_store._handle)); ++ g_TextureBrowser.m_available_store.unref(); ++ g_TextureBrowser.m_available_tree.connect( "row-activated", (GCallback) TextureBrowser_assignTags, NULL ); ++ gtk_tree_view_set_headers_visible(g_TextureBrowser.m_available_tree, FALSE ); ++ ++ auto selection = gtk_tree_view_get_selection(g_TextureBrowser.m_available_tree ); ++ gtk_tree_selection_set_mode( selection, GTK_SELECTION_MULTIPLE ); ++ ++ auto column = ui::TreeViewColumn( "", renderer, {{"text", TAG_COLUMN}} ); ++ gtk_tree_view_append_column(g_TextureBrowser.m_available_tree, column ); ++ g_TextureBrowser.m_available_tree.show(); ++ ++ scrolled_win.show(); ++ gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW( scrolled_win ), g_TextureBrowser.m_available_tree ); ++ ++ frame_table.attach(scrolled_win, {2, 3, 1, 3}, {GTK_FILL, GTK_FILL}); ++ } ++ { // tag arrow buttons ++ auto m_btn_left = ui::Button(ui::New); ++ auto m_btn_right = ui::Button(ui::New); ++ auto m_arrow_left = ui::Widget::from(gtk_arrow_new( GTK_ARROW_LEFT, GTK_SHADOW_OUT )); ++ auto m_arrow_right = ui::Widget::from(gtk_arrow_new( GTK_ARROW_RIGHT, GTK_SHADOW_OUT )); ++ m_btn_left.add(m_arrow_left); ++ m_btn_right.add(m_arrow_right); ++ ++ // workaround. the size of the tag frame depends of the requested size of the arrow buttons. ++ m_arrow_left.dimensions(-1, 68); ++ m_arrow_right.dimensions(-1, 68); ++ ++ frame_table.attach(m_btn_left, {1, 2, 1, 2}, {GTK_SHRINK, GTK_EXPAND}); ++ frame_table.attach(m_btn_right, {1, 2, 2, 3}, {GTK_SHRINK, GTK_EXPAND}); ++ ++ m_btn_left.connect( "clicked", G_CALLBACK( TextureBrowser_assignTags ), NULL ); ++ m_btn_right.connect( "clicked", G_CALLBACK( TextureBrowser_removeTags ), NULL ); ++ ++ m_btn_left.show(); ++ m_btn_right.show(); ++ m_arrow_left.show(); ++ m_arrow_right.show(); ++ } ++ { // tag fram labels ++ ui::Widget m_lbl_assigned = ui::Label( "Assigned" ); ++ ui::Widget m_lbl_unassigned = ui::Label( "Available" ); ++ ++ frame_table.attach(m_lbl_assigned, {0, 1, 0, 1}, {GTK_EXPAND, GTK_SHRINK}); ++ frame_table.attach(m_lbl_unassigned, {2, 3, 0, 1}, {GTK_EXPAND, GTK_SHRINK}); ++ ++ m_lbl_assigned.show(); ++ m_lbl_unassigned.show(); ++ } ++ } ++ else { // no tag support, show the texture tree only ++ vbox.pack_start( g_TextureBrowser.m_scr_win_tree, TRUE, TRUE, 0 ); ++ } ++ ++ // TODO do we need this? ++ //gtk_container_set_focus_chain(GTK_CONTAINER(hbox_table), NULL); ++ ++ return table; ++} ++ ++void TextureBrowser_destroyWindow(){ ++ GlobalShaderSystem().setActiveShadersChangedNotify( Callback() ); ++ ++ g_signal_handler_disconnect( G_OBJECT( g_TextureBrowser.m_gl_widget ), g_TextureBrowser.m_sizeHandler ); ++ g_signal_handler_disconnect( G_OBJECT( g_TextureBrowser.m_gl_widget ), g_TextureBrowser.m_exposeHandler ); ++ ++ g_TextureBrowser.m_gl_widget.unref(); ++} ++ ++const Vector3& TextureBrowser_getBackgroundColour( TextureBrowser& textureBrowser ){ ++ return textureBrowser.color_textureback; ++} ++ ++void TextureBrowser_setBackgroundColour( TextureBrowser& textureBrowser, const Vector3& colour ){ ++ textureBrowser.color_textureback = colour; ++ TextureBrowser_queueDraw( textureBrowser ); ++} ++ ++void TextureBrowser_selectionHelper( ui::TreeModel model, ui::TreePath path, GtkTreeIter* iter, GSList** selected ){ ++ g_assert( selected != NULL ); ++ ++ gchar* name; ++ gtk_tree_model_get( model, iter, TAG_COLUMN, &name, -1 ); ++ *selected = g_slist_append( *selected, name ); ++} ++ ++void TextureBrowser_shaderInfo(){ ++ const char* name = TextureBrowser_GetSelectedShader( g_TextureBrowser ); ++ IShader* shader = QERApp_Shader_ForName( name ); ++ ++ DoShaderInfoDlg( name, shader->getShaderFileName(), "Shader Info" ); ++ ++ shader->DecRef(); ++} ++ ++void TextureBrowser_addTag(){ ++ CopiedString tag; ++ ++ EMessageBoxReturn result = DoShaderTagDlg( &tag, "Add shader tag" ); ++ ++ if ( result == eIDOK && !tag.empty() ) { ++ GtkTreeIter iter; ++ g_TextureBrowser.m_all_tags.insert( tag.c_str() ); ++ gtk_list_store_append( g_TextureBrowser.m_available_store, &iter ); ++ gtk_list_store_set( g_TextureBrowser.m_available_store, &iter, TAG_COLUMN, tag.c_str(), -1 ); ++ ++ // Select the currently added tag in the available list ++ auto selection = gtk_tree_view_get_selection(g_TextureBrowser.m_available_tree ); ++ gtk_tree_selection_select_iter( selection, &iter ); ++ ++ g_TextureBrowser.m_all_tags_list.append(TAG_COLUMN, tag.c_str()); ++ } ++} ++ ++void TextureBrowser_renameTag(){ ++ /* WORKAROUND: The tag treeview is set to GTK_SELECTION_MULTIPLE. Because ++ gtk_tree_selection_get_selected() doesn't work with GTK_SELECTION_MULTIPLE, ++ we need to count the number of selected rows first and use ++ gtk_tree_selection_selected_foreach() then to go through the list of selected ++ rows (which always containins a single row). ++ */ ++ ++ GSList* selected = NULL; ++ ++ auto selection = gtk_tree_view_get_selection(g_TextureBrowser.m_treeViewTags ); ++ gtk_tree_selection_selected_foreach( selection, GtkTreeSelectionForeachFunc( TextureBrowser_selectionHelper ), &selected ); ++ ++ if ( g_slist_length( selected ) == 1 ) { // we only rename a single tag ++ CopiedString newTag; ++ EMessageBoxReturn result = DoShaderTagDlg( &newTag, "Rename shader tag" ); ++ ++ if ( result == eIDOK && !newTag.empty() ) { ++ GtkTreeIter iterList; ++ gchar* rowTag; ++ gchar* oldTag = (char*)selected->data; ++ ++ bool row = gtk_tree_model_get_iter_first(g_TextureBrowser.m_all_tags_list, &iterList ) != 0; ++ ++ while ( row ) ++ { ++ gtk_tree_model_get(g_TextureBrowser.m_all_tags_list, &iterList, TAG_COLUMN, &rowTag, -1 ); ++ ++ if ( strcmp( rowTag, oldTag ) == 0 ) { ++ gtk_list_store_set( g_TextureBrowser.m_all_tags_list, &iterList, TAG_COLUMN, newTag.c_str(), -1 ); ++ } ++ row = gtk_tree_model_iter_next(g_TextureBrowser.m_all_tags_list, &iterList ) != 0; ++ } ++ ++ TagBuilder.RenameShaderTag( oldTag, newTag.c_str() ); ++ ++ g_TextureBrowser.m_all_tags.erase( (CopiedString)oldTag ); ++ g_TextureBrowser.m_all_tags.insert( newTag ); ++ ++ BuildStoreAssignedTags( g_TextureBrowser.m_assigned_store, g_TextureBrowser.shader.c_str(), &g_TextureBrowser ); ++ BuildStoreAvailableTags( g_TextureBrowser.m_available_store, g_TextureBrowser.m_assigned_store, g_TextureBrowser.m_all_tags, &g_TextureBrowser ); ++ } ++ } ++ else ++ { ++ ui::alert( g_TextureBrowser.m_parent, "Select a single tag for renaming." ); ++ } ++} ++ ++void TextureBrowser_deleteTag(){ ++ GSList* selected = NULL; ++ ++ auto selection = gtk_tree_view_get_selection(g_TextureBrowser.m_treeViewTags ); ++ gtk_tree_selection_selected_foreach( selection, GtkTreeSelectionForeachFunc( TextureBrowser_selectionHelper ), &selected ); ++ ++ if ( g_slist_length( selected ) == 1 ) { // we only delete a single tag ++ auto result = ui::alert( g_TextureBrowser.m_parent, "Are you sure you want to delete the selected tag?", "Delete Tag", ui::alert_type::YESNO, ui::alert_icon::Question ); ++ ++ if ( result == ui::alert_response::YES ) { ++ GtkTreeIter iterSelected; ++ gchar *rowTag; ++ ++ gchar* tagSelected = (char*)selected->data; ++ ++ bool row = gtk_tree_model_get_iter_first(g_TextureBrowser.m_all_tags_list, &iterSelected ) != 0; ++ ++ while ( row ) ++ { ++ gtk_tree_model_get(g_TextureBrowser.m_all_tags_list, &iterSelected, TAG_COLUMN, &rowTag, -1 ); ++ ++ if ( strcmp( rowTag, tagSelected ) == 0 ) { ++ gtk_list_store_remove( g_TextureBrowser.m_all_tags_list, &iterSelected ); ++ break; ++ } ++ row = gtk_tree_model_iter_next(g_TextureBrowser.m_all_tags_list, &iterSelected ) != 0; ++ } ++ ++ TagBuilder.DeleteTag( tagSelected ); ++ g_TextureBrowser.m_all_tags.erase( (CopiedString)tagSelected ); ++ ++ BuildStoreAssignedTags( g_TextureBrowser.m_assigned_store, g_TextureBrowser.shader.c_str(), &g_TextureBrowser ); ++ BuildStoreAvailableTags( g_TextureBrowser.m_available_store, g_TextureBrowser.m_assigned_store, g_TextureBrowser.m_all_tags, &g_TextureBrowser ); ++ } ++ } ++ else { ++ ui::alert( g_TextureBrowser.m_parent, "Select a single tag for deletion." ); ++ } ++} ++ ++void TextureBrowser_copyTag(){ ++ g_TextureBrowser.m_copied_tags.clear(); ++ TagBuilder.GetShaderTags( g_TextureBrowser.shader.c_str(), g_TextureBrowser.m_copied_tags ); ++} ++ ++void TextureBrowser_pasteTag(){ ++ IShader* ishader = QERApp_Shader_ForName( g_TextureBrowser.shader.c_str() ); ++ CopiedString shader = g_TextureBrowser.shader.c_str(); ++ ++ if ( !TagBuilder.CheckShaderTag( shader.c_str() ) ) { ++ CopiedString shaderFile = ishader->getShaderFileName(); ++ if ( shaderFile.empty() ) { ++ // it's a texture ++ TagBuilder.AddShaderNode( shader.c_str(), CUSTOM, TEXTURE ); ++ } ++ else ++ { ++ // it's a shader ++ TagBuilder.AddShaderNode( shader.c_str(), CUSTOM, SHADER ); ++ } ++ ++ for ( size_t i = 0; i < g_TextureBrowser.m_copied_tags.size(); ++i ) ++ { ++ TagBuilder.AddShaderTag( shader.c_str(), g_TextureBrowser.m_copied_tags[i].c_str(), TAG ); ++ } ++ } ++ else ++ { ++ for ( size_t i = 0; i < g_TextureBrowser.m_copied_tags.size(); ++i ) ++ { ++ if ( !TagBuilder.CheckShaderTag( shader.c_str(), g_TextureBrowser.m_copied_tags[i].c_str() ) ) { ++ // the tag doesn't exist - let's add it ++ TagBuilder.AddShaderTag( shader.c_str(), g_TextureBrowser.m_copied_tags[i].c_str(), TAG ); ++ } ++ } ++ } ++ ++ ishader->DecRef(); ++ ++ TagBuilder.SaveXmlDoc(); ++ BuildStoreAssignedTags( g_TextureBrowser.m_assigned_store, shader.c_str(), &g_TextureBrowser ); ++ BuildStoreAvailableTags( g_TextureBrowser.m_available_store, g_TextureBrowser.m_assigned_store, g_TextureBrowser.m_all_tags, &g_TextureBrowser ); ++} ++ ++void TextureBrowser_RefreshShaders(){ ++ ++ /* When shaders are refreshed, forces reloading the textures as well. ++ Previously it would at best only display shaders, at worst mess up some textured objects. */ ++ ++ auto selection = gtk_tree_view_get_selection(GlobalTextureBrowser().m_treeViewTree); ++ GtkTreeModel* model = NULL; ++ GtkTreeIter iter; ++ if ( gtk_tree_selection_get_selected (selection, &model, &iter) ) ++ { ++ gchar dirName[1024]; ++ ++ gchar* buffer; ++ gtk_tree_model_get( model, &iter, 0, &buffer, -1 ); ++ strcpy( dirName, buffer ); ++ g_free( buffer ); ++ if ( !TextureBrowser_showWads() ) { ++ strcat( dirName, "/" ); ++ } ++ ++ ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Shaders" ); ++ GlobalShaderSystem().refresh(); ++ /* texturebrowser tree update on vfs restart */ ++ TextureBrowser_constructTreeStore(); ++ UpdateAllWindows(); ++ ++ TextureBrowser_ShowDirectory( GlobalTextureBrowser(), dirName ); ++ TextureBrowser_queueDraw( GlobalTextureBrowser() ); ++ } ++ ++ else{ ++ ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Shaders" ); ++ GlobalShaderSystem().refresh(); ++ /* texturebrowser tree update on vfs restart */ ++ TextureBrowser_constructTreeStore(); ++ UpdateAllWindows(); ++ } ++} ++ ++void TextureBrowser_ToggleShowShaders(){ ++ g_TextureBrowser.m_showShaders ^= 1; ++ g_TextureBrowser.m_showshaders_item.update(); ++ ++ g_TextureBrowser.m_heightChanged = true; ++ g_TextureBrowser.m_originInvalid = true; ++ g_activeShadersChangedCallbacks(); ++ ++ TextureBrowser_queueDraw( g_TextureBrowser ); ++} ++ ++void TextureBrowser_ToggleShowTextures(){ ++ g_TextureBrowser.m_showTextures ^= 1; ++ g_TextureBrowser.m_showtextures_item.update(); ++ ++ g_TextureBrowser.m_heightChanged = true; ++ g_TextureBrowser.m_originInvalid = true; ++ g_activeShadersChangedCallbacks(); ++ ++ TextureBrowser_queueDraw( g_TextureBrowser ); ++} ++ ++void TextureBrowser_ToggleShowShaderListOnly(){ ++ g_TextureBrowser_shaderlistOnly ^= 1; ++ g_TextureBrowser.m_showshaderlistonly_item.update(); ++ ++ TextureBrowser_constructTreeStore(); ++} ++ ++void TextureBrowser_showAll(){ ++ g_TextureBrowser_currentDirectory = ""; ++ g_TextureBrowser.m_searchedTags = false; ++// TextureBrowser_SetHideUnused( g_TextureBrowser, false ); ++ TextureBrowser_ToggleHideUnused(); ++ //TextureBrowser_heightChanged( g_TextureBrowser ); ++ TextureBrowser_updateTitle(); ++} ++ ++void TextureBrowser_showUntagged(){ ++ auto result = ui::alert( g_TextureBrowser.m_parent, "WARNING! This function might need a lot of memory and time. Are you sure you want to use it?", "Show Untagged", ui::alert_type::YESNO, ui::alert_icon::Warning ); ++ ++ if ( result == ui::alert_response::YES ) { ++ g_TextureBrowser.m_found_shaders.clear(); ++ TagBuilder.GetUntagged( g_TextureBrowser.m_found_shaders ); ++ std::set::iterator iter; ++ ++ ScopeDisableScreenUpdates disableScreenUpdates( "Searching untagged textures...", "Loading Textures" ); ++ ++ for ( iter = g_TextureBrowser.m_found_shaders.begin(); iter != g_TextureBrowser.m_found_shaders.end(); iter++ ) ++ { ++ std::string path = ( *iter ).c_str(); ++ size_t pos = path.find_last_of( "/", path.size() ); ++ std::string name = path.substr( pos + 1, path.size() ); ++ path = path.substr( 0, pos + 1 ); ++ TextureDirectory_loadTexture( path.c_str(), name.c_str() ); ++ globalErrorStream() << path.c_str() << name.c_str() << "\n"; ++ } ++ ++ g_TextureBrowser_currentDirectory = "Untagged"; ++ TextureBrowser_queueDraw( GlobalTextureBrowser() ); ++ TextureBrowser_heightChanged( g_TextureBrowser ); ++ TextureBrowser_updateTitle(); ++ } ++} ++ ++void TextureBrowser_FixedSize(){ ++ g_TextureBrowser_fixedSize ^= 1; ++ GlobalTextureBrowser().m_fixedsize_item.update(); ++ TextureBrowser_activeShadersChanged( GlobalTextureBrowser() ); ++} ++ ++void TextureBrowser_FilterMissing(){ ++ g_TextureBrowser_filterMissing ^= 1; ++ GlobalTextureBrowser().m_filternotex_item.update(); ++ TextureBrowser_activeShadersChanged( GlobalTextureBrowser() ); ++ TextureBrowser_RefreshShaders(); ++} ++ ++void TextureBrowser_FilterFallback(){ ++ g_TextureBrowser_filterFallback ^= 1; ++ GlobalTextureBrowser().m_hidenotex_item.update(); ++ TextureBrowser_activeShadersChanged( GlobalTextureBrowser() ); ++ TextureBrowser_RefreshShaders(); ++} ++ ++void TextureBrowser_EnableAlpha(){ ++ g_TextureBrowser_enableAlpha ^= 1; ++ GlobalTextureBrowser().m_enablealpha_item.update(); ++ TextureBrowser_activeShadersChanged( GlobalTextureBrowser() ); ++} ++ ++void TextureBrowser_exportTitle( const Callback & importer ){ ++ StringOutputStream buffer( 64 ); ++ buffer << "Textures: "; ++ if ( !string_empty( g_TextureBrowser_currentDirectory.c_str() ) ) { ++ buffer << g_TextureBrowser_currentDirectory.c_str(); ++ } ++ else ++ { ++ buffer << "all"; ++ } ++ importer( buffer.c_str() ); ++} ++ ++struct TextureScale { ++ static void Export(const TextureBrowser &self, const Callback &returnz) { ++ switch (self.m_textureScale) { ++ case 10: ++ returnz(0); ++ break; ++ case 25: ++ returnz(1); ++ break; ++ case 50: ++ returnz(2); ++ break; ++ case 100: ++ returnz(3); ++ break; ++ case 200: ++ returnz(4); ++ break; ++ } ++ } ++ ++ static void Import(TextureBrowser &self, int value) { ++ switch (value) { ++ case 0: ++ TextureBrowser_setScale(self, 10); ++ break; ++ case 1: ++ TextureBrowser_setScale(self, 25); ++ break; ++ case 2: ++ TextureBrowser_setScale(self, 50); ++ break; ++ case 3: ++ TextureBrowser_setScale(self, 100); ++ break; ++ case 4: ++ TextureBrowser_setScale(self, 200); ++ break; ++ } ++ } ++}; ++ ++struct UniformTextureSize { ++ static void Export(const TextureBrowser &self, const Callback &returnz) { ++ returnz(g_TextureBrowser.m_uniformTextureSize); ++ } ++ ++ static void Import(TextureBrowser &self, int value) { ++ if (value > 16) ++ TextureBrowser_setUniformSize(self, value); ++ } ++}; ++ ++struct UniformTextureMinSize { ++ static void Export(const TextureBrowser &self, const Callback &returnz) { ++ returnz(g_TextureBrowser.m_uniformTextureMinSize); ++ } ++ ++ static void Import(TextureBrowser &self, int value) { ++ if (value > 16) ++ TextureBrowser_setUniformSize(self, value); ++ } ++}; ++ ++void TextureBrowser_constructPreferences( PreferencesPage& page ){ ++ page.appendCheckBox( ++ "", "Texture scrollbar", ++ make_property(GlobalTextureBrowser()) ++ ); ++ { ++ const char* texture_scale[] = { "10%", "25%", "50%", "100%", "200%" }; ++ page.appendCombo( ++ "Texture Thumbnail Scale", ++ STRING_ARRAY_RANGE( texture_scale ), ++ make_property(GlobalTextureBrowser()) ++ ); ++ } ++ page.appendSpinner( "Thumbnails Max Size", GlobalTextureBrowser().m_uniformTextureSize, GlobalTextureBrowser().m_uniformTextureSize, 16, 8192 ); ++ page.appendSpinner( "Thumbnails Min Size", GlobalTextureBrowser().m_uniformTextureMinSize, GlobalTextureBrowser().m_uniformTextureMinSize, 16, 8192 ); ++ page.appendEntry( "Mousewheel Increment", GlobalTextureBrowser().m_mouseWheelScrollIncrement ); ++ { ++ const char* startup_shaders[] = { "None", TextureBrowser_getComonShadersName() }; ++ page.appendCombo( "Load Shaders at Startup", reinterpret_cast( GlobalTextureBrowser().m_startupShaders ), STRING_ARRAY_RANGE( startup_shaders ) ); ++ } ++ { ++ StringOutputStream sstream( 256 ); ++ sstream << "Hide nonShaders in " << TextureBrowser_getComonShadersDir() << " folder"; ++ page.appendCheckBox( ++ "", sstream.c_str(), ++ GlobalTextureBrowser().m_hideNonShadersInCommon ++ ); ++ } ++} ++ ++void TextureBrowser_constructPage( PreferenceGroup& group ){ ++ PreferencesPage page( group.createPage( "Texture Browser", "Texture Browser Preferences" ) ); ++ TextureBrowser_constructPreferences( page ); ++} ++ ++void TextureBrowser_registerPreferencesPage(){ ++ PreferencesDialog_addSettingsPage( makeCallbackF(TextureBrowser_constructPage) ); ++} ++ ++ ++#include "preferencesystem.h" ++#include "stringio.h" ++ ++ ++void TextureClipboard_textureSelected( const char* shader ); ++ ++void TextureBrowser_Construct(){ ++<<<<<<< HEAD ++ GlobalCommands_insert( "ShaderInfo", makeCallbackF(TextureBrowser_shaderInfo) ); ++ GlobalCommands_insert( "ShowUntagged", makeCallbackF(TextureBrowser_showUntagged) ); ++ GlobalCommands_insert( "AddTag", makeCallbackF(TextureBrowser_addTag) ); ++ GlobalCommands_insert( "RenameTag", makeCallbackF(TextureBrowser_renameTag) ); ++ GlobalCommands_insert( "DeleteTag", makeCallbackF(TextureBrowser_deleteTag) ); ++ GlobalCommands_insert( "CopyTag", makeCallbackF(TextureBrowser_copyTag) ); ++ GlobalCommands_insert( "PasteTag", makeCallbackF(TextureBrowser_pasteTag) ); ++ GlobalCommands_insert( "RefreshShaders", makeCallbackF(VFS_Refresh) ); ++ GlobalToggles_insert( "ShowInUse", makeCallbackF(TextureBrowser_ToggleHideUnused), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_hideunused_item ), Accelerator( 'U' ) ); ++ GlobalCommands_insert( "ShowAllTextures", makeCallbackF(TextureBrowser_showAll), Accelerator( 'A', (GdkModifierType)GDK_CONTROL_MASK ) ); ++ GlobalCommands_insert( "ToggleTextures", makeCallbackF(TextureBrowser_toggleShow), Accelerator( 'T' ) ); ++ GlobalToggles_insert( "ToggleShowShaders", makeCallbackF(TextureBrowser_ToggleShowShaders), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showshaders_item ) ); ++ GlobalToggles_insert( "ToggleShowTextures", makeCallbackF(TextureBrowser_ToggleShowTextures), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showtextures_item ) ); ++ GlobalToggles_insert( "ToggleShowShaderlistOnly", makeCallbackF(TextureBrowser_ToggleShowShaderListOnly), ++ ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showshaderlistonly_item ) ); ++ GlobalToggles_insert( "FixedSize", makeCallbackF(TextureBrowser_FixedSize), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_fixedsize_item ) ); ++ GlobalToggles_insert( "FilterMissing", makeCallbackF(TextureBrowser_FilterMissing), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_filternotex_item ) ); ++ GlobalToggles_insert( "FilterFallback", makeCallbackF(TextureBrowser_FilterFallback), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_hidenotex_item ) ); ++ GlobalToggles_insert( "EnableAlpha", makeCallbackF(TextureBrowser_EnableAlpha), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_enablealpha_item ) ); ++ ++ GlobalPreferenceSystem().registerPreference( "TextureScale", make_property_string(g_TextureBrowser) ); ++ GlobalPreferenceSystem().registerPreference( "UniformTextureSize", make_property_string(g_TextureBrowser) ); ++ GlobalPreferenceSystem().registerPreference( "UniformTextureMinSize", make_property_string(g_TextureBrowser) ); ++ GlobalPreferenceSystem().registerPreference( "TextureScrollbar", make_property_string(GlobalTextureBrowser())); ++ GlobalPreferenceSystem().registerPreference( "ShowShaders", make_property_string( GlobalTextureBrowser().m_showShaders ) ); ++ GlobalPreferenceSystem().registerPreference( "ShowTextures", make_property_string( GlobalTextureBrowser().m_showTextures ) ); ++ GlobalPreferenceSystem().registerPreference( "ShowShaderlistOnly", make_property_string( g_TextureBrowser_shaderlistOnly ) ); ++ GlobalPreferenceSystem().registerPreference( "FixedSize", make_property_string( g_TextureBrowser_fixedSize ) ); ++ GlobalPreferenceSystem().registerPreference( "FilterMissing", make_property_string( g_TextureBrowser_filterMissing ) ); ++ GlobalPreferenceSystem().registerPreference( "EnableAlpha", make_property_string( g_TextureBrowser_enableAlpha ) ); ++ GlobalPreferenceSystem().registerPreference( "LoadShaders", make_property_string( reinterpret_cast( GlobalTextureBrowser().m_startupShaders ) ) ); ++ GlobalPreferenceSystem().registerPreference( "WheelMouseInc", make_property_string( GlobalTextureBrowser().m_mouseWheelScrollIncrement ) ); ++ GlobalPreferenceSystem().registerPreference( "SI_Colors0", make_property_string( GlobalTextureBrowser().color_textureback ) ); ++======= ++ GlobalCommands_insert( "ShaderInfo", FreeCaller() ); ++ GlobalCommands_insert( "ShowUntagged", FreeCaller() ); ++ GlobalCommands_insert( "AddTag", FreeCaller() ); ++ GlobalCommands_insert( "RenameTag", FreeCaller() ); ++ GlobalCommands_insert( "DeleteTag", FreeCaller() ); ++ GlobalCommands_insert( "CopyTag", FreeCaller() ); ++ GlobalCommands_insert( "PasteTag", FreeCaller() ); ++ GlobalCommands_insert( "RefreshShaders", FreeCaller() ); ++ GlobalToggles_insert( "ShowInUse", FreeCaller(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_hideunused_item ), Accelerator( 'U' ) ); ++ GlobalCommands_insert( "ShowAllTextures", FreeCaller(), Accelerator( 'A', (GdkModifierType)GDK_CONTROL_MASK ) ); ++ GlobalCommands_insert( "ToggleTextures", FreeCaller(), Accelerator( 'T' ) ); ++ GlobalToggles_insert( "ToggleShowShaders", FreeCaller(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showshaders_item ) ); ++ GlobalToggles_insert( "ToggleShowTextures", FreeCaller(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showtextures_item ) ); ++ GlobalToggles_insert( "ToggleShowShaderlistOnly", FreeCaller(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showshaderlistonly_item ) ); ++ GlobalToggles_insert( "FixedSize", FreeCaller(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_fixedsize_item ) ); ++ GlobalToggles_insert( "FilterNotex", FreeCaller(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_filternotex_item ) ); ++ GlobalToggles_insert( "EnableAlpha", FreeCaller(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_enablealpha_item ) ); ++ ++ GlobalPreferenceSystem().registerPreference( "TextureScale", ++ makeSizeStringImportCallback( TextureBrowserSetScaleCaller( g_TextureBrowser ) ), ++ SizeExportStringCaller( g_TextureBrowser.m_textureScale ) ++ ); ++ GlobalPreferenceSystem().registerPreference( "UniformTextureSize", ++ makeIntStringImportCallback(UniformTextureSizeImportCaller(g_TextureBrowser)), ++ IntExportStringCaller(g_TextureBrowser.m_uniformTextureSize) ); ++ GlobalPreferenceSystem().registerPreference( "UniformTextureMinSize", ++ makeIntStringImportCallback(UniformTextureMinSizeImportCaller(g_TextureBrowser)), ++ IntExportStringCaller(g_TextureBrowser.m_uniformTextureMinSize) ); ++ GlobalPreferenceSystem().registerPreference( "TextureScrollbar", ++ makeBoolStringImportCallback( TextureBrowserImportShowScrollbarCaller( g_TextureBrowser ) ), ++ BoolExportStringCaller( GlobalTextureBrowser().m_showTextureScrollbar ) ++ ); ++ GlobalPreferenceSystem().registerPreference( "ShowShaders", BoolImportStringCaller( GlobalTextureBrowser().m_showShaders ), BoolExportStringCaller( GlobalTextureBrowser().m_showShaders ) ); ++ GlobalPreferenceSystem().registerPreference( "ShowTextures", BoolImportStringCaller( GlobalTextureBrowser().m_showTextures ), BoolExportStringCaller( GlobalTextureBrowser().m_showTextures ) ); ++ GlobalPreferenceSystem().registerPreference( "ShowShaderlistOnly", BoolImportStringCaller( g_TextureBrowser_shaderlistOnly ), BoolExportStringCaller( g_TextureBrowser_shaderlistOnly ) ); ++ GlobalPreferenceSystem().registerPreference( "FixedSize", BoolImportStringCaller( g_TextureBrowser_fixedSize ), BoolExportStringCaller( g_TextureBrowser_fixedSize ) ); ++ GlobalPreferenceSystem().registerPreference( "FilterNotex", BoolImportStringCaller( g_TextureBrowser_filterNotex ), BoolExportStringCaller( g_TextureBrowser_filterNotex ) ); ++ GlobalPreferenceSystem().registerPreference( "EnableAlpha", BoolImportStringCaller( g_TextureBrowser_enableAlpha ), BoolExportStringCaller( g_TextureBrowser_enableAlpha ) ); ++ GlobalPreferenceSystem().registerPreference( "LoadShaders", IntImportStringCaller( reinterpret_cast( GlobalTextureBrowser().m_startupShaders ) ), IntExportStringCaller( reinterpret_cast( GlobalTextureBrowser().m_startupShaders ) ) ); ++ GlobalPreferenceSystem().registerPreference( "WheelMouseInc", SizeImportStringCaller( GlobalTextureBrowser().m_mouseWheelScrollIncrement ), SizeExportStringCaller( GlobalTextureBrowser().m_mouseWheelScrollIncrement ) ); ++ GlobalPreferenceSystem().registerPreference( "SI_Colors0", Vector3ImportStringCaller( GlobalTextureBrowser().color_textureback ), Vector3ExportStringCaller( GlobalTextureBrowser().color_textureback ) ); ++ GlobalPreferenceSystem().registerPreference( "HideNonShadersInCommon", BoolImportStringCaller( GlobalTextureBrowser().m_hideNonShadersInCommon ), BoolExportStringCaller( GlobalTextureBrowser().m_hideNonShadersInCommon ) ); ++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84 ++ ++ g_TextureBrowser.shader = texdef_name_default(); ++ ++ Textures_setModeChangedNotify( ReferenceCaller( g_TextureBrowser ) ); ++ ++ TextureBrowser_registerPreferencesPage(); ++ ++ GlobalShaderSystem().attach( g_ShadersObserver ); ++ ++ TextureBrowser_textureSelected = TextureClipboard_textureSelected; ++} ++ ++void TextureBrowser_Destroy(){ ++ GlobalShaderSystem().detach( g_ShadersObserver ); ++ ++ Textures_setModeChangedNotify( Callback() ); ++} ++ ++ui::Widget TextureBrowser_getGLWidget(){ ++ return GlobalTextureBrowser().m_gl_widget; ++}