#include "brush_builder.h" #include "debugging/debugging.h" #include "ibrush.h" #include "ientity.h" #include "ieclass.h" #include "iscenegraph.h" #include "qerplugin.h" #include "scenelib.h" const unsigned int BRUSH_DETAIL_FLAG = 27; const unsigned int BRUSH_DETAIL_MASK = ( 1 << BRUSH_DETAIL_FLAG ); // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- static void fill_face( _QERFaceData& face, double x0, double y0, double z0, double x1, double y1, double z1, double x2, double y2, double z2, const char* shader ){ face.m_p0[0] = x0; face.m_p0[1] = y0; face.m_p0[2] = z0; face.m_p1[0] = x1; face.m_p1[1] = y1; face.m_p1[2] = z1; face.m_p2[0] = x2; face.m_p2[1] = y2; face.m_p2[2] = z2; face.m_shader = shader; face.m_texdef.scale[0] = 0.03125f; // matches ( ( 0.03125 0 0 ) ( 0 0.03125 0 ) ) in .map face.m_texdef.scale[1] = 0.03125f; face.m_texdef.shift[0] = 0; face.m_texdef.shift[1] = 0; face.m_texdef.rotate = 0; face.contents = BRUSH_DETAIL_MASK; face.flags = 0; face.value = 0; } static scene::Node& create_func_group(){ EntityClass* ec = GlobalEntityClassManager().findOrInsert( "func_group", true ); NodeSmartReference entity( GlobalEntityCreator().createEntity( ec ) ); Node_getTraversable( GlobalSceneGraph().root() )->insert( entity ); return entity; } // Select a generated func_group so the user can immediately move it. For a group // node Entity_setSelected selects its child brushes (what the manipulator acts // on), which is how Radiant makes a group movable — selecting the entity instance // directly leaves it unmovable. static void select_generated( scene::Node& node ){ scene::Path path( makeReference( GlobalSceneGraph().root() ) ); path.push( makeReference( node ) ); if ( scene::Instance* instance = GlobalSceneGraph().find( path ) ) Entity_setSelected( *instance, true ); } static void insert_brush_into( scene::Node& entity, double x, double y, double min_z, double mx, double my, double base_max_z, double z_bl, double z_tl, double z_br, double z_tr, const char* top_tex, const char* caulk, bool split_diagonally, bool alt_dir ){ if ( !split_diagonally ) { NodeSmartReference brush( GlobalBrushCreator().createBrush() ); _QERFaceData face; fill_face( face, x, y, base_max_z, x, my, base_max_z, mx, y, base_max_z, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, y, min_z, mx, y, min_z, x, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, mx, y, min_z, mx, y, base_max_z, mx, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, y, min_z, x, my, min_z, x, y, base_max_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, my, min_z, mx, my, min_z, x, my, base_max_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, y, min_z, x, y, base_max_z, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); Node_getTraversable( entity )->insert( brush ); } else if ( !alt_dir ) { // Standard diagonal: BL→TR split // Triangle 1: BL, TL, BR { NodeSmartReference brush( GlobalBrushCreator().createBrush() ); _QERFaceData face; fill_face( face, x, y, z_bl, x, my, z_tl, mx, y, z_br, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, y, min_z, mx, y, min_z, x, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, y, min_z, x, my, min_z, x, y, base_max_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, y, min_z, x, y, base_max_z, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, mx, y, min_z, mx, y, base_max_z, x, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); Node_getTraversable( entity )->insert( brush ); } // Triangle 2: TR, BR, TL { NodeSmartReference brush( GlobalBrushCreator().createBrush() ); _QERFaceData face; fill_face( face, mx, my, z_tr, mx, y, z_br, x, my, z_tl, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, mx, my, min_z, x, my, min_z, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, mx, y, min_z, mx, y, base_max_z, mx, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, my, min_z, mx, my, min_z, x, my, base_max_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, my, min_z, x, my, base_max_z, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); Node_getTraversable( entity )->insert( brush ); } } else { // Alternate diagonal: TL→BR split (checkerboard pattern) // Triangle 1: TL, TR, BL { NodeSmartReference brush( GlobalBrushCreator().createBrush() ); _QERFaceData face; fill_face( face, x, my, z_tl, mx, my, z_tr, x, y, z_bl, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, my, min_z, x, y, min_z, mx, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, y, min_z, x, my, min_z, x, y, base_max_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, my, min_z, mx, my, min_z, x, my, base_max_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, mx, my, min_z, x, y, min_z, x, y, base_max_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); Node_getTraversable( entity )->insert( brush ); } // Triangle 2: TR, BR, BL { NodeSmartReference brush( GlobalBrushCreator().createBrush() ); _QERFaceData face; fill_face( face, mx, my, z_tr, mx, y, z_br, x, y, z_bl, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, mx, my, min_z, x, y, min_z, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, mx, y, min_z, mx, y, base_max_z, mx, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, y, min_z, x, y, base_max_z, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, y, min_z, mx, my, min_z, x, y, base_max_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); Node_getTraversable( entity )->insert( brush ); } } } // --------------------------------------------------------------------------- // Standard terrain // --------------------------------------------------------------------------- void build_terrain_brushes( const BrushData& target, double step_x, double step_y, const HeightMap& height_map, const char* top_texture, bool split_diagonally ){ // Undo is started by the caller so deletion + generation form one step. scene::Node& entity = create_func_group(); const char* caulk = "textures/common/caulk"; double min_z = target.min_z; double base_max_z = target.max_z; auto r2 = []( double v ) { return std::round( v * 100.0 ) / 100.0; }; int x_index = 0; for ( double x = target.min_x; x < target.max_x - 0.01; x += step_x, ++x_index ) { int y_index = 0; for ( double y = target.min_y; y < target.max_y - 0.01; y += step_y, ++y_index ) { double mx = x + step_x < target.max_x ? x + step_x : target.max_x; double my = y + step_y < target.max_y ? y + step_y : target.max_y; auto lookup = [&]( double kx, double ky ) -> double { auto it = height_map.find({ r2( kx ), r2( ky ) }); return it != height_map.end() ? it->second : min_z; }; double z_bl = lookup( x, y ); double z_tl = lookup( x, my ); double z_br = lookup( mx, y ); double z_tr = lookup( mx, my ); bool alt_dir = ( ( x_index + y_index ) % 2 ) != 0; insert_brush_into( entity, x, y, min_z, mx, my, base_max_z, z_bl, z_tl, z_br, z_tr, top_texture, caulk, split_diagonally, alt_dir ); } } select_generated( entity ); SceneChangeNotify(); } // --------------------------------------------------------------------------- // Tunnel terrain // --------------------------------------------------------------------------- static void insert_floor_ceil_brush( scene::Node& entity, double x, double y, double mx, double my, double z_bl, double z_tl, double z_br, double z_tr, double min_z, double solid_top, const char* top_tex, const char* caulk, bool is_ceiling, bool alt_dir ){ _QERFaceData face; if ( !alt_dir ) { // BL→TR split // Triangle 1: BL, TL, BR { NodeSmartReference brush( GlobalBrushCreator().createBrush() ); if ( !is_ceiling ) { fill_face( face, x, y, z_bl, x, my, z_tl, mx, y, z_br, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, y, min_z, mx, y, min_z, x, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } else { fill_face( face, x, y, z_bl, mx, y, z_br, x, my, z_tl, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, y, solid_top, x, my, solid_top, mx, y, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } fill_face( face, x, y, min_z, x, my, min_z, x, y, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, y, min_z, x, y, solid_top, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, mx, y, min_z, mx, y, solid_top, x, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); Node_getTraversable( entity )->insert( brush ); } // Triangle 2: TR, BR, TL { NodeSmartReference brush( GlobalBrushCreator().createBrush() ); if ( !is_ceiling ) { fill_face( face, mx, my, z_tr, mx, y, z_br, x, my, z_tl, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, mx, my, min_z, x, my, min_z, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } else { fill_face( face, mx, my, z_tr, x, my, z_tl, mx, y, z_br, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, mx, my, solid_top, mx, y, solid_top, x, my, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } fill_face( face, mx, y, min_z, mx, y, solid_top, mx, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, my, min_z, mx, my, min_z, x, my, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, my, min_z, x, my, solid_top, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); Node_getTraversable( entity )->insert( brush ); } } else { // TL→BR split // Triangle 1: TL, TR, BL { NodeSmartReference brush( GlobalBrushCreator().createBrush() ); if ( !is_ceiling ) { fill_face( face, x, my, z_tl, mx, my, z_tr, x, y, z_bl, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, my, min_z, x, y, min_z, mx, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } else { fill_face( face, x, my, z_tl, x, y, z_bl, mx, my, z_tr, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, my, solid_top, mx, my, solid_top, x, y, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } fill_face( face, x, y, min_z, x, my, min_z, x, y, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, my, min_z, mx, my, min_z, x, my, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, mx, my, min_z, x, y, min_z, x, y, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); Node_getTraversable( entity )->insert( brush ); } // Triangle 2: TR, BR, BL { NodeSmartReference brush( GlobalBrushCreator().createBrush() ); if ( !is_ceiling ) { fill_face( face, mx, my, z_tr, mx, y, z_br, x, y, z_bl, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, mx, my, min_z, x, y, min_z, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } else { fill_face( face, mx, my, z_tr, x, y, z_bl, mx, y, z_br, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, mx, my, solid_top, mx, y, solid_top, x, y, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } fill_face( face, mx, y, min_z, mx, y, solid_top, mx, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, y, min_z, x, y, solid_top, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fill_face( face, x, y, min_z, mx, my, min_z, x, y, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); Node_getTraversable( entity )->insert( brush ); } } } static void insert_wall_brush( scene::Node& entity, double x_bl, double x_tl, double x_br, double x_tr, double gy, double gmx_y, double gz, double gmx_z, double outer_x, double limit_x, const char* top_tex, const char* caulk, bool is_left, bool alt_dir ){ // Wall geometry is floor/ceiling rotated 90°. // Coordinate mapping: floor(fx,fy,fz) → world(fz, fx, fy) // gy→fx, gz→fy, gmx_y→fmx, gmx_z→fmy, x_*→fz_*, outer_x/limit_x→fmin_z/fsolid_top // Left wall: inner surface faces +X (like floor), cap at outer_x (min_z side). // Right wall: inner surface faces −X (like ceiling), cap at outer_x (solid_top side). _QERFaceData face; const bool is_ceiling = !is_left; const double min_z = is_left ? outer_x : limit_x; const double solid_top = is_left ? limit_x : outer_x; // Aliases to match insert_floor_ceil_brush variable names exactly. const double x = gy, y = gz, mx = gmx_y, my = gmx_z; const double z_bl = x_bl, z_tl = x_tl, z_br = x_br, z_tr = x_tr; // fw: emit a face using floor coordinate order but with world axes permuted. auto fw = [&]( double fx0, double fy0, double fz0, double fx1, double fy1, double fz1, double fx2, double fy2, double fz2, const char* shader ){ fill_face( face, fz0, fx0, fy0, fz1, fx1, fy1, fz2, fx2, fy2, shader ); }; if ( !alt_dir ) { // BL→TR split { NodeSmartReference brush( GlobalBrushCreator().createBrush() ); if ( !is_ceiling ) { fw( x, y, z_bl, x, my, z_tl, mx, y, z_br, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( x, y, min_z, mx, y, min_z, x, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } else { fw( x, y, z_bl, mx, y, z_br, x, my, z_tl, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( x, y, solid_top, x, my, solid_top, mx, y, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } fw( x, y, min_z, x, my, min_z, x, y, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( x, y, min_z, x, y, solid_top, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( mx, y, min_z, mx, y, solid_top, x, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); Node_getTraversable( entity )->insert( brush ); } { NodeSmartReference brush( GlobalBrushCreator().createBrush() ); if ( !is_ceiling ) { fw( mx, my, z_tr, mx, y, z_br, x, my, z_tl, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( mx, my, min_z, x, my, min_z, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } else { fw( mx, my, z_tr, x, my, z_tl, mx, y, z_br, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( mx, my, solid_top, mx, y, solid_top, x, my, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } fw( mx, y, min_z, mx, y, solid_top, mx, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( x, my, min_z, mx, my, min_z, x, my, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( x, my, min_z, x, my, solid_top, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); Node_getTraversable( entity )->insert( brush ); } } else { // TL→BR split { NodeSmartReference brush( GlobalBrushCreator().createBrush() ); if ( !is_ceiling ) { fw( x, my, z_tl, mx, my, z_tr, x, y, z_bl, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( x, my, min_z, x, y, min_z, mx, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } else { fw( x, my, z_tl, x, y, z_bl, mx, my, z_tr, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( x, my, solid_top, mx, my, solid_top, x, y, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } fw( x, y, min_z, x, my, min_z, x, y, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( x, my, min_z, mx, my, min_z, x, my, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( mx, my, min_z, x, y, min_z, x, y, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); Node_getTraversable( entity )->insert( brush ); } { NodeSmartReference brush( GlobalBrushCreator().createBrush() ); if ( !is_ceiling ) { fw( mx, my, z_tr, mx, y, z_br, x, y, z_bl, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( mx, my, min_z, x, y, min_z, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } else { fw( mx, my, z_tr, x, y, z_bl, mx, y, z_br, top_tex ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( mx, my, solid_top, mx, y, solid_top, x, y, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); } fw( mx, y, min_z, mx, y, solid_top, mx, my, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( x, y, min_z, x, y, solid_top, mx, y, min_z, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); fw( x, y, min_z, mx, my, min_z, x, y, solid_top, caulk ); GlobalBrushCreator().Brush_addFace( brush, face ); Node_getTraversable( entity )->insert( brush ); } } } void build_tunnel_brushes( const BrushData& target, double step_x, double step_y, const TunnelMaps& maps, const char* top_texture, double cave_height, double slope_height ){ // Undo is started by the caller so deletion + generation form one step. scene::Node& floor_entity = create_func_group(); scene::Node& ceil_entity = create_func_group(); scene::Node& lwall_entity = create_func_group(); scene::Node& rwall_entity = create_func_group(); const char* caulk = "textures/common/caulk"; double min_z = target.min_z; // Highest ceiling point — slope_height may be negative (downward slope), // so the ceiling peak is always at the high end of the slope. double max_ceil_z = target.max_z + cave_height + std::max( 0.0, slope_height ); double ceil_solid_top = max_ceil_z + target.height_z; auto r2 = []( double v ) { return std::round( v * 100.0 ) / 100.0; }; // Floor and ceiling int x_index = 0; for ( double x = target.min_x; x < target.max_x - 0.01; x += step_x, ++x_index ) { int y_index = 0; for ( double y = target.min_y; y < target.max_y - 0.01; y += step_y, ++y_index ) { double mx = x + step_x < target.max_x ? x + step_x : target.max_x; double my = y + step_y < target.max_y ? y + step_y : target.max_y; auto safe_at = [&]( const HeightMap& m, double kx, double ky, double fallback ) -> double { auto it = m.find( { r2( kx ), r2( ky ) } ); if ( it == m.end() ) { globalErrorStream() << "TerrainGenerator: height map missing key (" << kx << ", " << ky << ") - using fallback\n"; return fallback; } return it->second; }; double f_bl = safe_at( maps.floor_map, x, y, min_z ); double f_tl = safe_at( maps.floor_map, x, my, min_z ); double f_br = safe_at( maps.floor_map, mx, y, min_z ); double f_tr = safe_at( maps.floor_map, mx, my, min_z ); double c_bl = safe_at( maps.ceiling_map, x, y, max_ceil_z ); double c_tl = safe_at( maps.ceiling_map, x, my, max_ceil_z ); double c_br = safe_at( maps.ceiling_map, mx, y, max_ceil_z ); double c_tr = safe_at( maps.ceiling_map, mx, my, max_ceil_z ); bool alt_dir = ( ( x_index + y_index ) % 2 ) != 0; insert_floor_ceil_brush( floor_entity, x, y, mx, my, f_bl, f_tl, f_br, f_tr, min_z, max_ceil_z, top_texture, caulk, false, alt_dir ); insert_floor_ceil_brush( ceil_entity, x, y, mx, my, c_bl, c_tl, c_br, c_tr, min_z, ceil_solid_top, top_texture, caulk, true, alt_dir ); } } // Walls — must match the range generated by terrain_engine. // slope_height may be negative (downward slope), so clamp accordingly. double wall_min_z = target.max_z + std::min( 0.0, slope_height ); double wall_max_z = wall_min_z + cave_height + std::abs( slope_height ); double step_z = maps.step_z; double outer_left = target.min_x - step_x; double outer_right = target.max_x + step_x; double limit_x = ( target.min_x + target.max_x ) / 2.0; int gy_index = 0; for ( double gy = target.min_y; gy < target.max_y - 0.01; gy += step_y, ++gy_index ) { int gz_index = 0; for ( double gz = wall_min_z; gz < wall_max_z - 0.01; gz += step_z, ++gz_index ) { double gmx_y = gy + step_y < target.max_y ? gy + step_y : target.max_y; double gmx_z = gz + step_z < wall_max_z ? gz + step_z : wall_max_z; auto safe_wall = [&]( const HeightMap& m, double kx, double ky, double fallback ) -> double { auto it = m.find( { r2( kx ), r2( ky ) } ); if ( it == m.end() ) { globalErrorStream() << "TerrainGenerator: wall map missing key (" << kx << ", " << ky << ") - using fallback\n"; return fallback; } return it->second; }; double lx_bl = safe_wall( maps.left_wall_map, gy, gz, limit_x ); double lx_tl = safe_wall( maps.left_wall_map, gy, gmx_z, limit_x ); double lx_br = safe_wall( maps.left_wall_map, gmx_y, gz, limit_x ); double lx_tr = safe_wall( maps.left_wall_map, gmx_y, gmx_z, limit_x ); double rx_bl = safe_wall( maps.right_wall_map, gy, gz, limit_x ); double rx_tl = safe_wall( maps.right_wall_map, gy, gmx_z, limit_x ); double rx_br = safe_wall( maps.right_wall_map, gmx_y, gz, limit_x ); double rx_tr = safe_wall( maps.right_wall_map, gmx_y, gmx_z, limit_x ); bool alt_dir = ( ( gy_index + gz_index ) % 2 ) != 0; insert_wall_brush( lwall_entity, lx_bl, lx_tl, lx_br, lx_tr, gy, gmx_y, gz, gmx_z, outer_left, limit_x, top_texture, caulk, true, alt_dir ); insert_wall_brush( rwall_entity, rx_bl, rx_tl, rx_br, rx_tr, gy, gmx_y, gz, gmx_z, outer_right, limit_x, top_texture, caulk, false, alt_dir ); } } select_generated( floor_entity ); select_generated( ceil_entity ); select_generated( lwall_entity ); select_generated( rwall_entity ); SceneChangeNotify(); }