Radiant:
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:
parent
f8b2b1f25e
commit
a59855266c
|
|
@ -231,6 +231,12 @@ inline bool Tokeniser_getFloat( Tokeniser& tokeniser, float& f ){
|
|||
if ( token != 0 && string_parse_float( token, f ) ) {
|
||||
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" );
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1450,7 +1450,7 @@ void Brush_constructMenu( GtkMenu* menu ){
|
|||
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_menu_item_with_mnemonic( menu, "Snap selection to _grid", "SnapToGrid" );
|
||||
|
||||
create_check_menu_item_with_mnemonic( menu, "Texture Lock", "TogTexLock" );
|
||||
menu_separator( menu );
|
||||
|
|
|
|||
|
|
@ -970,6 +970,24 @@ void CamWnd_registerCommands( CamWnd& camwnd ){
|
|||
FreeMoveCameraMoveRightKeyDownCaller( 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 ),
|
||||
FreeMoveCameraMoveUpKeyDownCaller( camwnd.getCamera() ),
|
||||
FreeMoveCameraMoveUpKeyUpCaller( camwnd.getCamera() )
|
||||
|
|
@ -1119,6 +1137,12 @@ void CamWnd_Add_Handlers_FreeMove( CamWnd& camwnd ){
|
|||
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" );
|
||||
}
|
||||
|
|
@ -1128,6 +1152,12 @@ void CamWnd_Remove_Handlers_FreeMove( CamWnd& camwnd ){
|
|||
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" );
|
||||
|
||||
|
|
@ -1910,6 +1940,11 @@ void CamWnd_Construct(){
|
|||
GlobalShortcuts_insert( "CameraFreeMoveLeft", Accelerator( 'A' ) );
|
||||
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 ) );
|
||||
|
||||
GlobalPreferenceSystem().registerPreference( "ShowStats", BoolImportStringCaller( g_camwindow_globals_private.m_showStats ), BoolExportStringCaller( g_camwindow_globals_private.m_showStats ) );
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@
|
|||
#include "qe3.h"
|
||||
#include "commands.h"
|
||||
|
||||
#include "brushmanip.h"
|
||||
#include "patchmanip.h"
|
||||
|
||||
struct entity_globals_t
|
||||
{
|
||||
Vector3 color_entity;
|
||||
|
|
@ -386,6 +389,18 @@ void Entity_createFromSelection( const char* name, const Vector3& origin ){
|
|||
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
|
||||
|
|
|
|||
|
|
@ -257,6 +257,18 @@ void environment_init( int argc, char* argv[] ){
|
|||
|
||||
#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[] ){
|
||||
args_init( argc, argv );
|
||||
|
||||
|
|
@ -294,6 +306,7 @@ void environment_init( int argc, char* argv[] ){
|
|||
home_path = home.c_str();
|
||||
}
|
||||
gamedetect();
|
||||
cmdMap();
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -29,4 +29,9 @@ const char* environment_get_app_path();
|
|||
extern int g_argc;
|
||||
extern char** g_argv;
|
||||
|
||||
#if defined( WIN32 )
|
||||
extern char openCmdMap[260];
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ void Grid_constructMenu( GtkMenu* menu ){
|
|||
}
|
||||
|
||||
void Grid_registerShortcuts(){
|
||||
command_connect_accelerator( "ToggleGrid" );
|
||||
// command_connect_accelerator( "ToggleGrid" );
|
||||
command_connect_accelerator( "GridDown" );
|
||||
command_connect_accelerator( "GridUp" );
|
||||
command_connect_accelerator( "ToggleGridSnap" );
|
||||
|
|
|
|||
|
|
@ -554,8 +554,18 @@ int main( int argc, char* argv[] ){
|
|||
if ( lib != 0 ) {
|
||||
void ( WINAPI *qDwmEnableComposition )( bool bEnable ) = ( void (WINAPI *) ( bool bEnable ) )GetProcAddress( lib, "DwmEnableComposition" );
|
||||
if ( qDwmEnableComposition ) {
|
||||
bool Aero = false;
|
||||
for ( int i = 1; i < argc; ++i ){
|
||||
if ( !stricmp( argv[i], "-aero" ) ){
|
||||
Aero = true;
|
||||
qDwmEnableComposition( TRUE );
|
||||
break;
|
||||
}
|
||||
}
|
||||
// disable Aero
|
||||
qDwmEnableComposition( FALSE );
|
||||
if ( !Aero ){
|
||||
qDwmEnableComposition( FALSE );
|
||||
}
|
||||
}
|
||||
FreeLibrary( lib );
|
||||
}
|
||||
|
|
@ -624,6 +634,12 @@ int main( int argc, char* argv[] ){
|
|||
|
||||
hide_splash();
|
||||
|
||||
#ifdef WIN32
|
||||
if( openCmdMap[0] != '\0' ){
|
||||
Map_LoadFile( openCmdMap );
|
||||
}
|
||||
else
|
||||
#endif // WIN32
|
||||
if ( g_bLoadLastMap && !g_strLastMap.empty() ) {
|
||||
Map_LoadFile( g_strLastMap.c_str() );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2221,13 +2221,13 @@ void PatchInspector_registerShortcuts(){
|
|||
}
|
||||
|
||||
void Patch_registerShortcuts(){
|
||||
command_connect_accelerator( "InvertCurveTextureX" );
|
||||
command_connect_accelerator( "InvertCurveTextureY" );
|
||||
// command_connect_accelerator( "InvertCurveTextureX" );
|
||||
// command_connect_accelerator( "InvertCurveTextureY" );
|
||||
command_connect_accelerator( "IncPatchColumn" );
|
||||
command_connect_accelerator( "IncPatchRow" );
|
||||
command_connect_accelerator( "DecPatchColumn" );
|
||||
command_connect_accelerator( "DecPatchRow" );
|
||||
command_connect_accelerator( "NaturalizePatch" );
|
||||
// command_connect_accelerator( "NaturalizePatch" );
|
||||
//command_connect_accelerator("CapCurrentCurve");
|
||||
}
|
||||
|
||||
|
|
@ -2259,6 +2259,8 @@ void SelectNudge_registerShortcuts(){
|
|||
//command_connect_accelerator("SelectNudgeRight");
|
||||
//command_connect_accelerator("SelectNudgeUp");
|
||||
//command_connect_accelerator("SelectNudgeDown");
|
||||
command_connect_accelerator( "UnSelectSelection2" );
|
||||
command_connect_accelerator( "DeleteSelection2" );
|
||||
}
|
||||
|
||||
void SnapToGrid_registerShortcuts(){
|
||||
|
|
@ -2275,17 +2277,17 @@ void SurfaceInspector_registerShortcuts(){
|
|||
|
||||
|
||||
void register_shortcuts(){
|
||||
PatchInspector_registerShortcuts();
|
||||
// PatchInspector_registerShortcuts();
|
||||
Patch_registerShortcuts();
|
||||
Grid_registerShortcuts();
|
||||
XYWnd_registerShortcuts();
|
||||
// XYWnd_registerShortcuts();
|
||||
CamWnd_registerShortcuts();
|
||||
Manipulators_registerShortcuts();
|
||||
SurfaceInspector_registerShortcuts();
|
||||
TexdefNudge_registerShortcuts();
|
||||
SelectNudge_registerShortcuts();
|
||||
SnapToGrid_registerShortcuts();
|
||||
SelectByType_registerShortcuts();
|
||||
// SnapToGrid_registerShortcuts();
|
||||
// SelectByType_registerShortcuts();
|
||||
}
|
||||
|
||||
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, "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, "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 ) );
|
||||
toolbar_append_button( plugin_toolbar, "InvertFilters", "f-invert.bmp", "InvertFilters" );
|
||||
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( "CloneSelectionAndMakeUnique", FreeCaller<Selection_Clone_MakeUnique>(), Accelerator( GDK_space, (GdkModifierType)GDK_SHIFT_MASK ) );
|
||||
// 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( "ParentSelection", FreeCaller<Scene_parentSelected>() );
|
||||
// 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( "InvertSelection", FreeCaller<Select_Invert>(), Accelerator( 'I' ) );
|
||||
GlobalCommands_insert( "SelectInside", FreeCaller<Select_Inside>() );
|
||||
|
|
|
|||
|
|
@ -1521,7 +1521,7 @@ bool Map_ImportFile( const char* filename ){
|
|||
|
||||
tryDecompile:
|
||||
|
||||
const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" );
|
||||
const char *type = GlobalRadiant().getGameDescriptionKeyValue( "q3map2_type" );
|
||||
int n = string_length( path_get_extension( filename ) );
|
||||
if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
|
||||
StringBuffer output;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -880,6 +880,12 @@ const_iterator end() const {
|
|||
PatchControlArray& getControlPoints(){
|
||||
return m_ctrl;
|
||||
}
|
||||
|
||||
// Same as above, just for const arguments
|
||||
const PatchControlArray& getControlPoints() const {
|
||||
return m_ctrl;
|
||||
}
|
||||
|
||||
PatchControlArray& getControlPointsTransformed(){
|
||||
return m_ctrlTransformed;
|
||||
}
|
||||
|
|
@ -916,6 +922,8 @@ void SetTextureRepeat( float s, float t ); // call with s=1 t=1 for FIT
|
|||
void CapTexture();
|
||||
void NaturalTexture();
|
||||
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(){
|
||||
if ( m_map != 0 ) {
|
||||
|
|
|
|||
|
|
@ -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(){
|
||||
if ( GlobalSelectionSystem().countSelected() != 0 ) {
|
||||
scene::Node& node = GlobalSelectionSystem().ultimateSelected().path().top();
|
||||
|
|
@ -618,6 +695,13 @@ void Patch_Deform(){
|
|||
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( "ClearPatchOverlays", FreeCaller<Patch_OverlayOff>(), Accelerator( 'L', (GdkModifierType)GDK_CONTROL_MASK ) );
|
||||
GlobalCommands_insert( "PatchDeform", FreeCaller<Patch_Deform>() );
|
||||
GlobalCommands_insert( "PatchThicken", FreeCaller<Patch_Thicken>() );
|
||||
}
|
||||
|
||||
void Patch_constructToolbar( GtkToolbar* toolbar ){
|
||||
|
|
@ -840,6 +925,7 @@ void Patch_constructMenu( GtkMenu* menu ){
|
|||
}
|
||||
menu_separator( menu );
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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 ) );
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user