-/*
- Copyright (C) 2001-2006, William Joseph.
- All Rights Reserved.
+#include "xorrectangle.h"
- This file is part of GtkRadiant.
+#include <gtk/gtk.h>
- 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 <gtk/gtkglwidget.h>
- 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;
+ }
+}
--- /dev/null
--- /dev/null
++/*
++ 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 <cairo.h>
++#include <uilib/uilib.h>
++#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
--- /dev/null
--- /dev/null
++/*
++ 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 <list>
++#include <gdk/gdkkeysyms.h>
++
++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<float>( floor( mid[( axis + 1 ) % 3] + radius * cv + 0.5 ) );
++// planepts[0][( axis + 2 ) % 3] = static_cast<float>( floor( mid[( axis + 2 ) % 3] + radius * sv + 0.5 ) );
++ planepts[0][( axis + 1 ) % 3] = static_cast<float>( mid[( axis + 1 ) % 3] + radius * cv );
++ planepts[0][( axis + 2 ) % 3] = static_cast<float>( 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<float>( floor( planepts[0][( axis + 1 ) % 3] - radius * sv + 0.5 ) );
++// planepts[2][( axis + 2 ) % 3] = static_cast<float>( floor( planepts[0][( axis + 2 ) % 3] + radius * cv + 0.5 ) );
++ planepts[2][( axis + 1 ) % 3] = static_cast<float>( planepts[0][( axis + 1 ) % 3] - radius * sv );
++ planepts[2][( axis + 2 ) % 3] = static_cast<float>( 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<float>( mid[0] + radius * cv );
++ planepts[0][1] = static_cast<float>( mid[1] + radius * sv );
++// planepts[0][0] = static_cast<float>( floor( mid[0] + radius * cv + 0.5 ) );
++// planepts[0][1] = static_cast<float>( 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<float>( planepts[0][0] - radius * sv );
++ planepts[2][1] = static_cast<float>( planepts[0][1] + radius * cv );
++// planepts[2][0] = static_cast<float>( floor( planepts[0][0] - radius * sv + 0.5 ) );
++// planepts[2][1] = static_cast<float>( 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<float>( c_quantise_normal - j - i ),
++ static_cast<float>( i ),
++ static_cast<float>( 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<float>( 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<float>( 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<std::size_t>( zbits );
++ std::size_t ybits_q = static_cast<std::size_t>( 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<float>( 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<const Selectable&>
++{
++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<BrushMakeSided, void(), &BrushMakeSided::set> 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<BrushPrefab, void(), &BrushPrefab::set> 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<void()> g_texture_lock_status_changed;
++ConstReferenceCaller<bool, void(const Callback<void(bool)> &), PropertyImpl<bool>::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" );
++}
}
void CamWnd_registerCommands( CamWnd& camwnd ){
- GlobalKeyEvents_insert( "CameraForward", Accelerator( GDK_KEY_Up ),
+ GlobalKeyEvents_insert( "CameraForward", accelerator_null(),
- ReferenceCaller<camera_t, Camera_MoveForward_KeyDown>( camwnd.getCamera() ),
- ReferenceCaller<camera_t, Camera_MoveForward_KeyUp>( camwnd.getCamera() )
+ ReferenceCaller<camera_t, void(), Camera_MoveForward_KeyDown>( camwnd.getCamera() ),
+ ReferenceCaller<camera_t, void(), Camera_MoveForward_KeyUp>( camwnd.getCamera() )
);
- GlobalKeyEvents_insert( "CameraBack", Accelerator( GDK_KEY_Down ),
+ GlobalKeyEvents_insert( "CameraBack", accelerator_null(),
- ReferenceCaller<camera_t, Camera_MoveBack_KeyDown>( camwnd.getCamera() ),
- ReferenceCaller<camera_t, Camera_MoveBack_KeyUp>( camwnd.getCamera() )
+ ReferenceCaller<camera_t, void(), Camera_MoveBack_KeyDown>( camwnd.getCamera() ),
+ ReferenceCaller<camera_t, void(), Camera_MoveBack_KeyUp>( camwnd.getCamera() )
);
- GlobalKeyEvents_insert( "CameraLeft", Accelerator( GDK_KEY_Left ),
+ GlobalKeyEvents_insert( "CameraLeft", accelerator_null(),
- ReferenceCaller<camera_t, Camera_RotateLeft_KeyDown>( camwnd.getCamera() ),
- ReferenceCaller<camera_t, Camera_RotateLeft_KeyUp>( camwnd.getCamera() )
+ ReferenceCaller<camera_t, void(), Camera_RotateLeft_KeyDown>( camwnd.getCamera() ),
+ ReferenceCaller<camera_t, void(), Camera_RotateLeft_KeyUp>( camwnd.getCamera() )
);
- GlobalKeyEvents_insert( "CameraRight", Accelerator( GDK_KEY_Right ),
+ GlobalKeyEvents_insert( "CameraRight", accelerator_null(),
- ReferenceCaller<camera_t, Camera_RotateRight_KeyDown>( camwnd.getCamera() ),
- ReferenceCaller<camera_t, Camera_RotateRight_KeyUp>( camwnd.getCamera() )
+ ReferenceCaller<camera_t, void(), Camera_RotateRight_KeyDown>( camwnd.getCamera() ),
+ ReferenceCaller<camera_t, void(), Camera_RotateRight_KeyUp>( camwnd.getCamera() )
);
- GlobalKeyEvents_insert( "CameraStrafeRight", Accelerator( GDK_KEY_period ),
+ GlobalKeyEvents_insert( "CameraStrafeRight", accelerator_null(),
- ReferenceCaller<camera_t, Camera_MoveRight_KeyDown>( camwnd.getCamera() ),
- ReferenceCaller<camera_t, Camera_MoveRight_KeyUp>( camwnd.getCamera() )
+ ReferenceCaller<camera_t, void(), Camera_MoveRight_KeyDown>( camwnd.getCamera() ),
+ ReferenceCaller<camera_t, void(), Camera_MoveRight_KeyUp>( camwnd.getCamera() )
);
- GlobalKeyEvents_insert( "CameraStrafeLeft", Accelerator( GDK_KEY_comma ),
+ GlobalKeyEvents_insert( "CameraStrafeLeft", accelerator_null(),
- ReferenceCaller<camera_t, Camera_MoveLeft_KeyDown>( camwnd.getCamera() ),
- ReferenceCaller<camera_t, Camera_MoveLeft_KeyUp>( camwnd.getCamera() )
+ ReferenceCaller<camera_t, void(), Camera_MoveLeft_KeyDown>( camwnd.getCamera() ),
+ ReferenceCaller<camera_t, void(), Camera_MoveLeft_KeyUp>( camwnd.getCamera() )
);
- GlobalKeyEvents_insert( "CameraUp", Accelerator( 'D' ),
+ GlobalKeyEvents_insert( "CameraUp", accelerator_null(),
- ReferenceCaller<camera_t, Camera_MoveUp_KeyDown>( camwnd.getCamera() ),
- ReferenceCaller<camera_t, Camera_MoveUp_KeyUp>( camwnd.getCamera() )
+ ReferenceCaller<camera_t, void(), Camera_MoveUp_KeyDown>( camwnd.getCamera() ),
+ ReferenceCaller<camera_t, void(), Camera_MoveUp_KeyUp>( camwnd.getCamera() )
);
- GlobalKeyEvents_insert( "CameraDown", Accelerator( 'C' ),
+ GlobalKeyEvents_insert( "CameraDown", accelerator_null(),
- ReferenceCaller<camera_t, Camera_MoveDown_KeyDown>( camwnd.getCamera() ),
- ReferenceCaller<camera_t, Camera_MoveDown_KeyUp>( camwnd.getCamera() )
+ ReferenceCaller<camera_t, void(), Camera_MoveDown_KeyDown>( camwnd.getCamera() ),
+ ReferenceCaller<camera_t, void(), Camera_MoveDown_KeyUp>( camwnd.getCamera() )
);
- GlobalKeyEvents_insert( "CameraAngleDown", Accelerator( 'A' ),
- ReferenceCaller<camera_t, void(), Camera_PitchDown_KeyDown>( camwnd.getCamera() ),
- ReferenceCaller<camera_t, void(), Camera_PitchDown_KeyUp>( camwnd.getCamera() )
- );
- GlobalKeyEvents_insert( "CameraAngleUp", Accelerator( 'Z' ),
+ GlobalKeyEvents_insert( "CameraAngleUp", accelerator_null(),
- ReferenceCaller<camera_t, Camera_PitchUp_KeyDown>( camwnd.getCamera() ),
- ReferenceCaller<camera_t, Camera_PitchUp_KeyUp>( camwnd.getCamera() )
+ ReferenceCaller<camera_t, void(), Camera_PitchUp_KeyDown>( camwnd.getCamera() ),
+ ReferenceCaller<camera_t, void(), Camera_PitchUp_KeyUp>( camwnd.getCamera() )
);
- ReferenceCaller<camera_t, Camera_PitchDown_KeyDown>( camwnd.getCamera() ),
- ReferenceCaller<camera_t, Camera_PitchDown_KeyUp>( camwnd.getCamera() )
+ GlobalKeyEvents_insert( "CameraAngleDown", accelerator_null(),
++ ReferenceCaller<camera_t, void(), Camera_PitchDown_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, void(), Camera_PitchDown_KeyUp>( camwnd.getCamera() )
+ );
- GlobalKeyEvents_insert( "CameraFreeMoveForward", Accelerator( GDK_KEY_Up ),
++
+ GlobalKeyEvents_insert( "CameraFreeMoveForward", accelerator_null(),
FreeMoveCameraMoveForwardKeyDownCaller( camwnd.getCamera() ),
FreeMoveCameraMoveForwardKeyUpCaller( camwnd.getCamera() )
);
FreeMoveCameraMoveDownKeyUpCaller( camwnd.getCamera() )
);
- GlobalCommands_insert( "CameraForward", ReferenceCaller<camera_t, void(), Camera_MoveForward_Discrete>( camwnd.getCamera() ), Accelerator( GDK_KEY_Up ) );
- GlobalCommands_insert( "CameraBack", ReferenceCaller<camera_t, void(), Camera_MoveBack_Discrete>( camwnd.getCamera() ), Accelerator( GDK_KEY_Down ) );
- GlobalCommands_insert( "CameraLeft", ReferenceCaller<camera_t, void(), Camera_RotateLeft_Discrete>( camwnd.getCamera() ), Accelerator( GDK_KEY_Left ) );
- GlobalCommands_insert( "CameraRight", ReferenceCaller<camera_t, void(), Camera_RotateRight_Discrete>( camwnd.getCamera() ), Accelerator( GDK_KEY_Right ) );
- GlobalCommands_insert( "CameraStrafeRight", ReferenceCaller<camera_t, void(), Camera_MoveRight_Discrete>( camwnd.getCamera() ), Accelerator( GDK_KEY_period ) );
- GlobalCommands_insert( "CameraStrafeLeft", ReferenceCaller<camera_t, void(), Camera_MoveLeft_Discrete>( camwnd.getCamera() ), Accelerator( GDK_KEY_comma ) );
- GlobalCommands_insert( "CameraForward", ReferenceCaller<camera_t, Camera_MoveForward_Discrete>( camwnd.getCamera() ) );
- GlobalCommands_insert( "CameraBack", ReferenceCaller<camera_t, Camera_MoveBack_Discrete>( camwnd.getCamera() ) );
- GlobalCommands_insert( "CameraLeft", ReferenceCaller<camera_t, Camera_RotateLeft_Discrete>( camwnd.getCamera() ) );
- GlobalCommands_insert( "CameraRight", ReferenceCaller<camera_t, Camera_RotateRight_Discrete>( camwnd.getCamera() ) );
- GlobalCommands_insert( "CameraStrafeRight", ReferenceCaller<camera_t, Camera_MoveRight_Discrete>( camwnd.getCamera() ) );
- GlobalCommands_insert( "CameraStrafeLeft", ReferenceCaller<camera_t, Camera_MoveLeft_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraForward", ReferenceCaller<camera_t, void(), Camera_MoveForward_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraBack", ReferenceCaller<camera_t, void(), Camera_MoveBack_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraLeft", ReferenceCaller<camera_t, void(), Camera_RotateLeft_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraRight", ReferenceCaller<camera_t, void(), Camera_RotateRight_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraStrafeRight", ReferenceCaller<camera_t, void(), Camera_MoveRight_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraStrafeLeft", ReferenceCaller<camera_t, void(), Camera_MoveLeft_Discrete>( camwnd.getCamera() ) );
- GlobalCommands_insert( "CameraUp", ReferenceCaller<camera_t, void(), Camera_MoveUp_Discrete>( camwnd.getCamera() ), Accelerator( 'D' ) );
- GlobalCommands_insert( "CameraDown", ReferenceCaller<camera_t, void(), Camera_MoveDown_Discrete>( camwnd.getCamera() ), Accelerator( 'C' ) );
- GlobalCommands_insert( "CameraAngleUp", ReferenceCaller<camera_t, void(), Camera_PitchUp_Discrete>( camwnd.getCamera() ), Accelerator( 'A' ) );
- GlobalCommands_insert( "CameraAngleDown", ReferenceCaller<camera_t, void(), Camera_PitchDown_Discrete>( camwnd.getCamera() ), Accelerator( 'Z' ) );
- GlobalCommands_insert( "CameraUp", ReferenceCaller<camera_t, Camera_MoveUp_Discrete>( camwnd.getCamera() ) );
- GlobalCommands_insert( "CameraDown", ReferenceCaller<camera_t, Camera_MoveDown_Discrete>( camwnd.getCamera() ) );
- GlobalCommands_insert( "CameraAngleUp", ReferenceCaller<camera_t, Camera_PitchUp_Discrete>( camwnd.getCamera() ) );
- GlobalCommands_insert( "CameraAngleDown", ReferenceCaller<camera_t, Camera_PitchDown_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraUp", ReferenceCaller<camera_t, void(), Camera_MoveUp_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraDown", ReferenceCaller<camera_t, void(), Camera_MoveDown_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraAngleUp", ReferenceCaller<camera_t, void(), Camera_PitchUp_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraAngleDown", ReferenceCaller<camera_t, void(), Camera_PitchDown_Discrete>( camwnd.getCamera() ) );
}
void CamWnd_Move_Enable( CamWnd& camwnd ){
GlobalShortcuts_insert( "CameraFreeMoveLeft2", Accelerator( GDK_Left ) );
GlobalShortcuts_insert( "CameraFreeMoveRight2", Accelerator( GDK_Right ) );
- GlobalToggles_insert( "ShowStats", FreeCaller<ShowStatsToggle>(), 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 ) );
+ GlobalShortcuts_insert( "CameraFreeMoveUp", accelerator_null() );
+ GlobalShortcuts_insert( "CameraFreeMoveDown", accelerator_null() );
+
+ 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<CamWnd_Move_Discrete>());
+ 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<RenderMode>() );
+ GlobalPreferenceSystem().registerPreference( "StrafeMode", make_property_string( g_camwindow_globals_private.m_nStrafeMode ) );
CamWnd_constructStatic();
--- /dev/null
--- /dev/null
++/*
++ 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 <gtk/gtk.h>
++#include <gdk/gdkkeysyms.h>
++
++#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<camera_t*>( data ), x, y );
++ }
++
++ View* m_view;
++ Callback<void()> m_update;
++
++ static camera_draw_mode draw_mode;
++
++ camera_t( View* view, const Callback<void()>& 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<float>( near_z * tan( degrees_to_radians( fieldOfView * 0.5 ) ) );
++ const float half_height = half_width * ( static_cast<float>( height ) / static_cast<float>( 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<float>( cos( ya ) );
++ camera.forward[1] = static_cast<float>( 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<float>( 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<camera_t*>( 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<camera_t, void(), &Camera_MoveForward_KeyDown> FreeMoveCameraMoveForwardKeyDownCaller;
++typedef ReferenceCaller<camera_t, void(), &Camera_MoveForward_KeyUp> FreeMoveCameraMoveForwardKeyUpCaller;
++typedef ReferenceCaller<camera_t, void(), &Camera_MoveBack_KeyDown> FreeMoveCameraMoveBackKeyDownCaller;
++typedef ReferenceCaller<camera_t, void(), &Camera_MoveBack_KeyUp> FreeMoveCameraMoveBackKeyUpCaller;
++typedef ReferenceCaller<camera_t, void(), &Camera_MoveLeft_KeyDown> FreeMoveCameraMoveLeftKeyDownCaller;
++typedef ReferenceCaller<camera_t, void(), &Camera_MoveLeft_KeyUp> FreeMoveCameraMoveLeftKeyUpCaller;
++typedef ReferenceCaller<camera_t, void(), &Camera_MoveRight_KeyDown> FreeMoveCameraMoveRightKeyDownCaller;
++typedef ReferenceCaller<camera_t, void(), &Camera_MoveRight_KeyUp> FreeMoveCameraMoveRightKeyUpCaller;
++typedef ReferenceCaller<camera_t, void(), &Camera_MoveUp_KeyDown> FreeMoveCameraMoveUpKeyDownCaller;
++typedef ReferenceCaller<camera_t, void(), &Camera_MoveUp_KeyUp> FreeMoveCameraMoveUpKeyUpCaller;
++typedef ReferenceCaller<camera_t, void(), &Camera_MoveDown_KeyDown> FreeMoveCameraMoveDownKeyDownCaller;
++typedef ReferenceCaller<camera_t, void(), &Camera_MoveDown_KeyUp> 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<void()> m_update;
++public:
++RadiantCameraView( camera_t& camera, View* view, const Callback<void()>& 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<camera_t*>( 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<CamWnd, void(), &CamWnd::queue_draw> 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<WindowObserver*>( 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<float>( allocation.width / 2 ), static_cast<float>(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<float>( 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<float>( 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<CamWnd*>( 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<camera_t, void(), Camera_MoveForward_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, void(), Camera_MoveForward_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraBack", Accelerator( GDK_KEY_Down ),
++ ReferenceCaller<camera_t, void(), Camera_MoveBack_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, void(), Camera_MoveBack_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraLeft", Accelerator( GDK_KEY_Left ),
++ ReferenceCaller<camera_t, void(), Camera_RotateLeft_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, void(), Camera_RotateLeft_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraRight", Accelerator( GDK_KEY_Right ),
++ ReferenceCaller<camera_t, void(), Camera_RotateRight_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, void(), Camera_RotateRight_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraStrafeRight", Accelerator( GDK_KEY_period ),
++ ReferenceCaller<camera_t, void(), Camera_MoveRight_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, void(), Camera_MoveRight_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraStrafeLeft", Accelerator( GDK_KEY_comma ),
++ ReferenceCaller<camera_t, void(), Camera_MoveLeft_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, void(), Camera_MoveLeft_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraUp", Accelerator( 'D' ),
++ ReferenceCaller<camera_t, void(), Camera_MoveUp_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, void(), Camera_MoveUp_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraDown", Accelerator( 'C' ),
++ ReferenceCaller<camera_t, void(), Camera_MoveDown_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, void(), Camera_MoveDown_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraAngleDown", Accelerator( 'A' ),
++ ReferenceCaller<camera_t, void(), Camera_PitchDown_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, void(), Camera_PitchDown_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraAngleUp", Accelerator( 'Z' ),
++ ReferenceCaller<camera_t, void(), Camera_PitchUp_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, void(), Camera_PitchUp_KeyUp>( camwnd.getCamera() )
++=======
++ GlobalKeyEvents_insert( "CameraForward", accelerator_null(),
++ ReferenceCaller<camera_t, Camera_MoveForward_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, Camera_MoveForward_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraBack", accelerator_null(),
++ ReferenceCaller<camera_t, Camera_MoveBack_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, Camera_MoveBack_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraLeft", accelerator_null(),
++ ReferenceCaller<camera_t, Camera_RotateLeft_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, Camera_RotateLeft_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraRight", accelerator_null(),
++ ReferenceCaller<camera_t, Camera_RotateRight_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, Camera_RotateRight_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraStrafeRight", accelerator_null(),
++ ReferenceCaller<camera_t, Camera_MoveRight_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, Camera_MoveRight_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraStrafeLeft", accelerator_null(),
++ ReferenceCaller<camera_t, Camera_MoveLeft_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, Camera_MoveLeft_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraUp", accelerator_null(),
++ ReferenceCaller<camera_t, Camera_MoveUp_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, Camera_MoveUp_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraDown", accelerator_null(),
++ ReferenceCaller<camera_t, Camera_MoveDown_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, Camera_MoveDown_KeyUp>( camwnd.getCamera() )
++ );
++ GlobalKeyEvents_insert( "CameraAngleUp", accelerator_null(),
++ ReferenceCaller<camera_t, Camera_PitchUp_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, Camera_PitchUp_KeyUp>( camwnd.getCamera() )
++>>>>>>> 3a78d902017a780e65f21f12c709aa746dfcab84
++ );
++ GlobalKeyEvents_insert( "CameraAngleDown", accelerator_null(),
++ ReferenceCaller<camera_t, Camera_PitchDown_KeyDown>( camwnd.getCamera() ),
++ ReferenceCaller<camera_t, Camera_PitchDown_KeyUp>( 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<camera_t, void(), Camera_MoveForward_Discrete>( camwnd.getCamera() ), Accelerator( GDK_KEY_Up ) );
++ GlobalCommands_insert( "CameraBack", ReferenceCaller<camera_t, void(), Camera_MoveBack_Discrete>( camwnd.getCamera() ), Accelerator( GDK_KEY_Down ) );
++ GlobalCommands_insert( "CameraLeft", ReferenceCaller<camera_t, void(), Camera_RotateLeft_Discrete>( camwnd.getCamera() ), Accelerator( GDK_KEY_Left ) );
++ GlobalCommands_insert( "CameraRight", ReferenceCaller<camera_t, void(), Camera_RotateRight_Discrete>( camwnd.getCamera() ), Accelerator( GDK_KEY_Right ) );
++ GlobalCommands_insert( "CameraStrafeRight", ReferenceCaller<camera_t, void(), Camera_MoveRight_Discrete>( camwnd.getCamera() ), Accelerator( GDK_KEY_period ) );
++ GlobalCommands_insert( "CameraStrafeLeft", ReferenceCaller<camera_t, void(), Camera_MoveLeft_Discrete>( camwnd.getCamera() ), Accelerator( GDK_KEY_comma ) );
++
++ GlobalCommands_insert( "CameraUp", ReferenceCaller<camera_t, void(), Camera_MoveUp_Discrete>( camwnd.getCamera() ), Accelerator( 'D' ) );
++ GlobalCommands_insert( "CameraDown", ReferenceCaller<camera_t, void(), Camera_MoveDown_Discrete>( camwnd.getCamera() ), Accelerator( 'C' ) );
++ GlobalCommands_insert( "CameraAngleUp", ReferenceCaller<camera_t, void(), Camera_PitchUp_Discrete>( camwnd.getCamera() ), Accelerator( 'A' ) );
++ GlobalCommands_insert( "CameraAngleDown", ReferenceCaller<camera_t, void(), Camera_PitchDown_Discrete>( camwnd.getCamera() ), Accelerator( 'Z' ) );
++=======
++ GlobalCommands_insert( "CameraForward", ReferenceCaller<camera_t, Camera_MoveForward_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraBack", ReferenceCaller<camera_t, Camera_MoveBack_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraLeft", ReferenceCaller<camera_t, Camera_RotateLeft_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraRight", ReferenceCaller<camera_t, Camera_RotateRight_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraStrafeRight", ReferenceCaller<camera_t, Camera_MoveRight_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraStrafeLeft", ReferenceCaller<camera_t, Camera_MoveLeft_Discrete>( camwnd.getCamera() ) );
++
++ GlobalCommands_insert( "CameraUp", ReferenceCaller<camera_t, Camera_MoveUp_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraDown", ReferenceCaller<camera_t, Camera_MoveDown_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraAngleUp", ReferenceCaller<camera_t, Camera_PitchUp_Discrete>( camwnd.getCamera() ) );
++ GlobalCommands_insert( "CameraAngleDown", ReferenceCaller<camera_t, Camera_PitchDown_Discrete>( 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<void(bool)> &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<CamWnd, void(), CamWnd_Update>( *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<CamWnd, void(rect_t), camwnd_update_xor_rectangle>( *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<CamWnd, void(), CamWnd_Update>( *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<CamWnd*>( 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<state_type> 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<void(bool)> &importer ){
++ importer( g_camwindow_globals_private.m_showStats );
++}
++
++FreeCaller<void(const Callback<void(bool)>&), ShowStatsExport> g_show_stats_caller;
++Callback<void(const Callback<void(bool)> &)> 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<void(), ShowStatsToggle> ShowStatsToggleCaller;
++void ShowStatsExport( const Callback<void(bool)> & importer ){
++ importer( g_camwindow_globals_private.m_showStats );
++}
++typedef FreeCaller<void(const Callback<void(bool)> &), ShowStatsExport> ShowStatsExportCaller;
++
++ShowStatsExportCaller g_show_stats_caller;
++Callback<void(const Callback<void(bool)> &)> 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<const float*>( &m_Camera.projection ) );
++
++ glMatrixMode( GL_MODELVIEW );
++ glLoadMatrixf( reinterpret_cast<const float*>( &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<float>( m_Camera.height ) - GlobalOpenGL().m_font->getPixelDescent(), 0.0f );
++ extern const char* Renderer_GetStats();
++ GlobalOpenGL().drawString( Renderer_GetStats() );
++
++ glRasterPos3f( 1.0f, static_cast<float>( 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<float>( 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<float>( 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<bool, void(const Callback<void(bool)> &), PropertyImpl<bool>::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<void(bool)> &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<void()>() );
++ g_camera_model = 0;
++ Camera_updateModelview( camwnd.getCamera() );
++ Camera_updateProjection( camwnd.getCamera() );
++ CamWnd_Update( camwnd );
++ }
++}
++
++inline CameraModel* Instance_getCameraModel( scene::Instance& instance ){
++ return InstanceTypeCast<CameraModel>::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(), CamWnd_LookThroughCamera>( camwnd ) );
++ }
++ }
++}
++
++void GlobalCamera_LookThroughSelected(){
++ CamWnd_LookThroughSelected( *g_camwnd );
++}
++
++void GlobalCamera_LookThroughCamera(){
++ CamWnd_LookThroughCamera( *g_camwnd );
++}
++
++struct RenderMode {
++ static void Export(const Callback<void(int)> &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<CamWnd_Move_Discrete>()
++ );
++ page.appendCheckBox(
++ "", "Enable far-clip plane",
++ make_property<Camera_FarClip>()
++ );
++
++ if ( g_pGameDescription->mGameType == "doom3" ) {
++ const char* render_mode[] = { "Wireframe", "Flatshade", "Textured", "Lighting" };
++
++ page.appendCombo(
++ "Render Mode",
++ STRING_ARRAY_RANGE( render_mode ),
++ make_property<RenderMode>()
++ );
++ }
++ else
++ {
++ const char* render_mode[] = { "Wireframe", "Flatshade", "Textured" };
++
++ page.appendCombo(
++ "Render Mode",
++ STRING_ARRAY_RANGE( render_mode ),
++ make_property<RenderMode>()
++ );
++ }
++
++ 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<CamWnd_Move_Discrete>());
++ 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<RenderMode>() );
++ 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<ShowStatsToggle>(), 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();
++}
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;
--- /dev/null
--- /dev/null
++/*
++ 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 <gtk/gtk.h>
++
++#include "debugging/debugging.h"
++
++#include "ientity.h"
++#include "iselection.h"
++#include "iundo.h"
++
++#include <vector>
++
++#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 TSelectionPolicy>
++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<TSelectionPolicy>( 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<void(), Selection_UpdateWorkzone> 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<const char*> 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<SelectionPolicy_Inside>::DoSelection();
++}
++
++void Select_Touching( void ){
++ SelectByBounds<SelectionPolicy_Touching>::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<bool, void(const Callback<void(bool)> &), PropertyImpl<bool>::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<void(const Selectable&), SceneSelectionChange> SceneSelectionChangeCaller;
++ GlobalSelectionSystem().addSelectionChangeCallback( SceneSelectionChangeCaller() );
++ typedef FreeCaller<void(const Selectable&), UpdateWorkzone_ForSelectionChanged> UpdateWorkzoneForSelectionChangedCaller;
++ GlobalSelectionSystem().addSelectionChangeCallback( UpdateWorkzoneForSelectionChangedCaller() );
++ typedef FreeCaller<void(), UpdateWorkzone_ForSelection> UpdateWorkzoneForSelectionCaller;
++ Selection_boundsChanged = GlobalSceneGraph().addBoundsChangedCallback( UpdateWorkzoneForSelectionCaller() );
++}
++
++void Selection_destroy(){
++ GlobalSceneGraph().removeBoundsChangedCallback( Selection_boundsChanged );
++}
++
++
++#include "gtkdlgs.h"
++#include <gdk/gdkkeysyms.h>
++
++
++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<float>( gtk_spin_button_get_value( rotateDialog->x ) );
++ eulerXYZ[1] = static_cast<float>( gtk_spin_button_get_value( rotateDialog->y ) );
++ eulerXYZ[2] = static_cast<float>( 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<float>( atof( gtk_entry_get_text( GTK_ENTRY( scaleDialog->x ) ) ) );
++ sy = static_cast<float>( atof( gtk_entry_get_text( GTK_ENTRY( scaleDialog->y ) ) ) );
++ sz = static_cast<float>( 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();
++}
}
void mouseMoved( DeviceVector position ){
- getSelectionSystem().MoveSelected( *m_view, &position[0] );
+ getSelectionSystem().MoveSelected( *m_view, &position[0], ( m_state & c_modifierShift ) == c_modifierShift );
}
-typedef MemberCaller1<Manipulator_, DeviceVector, &Manipulator_::mouseMoved> MouseMovedCaller;
+typedef MemberCaller<Manipulator_, void(DeviceVector), &Manipulator_::mouseMoved> MouseMovedCaller;
void mouseUp( DeviceVector position ){
getSelectionSystem().endMove();
g_mouseMovedCallback.clear();
g_mouseUpCallback.clear();
}
-typedef MemberCaller1<Manipulator_, DeviceVector, &Manipulator_::mouseUp> MouseUpCaller;
+typedef MemberCaller<Manipulator_, void(DeviceVector), &Manipulator_::mouseUp> 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 );
--- /dev/null
--- /dev/null
++/*
++ 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 <map>
++#include <list>
++#include <set>
++
++#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<float>( 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<float>( 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<float>( 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<float>( c_pi / 12.0 ) ) ) );
++ }
++ else{
++ m_rotatable.rotate( quaternion_for_axisangle( m_axis, angle_for_axis( m_start, current, m_axis ) ) );
++ }
++}
++
++void SetAxis( const Vector3& axis ){
++ m_axis = axis;
++}
++};
++
++void 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<primitive_t> 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<float>( 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<float>( vector3_length_squared( point ) );
++
++ assign_if_closer( best, SelectionIntersection( depth, distance ) );
++ }
++ }
++ else if ( count > 2 ) {
++ assign_if_closer(
++ best,
++ SelectionIntersection(
++ static_cast<float>( 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<const Vector3&>( ( *x ).vertex ),
++ reinterpret_cast<const Vector3&>( ( *y ).vertex ),
++ reinterpret_cast<const Vector3&>( ( *z ).vertex ),
++ clipped
++ ),
++ clipped,
++ best,
++ cull
++ );
++ }
++}
++
++
++typedef std::multimap<SelectionIntersection, Selectable*> 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<typename remap_policy>
++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<float>( radius * cos( theta ) );
++ y = static_cast<float>( 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<PointVertex> 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<PointVertex>::iterator i = m_vertices.begin(); i != m_vertices.end(); ++i )
++ {
++ ( *i ).colour = colour;
++ }
++ }
++};
++
++struct RenderableSemiCircle : public OpenGLRenderable
++{
++ Array<PointVertex> 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<PointVertex>::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<typename VertexRemap, typename NormalRemap>
++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<float>( cos( i * head_segment ) );
++ VertexRemap::z( point.vertex ) = arrowhead_radius * static_cast<float>( sin( i * head_segment ) );
++ NormalRemap::x( point.normal ) = arrowhead_radius / arrowhead_length;
++ NormalRemap::y( point.normal ) = static_cast<float>( cos( i * head_segment ) );
++ NormalRemap::z( point.normal ) = static_cast<float>( 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<float>( cos( ( i + 0.5 ) * head_segment ) );
++ NormalRemap::z( point.normal ) = static_cast<float>( 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<float>( cos( ( i + 1 ) * head_segment ) );
++ VertexRemap::z( point.vertex ) = arrowhead_radius * static_cast<float>( sin( ( i + 1 ) * head_segment ) );
++ NormalRemap::x( point.normal ) = arrowhead_radius / arrowhead_length;
++ NormalRemap::y( point.normal ) = static_cast<float>( cos( ( i + 1 ) * head_segment ) );
++ NormalRemap::z( point.normal ) = static_cast<float>( 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<float>( cos( i * head_segment ) );
++ VertexRemap::z( point.vertex ) = arrowhead_radius * static_cast<float>( 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<float>( cos( ( i + 1 ) * head_segment ) );
++ VertexRemap::z( point.vertex ) = arrowhead_radius * static_cast<float>( sin( ( i + 1 ) * head_segment ) );
++ NormalRemap::x( point.normal ) = -1;
++ NormalRemap::y( point.normal ) = 0;
++ NormalRemap::z( point.normal ) = 0;
++ }
++ }
++}
++
++template<typename Triple>
++class TripleRemapXYZ
++{
++public:
++static float& x( Triple& triple ){
++ return triple.x();
++}
++static float& y( Triple& triple ){
++ return triple.y();
++}
++static float& z( Triple& triple ){
++ return triple.z();
++}
++};
++
++template<typename Triple>
++class TripleRemapYZX
++{
++public:
++static float& x( Triple& triple ){
++ return triple.y();
++}
++static float& y( Triple& triple ){
++ return triple.z();
++}
++static float& z( Triple& triple ){
++ return triple.x();
++}
++};
++
++template<typename Triple>
++class TripleRemapZXY
++{
++public:
++static float& x( Triple& triple ){
++ return triple.z();
++}
++static float& y( Triple& triple ){
++ return triple.x();
++}
++static float& z( Triple& triple ){
++ return triple.y();
++}
++};
++
++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<FlatShadedVertex> 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<FlatShadedVertex>::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<Vertex3f>(), TripleRemapXYZ<Normal3f>() );
++ draw_arrowline( length, m_arrow_y.m_line, 1 );
++ draw_arrowhead( segments, length, m_arrow_head_y.m_vertices.data(), TripleRemapYZX<Vertex3f>(), TripleRemapYZX<Normal3f>() );
++ draw_arrowline( length, m_arrow_z.m_line, 2 );
++ draw_arrowhead( segments, length, m_arrow_head_z.m_vertices.data(), TripleRemapZXY<Vertex3f>(), TripleRemapZXY<Normal3f>() );
++
++ draw_quad( 16, m_quad_screen.m_quad );
++}
++
++void UpdateColours(){
++ m_arrow_x.setColour( colourSelected( g_colour_x, m_selectable_x.isSelected() ) );
++ m_arrow_head_x.setColour( colourSelected( g_colour_x, m_selectable_x.isSelected() ) );
++ m_arrow_y.setColour( colourSelected( g_colour_y, m_selectable_y.isSelected() ) );
++ m_arrow_head_y.setColour( colourSelected( g_colour_y, m_selectable_y.isSelected() ) );
++ m_arrow_z.setColour( colourSelected( g_colour_z, m_selectable_z.isSelected() ) );
++ m_arrow_head_z.setColour( colourSelected( g_colour_z, m_selectable_z.isSelected() ) );
++ m_quad_screen.setColour( colourSelected( g_colour_screen, m_selectable_screen.isSelected() ) );
++}
++
++bool manipulator_show_axis( const Pivot2World& pivot, const Vector3& axis ){
++ return 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<PlaneSelectable>::cast( instance );
++}
++
++class PlaneSelectableSelectPlanes : public scene::Graph::Walker
++{
++Selector& m_selector;
++SelectionTest& m_test;
++PlaneCallback m_selectedPlaneCallback;
++public:
++PlaneSelectableSelectPlanes( Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback )
++ : m_selector( selector ), m_test( test ), m_selectedPlaneCallback( selectedPlaneCallback ){
++}
++bool pre( const scene::Path& path, scene::Instance& instance ) const {
++ 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<Plane3, PlaneLess> 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<SelectedPlaneSet, void(const Plane3&), &SelectedPlaneSet::insert> InsertCaller;
++};
++
++
++bool Scene_forEachPlaneSelectable_selectPlanes( scene::Graph& graph, Selector& selector, SelectionTest& test ){
++ SelectedPlaneSet selectedPlanes;
++
++ Scene_forEachPlaneSelectable_selectPlanes( graph, selector, test, SelectedPlaneSet::InsertCaller( selectedPlanes ) );
++ Scene_forEachPlaneSelectable_selectReversedPlanes( graph, selector, selectedPlanes );
++
++ return !selectedPlanes.empty();
++}
++
++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<const Vector3&>( vertices[0] ),
++ reinterpret_cast<const Vector3&>( vertices[i + 1] ),
++ reinterpret_cast<const Vector3&>( 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<const Vector3&>( ( *prev ) ),
++ reinterpret_cast<const Vector3&>( ( *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<const Vector3&>( ( *i ) ),
++ reinterpret_cast<const Vector3&>( ( *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<const Vector3&>( ( *i ) ),
++ reinterpret_cast<const Vector3&>( ( *( 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<const Vector3&>( vertices[*i] ),
++ reinterpret_cast<const Vector3&>( vertices[*( i + 1 )] ),
++ reinterpret_cast<const Vector3&>( vertices[*( i + 2 )] ),
++ clipped
++ ),
++ clipped,
++ best,
++ m_cull
++ );
++ }
++}
++void TestQuads( const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best ){
++ Vector4 clipped[9];
++ for ( IndexPointer::iterator i( indices.begin() ); i != indices.end(); i += 4 )
++ {
++ BestPoint(
++ matrix4_clip_triangle(
++ m_local2view,
++ reinterpret_cast<const Vector3&>( vertices[*i] ),
++ reinterpret_cast<const Vector3&>( vertices[*( i + 1 )] ),
++ reinterpret_cast<const Vector3&>( vertices[*( i + 3 )] ),
++ clipped
++ ),
++ clipped,
++ best,
++ m_cull
++ );
++ BestPoint(
++ matrix4_clip_triangle(
++ m_local2view,
++ reinterpret_cast<const Vector3&>( vertices[*( i + 1 )] ),
++ reinterpret_cast<const Vector3&>( vertices[*( i + 2 )] ),
++ reinterpret_cast<const Vector3&>( vertices[*( i + 3 )] ),
++ clipped
++ ),
++ clipped,
++ best,
++ m_cull
++ );
++ }
++}
++void TestQuadStrip( const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best ){
++ Vector4 clipped[9];
++ for ( IndexPointer::iterator i( indices.begin() ); i + 2 != indices.end(); i += 2 )
++ {
++ BestPoint(
++ matrix4_clip_triangle(
++ m_local2view,
++ reinterpret_cast<const Vector3&>( vertices[*i] ),
++ reinterpret_cast<const Vector3&>( vertices[*( i + 1 )] ),
++ reinterpret_cast<const Vector3&>( vertices[*( i + 2 )] ),
++ clipped
++ ),
++ clipped,
++ best,
++ m_cull
++ );
++ BestPoint(
++ matrix4_clip_triangle(
++ m_local2view,
++ reinterpret_cast<const Vector3&>( vertices[*( i + 2 )] ),
++ reinterpret_cast<const Vector3&>( vertices[*( i + 1 )] ),
++ reinterpret_cast<const Vector3&>( vertices[*( i + 3 )] ),
++ clipped
++ ),
++ clipped,
++ best,
++ m_cull
++ );
++ }
++}
++};
++
++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<Selectable*> 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<Selectable*>& 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<Selectable*>::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<scene::Instance> selection_t;
++selection_t m_selection;
++selection_t m_component_selection;
++
++Signal1<const Selectable&> 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<RadiantSelectionSystem, void(), &RadiantSelectionSystem::pivotChanged> PivotChangedCaller;
++void pivotChangedSelection( const Selectable& selectable ){
++ pivotChanged();
++}
++typedef MemberCaller<RadiantSelectionSystem, void(const Selectable&), &RadiantSelectionSystem::pivotChangedSelection> 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<RadiantSelectionSystem, void(const Selectable&), &RadiantSelectionSystem::selectionChanged> 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(PreferenceGroup&), SelectionSystem_constructPage>() );
++}
++
++
++
++void SelectionSystem_OnBoundsChanged(){
++ getSelectionSystem().pivotChanged();
++}
++
++SignalHandlerId SelectionSystem_boundsChanged;
++
++void SelectionSystem_Construct(){
++ RadiantSelectionSystem::constructStatic();
++
++ g_RadiantSelectionSystem = new RadiantSelectionSystem;
++
++ SelectionSystem_boundsChanged = GlobalSceneGraph().addBoundsChangedCallback( FreeCaller<void(), SelectionSystem_OnBoundsChanged>() );
++
++ 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<float>( origin + size ), std::max( static_cast<float>( 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<void(DeviceVector)> MouseEventCallback;
++
++Single<MouseEventCallback> g_mouseMovedCallback;
++Single<MouseEventCallback> 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<Selector_, void(DeviceVector), &Selector_::mouseMoved> 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<Selector_, void(DeviceVector), &Selector_::mouseUp> 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<Manipulator_, void(DeviceVector), &Manipulator_::mouseMoved> MouseMovedCaller;
++
++void mouseUp( DeviceVector position ){
++ getSelectionSystem().endMove();
++ g_mouseMovedCallback.clear();
++ g_mouseUpCallback.clear();
++}
++<<<<<<< HEAD
++typedef MemberCaller<Manipulator_, void(DeviceVector), &Manipulator_::mouseUp> MouseUpCaller;
++=======
++typedef MemberCaller1<Manipulator_, DeviceVector, &Manipulator_::mouseUp> 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<float>( m_width ), SELECT_EPSILON / static_cast<float>( 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<SelectionAPI, SelectionDependencies> SelectionModule;
++typedef Static<SelectionModule> StaticSelectionModule;
++StaticRegisterModule staticRegisterSelection( StaticSelectionModule::instance() );
}
}
-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" ) {
bool TextureBrowser_hideUnused();
-void TextureBrowser_hideUnusedExport( const BoolImportCallback& importer ){
+void TextureBrowser_hideUnusedExport( const Callback<void(bool)> & importer ){
importer( TextureBrowser_hideUnused() );
}
-typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_hideUnusedExport> TextureBrowserHideUnusedExport;
-void TextureBrowser_showShadersExport( const BoolImportCallback& importer ){
+typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_hideUnusedExport> TextureBrowserHideUnusedExport;
+
+void TextureBrowser_showShadersExport( const Callback<void(bool)> & importer ){
importer( GlobalTextureBrowser().m_showShaders );
}
-typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_showShadersExport> TextureBrowserShowShadersExport;
-void TextureBrowser_showTexturesExport( const BoolImportCallback& importer ){
+typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_showShadersExport> TextureBrowserShowShadersExport;
+
+void TextureBrowser_showTexturesExport( const Callback<void(bool)> & importer ){
importer( GlobalTextureBrowser().m_showTextures );
}
-typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_showTexturesExport> TextureBrowserShowTexturesExport;
-void TextureBrowser_showShaderlistOnly( const BoolImportCallback& importer ){
+typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_showTexturesExport> TextureBrowserShowTexturesExport;
+
+void TextureBrowser_showShaderlistOnly( const Callback<void(bool)> & importer ){
importer( g_TextureBrowser_shaderlistOnly );
}
-typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_showShaderlistOnly> TextureBrowserShowShaderlistOnlyExport;
-void TextureBrowser_fixedSize( const BoolImportCallback& importer ){
+typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_showShaderlistOnly> TextureBrowserShowShaderlistOnlyExport;
+
+void TextureBrowser_fixedSize( const Callback<void(bool)> & importer ){
importer( g_TextureBrowser_fixedSize );
}
-typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_fixedSize> TextureBrowser_FixedSizeExport;
-void TextureBrowser_filterNotex( const BoolImportCallback& importer ){
- importer( g_TextureBrowser_filterNotex );
+typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_fixedSize> TextureBrowser_FixedSizeExport;
+
+void TextureBrowser_filterMissing( const Callback<void(bool)> & importer ){
+ importer( g_TextureBrowser_filterMissing );
}
-typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_filterNotex> TextureBrowser_filterNotexExport;
-void TextureBrowser_enableAlpha( const BoolImportCallback& importer ){
+typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_filterMissing> TextureBrowser_filterMissingExport;
+
+void TextureBrowser_filterFallback( const Callback<void(bool)> & importer ){
+ importer( g_TextureBrowser_filterFallback );
+}
+
+typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_filterFallback> TextureBrowser_filterFallbackExport;
+
+void TextureBrowser_enableAlpha( const Callback<void(bool)> & importer ){
importer( g_TextureBrowser_enableAlpha );
}
-typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_enableAlpha> TextureBrowser_enableAlphaExport;
+
+typedef FreeCaller<void(const Callback<void(bool)> &), 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();
const char* startup_shaders[] = { "None", TextureBrowser_getComonShadersName() };
page.appendCombo( "Load Shaders at Startup", reinterpret_cast<int&>( 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 TextureClipboard_textureSelected( const char* shader );
void TextureBrowser_Construct(){
- GlobalCommands_insert( "ShaderInfo", FreeCaller<TextureBrowser_shaderInfo>() );
- GlobalCommands_insert( "ShowUntagged", FreeCaller<TextureBrowser_showUntagged>() );
- GlobalCommands_insert( "AddTag", FreeCaller<TextureBrowser_addTag>() );
- GlobalCommands_insert( "RenameTag", FreeCaller<TextureBrowser_renameTag>() );
- GlobalCommands_insert( "DeleteTag", FreeCaller<TextureBrowser_deleteTag>() );
- GlobalCommands_insert( "CopyTag", FreeCaller<TextureBrowser_copyTag>() );
- GlobalCommands_insert( "PasteTag", FreeCaller<TextureBrowser_pasteTag>() );
- GlobalCommands_insert( "RefreshShaders", FreeCaller<RefreshShaders>() );
- GlobalToggles_insert( "ShowInUse", FreeCaller<TextureBrowser_ToggleHideUnused>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_hideunused_item ), Accelerator( 'U' ) );
- GlobalCommands_insert( "ShowAllTextures", FreeCaller<TextureBrowser_showAll>(), Accelerator( 'A', (GdkModifierType)GDK_CONTROL_MASK ) );
- GlobalCommands_insert( "ToggleTextures", FreeCaller<TextureBrowser_toggleShow>(), Accelerator( 'T' ) );
- GlobalToggles_insert( "ToggleShowShaders", FreeCaller<TextureBrowser_ToggleShowShaders>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showshaders_item ) );
- GlobalToggles_insert( "ToggleShowTextures", FreeCaller<TextureBrowser_ToggleShowTextures>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showtextures_item ) );
- GlobalToggles_insert( "ToggleShowShaderlistOnly", FreeCaller<TextureBrowser_ToggleShowShaderListOnly>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showshaderlistonly_item ) );
- GlobalToggles_insert( "FixedSize", FreeCaller<TextureBrowser_FixedSize>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_fixedsize_item ) );
- GlobalToggles_insert( "FilterNotex", FreeCaller<TextureBrowser_FilterNotex>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_filternotex_item ) );
- GlobalToggles_insert( "EnableAlpha", FreeCaller<TextureBrowser_EnableAlpha>(), 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<int&>( GlobalTextureBrowser().m_startupShaders ) ), IntExportStringCaller( reinterpret_cast<int&>( 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<TextureScale>(g_TextureBrowser) );
+ GlobalPreferenceSystem().registerPreference( "UniformTextureSize", make_property_string<UniformTextureSize>(g_TextureBrowser) );
+ GlobalPreferenceSystem().registerPreference( "UniformTextureMinSize", make_property_string<UniformTextureMinSize>(g_TextureBrowser) );
+ GlobalPreferenceSystem().registerPreference( "TextureScrollbar", make_property_string<TextureBrowser_ShowScrollbar>(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<int&>( 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();
--- /dev/null
--- /dev/null
++/*
++ 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 <gtk/gtk.h>
++
++#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 <set>
++#include <string>
++#include <vector>
++
++#include <uilib/uilib.h>
++
++#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<CopiedString> 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<TextureGroups, void(const char*), TextureGroups_addWad> 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<TextureGroups, void(const char*), TextureGroups_addShader> TextureGroupsAddShaderCaller;
++
++void TextureGroups_addDirectory( TextureGroups& groups, const char* directory ){
++ groups.insert( directory );
++}
++
++typedef ReferenceCaller<TextureGroups, void(const char*), TextureGroups_addDirectory> 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<DeferredAdjustment*>( data )->m_function(
++ reinterpret_cast<DeferredAdjustment*>( data )->m_data,
++ reinterpret_cast<DeferredAdjustment*>( data )->m_value
++ );
++ reinterpret_cast<DeferredAdjustment*>( data )->m_handler = 0;
++ reinterpret_cast<DeferredAdjustment*>( 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<TextureBrowser, void(), TextureBrowser_queueDraw> TextureBrowserQueueDrawCaller;
++
++void TextureBrowser_scrollChanged( void* data, gdouble value );
++
++
++enum StartupShaders
++{
++ STARTUPSHADERS_NONE = 0,
++ STARTUPSHADERS_COMMON,
++};
++
++void TextureBrowser_hideUnusedExport( const Callback<void(bool)> & importer );
++
++typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_hideUnusedExport> TextureBrowserHideUnusedExport;
++
++void TextureBrowser_showShadersExport( const Callback<void(bool)> & importer );
++
++typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_showShadersExport> TextureBrowserShowShadersExport;
++
++void TextureBrowser_showTexturesExport( const Callback<void(bool)> & importer );
++
++typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_showTexturesExport> TextureBrowserShowTexturesExport;
++
++void TextureBrowser_showShaderlistOnly( const Callback<void(bool)> & importer );
++
++typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_showShaderlistOnly> TextureBrowserShowShaderlistOnlyExport;
++
++void TextureBrowser_fixedSize( const Callback<void(bool)> & importer );
++
++typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_fixedSize> TextureBrowserFixedSizeExport;
++
++void TextureBrowser_filterMissing( const Callback<void(bool)> & importer );
++
++typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_filterMissing> TextureBrowserFilterMissingExport;
++
++void TextureBrowser_filterFallback( const Callback<void(bool)> & importer );
++
++typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_filterFallback> TextureBrowserFilterFallbackExport;
++
++void TextureBrowser_enableAlpha( const Callback<void(bool)> & importer );
++
++typedef FreeCaller<void(const Callback<void(bool)> &), 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<CopiedString> m_all_tags;
++ui::ListStore m_all_tags_list{ui::null};
++std::vector<CopiedString> m_copied_tags;
++std::set<CopiedString> 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<CopiedString>::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<CopiedString>::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<void(bool)> &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<char, void(const char*), TextureDirectory_loadTexture> 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<void(bool)> & importer ){
++ importer( TextureBrowser_hideUnused() );
++}
++
++typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_hideUnusedExport> TextureBrowserHideUnusedExport;
++
++void TextureBrowser_showShadersExport( const Callback<void(bool)> & importer ){
++ importer( GlobalTextureBrowser().m_showShaders );
++}
++
++typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_showShadersExport> TextureBrowserShowShadersExport;
++
++void TextureBrowser_showTexturesExport( const Callback<void(bool)> & importer ){
++ importer( GlobalTextureBrowser().m_showTextures );
++}
++
++typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_showTexturesExport> TextureBrowserShowTexturesExport;
++
++void TextureBrowser_showShaderlistOnly( const Callback<void(bool)> & importer ){
++ importer( g_TextureBrowser_shaderlistOnly );
++}
++
++typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_showShaderlistOnly> TextureBrowserShowShaderlistOnlyExport;
++
++void TextureBrowser_fixedSize( const Callback<void(bool)> & importer ){
++ importer( g_TextureBrowser_fixedSize );
++}
++
++typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_fixedSize> TextureBrowser_FixedSizeExport;
++
++void TextureBrowser_filterMissing( const Callback<void(bool)> & importer ){
++ importer( g_TextureBrowser_filterMissing );
++}
++
++typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_filterMissing> TextureBrowser_filterMissingExport;
++
++void TextureBrowser_filterFallback( const Callback<void(bool)> & importer ){
++ importer( g_TextureBrowser_filterFallback );
++}
++
++typedef FreeCaller<void(const Callback<void(bool)> &), TextureBrowser_filterFallback> TextureBrowser_filterFallbackExport;
++
++void TextureBrowser_enableAlpha( const Callback<void(bool)> & importer ){
++ importer( g_TextureBrowser_enableAlpha );
++}
++
++typedef FreeCaller<void(const Callback<void(bool)> &), 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<TextureBrowser*>( 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<CopiedString> 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<CopiedString>& allTags,
++ TextureBrowser* textureBrowser ){
++ GtkTreeIter iterAssigned;
++ GtkTreeIter iterAvailable;
++ std::set<CopiedString>::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<int>( event->x ), static_cast<int>( 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<int>( event->x ), static_cast<int>( 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<char*>( 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<TextureBrowser*>( 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<CopiedString>::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<CopiedString>::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<CopiedString>::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<TextureBrowser, void(), TextureBrowser_activeShadersChanged>( 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<void()>() );
++
++ 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<CopiedString>::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<void(const char *)> & 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<void(int)> &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<void(int)> &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<void(int)> &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<TextureBrowser_ShowScrollbar>(GlobalTextureBrowser())
++ );
++ {
++ const char* texture_scale[] = { "10%", "25%", "50%", "100%", "200%" };
++ page.appendCombo(
++ "Texture Thumbnail Scale",
++ STRING_ARRAY_RANGE( texture_scale ),
++ make_property<TextureScale>(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<int&>( 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<TextureScale>(g_TextureBrowser) );
++ GlobalPreferenceSystem().registerPreference( "UniformTextureSize", make_property_string<UniformTextureSize>(g_TextureBrowser) );
++ GlobalPreferenceSystem().registerPreference( "UniformTextureMinSize", make_property_string<UniformTextureMinSize>(g_TextureBrowser) );
++ GlobalPreferenceSystem().registerPreference( "TextureScrollbar", make_property_string<TextureBrowser_ShowScrollbar>(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<int&>( 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<TextureBrowser_shaderInfo>() );
++ GlobalCommands_insert( "ShowUntagged", FreeCaller<TextureBrowser_showUntagged>() );
++ GlobalCommands_insert( "AddTag", FreeCaller<TextureBrowser_addTag>() );
++ GlobalCommands_insert( "RenameTag", FreeCaller<TextureBrowser_renameTag>() );
++ GlobalCommands_insert( "DeleteTag", FreeCaller<TextureBrowser_deleteTag>() );
++ GlobalCommands_insert( "CopyTag", FreeCaller<TextureBrowser_copyTag>() );
++ GlobalCommands_insert( "PasteTag", FreeCaller<TextureBrowser_pasteTag>() );
++ GlobalCommands_insert( "RefreshShaders", FreeCaller<RefreshShaders>() );
++ GlobalToggles_insert( "ShowInUse", FreeCaller<TextureBrowser_ToggleHideUnused>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_hideunused_item ), Accelerator( 'U' ) );
++ GlobalCommands_insert( "ShowAllTextures", FreeCaller<TextureBrowser_showAll>(), Accelerator( 'A', (GdkModifierType)GDK_CONTROL_MASK ) );
++ GlobalCommands_insert( "ToggleTextures", FreeCaller<TextureBrowser_toggleShow>(), Accelerator( 'T' ) );
++ GlobalToggles_insert( "ToggleShowShaders", FreeCaller<TextureBrowser_ToggleShowShaders>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showshaders_item ) );
++ GlobalToggles_insert( "ToggleShowTextures", FreeCaller<TextureBrowser_ToggleShowTextures>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showtextures_item ) );
++ GlobalToggles_insert( "ToggleShowShaderlistOnly", FreeCaller<TextureBrowser_ToggleShowShaderListOnly>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_showshaderlistonly_item ) );
++ GlobalToggles_insert( "FixedSize", FreeCaller<TextureBrowser_FixedSize>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_fixedsize_item ) );
++ GlobalToggles_insert( "FilterNotex", FreeCaller<TextureBrowser_FilterNotex>(), ToggleItem::AddCallbackCaller( g_TextureBrowser.m_filternotex_item ) );
++ GlobalToggles_insert( "EnableAlpha", FreeCaller<TextureBrowser_EnableAlpha>(), 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<int&>( GlobalTextureBrowser().m_startupShaders ) ), IntExportStringCaller( reinterpret_cast<int&>( 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<TextureBrowser, void(), TextureBrowser_queueDraw>( g_TextureBrowser ) );
++
++ TextureBrowser_registerPreferencesPage();
++
++ GlobalShaderSystem().attach( g_ShadersObserver );
++
++ TextureBrowser_textureSelected = TextureClipboard_textureSelected;
++}
++
++void TextureBrowser_Destroy(){
++ GlobalShaderSystem().detach( g_ShadersObserver );
++
++ Textures_setModeChangedNotify( Callback<void()>() );
++}
++
++ui::Widget TextureBrowser_getGLWidget(){
++ return GlobalTextureBrowser().m_gl_widget;
++}