binds...
	* douplicated basic shortcuts: arrows - camera freemove, ESC - deSelect, backSpace - delete

menus...
	* modify+: snap to grid (ctrl+g)

misc...
	* fixed warning on import (q3map2_type not found)
	* patch thicken
	* removed douplicate accelerators registering
	* trigger_* entities get textures/common/trigger tex on creation
		( unless different is set in .game file via shader_trigger key )
	* opening *.map, sent via cmd line (can assign *.map files in system to be opened with radiant)
	* -aero cmd line key to enable aero transparency
	* opening maps with bad tex defs (.#QNAN, .#IND, .#INF)
		(happens sometimes after rotating & often scaling with tex lock (in BP mode)); error->warning
This commit is contained in:
Garux 2017-08-01 14:00:12 +03:00
parent f8b2b1f25e
commit a59855266c
13 changed files with 640 additions and 12 deletions

View File

@ -231,6 +231,12 @@ inline bool Tokeniser_getFloat( Tokeniser& tokeniser, float& f ){
if ( token != 0 && string_parse_float( token, f ) ) { if ( token != 0 && string_parse_float( token, f ) ) {
return true; return true;
} }
//fallback for 1.#IND 1.#INF 1.#QNAN cases, happening sometimes after rotating & often scaling with tex lock in BP mode
else if ( token != 0 && strstr( token, ".#" ) ) {
globalErrorStream() << "Warning: " << Unsigned( tokeniser.getLine() ) << ":" << Unsigned( tokeniser.getColumn() ) << ": expected parse problem at '" << token << "': wanted '#number'\nProcessing anyway\n";
*strstr( token, ".#" ) = '\0';
return true;
}
Tokeniser_unexpectedError( tokeniser, token, "#number" ); Tokeniser_unexpectedError( tokeniser, token, "#number" );
return false; return false;
} }

View File

@ -1450,7 +1450,7 @@ void Brush_constructMenu( GtkMenu* menu ){
menu_separator( menu ); menu_separator( menu );
create_menu_item_with_mnemonic( menu, "Make detail", "MakeDetail" ); create_menu_item_with_mnemonic( menu, "Make detail", "MakeDetail" );
create_menu_item_with_mnemonic( menu, "Make structural", "MakeStructural" ); create_menu_item_with_mnemonic( menu, "Make structural", "MakeStructural" );
create_menu_item_with_mnemonic( menu, "Snap selection to _grid", "SnapToGrid" ); // create_menu_item_with_mnemonic( menu, "Snap selection to _grid", "SnapToGrid" );
create_check_menu_item_with_mnemonic( menu, "Texture Lock", "TogTexLock" ); create_check_menu_item_with_mnemonic( menu, "Texture Lock", "TogTexLock" );
menu_separator( menu ); menu_separator( menu );

View File

@ -970,6 +970,24 @@ void CamWnd_registerCommands( CamWnd& camwnd ){
FreeMoveCameraMoveRightKeyDownCaller( camwnd.getCamera() ), FreeMoveCameraMoveRightKeyDownCaller( camwnd.getCamera() ),
FreeMoveCameraMoveRightKeyUpCaller( camwnd.getCamera() ) FreeMoveCameraMoveRightKeyUpCaller( camwnd.getCamera() )
); );
GlobalKeyEvents_insert( "CameraFreeMoveForward2", Accelerator( GDK_Up ),
FreeMoveCameraMoveForwardKeyDownCaller( camwnd.getCamera() ),
FreeMoveCameraMoveForwardKeyUpCaller( camwnd.getCamera() )
);
GlobalKeyEvents_insert( "CameraFreeMoveBack2", Accelerator( GDK_Down ),
FreeMoveCameraMoveBackKeyDownCaller( camwnd.getCamera() ),
FreeMoveCameraMoveBackKeyUpCaller( camwnd.getCamera() )
);
GlobalKeyEvents_insert( "CameraFreeMoveLeft2", Accelerator( GDK_Left ),
FreeMoveCameraMoveLeftKeyDownCaller( camwnd.getCamera() ),
FreeMoveCameraMoveLeftKeyUpCaller( camwnd.getCamera() )
);
GlobalKeyEvents_insert( "CameraFreeMoveRight2", Accelerator( GDK_Right ),
FreeMoveCameraMoveRightKeyDownCaller( camwnd.getCamera() ),
FreeMoveCameraMoveRightKeyUpCaller( camwnd.getCamera() )
);
GlobalKeyEvents_insert( "CameraFreeMoveUp", Accelerator( GDK_period ), GlobalKeyEvents_insert( "CameraFreeMoveUp", Accelerator( GDK_period ),
FreeMoveCameraMoveUpKeyDownCaller( camwnd.getCamera() ), FreeMoveCameraMoveUpKeyDownCaller( camwnd.getCamera() ),
FreeMoveCameraMoveUpKeyUpCaller( camwnd.getCamera() ) FreeMoveCameraMoveUpKeyUpCaller( camwnd.getCamera() )
@ -1119,6 +1137,12 @@ void CamWnd_Add_Handlers_FreeMove( CamWnd& camwnd ){
KeyEvent_connect( "CameraFreeMoveBack" ); KeyEvent_connect( "CameraFreeMoveBack" );
KeyEvent_connect( "CameraFreeMoveLeft" ); KeyEvent_connect( "CameraFreeMoveLeft" );
KeyEvent_connect( "CameraFreeMoveRight" ); KeyEvent_connect( "CameraFreeMoveRight" );
KeyEvent_connect( "CameraFreeMoveForward2" );
KeyEvent_connect( "CameraFreeMoveBack2" );
KeyEvent_connect( "CameraFreeMoveLeft2" );
KeyEvent_connect( "CameraFreeMoveRight2" );
KeyEvent_connect( "CameraFreeMoveUp" ); KeyEvent_connect( "CameraFreeMoveUp" );
KeyEvent_connect( "CameraFreeMoveDown" ); KeyEvent_connect( "CameraFreeMoveDown" );
} }
@ -1128,6 +1152,12 @@ void CamWnd_Remove_Handlers_FreeMove( CamWnd& camwnd ){
KeyEvent_disconnect( "CameraFreeMoveBack" ); KeyEvent_disconnect( "CameraFreeMoveBack" );
KeyEvent_disconnect( "CameraFreeMoveLeft" ); KeyEvent_disconnect( "CameraFreeMoveLeft" );
KeyEvent_disconnect( "CameraFreeMoveRight" ); KeyEvent_disconnect( "CameraFreeMoveRight" );
KeyEvent_disconnect( "CameraFreeMoveForward2" );
KeyEvent_disconnect( "CameraFreeMoveBack2" );
KeyEvent_disconnect( "CameraFreeMoveLeft2" );
KeyEvent_disconnect( "CameraFreeMoveRight2" );
KeyEvent_disconnect( "CameraFreeMoveUp" ); KeyEvent_disconnect( "CameraFreeMoveUp" );
KeyEvent_disconnect( "CameraFreeMoveDown" ); KeyEvent_disconnect( "CameraFreeMoveDown" );
@ -1910,6 +1940,11 @@ void CamWnd_Construct(){
GlobalShortcuts_insert( "CameraFreeMoveLeft", Accelerator( 'A' ) ); GlobalShortcuts_insert( "CameraFreeMoveLeft", Accelerator( 'A' ) );
GlobalShortcuts_insert( "CameraFreeMoveRight", Accelerator( 'D' ) ); GlobalShortcuts_insert( "CameraFreeMoveRight", Accelerator( 'D' ) );
GlobalShortcuts_insert( "CameraFreeMoveForward2", Accelerator( GDK_Up ) );
GlobalShortcuts_insert( "CameraFreeMoveBack2", Accelerator( GDK_Down ) );
GlobalShortcuts_insert( "CameraFreeMoveLeft2", Accelerator( GDK_Left ) );
GlobalShortcuts_insert( "CameraFreeMoveRight2", Accelerator( GDK_Right ) );
GlobalToggles_insert( "ShowStats", ShowStatsToggleCaller(), ToggleItem::AddCallbackCaller( g_show_stats ) ); GlobalToggles_insert( "ShowStats", ShowStatsToggleCaller(), ToggleItem::AddCallbackCaller( g_show_stats ) );
GlobalPreferenceSystem().registerPreference( "ShowStats", BoolImportStringCaller( g_camwindow_globals_private.m_showStats ), BoolExportStringCaller( g_camwindow_globals_private.m_showStats ) ); GlobalPreferenceSystem().registerPreference( "ShowStats", BoolImportStringCaller( g_camwindow_globals_private.m_showStats ), BoolExportStringCaller( g_camwindow_globals_private.m_showStats ) );

View File

@ -45,6 +45,9 @@
#include "qe3.h" #include "qe3.h"
#include "commands.h" #include "commands.h"
#include "brushmanip.h"
#include "patchmanip.h"
struct entity_globals_t struct entity_globals_t
{ {
Vector3 color_entity; Vector3 color_entity;
@ -386,6 +389,18 @@ void Entity_createFromSelection( const char* name, const Vector3& origin ){
Node_getEntity( node )->setKeyValue( "model", model ); Node_getEntity( node )->setKeyValue( "model", model );
} }
} }
if ( string_compare_nocase_n( name, "trigger_", 8 ) == 0 && brushesSelected ){
const char* shader = g_pGameDescription->getKeyValue( "trigger_shader" );
if ( shader && *shader ){
Scene_PatchSetShader_Selected( GlobalSceneGraph(), shader );
Scene_BrushSetShader_Selected( GlobalSceneGraph(), shader );
}
else{
Scene_PatchSetShader_Selected( GlobalSceneGraph(), "textures/common/trigger" );
Scene_BrushSetShader_Selected( GlobalSceneGraph(), "textures/common/trigger" );
}
}
} }
#if 0 #if 0

View File

@ -257,6 +257,18 @@ void environment_init( int argc, char* argv[] ){
#include <windows.h> #include <windows.h>
char openCmdMap[260];
void cmdMap(){
openCmdMap[0] = '\0';
for ( int i = 1; i < g_argc; ++i )
{
if ( !stricmp( g_argv[i] + strlen(g_argv[i]) - 4, ".map" ) ){
strcpy( openCmdMap, g_argv[i] );
}
}
}
void environment_init( int argc, char* argv[] ){ void environment_init( int argc, char* argv[] ){
args_init( argc, argv ); args_init( argc, argv );
@ -294,6 +306,7 @@ void environment_init( int argc, char* argv[] ){
home_path = home.c_str(); home_path = home.c_str();
} }
gamedetect(); gamedetect();
cmdMap();
} }
#else #else

View File

@ -29,4 +29,9 @@ const char* environment_get_app_path();
extern int g_argc; extern int g_argc;
extern char** g_argv; extern char** g_argv;
#if defined( WIN32 )
extern char openCmdMap[260];
#endif
#endif #endif

View File

@ -228,7 +228,7 @@ void Grid_constructMenu( GtkMenu* menu ){
} }
void Grid_registerShortcuts(){ void Grid_registerShortcuts(){
command_connect_accelerator( "ToggleGrid" ); // command_connect_accelerator( "ToggleGrid" );
command_connect_accelerator( "GridDown" ); command_connect_accelerator( "GridDown" );
command_connect_accelerator( "GridUp" ); command_connect_accelerator( "GridUp" );
command_connect_accelerator( "ToggleGridSnap" ); command_connect_accelerator( "ToggleGridSnap" );

View File

@ -554,9 +554,19 @@ int main( int argc, char* argv[] ){
if ( lib != 0 ) { if ( lib != 0 ) {
void ( WINAPI *qDwmEnableComposition )( bool bEnable ) = ( void (WINAPI *) ( bool bEnable ) )GetProcAddress( lib, "DwmEnableComposition" ); void ( WINAPI *qDwmEnableComposition )( bool bEnable ) = ( void (WINAPI *) ( bool bEnable ) )GetProcAddress( lib, "DwmEnableComposition" );
if ( qDwmEnableComposition ) { if ( qDwmEnableComposition ) {
bool Aero = false;
for ( int i = 1; i < argc; ++i ){
if ( !stricmp( argv[i], "-aero" ) ){
Aero = true;
qDwmEnableComposition( TRUE );
break;
}
}
// disable Aero // disable Aero
if ( !Aero ){
qDwmEnableComposition( FALSE ); qDwmEnableComposition( FALSE );
} }
}
FreeLibrary( lib ); FreeLibrary( lib );
} }
_setmaxstdio(2048); _setmaxstdio(2048);
@ -624,6 +634,12 @@ int main( int argc, char* argv[] ){
hide_splash(); hide_splash();
#ifdef WIN32
if( openCmdMap[0] != '\0' ){
Map_LoadFile( openCmdMap );
}
else
#endif // WIN32
if ( g_bLoadLastMap && !g_strLastMap.empty() ) { if ( g_bLoadLastMap && !g_strLastMap.empty() ) {
Map_LoadFile( g_strLastMap.c_str() ); Map_LoadFile( g_strLastMap.c_str() );
} }

View File

@ -2221,13 +2221,13 @@ void PatchInspector_registerShortcuts(){
} }
void Patch_registerShortcuts(){ void Patch_registerShortcuts(){
command_connect_accelerator( "InvertCurveTextureX" ); // command_connect_accelerator( "InvertCurveTextureX" );
command_connect_accelerator( "InvertCurveTextureY" ); // command_connect_accelerator( "InvertCurveTextureY" );
command_connect_accelerator( "IncPatchColumn" ); command_connect_accelerator( "IncPatchColumn" );
command_connect_accelerator( "IncPatchRow" ); command_connect_accelerator( "IncPatchRow" );
command_connect_accelerator( "DecPatchColumn" ); command_connect_accelerator( "DecPatchColumn" );
command_connect_accelerator( "DecPatchRow" ); command_connect_accelerator( "DecPatchRow" );
command_connect_accelerator( "NaturalizePatch" ); // command_connect_accelerator( "NaturalizePatch" );
//command_connect_accelerator("CapCurrentCurve"); //command_connect_accelerator("CapCurrentCurve");
} }
@ -2259,6 +2259,8 @@ void SelectNudge_registerShortcuts(){
//command_connect_accelerator("SelectNudgeRight"); //command_connect_accelerator("SelectNudgeRight");
//command_connect_accelerator("SelectNudgeUp"); //command_connect_accelerator("SelectNudgeUp");
//command_connect_accelerator("SelectNudgeDown"); //command_connect_accelerator("SelectNudgeDown");
command_connect_accelerator( "UnSelectSelection2" );
command_connect_accelerator( "DeleteSelection2" );
} }
void SnapToGrid_registerShortcuts(){ void SnapToGrid_registerShortcuts(){
@ -2275,17 +2277,17 @@ void SurfaceInspector_registerShortcuts(){
void register_shortcuts(){ void register_shortcuts(){
PatchInspector_registerShortcuts(); // PatchInspector_registerShortcuts();
Patch_registerShortcuts(); Patch_registerShortcuts();
Grid_registerShortcuts(); Grid_registerShortcuts();
XYWnd_registerShortcuts(); // XYWnd_registerShortcuts();
CamWnd_registerShortcuts(); CamWnd_registerShortcuts();
Manipulators_registerShortcuts(); Manipulators_registerShortcuts();
SurfaceInspector_registerShortcuts(); SurfaceInspector_registerShortcuts();
TexdefNudge_registerShortcuts(); TexdefNudge_registerShortcuts();
SelectNudge_registerShortcuts(); SelectNudge_registerShortcuts();
SnapToGrid_registerShortcuts(); // SnapToGrid_registerShortcuts();
SelectByType_registerShortcuts(); // SelectByType_registerShortcuts();
} }
void File_constructToolbar( GtkToolbar* toolbar ){ void File_constructToolbar( GtkToolbar* toolbar ){
@ -2804,7 +2806,7 @@ void MainFrame::Create(){
toolbar_append_toggle_button( plugin_toolbar, "Lights (ALT + 0)", "lightinspector.bmp", "FilterLights" ); toolbar_append_toggle_button( plugin_toolbar, "Lights (ALT + 0)", "lightinspector.bmp", "FilterLights" );
toolbar_append_toggle_button( plugin_toolbar, "Models (SHIFT + M)", "f-models.bmp", "FilterModels" ); toolbar_append_toggle_button( plugin_toolbar, "Models (SHIFT + M)", "f-models.bmp", "FilterModels" );
toolbar_append_toggle_button( plugin_toolbar, "Triggers (CTRL + SHIFT + T)", "f-triggers.bmp", "FilterTriggers" ); toolbar_append_toggle_button( plugin_toolbar, "Triggers (CTRL + SHIFT + T)", "f-triggers.bmp", "FilterTriggers" );
toolbar_append_toggle_button( plugin_toolbar, "Decals (SHIFT + D)", "f-decals.bmp", "FilterDecals" ); // toolbar_append_toggle_button( plugin_toolbar, "Decals (SHIFT + D)", "f-decals.bmp", "FilterDecals" );
gtk_toolbar_append_space( GTK_TOOLBAR( plugin_toolbar ) ); gtk_toolbar_append_space( GTK_TOOLBAR( plugin_toolbar ) );
toolbar_append_button( plugin_toolbar, "InvertFilters", "f-invert.bmp", "InvertFilters" ); toolbar_append_button( plugin_toolbar, "InvertFilters", "f-invert.bmp", "InvertFilters" );
toolbar_append_button( plugin_toolbar, "ResetFilters", "f-reset.bmp", "ResetFilters" ); toolbar_append_button( plugin_toolbar, "ResetFilters", "f-reset.bmp", "ResetFilters" );
@ -3245,9 +3247,11 @@ void MainFrame_Construct(){
GlobalCommands_insert( "CloneSelection", FreeCaller<Selection_Clone>(), Accelerator( GDK_space ) ); GlobalCommands_insert( "CloneSelection", FreeCaller<Selection_Clone>(), Accelerator( GDK_space ) );
GlobalCommands_insert( "CloneSelectionAndMakeUnique", FreeCaller<Selection_Clone_MakeUnique>(), Accelerator( GDK_space, (GdkModifierType)GDK_SHIFT_MASK ) ); GlobalCommands_insert( "CloneSelectionAndMakeUnique", FreeCaller<Selection_Clone_MakeUnique>(), Accelerator( GDK_space, (GdkModifierType)GDK_SHIFT_MASK ) );
// GlobalCommands_insert( "DeleteSelection", FreeCaller<deleteSelection>(), Accelerator( GDK_BackSpace ) ); // GlobalCommands_insert( "DeleteSelection", FreeCaller<deleteSelection>(), Accelerator( GDK_BackSpace ) );
GlobalCommands_insert( "DeleteSelection2", FreeCaller<deleteSelection>(), Accelerator( GDK_BackSpace ) );
GlobalCommands_insert( "DeleteSelection", FreeCaller<deleteSelection>(), Accelerator( 'Z' ) ); GlobalCommands_insert( "DeleteSelection", FreeCaller<deleteSelection>(), Accelerator( 'Z' ) );
GlobalCommands_insert( "ParentSelection", FreeCaller<Scene_parentSelected>() ); GlobalCommands_insert( "ParentSelection", FreeCaller<Scene_parentSelected>() );
// GlobalCommands_insert( "UnSelectSelection", FreeCaller<Selection_Deselect>(), Accelerator( GDK_Escape ) ); // GlobalCommands_insert( "UnSelectSelection", FreeCaller<Selection_Deselect>(), Accelerator( GDK_Escape ) );
GlobalCommands_insert( "UnSelectSelection2", FreeCaller<Selection_Deselect>(), Accelerator( GDK_Escape ) );
GlobalCommands_insert( "UnSelectSelection", FreeCaller<Selection_Deselect>(), Accelerator( 'C' ) ); GlobalCommands_insert( "UnSelectSelection", FreeCaller<Selection_Deselect>(), Accelerator( 'C' ) );
GlobalCommands_insert( "InvertSelection", FreeCaller<Select_Invert>(), Accelerator( 'I' ) ); GlobalCommands_insert( "InvertSelection", FreeCaller<Select_Invert>(), Accelerator( 'I' ) );
GlobalCommands_insert( "SelectInside", FreeCaller<Select_Inside>() ); GlobalCommands_insert( "SelectInside", FreeCaller<Select_Inside>() );

View File

@ -1521,7 +1521,7 @@ bool Map_ImportFile( const char* filename ){
tryDecompile: tryDecompile:
const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" ); const char *type = GlobalRadiant().getGameDescriptionKeyValue( "q3map2_type" );
int n = string_length( path_get_extension( filename ) ); int n = string_length( path_get_extension( filename ) );
if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) { if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
StringBuffer output; StringBuffer output;

View File

@ -2778,6 +2778,321 @@ void Patch::BuildVertexArray(){
} }
Vector3 getAverageNormal(const Vector3& normal1, const Vector3& normal2, double thickness)
{
// Beware of normals with 0 length
if ( ( fabs( normal1[0] ) + fabs( normal1[1] ) + fabs( normal1[2] ) ) == 0 ) return normal2;
if ( ( fabs( normal2[0] ) + fabs( normal2[1] ) + fabs( normal2[2] ) ) == 0) return normal1;
// Both normals have length > 0
Vector3 n1 = vector3_normalised( normal1 );
Vector3 n2 = vector3_normalised( normal2 );
// Get the angle bisector
Vector3 normal = vector3_normalised (n1 + n2);
// Now calculate the length correction out of the angle
// of the two normals
/* float factor = cos(n1.angle(n2) * 0.5); */
float factor = (float) vector3_dot( n1, n2 );
if ( factor > 1.0 ) factor = 1;
factor = acos( factor );
factor = cos( factor * 0.5 );
// Stretch the normal to fit the required thickness
normal *= thickness;
// Check for div by zero (if the normals are antiparallel)
// and stretch the resulting normal, if necessary
if (factor != 0)
{
normal /= factor;
}
return normal;
}
void Patch::createThickenedOpposite(const Patch& sourcePatch,
const float thickness,
const int axis,
bool& no12,
bool& no34)
{
// Clone the dimensions from the other patch
setDims(sourcePatch.getWidth(), sourcePatch.getHeight());
// Also inherit the tesselation from the source patch
//setFixedSubdivisions(sourcePatch.subdivionsFixed(), sourcePatch.getSubdivisions());
// Copy the shader from the source patch
SetShader(sourcePatch.GetShader());
// if extrudeAxis == 0,0,0 the patch is extruded along its vertex normals
Vector3 extrudeAxis(0,0,0);
switch (axis) {
case 0: // X-Axis
extrudeAxis = Vector3(1,0,0);
break;
case 1: // Y-Axis
extrudeAxis = Vector3(0,1,0);
break;
case 2: // Z-Axis
extrudeAxis = Vector3(0,0,1);
break;
default:
// Default value already set during initialisation
break;
}
//check if certain seams are required
//( endpoints != startpoints ) - not a cylinder or something
for (std::size_t col = 0; col < m_width; col++){
if( vector3_length_squared( sourcePatch.ctrlAt( 0, col ).m_vertex - sourcePatch.ctrlAt( m_height - 1, col ).m_vertex ) > 0.1f ){
//globalOutputStream() << "yes12.\n";
no12 = false;
break;
}
}
for (std::size_t row = 0; row < m_height; row++){
if( vector3_length_squared( sourcePatch.ctrlAt( row, 0 ).m_vertex - sourcePatch.ctrlAt( row, m_width - 1 ).m_vertex ) > 0.1f ){
no34 = false;
//globalOutputStream() << "yes34.\n";
break;
}
}
for (std::size_t col = 0; col < m_width; col++)
{
for (std::size_t row = 0; row < m_height; row++)
{
// The current control vertex on the other patch
const PatchControl& curCtrl = sourcePatch.ctrlAt(row, col);
Vector3 normal;
// Are we extruding along vertex normals (i.e. extrudeAxis == 0,0,0)?
if (extrudeAxis == Vector3(0,0,0))
{
// The col tangents (empty if 0,0,0)
Vector3 colTangent[2] = { Vector3(0,0,0), Vector3(0,0,0) };
// Are we at the beginning/end of the column?
if (col == 0 || col == m_width - 1)
{
// Get the next row index
std::size_t nextCol = (col == m_width - 1) ? (col - 1) : (col + 1);
const PatchControl& colNeighbour = sourcePatch.ctrlAt(row, nextCol);
// One available tangent
colTangent[0] = colNeighbour.m_vertex - curCtrl.m_vertex;
// Reverse it if we're at the end of the column
colTangent[0] *= (col == m_width - 1) ? -1 : +1;
}
// We are in between, two tangents can be calculated
else
{
// Take two neighbouring vertices that should form a line segment
const PatchControl& neighbour1 = sourcePatch.ctrlAt(row, col+1);
const PatchControl& neighbour2 = sourcePatch.ctrlAt(row, col-1);
// Calculate both available tangents
colTangent[0] = neighbour1.m_vertex - curCtrl.m_vertex;
colTangent[1] = neighbour2.m_vertex - curCtrl.m_vertex;
// Reverse the second one
colTangent[1] *= -1;
// Cull redundant tangents (parallel)
if ( ( fabs( colTangent[1][0] + colTangent[0][0] ) + fabs( colTangent[1][1] + colTangent[0][1] ) + fabs( colTangent[1][2] + colTangent[0][2] ) ) < 0.00001 ||
( fabs( colTangent[1][0] - colTangent[0][0] ) + fabs( colTangent[1][1] - colTangent[0][1] ) + fabs( colTangent[1][2] - colTangent[0][2] ) ) < 0.00001 )
{
colTangent[1] = Vector3(0,0,0);
}
}
// Calculate the tangent vectors to the next row
Vector3 rowTangent[2] = { Vector3(0,0,0), Vector3(0,0,0) };
// Are we at the beginning or the end?
if (row == 0 || row == m_height - 1)
{
// Yes, only calculate one row tangent
// Get the next row index
std::size_t nextRow = (row == m_height - 1) ? (row - 1) : (row + 1);
const PatchControl& rowNeighbour = sourcePatch.ctrlAt(nextRow, col);
// First tangent
rowTangent[0] = rowNeighbour.m_vertex - curCtrl.m_vertex;
// Reverse it accordingly
rowTangent[0] *= (row == m_height - 1) ? -1 : +1;
}
else
{
// Two tangents to calculate
const PatchControl& rowNeighbour1 = sourcePatch.ctrlAt(row + 1, col);
const PatchControl& rowNeighbour2 = sourcePatch.ctrlAt(row - 1, col);
// First tangent
rowTangent[0] = rowNeighbour1.m_vertex - curCtrl.m_vertex;
rowTangent[1] = rowNeighbour2.m_vertex - curCtrl.m_vertex;
// Reverse the second one
rowTangent[1] *= -1;
// Cull redundant tangents
if ( ( fabs( rowTangent[1][0] + rowTangent[0][0] ) + fabs( rowTangent[1][1] + rowTangent[0][1] ) + fabs( rowTangent[1][2] + rowTangent[0][2] ) ) < 0.00001 ||
( fabs( rowTangent[1][0] - rowTangent[0][0] ) + fabs( rowTangent[1][1] - rowTangent[0][1] ) + fabs( rowTangent[1][2] - rowTangent[0][2] ) ) < 0.00001 )
{
rowTangent[1] = Vector3(0,0,0);
}
}
// If two column tangents are available, take the length-corrected average
if ( ( fabs( colTangent[1][0] ) + fabs( colTangent[1][1] ) + fabs( colTangent[1][2] ) ) > 0)
{
// Two column normals to calculate
Vector3 normal1 = vector3_normalised( vector3_cross( rowTangent[0], colTangent[0] ) );
Vector3 normal2 = vector3_normalised( vector3_cross( rowTangent[0], colTangent[1] ) );
normal = getAverageNormal(normal1, normal2, thickness);
// Scale the normal down, as it is multiplied with thickness later on
normal /= thickness;
}
else
{
// One column tangent available, maybe we have a second rowtangent?
if ( ( fabs( rowTangent[1][0] ) + fabs( rowTangent[1][1] ) + fabs( rowTangent[1][2] ) ) > 0)
{
// Two row normals to calculate
Vector3 normal1 = vector3_normalised( vector3_cross( rowTangent[0], colTangent[0] ) );
Vector3 normal2 = vector3_normalised( vector3_cross( rowTangent[1], colTangent[0] ) );
normal = getAverageNormal(normal1, normal2, thickness);
// Scale the normal down, as it is multiplied with thickness later on
normal /= thickness;
}
else
{
if ( vector3_length_squared( vector3_cross( rowTangent[0], colTangent[0] ) ) > 0 ){
normal = vector3_normalised( vector3_cross( rowTangent[0], colTangent[0] ) );
}
else{
normal = extrudeAxis;
}
}
}
}
else
{
// Take the predefined extrude direction instead
normal = extrudeAxis;
}
// Store the new coordinates into this patch at the current coords
ctrlAt(row, col).m_vertex = curCtrl.m_vertex + normal*thickness;
// Clone the texture cooordinates of the source patch
ctrlAt(row, col).m_texcoord = curCtrl.m_texcoord;
}
}
// Notify the patch about the change
controlPointsChanged();
}
void Patch::createThickenedWall(const Patch& sourcePatch,
const Patch& targetPatch,
const int wallIndex)
{
// Copy the shader from the source patch
SetShader(sourcePatch.GetShader());
// The start and end control vertex indices
int start = 0;
int end = 0;
// The increment (incr = 1 for the "long" edge, incr = width for the "short" edge)
int incr = 1;
// These are the target dimensions of this wall
// The width is depending on which edge is "seamed".
int cols = 0;
int rows = 3;
int sourceWidth = static_cast<int>(sourcePatch.getWidth());
int sourceHeight = static_cast<int>(sourcePatch.getHeight());
/*
bool sourceTesselationFixed = sourcePatch.subdivionsFixed();
Subdivisions sourceTesselationX(sourcePatch.getSubdivisions().x(), 1);
Subdivisions sourceTesselationY(sourcePatch.getSubdivisions().y(), 1);
*/
// Determine which of the four edges have to be connected
// and calculate the start, end & stepsize for the following loop
switch (wallIndex) {
case 0:
cols = sourceWidth;
start = 0;
end = sourceWidth - 1;
incr = 1;
//setFixedSubdivisions(sourceTesselationFixed, sourceTesselationX);
break;
case 1:
cols = sourceWidth;
start = sourceWidth * (sourceHeight-1);
end = sourceWidth*sourceHeight - 1;
incr = 1;
//setFixedSubdivisions(sourceTesselationFixed, sourceTesselationX);
break;
case 2:
cols = sourceHeight;
start = 0;
end = sourceWidth*(sourceHeight-1);
incr = sourceWidth;
//setFixedSubdivisions(sourceTesselationFixed, sourceTesselationY);
break;
case 3:
cols = sourceHeight;
start = sourceWidth - 1;
end = sourceWidth*sourceHeight - 1;
incr = sourceWidth;
//setFixedSubdivisions(sourceTesselationFixed, sourceTesselationY);
break;
}
setDims(cols, rows);
const PatchControlArray& sourceCtrl = sourcePatch.getControlPoints();
const PatchControlArray& targetCtrl = targetPatch.getControlPoints();
int col = 0;
// Now go through the control vertices with these calculated stepsize
for (int idx = start; idx <= end; idx += incr, col++) {
Vector3 sourceCoord = sourceCtrl[idx].m_vertex;
Vector3 targetCoord = targetCtrl[idx].m_vertex;
Vector3 middleCoord = (sourceCoord + targetCoord) / 2;
// Now assign the vertex coordinates
ctrlAt(0, col).m_vertex = sourceCoord;
ctrlAt(1, col).m_vertex = middleCoord;
ctrlAt(2, col).m_vertex = targetCoord;
}
if (wallIndex == 0 || wallIndex == 3) {
InvertMatrix();
}
// Notify the patch about the change
controlPointsChanged();
// Texture the patch "naturally"
NaturalTexture();
}
class PatchFilterWrapper : public Filter class PatchFilterWrapper : public Filter
{ {

View File

@ -880,6 +880,12 @@ const_iterator end() const {
PatchControlArray& getControlPoints(){ PatchControlArray& getControlPoints(){
return m_ctrl; return m_ctrl;
} }
// Same as above, just for const arguments
const PatchControlArray& getControlPoints() const {
return m_ctrl;
}
PatchControlArray& getControlPointsTransformed(){ PatchControlArray& getControlPointsTransformed(){
return m_ctrlTransformed; return m_ctrlTransformed;
} }
@ -916,6 +922,8 @@ void SetTextureRepeat( float s, float t ); // call with s=1 t=1 for FIT
void CapTexture(); void CapTexture();
void NaturalTexture(); void NaturalTexture();
void ProjectTexture( int nAxis ); void ProjectTexture( int nAxis );
void createThickenedOpposite(const Patch& sourcePatch, const float thickness, const int axis, bool& no12, bool& no34 );
void createThickenedWall(const Patch& sourcePatch, const Patch& targetPatch, const int wallIndex);
void undoSave(){ void undoSave(){
if ( m_map != 0 ) { if ( m_map != 0 ) {

View File

@ -196,6 +196,83 @@ void Scene_PatchDeform( scene::Graph& graph, const int deform )
} }
void Patch_thicken( Patch& patch, scene::Instance& instance, const float thickness, bool seams, const int axis ){
// Create a new patch node
NodeSmartReference node(g_patchCreator->createPatch());
// Insert the node into worldspawn
Node_getTraversable(Map_FindOrInsertWorldspawn(g_map))->insert(node);
// Retrieve the contained patch from the node
Patch* targetPatch = Node_getPatch(node);
// Create the opposite patch with the given thickness = distance
bool no12 = true;
bool no34 = true;
targetPatch->createThickenedOpposite(patch, thickness, axis, no12, no34);
// Now select the newly created patches
{
scene::Path patchpath(makeReference(GlobalSceneGraph().root()));
patchpath.push(makeReference(*Map_GetWorldspawn(g_map)));
patchpath.push(makeReference(node.get()));
Instance_getSelectable(*GlobalSceneGraph().find(patchpath))->setSelected(true);
}
if (seams && thickness != 0.0f) {
int i = 0;
if ( no12 ){
i = 2;
}
int iend = 4;
if ( no34 ){
iend = 2;
}
// Now create the four walls
for ( ; i < iend; i++ ) {
// Allocate new patch
NodeSmartReference node = NodeSmartReference(g_patchCreator->createPatch());
// Insert each node into worldspawn
Node_getTraversable(Map_FindOrInsertWorldspawn(g_map))->insert(node);
// Retrieve the contained patch from the node
Patch* wallPatch = Node_getPatch(node);
// Create the wall patch by passing i as wallIndex
wallPatch->createThickenedWall( patch, *targetPatch, i);
if( ( wallPatch->localAABB().extents[0] <= 0.00005 && wallPatch->localAABB().extents[1] <= 0.00005 ) ||
( wallPatch->localAABB().extents[1] <= 0.00005 && wallPatch->localAABB().extents[2] <= 0.00005 ) ||
( wallPatch->localAABB().extents[0] <= 0.00005 && wallPatch->localAABB().extents[2] <= 0.00005 ) ){
//globalOutputStream() << "Thicken: Discarding degenerate patch.\n";
Node_getTraversable( Map_FindOrInsertWorldspawn(g_map) )->erase( node );
}
else
// Now select the newly created patches
{
scene::Path patchpath(makeReference(GlobalSceneGraph().root()));
patchpath.push(makeReference(*Map_GetWorldspawn(g_map)));
patchpath.push(makeReference(node.get()));
Instance_getSelectable(*GlobalSceneGraph().find(patchpath))->setSelected(true);
}
}
}
// Invert the target patch so that it faces the opposite direction
targetPatch->InvertMatrix();
}
void Scene_PatchThicken( scene::Graph& graph, const int thickness, bool seams, const int axis )
{
InstanceVector instances;
Scene_forEachVisibleSelectedPatchInstance( PatchStoreInstance( instances ) );
for ( InstanceVector::const_iterator i = instances.begin(); i != instances.end(); ++i )
{
Patch_thicken( *Node_getPatch( ( *i )->path().top() ), *( *i ), thickness, seams, axis );
}
}
Patch* Scene_GetUltimateSelectedVisiblePatch(){ Patch* Scene_GetUltimateSelectedVisiblePatch(){
if ( GlobalSelectionSystem().countSelected() != 0 ) { if ( GlobalSelectionSystem().countSelected() != 0 ) {
scene::Node& node = GlobalSelectionSystem().ultimateSelected().path().top(); scene::Node& node = GlobalSelectionSystem().ultimateSelected().path().top();
@ -618,6 +695,13 @@ void Patch_Deform(){
DoPatchDeformDlg(); DoPatchDeformDlg();
} }
void DoPatchThickenDlg();
void Patch_Thicken(){
UndoableCommand undo( "patchThicken" );
DoPatchThickenDlg();
}
@ -732,6 +816,7 @@ void Patch_registerCommands(){
GlobalCommands_insert( "MakeOverlayPatch", FreeCaller<Patch_OverlayOn>(), Accelerator( 'Y' ) ); GlobalCommands_insert( "MakeOverlayPatch", FreeCaller<Patch_OverlayOn>(), Accelerator( 'Y' ) );
GlobalCommands_insert( "ClearPatchOverlays", FreeCaller<Patch_OverlayOff>(), Accelerator( 'L', (GdkModifierType)GDK_CONTROL_MASK ) ); GlobalCommands_insert( "ClearPatchOverlays", FreeCaller<Patch_OverlayOff>(), Accelerator( 'L', (GdkModifierType)GDK_CONTROL_MASK ) );
GlobalCommands_insert( "PatchDeform", FreeCaller<Patch_Deform>() ); GlobalCommands_insert( "PatchDeform", FreeCaller<Patch_Deform>() );
GlobalCommands_insert( "PatchThicken", FreeCaller<Patch_Thicken>() );
} }
void Patch_constructToolbar( GtkToolbar* toolbar ){ void Patch_constructToolbar( GtkToolbar* toolbar ){
@ -840,6 +925,7 @@ void Patch_constructMenu( GtkMenu* menu ){
} }
menu_separator( menu ); menu_separator( menu );
create_menu_item_with_mnemonic( menu, "Deform...", "PatchDeform" ); create_menu_item_with_mnemonic( menu, "Deform...", "PatchDeform" );
create_menu_item_with_mnemonic( menu, "Thicken...", "PatchThicken" );
} }
@ -1198,3 +1284,128 @@ EMessageBoxReturn DoCapDlg( ECapDialog* type ){
return ret; return ret;
} }
void DoPatchThickenDlg(){
ModalDialog dialog;
GtkWidget* thicknessW;
GtkWidget* seamsW;
GtkWidget* radX;
GtkWidget* radY;
GtkWidget* radZ;
GtkWidget* radNormals;
GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Patch thicken", G_CALLBACK( dialog_delete_callback ), &dialog );
GtkAccelGroup* accel = gtk_accel_group_new();
gtk_window_add_accel_group( window, accel );
{
GtkHBox* hbox = create_dialog_hbox( 4, 4 );
gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( hbox ) );
{
GtkTable* table = create_dialog_table( 2, 4, 4, 4 );
gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
{
GtkLabel* label = GTK_LABEL( gtk_label_new( "Thickness:" ) );
gtk_widget_show( GTK_WIDGET( label ) );
gtk_table_attach( table, GTK_WIDGET( label ), 0, 1, 0, 1,
(GtkAttachOptions) ( GTK_FILL ),
(GtkAttachOptions) ( 0 ), 0, 0 );
gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
}
{
GtkWidget* entry = gtk_entry_new();
gtk_entry_set_text( GTK_ENTRY( entry ), "16" );
gtk_widget_set_size_request( entry, 40, -1 );
gtk_widget_show( entry );
gtk_table_attach( table, entry, 1, 2, 0, 1,
(GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
(GtkAttachOptions) ( 0 ), 0, 0 );
thicknessW = entry;
}
{
// Create the "create seams" label
GtkWidget* _seamsCheckBox = gtk_check_button_new_with_label( "Side walls" );
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( _seamsCheckBox ), TRUE );
gtk_widget_show( _seamsCheckBox );
gtk_table_attach( table, _seamsCheckBox, 3, 4, 0, 1,
(GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
(GtkAttachOptions) ( 0 ), 0, 0 );
seamsW = _seamsCheckBox;
}
{
// Create the radio button group for choosing the extrude axis
GtkWidget* _radNormals = gtk_radio_button_new_with_label( NULL, "Normal" );
GtkWidget* _radX = gtk_radio_button_new_with_label_from_widget( GTK_RADIO_BUTTON(_radNormals), "X" );
GtkWidget* _radY = gtk_radio_button_new_with_label_from_widget( GTK_RADIO_BUTTON(_radNormals), "Y" );
GtkWidget* _radZ = gtk_radio_button_new_with_label_from_widget( GTK_RADIO_BUTTON(_radNormals), "Z" );
gtk_widget_show( _radNormals );
gtk_widget_show( _radX );
gtk_widget_show( _radY );
gtk_widget_show( _radZ );
// Pack the buttons into the table
gtk_table_attach( table, _radNormals, 0, 1, 1, 2,
(GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
(GtkAttachOptions) ( 0 ), 0, 0 );
gtk_table_attach( table, _radX, 1, 2, 1, 2,
(GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
(GtkAttachOptions) ( 0 ), 0, 0 );
gtk_table_attach( table, _radY, 2, 3, 1, 2,
(GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
(GtkAttachOptions) ( 0 ), 0, 0 );
gtk_table_attach( table, _radZ, 3, 4, 1, 2,
(GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
(GtkAttachOptions) ( 0 ), 0, 0 );
radX = _radX;
radY = _radY;
radZ = _radZ;
radNormals = _radNormals;
}
}
{
GtkVBox* vbox = create_dialog_vbox( 4 );
gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
{
GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &dialog );
gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
widget_make_default( GTK_WIDGET( button ) );
gtk_widget_grab_focus( GTK_WIDGET( button ) );
gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
}
{
GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &dialog );
gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
}
}
}
if ( modal_dialog_show( window, dialog ) == eIDOK ) {
int axis;
bool seams;
float thickness = static_cast<float>( atoi( gtk_entry_get_text( GTK_ENTRY( thicknessW ) ) ) );
seams = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( seamsW )) ? true : false;
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radX))) {
axis = 0;
}
else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radY))) {
axis = 1;
}
else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radZ))) {
axis = 2;
}
else {
// Extrude along normals
axis = 3;
}
Scene_PatchThicken( GlobalSceneGraph(), thickness, seams, axis );
}
gtk_widget_destroy( GTK_WIDGET( window ) );
}