Beispiel #1
0
def Make_Extension_Content_XML(
        name        = None,
        author      = None,
        version     = None,
        save        = False,
        sync        = False,
        enabled     = True,
        description = None,
    ):
    '''
    Adds an xml file object defining the content.xml for the top
    of the extension.  Common fields can be specified, or left as defaults.

    * name
      - String, display name; defaults to extension folder name.
    * author
      - String, author name; defaults to X4 Customizer.
    * version
      - String, version code; defaults to customizer version.
    * save
      - Bool, True if saves made with the extension require it be enabled.
    * sync
      - Bool.
    * enabled
      - Bool, True to default to enabled, False disabled.
    * description
      - String, extended description to use, for all languages.
      - Newlines are automatically converted to "
" for display in-game.
    '''
    # If the content exists already, return early.
    if Load_File('content.xml', error_if_not_found = False) != None:
        return
    
    if not version:
        # Content version needs to have 3+ digits, with the last
        #  two being sub version. This doesn't mesh will with
        #  the version in the Change_Log, but can do a simple conversion of
        #  the top two version numbers.
        version_split = Framework.Change_Log.Get_Version().split('.')
        # Should have at least 2 version numbers, sometimes a third.
        assert len(version_split) >= 2
        # This could go awry on weird version strings, but for now just assume
        # they are always nice integers, and the second is 1-2 digits.
        version_major = version_split[0]
        version_minor = version_split[1].zfill(2)
        assert len(version_minor) == 2
        # Combine together.
        version = version_major + version_minor

    # If a description given, format slightly.
    if description:
        # Need to use "
" for newlines in-game.
        description = description.replace('\n', '
')
    else:
        description = ' '

    # Set the ID based on replacing spaces.
    this_id = Settings.extension_name.replace(' ','_')

    # Set up the root content node.
    content_node = ET.Element(
        'content',
        attrib = {
            # Swap spaces to _; unclear on if x4 accepts spaces.
            'id'         : this_id,
            'name'       : name if name else Settings.extension_name,
            'author'     : author if author else 'X4_Customizer',
            'version'    : version,
            'date'       : File_System.Get_Date(),
            # TODO: maybe track when changes are save breaking, eg. if
            #  adding in new ships or similar. Though in practice, it
            #  may be best to always keep this false, and offer transforms
            #  that can undo breaking changes safely before uninstall.
            'save'       : 'true' if save else 'false',
            'sync'       : 'true' if sync else 'false',
            'enabled'    : 'true' if enabled else 'false',
            'description': description,
            })

    
    # Fill out language description text.
    # This loops over all language ids noticed in the cat/dat t files.
    for lang_id in ['7','33','34','39','44','48','49','55','81','82','86','88']:
        # Set up a new text node.
        # TODO: per-language descriptions.
        text_node = ET.Element('language', language = lang_id, description = description)

        # Add to the content node.
        content_node.append(text_node)

    # Record it.
    File_System.Add_File( XML_File(
        xml_root = content_node,
        virtual_path = 'content.xml',
        modified = True))
    
    # Use shared code to fill in dependencies.
    Update_Content_XML_Dependencies()
    return
def Patch_Shader_Files(shader_names, testmode=False):
    '''
    Edit asteroid shader to support transparency.

    * shader_names
       - Names of shader ogl files to edit.
    * testmode
      - Sets asteroid color to white when unfaded, black when fully faded,
        for testing purposes.
    '''
    '''
    The asteroid shader, and ogl file linking to v/vh/etc., doesn't normally
    support camera fade. However, fading rules are in the common.v file,
    and fade parameters can be added to the shaer ogl.
    Note: ogl is xml format.

    Note:
    .v : vertex shader
    .f : fragment shader (most logic is here)

    Note: multiple ogl files can link to the same f shader file.
    Ensure a) that each shader file is modified once, and b) that each ogl
    file using a shader is also modified to fill in fade range defaults.

    Note: some ogl files are empty; skip them (will have load_error flagged).
    
    Note: in testing, other users of these shaders tend to error,
    apparently tracing back to the V_cameraposition or IO_world_pos
    values (at a guess), which even when not executed still cause a problem.

        It is possible the other ogl spec files do not have corresponding
        defines for the above values to be present.

        Possible fixes:
        - Duplicate all shaders being edited, link their ogl to the
            unique versions, so original ogl/shader files are unchanged.
        - Modify other ogl files to define whatever is needed to ensure
            these variables are available.
        - Modify the common header to ensure this vars are available.
        - Regenerate these vars here (if possible).
    '''
    # From ogl files, get their fragment shader names.
    shader_f_names = []

    # Go through ogl files and uniquify/modify them.
    for shader_name in shader_names:
        shader_ogl_file = Load_File(f'shadergl/high_spec/{shader_name}')
        # Skip empty files.
        if shader_ogl_file.load_error:
            continue

        # Copy out the root.
        xml_root = shader_ogl_file.Get_Root()

        # Grab the fragment shader name.
        shader_node = xml_root.find('./shader[@type="fragment"]')
        shader_f_name = shader_node.get('name')
        # Record it.
        if shader_f_name not in shader_f_names:
            shader_f_names.append(shader_f_name)
        # Uniquify it.
        shader_f_name = shader_f_name.replace('.f', '_faded.f')
        shader_node.set('name', shader_f_name)

        # Want to add to the properties list.
        properties = xml_root.find('./properties')

        # -Removed; these defines don't seem to work, and may be undesirable
        # anyway since they might conflict with existing logic/defines,
        # eg. doubling up fade multipliers.
        # Add defines for the camera distance and fade.
        # Put these at the top, with other defines (maybe doesn't matter).
        #for field in ['ABSOLUTE_CAMERA_FADE_DISTANCE', 'FADING_CAMERA_FADE_RANGE']:
        #    property = etree.Element('define', name = field, value = '/*def*/')
        #    properties.insert(0, property)
        #    assert property.tail == None

        # The fade calculation will use these properties.
        # Assign them initial defaults in the shader (which also covers possible
        # cases where the shader is used by materials that weren't customized).
        # Note: These default values are not expected to ever be used.
        for field, value in [('ast_camera_fade_range_start', '19900000.0'),
                             ('ast_camera_fade_range_stop', '20000000.0')]:
            property = etree.Element('float', name=field, value=value)
            properties.append(property)
            assert property.tail == None

        # Generate a new file for the new shader spec.
        File_System.Add_File(
            XML_File(virtual_path=shader_ogl_file.virtual_path.replace(
                '.ogl', '_faded.ogl'),
                     xml_root=xml_root,
                     modified=True))
        #shader_ogl_file.Update_Root(xml_root)

    # Need to update the .f file that holds the actual shader logic.
    for shader_f_name in shader_f_names:
        shader_f_file = Load_File(f'shadergl/shaders/{shader_f_name}')

        # Do raw text editing on this.
        text = shader_f_file.Get_Text()
        new_text = text

        # Dither based approach. If setting this to 0, experimental code
        # is tried further below.
        if 1:
            '''
            Note: these comments were made in X4 3.3.

            Various attempts were made to access proper transparency through
            the alpha channel, but none worked.
            Observations:
            - OUT_Color controls reflectivity (using low-res alt backdrop),
              though in theory should be actual color.
            - OUT_Color1 controls ?
            - OUT_Color2 controls actual color (when used?)
            - Vulkan lookups suggest transparency might depend on an alpha channel,
              and would be super expensive to compute anyway.

            Instead, use a dithering approach, showing more pixels as it gets closer.
            Can use the "discard" command to throw away a fragment.

            gl_FragCoord gives the screen pixel coordinates of the fragment.
            Vanilla code divides by V_viewportpixelsize to get a Percentage coordinate,
            but that should be unnecessary.

            Want to identify every 1/alpha'th pixel.
            Eg. alpha 0.5, want every 2nd pixel.
                v * a = pix_selected + fraction
                If this just rolled over, pick this pixel.
                if( fract(v*a) >= a) discard;
                If a == 1, fract will always be 0, 0 >= 1, so discards none.
                If a == 0, fract will always be 0, 0 >= 0, so discards all.


            Note: dithering on rotating asteroids creates a shimmer effect,
            as lighter or darker areas of the asteroid rotate into display pixels.
            Further, since dither is calculated by distance, and different points
            on the asteroid are at different distances, the pixels shown are
            also moving around (since asteroid isn't perfectly round).

                To mitigate shimmer, also adjust the object coloring.
                A couple options:
                - Dim to black. This is similar to some alpha examples. Would work
                  well on a dark background (common in space).
                - Average to the asteroid base color. This would require specifically
                  setting the base color for each asteroid type (brown for ore,
                  blueish for ice, etc.). Avoids a black->blueish transition on ice.
                  May look worse against a dark backdrop (eg. one blue/white pixel).

                This code will go with generic color dimming for now.
                Result: seems to help, though not perfect.


            Further shimmer mitigation is possible by reducing volatility of
            the camera depth.
                In reading, it doesn't appear pixel/fragment shaders normally
                support distance to the original object, only to their
                particular point on the surface.

                But, if the cam distance (or computed alpha) is rounded off, it
                will create somewhat more stable bins.  There would still be
                a problem when the asteroid surface moves from one bin to another,
                but most of the shimmer should be reduced.

                Rounding the fade factor is probably more robust than cam distance.
                Eg. fade = floor(fade * 100) / 100

                Result: somewhat works, but makes the pixel selection pattern
                really obvious. Where (dist.x+dist.y) is used, gets diagonal
                lines of drawn pixels.


            Cull pixel selection
                This has a problem with creating obvious patterns.
                Cull x < cuttoff || y < cuttoff:
                - Get mesh of dark lines (culled points).
                Cull x < cuttoff && y < cuttoff:
                - Get mesh of shown lines (non-culled points).
                Cull x + y < cuttoff
                - Get diagonal lines.

                TODO: what is a good culling formula that spreads out the points,
                both when mostly faded (first few points shown) and when mostly
                shown (last few points culled).
            
                Patterns are most obvious when zooming in, but can be somewhat
                seen by leaning forward, or identified when turning the camera.

        
            Reverse-shimmer is a somewhat different problem where eg. a blue/white
            ice asteroid that is mostly drawn will have some black background 
            pixels shimmering around and standing out.
           
                This could be addressed by fading in with two steps:
                - Dither region, where more pixels are drawn, all black.
                - Color region, where pixels adjusted from black to wanted color.
            
                Such an approach would also address other shimmer problems above,
                with the caveat that it might be even more sensitive to the
                overall background color (black is good, otherwise bad).

                Note: small asteroids drawing in front of already-visible larger
                asteroids would cause this background discrepency, eg. a small
                ice chunk starts dithering in black pixels when a large ice
                asteroid behind it is blue/white.

                For now, ignore this problem, as solutions are potentially worse.

            TODO: adjust for viewport width (or whatever it was called), so that
            zoomed in views see more of the asteroid draw (reducing obviousness
            of the dither effect when using a zoom hotkey).
        
            TODO: maybe discard if not gl_FrontFacing; probably not important.

            To stabalize pattern when camera turns:
                Use the angle from cam pos to object pos.
                angles = sin((cam.x - obj.x) / (cam.z - obj.z)) + sin((cam.y - obj.y) / (cam.z - obj.z))
                Tweak this based on:
                - Resolution; reduced angle between pixels at higher res.
                - Round to roughly matching up to pixel density.

                What is the angle between pixels?
                Guess: 100 fov at 1650 wide, so 5940 pix in full circle, ~6k.
                Can compute live based on V_viewportpixelsize.x, though fov
                is unclear (maybe based on x/y ratio?).

                In testing, centering on a nav beacon and counting turns of the
                camera to get back to it, it takes 4.3 turns, or 84 degrees.
                Note: trying with the 100 assumption had some dither noise when
                turning the camera, so that was wrong; maybe 84 is right?

                Scaling by resolution: fov = 84 / (10/16) * x/y = 52.5 * x/y

                Does this even need an actual angle?
                Can get the xyz vector from the camera to the object, and adjust
                to be a unit vector:
                    dir = (cam.xyz - obj.xyz)
                    dir = dir / distance(dir)
                That's probably good enough, but how to round for stability?
                May need to use atan anyway to get angles.

                Test result:
                - Looks mostly stable, except some shimmering noticed occasionally,
                notably near the +x axis center.
                - Zooming looks bad, but not much to be done about that.
                - Some shimmer when turning just due to resolution and aliasing,
                similar to general object edges.

            '''
            # Pick number of fade stepping bins, used to reduce shimmer.
            # Should be few enough that steppings don't stand out visually.
            num_bins = 20

            # Copy over the IO_Fade calculation from the common.v, and
            # do any customization. This also allows unique var names, to
            # avoid stepping on existing fade variables (eg. IO_fade).
            # Note: ast_fade is live through the function in testmode, so give
            # it a name likely to be unique.
            new_code = f'''
                float ast_cameradistance = abs(distance(V_cameraposition.xyz, IO_world_pos.xyz));
                float ast_faderange = U_ast_camera_fade_range_stop - U_ast_camera_fade_range_start;
                float ast_fade = 1.0 - clamp(abs(ast_cameradistance) - U_ast_camera_fade_range_start, 0.0, ast_faderange) / ast_faderange;
                ast_fade = round(ast_fade * {num_bins:.1f}) / {num_bins:.1f};
            '''
            # Add in the discard check if not in test mode.
            # Want to avoid diagonal patterns (x+y) in favor of a better scatter.
            if not testmode:
                #new_code += '''
                #    if( fract((gl_FragCoord.x + gl_FragCoord.y) * ast_fade) >= ast_fade)
                #        discard;
                #    '''

                # Make a 2-wide vector for this. Note: vulkan doesn't seem to
                # support something from documentation checked (sqrt? bvec?
                # greaterThanEqual?), so expand out the comparison.
                # Want to keep x/y crossing points, so discard unwanted x and y
                # (OR the check).
                #new_code += '''
                #    if (ast_fade < 0.999){
                #        float ast_fade_sqrt = sqrt(ast_fade);
                #        vec2 ast_factions = fract(gl_FragCoord.xy * ast_fade_sqrt);
                #        if( ast_factions.x >= ast_fade_sqrt || ast_factions.y >= ast_fade_sqrt)
                #            discard;
                #    }
                #    '''

                # Better idea: use the fragment xyz, so the discard pattern doesnt
                # follow the camera angle when turned.
                # If the coordinate is a noisy float (eg. not 2.00000), can use
                # its deep fractional part as a sort of random value.
                # Result: looks good on a still object, but asteroid rotation
                # creates shimmier, so reject.
                #new_code += '''
                #    if (ast_fade < 0.999){
                #        float psuedo_rand = fract((IO_world_pos.x + IO_world_pos.y + IO_world_pos.z) * 16.0);
                #        if( psuedo_rand >= ast_fade)
                #            discard;
                #    }
                #    '''

                # Try to create a random value from the screen xy position.
                # Note: quick reading indicates gpu sin/cos is just 1 cycle.
                # Example of randomizer calculation here:
                # https://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl
                # Works decently, though dither tracks with camera rotation.
                if 0:
                    new_code += '''
                    if (ast_fade < 0.999){
                        float psuedo_rand = fract(sin(dot(gl_FragCoord.xy, vec2(12.9898,78.233))) * 43758.5453);
                        if( psuedo_rand >= ast_fade)
                            discard;
                    }
                    '''

                # More complex case: compute a rounded angle.
                if 1:
                    # Fov is roughly 52.5 * x/y of the resolution.
                    # angle_per_pixel = fov / x
                    # Want to round based on angle_per_pixel.
                    # roundto = 52.5 * x/y / x = 52.5 / y
                    # Switch to radians.
                    round_y_ratio = math.pi * 2 / 360 * 52.5
                    new_code += f'''
                    if (ast_fade < 0.999){{
                        float roundto = {round_y_ratio} / V_viewportpixelsize.y;
                        vec3 dir = V_cameraposition.xyz - IO_world_pos.xyz;
                        // Note: atan is -pi to pi.
                        vec2 angles = vec2(
                            atan(dir.x, dir.z), 
                            atan(dir.y, dir.z)
                            );
                        // Round them.
                        angles = floor((angles / roundto) + 0.5) * roundto;
                        // Get a rand from the angles.
                        // Ideally don't want patterns, eg. circles of discards,
                        // so don't add the angles. Can do what was done above
                        // with xy.
                        float psuedo_rand = fract(sin(dot(angles, vec2(12.9898,78.233))) * 43758.5453);
                        if( psuedo_rand >= ast_fade)
                            discard;
                    }}
                    '''

            # Replace a line near the start of main, for fast discard (maybe slightly
            # more performant).
            # TODO: make the ref line more robust.
            ref_line = 'main()\n{'
            assert new_text.count(ref_line) == 1
            new_text = new_text.replace(ref_line, ref_line + new_code)

            # In test mode, shortcut the ast_fade to the asteroid color.
            # Close asteroids will be white, far away black (ideally).
            # This overwrites the normal asteroid output result.
            if testmode:
                new_code = '''
                    OUT_Color  = half4(0);
                    OUT_Color1 = vec4(0);
                    OUT_Color2 = vec4(ast_fade,ast_fade,ast_fade,0); 
                    '''
                # Replace a late commented line, overriding out_color.
                # TODO: more robust way to catch close of main.
                ref_line = '}'
                new_text = (new_code + ref_line).join(
                    new_text.rsplit(ref_line, 1))

            # When not in test mode, dim the asteroid final color.
            # TODO: does this work for all shaders, eg. color in out_color2?
            # Or perhaps can all color fields (eg. reflectivity) be dimmed
            # equally without impact?
            # Makes everything green tinted?
            if not testmode and 0:
                new_code = '''
                    OUT_Color  = OUT_Color  * ast_fade;
                    OUT_Color1 = OUT_Color1 * ast_fade;
                    OUT_Color2 = OUT_Color2 * ast_fade;
                    '''
                ref_line = '}'
                new_text = (new_code + ref_line).join(
                    new_text.rsplit(ref_line, 1))

            # Based on experimentation below, can play with the
            # asteroid normal/specular to introduce some light transparency,
            # which may help with a smoother fade-in alongside the dither.
            # Set the normal to average between 0 rgb and (1-ast_fade)
            # alpha, and what it would otherwise be, for smoother fade-in.
            #
            # -Removed; in practice, the norm/spec only draws when the
            # asteroid is somewhat closer than its render distance, eg.
            # it will be dither in 40% and then suddenly the semi-clear
            # normals kick in. (Tested on medium roids in Faulty Logic.)
            # TODO: maybe revisit if finding a way to extend
            # normals out to further distances (may require
            # messing with models or something).
            if not testmode and 0:
                new_code = '''
                    OUT_Color.a = (OUT_Color.a + 1 - ast_fade) / 2;
                    OUT_Color.rgb = OUT_Color.rgb * ast_fade;
                    //OUT_Color.a = 1.0;
                    //OUT_Color.rgb = half3(0.0, 0.0, 0.0);
                    '''
                ref_line = '}'
                new_text = (new_code + ref_line).join(
                    new_text.rsplit(ref_line, 1))

        elif 0:
            # Experiment with transparency, hopefully finding out how to make
            # it work in x4 4.0.
            '''
            The F_alphascale property appears to be supplied by the game
            engine. In testing, this appears to start at 0, says 0 for a
            while, then snaps to 1 when halfway to the asteroid (around
            when the asteroid goes fully opaque in egosoft's 4.0 handling).
            Egosofts actual fade-in appears to be done separately, outside
            of the shader, so this isn't useful.

            The region_lodvalues.xsd file indicates that there are
            two contributors to engine-calculated fade, one for time (if
            asteroids spawn in inside their render distance), one for
            distance. The distance version is janky in testing, particularly
            in Faulty Logic, where an asteroid quickly fades in over ~50m
            to ~50% transparent black, then slowly fills in color over ~30km
            (slowly losing transparency), then quickly goes from 80%->100%
            opaque at around 35km. Tested on a mid-sized silicon asteroid.

            This shader edit will be similar to the 3.3 version, calculating
            a smoother fade-in alpha, and will experiment with applying
            it to the asteroid alpha channel (maybe without success).

            Theory of shader operation:
                a) Differred rendering is on
                b) B_deferred_draw is true?
                c) Shader calls macro DEFERRED_OUTPUT which sets colors,
                   passing it the shader computed rgb as GLOW param.
                d) Macro assigns:
                   OUT_Color2 = ColorGlow.rgb, 3
                e) Macro calls other macro, STORE_GBUFFER
                f) STORE_GBUFFER assigns:
                   OUT_Color  = NORMAL.rgb, spec (=0.2)
                   OUT_Color1 = BASECOLOR.rgb, METAL

            Overall testing result: the egosoft transparency setting appears
            to be done separately from the shader, and while shader edits
            could expose some moderate transparency, side effects from 
            haziness just make it look bad (and cannot be hidden since
            asteroids wouldnt go fully transparent).
            Giving up on this path for now to go back to dithering.
            '''
            # Calculate the custom fade.
            # See notes above (on 3.3 version) for explanation.
            new_code = f'''
                float ast_cameradistance = abs(distance(V_cameraposition.xyz, IO_world_pos.xyz));
                float ast_faderange = U_ast_camera_fade_range_stop - U_ast_camera_fade_range_start;
                float ast_fade = 1.0 - clamp(abs(ast_cameradistance) - U_ast_camera_fade_range_start, 0.0, ast_faderange) / ast_faderange;
            '''
            # Take the min of this and the engine supplied value.
            # TODO: retest; remove for  now; alphascale doesnt actually appear
            # to be egosofts engine alpha.
            new_code += f'''
                //ast_fade = min(ast_fade, F_alphascale);
                //ast_fade = F_alphascale;
                ast_fade = 0.5;
            '''

            # Change uses of F_alphascale to instead use ast_fade later
            # in the shader (should just be two uses, one for z depth, other
            # for final output alpha adjustment).
            # (Do this before inserting ast_fade calculation).
            # -Removed for now;
            #new_text = new_text.replace('F_alphascale', 'ast_fade')

            # Replace a line near the start of main, so it is in place before
            # first use of ast_fade.
            # TODO: make the ref line more robust.
            ref_line = 'main()\n{'
            assert new_text.count(ref_line) == 1
            new_text = new_text.replace(ref_line, ref_line + new_code)

            # Apply to the final asteroid color.
            # TODO: does this work for all shaders, eg. color in out_color2?
            # Or perhaps can all color fields (eg. reflectivity) be dimmed
            # equally without impact?
            # Note: DEFERRED_OUTPUT appears to be in use, as in testing
            # a full reapplication of the last OUT_Color line just before
            # the function end causes oddities in asteroid appearance (they
            # show up half transparent at any range).
            new_code = '''
                //OUT_Color  = OUT_Color  * ast_fade;
                //OUT_Color1 = OUT_Color1 * ast_fade;
                //OUT_Color2 = OUT_Color2 * ast_fade;

                // Does nothing.
                //OUT_Color.a = 0.5;
                //OUT_Color.a = OUT_Color.a * ast_fade;

                //-Causes half transparent asteroids, any distance; background
                // is blurred through asteroid.
                // Asteroids also take on a haziness at a distance (seem to be
                // taking on some of the average background color), so they
                // show up dark against light colored background, and light
                // against dark colored background.
                //OUT_Color = half4(finalColor.rgb, ColorBaseDiffuse.a * F_alphascale);
                // Similar but less transparent.
                //OUT_Color = 0.5 * half4(finalColor.rgb, ColorBaseDiffuse.a * F_alphascale);
                // Distant asteroids slightly white/shiny; generally more transparent.
                //OUT_Color = 2 * half4(finalColor.rgb, ColorBaseDiffuse.a * F_alphascale);
                // Similar to above, distant slightly white (maybe background
                // showing through? but whiter than background, perhaps due
                // to blurred background).
                //OUT_Color = half4(finalColor.rgb, ColorBaseDiffuse.a * F_alphascale * 2);
                // Asteroid seems a bit desaturated and bland, but opaque.
                //OUT_Color.rgb = finalColor.rgb;
                // Doing rgb and a separately has the same effect as together,
                // so no oddities with half4.
                //OUT_Color.rgb = finalColor.rgb;
                //OUT_Color.a = ColorBaseDiffuse.a * F_alphascale;
                
                // Opaque but dark asteroids (not black though).
                //OUT_Color = half4(0,0,0,0);
                // Opaque, distant asteroids white, nearby colored normally;
                // zooming in switches white roids to normal (so something
                // to do with rendering angle and not distance).
                //OUT_Color = half4(0.5,0.5,0.5,0.5);
                // Opaque, distant asteroids white again, similar to above.
                //OUT_Color.rgb = half3(0.5,0.5,0.5);
                // Alpha changes did nothing notable.
                //OUT_Color.a = 0.5;
                //OUT_Color.a = 0.05;
                //OUT_Color.a = 0.95;
                //OUT_Color.a = ColorBaseDiffuse.a * F_alphascale;
                // Distant asteroids are lighter colored, normal up close.
                //OUT_Color.a = 10;
                // Distant asteroids even lighter/glowier with blue halo,
                // normal up close.
                //OUT_Color.a = 128;
                // Opaque and extremely shiny at distance, normal up close.
                //OUT_Color.rgb = half3(128,128,128);
                // Distant roids shiny red/green instead of white.
                //OUT_Color.r = 128;
                //OUT_Color.g = 128;

                // Just try to rescale alpha, to narrow down its number.
                //OUT_Color.rgb = finalColor.rgb;
                // Opaque to stars, seemingly, but background fog/nebula
                // show through slightly.
                //OUT_Color.a = 0.1;
                // Modestly transparant, can make out the blurred stars.
                //OUT_Color.a = 0.5;
                // More transparant, but feels like 80%; can still make out
                // the asteroid, and distance asteroids still have a light
                // colored haze to them.
                //OUT_Color.a = 1.0;
                // Similar to above.
                //OUT_Color.a = 1.5;

                // Try to rescale rgb, to narrow down its number.
                //OUT_Color.a = 1.0;
                // Around 80% transparent still, but asteroid has a darker hue.
                //OUT_Color.rgb = half3(0.0, 0.0, 0.0);
                // Asteroids on one half of the sector are bright white,
                // at any distance, based on player view point (eg. can strafe
                // to switch an asteroid from normal to white gradually).
                //OUT_Color.rgb = half3(1.0, 1.0, 1.0);
                // Giving up here; it might be negative or something, but
                // seems like a dead end with the haziness.

                // Try out the other color alphas.
                // No noticed effect from this.
                //OUT_Color2.a = 0.5;
                // Nor from this.
                //OUT_Color1.a = 0.5;
                '''
            ref_line = '}'
            new_text = (new_code + ref_line).join(new_text.rsplit(ref_line, 1))

            # In test mode, shortcut the ast_fade to the asteroid color.
            # Close asteroids will be white, far away black (ideally).
            # This overwrites the normal asteroid output result.
            if testmode:
                new_code = '''
                    OUT_Color  = half4(0);
                    OUT_Color1 = vec4(0);
                    OUT_Color2 = vec4(ast_fade,ast_fade,ast_fade,0); 
                    '''
                # Replace a late commented line, overriding out_color.
                # TODO: more robust way to catch close of main.
                ref_line = '}'
                new_text = (new_code + ref_line).join(
                    new_text.rsplit(ref_line, 1))

        # Uniquify the file.
        File_System.Add_File(
            Text_File(virtual_path=shader_f_file.virtual_path.replace(
                '.f', '_faded.f'),
                      text=new_text,
                      modified=True))

    return
Beispiel #3
0
def Update_Content_XML_Dependencies():
    '''
    Update the dependencies in the content xml file, based on which other
    extensions touched files modified by the current script.
    If applied to an existing content.xml (not one created here), existing
    dependencies are kept, and only customizer dependencies are updated.

    Note: an existing xml file may loose custom formatting.
    '''
    # TODO: framework needs more development to handle cases with an
    # existing content.xml cleanly, since currently the output extension is
    # always ignored, and there is no particular method of dealing with
    # output-ext new files not having an extensions/... path.

    # Try to load a locally created content.xml.
    content_file = Load_File('content.xml', error_if_not_found = False)

    # If not found, then search for an existing content.xml on disk.
    if not content_file:
        # Manually load it.
        content_path = Settings.Get_Output_Folder() / 'content.xml'
        # Verify the file exists.
        if not content_path.exists():
            Print('Error in Update_Content_XML_Dependencies: could not find an existing content.xml file')
            return

        content_file = File_System.Add_File( XML_File(
            # Plain file name as path, since this will write back to the
            # extension folder.
            virtual_path = 'content.xml',
            binary = content_path.read_bytes(),
            # Edit the existing file.
            edit_in_place = True,
            ))

    root = content_file.Get_Root()
    
    # Set the ID based on replacing spaces.
    this_id = Settings.extension_name.replace(' ','_')
    
    # Remove old dependencies from the customizer, and record others.
    existing_deps = []
    for dep in root.xpath('./dependency'):
        if dep.get('from_customizer'):
            dep.getparent().remove(dep)
        else:
            existing_deps.append(dep.get('id'))

    # Add in dependencies to existing extensions.
    # These should be limited to only those extensions which sourced
    #  any of the files which were modified.
    # TODO: future work can track specific node edits, and set dependencies
    #  only where transform modified nodes might overlap with extension
    #  modified nodes.
    # Dependencies use extension ids, so this will do name to id
    #  translation.
    # Note: multiple dependencies may share the same ID if those extensions
    #  have conflicting ids; don't worry about that here.
    source_extension_ids = set()
    for file_name, game_file in File_System.game_file_dict.items():
        if not game_file.modified:
            continue
        
        for ext_name in game_file.source_extension_names:
            # Translate extension names to ids.
            ext_id = File_System.source_reader.extension_source_readers[
                                ext_name].extension_summary.ext_id
            source_extension_ids.add(ext_id)

    # Add the elements; keep alphabetical for easy reading.
    for ext_id in sorted(source_extension_ids):

        # Omit self, just in case; shouldn't come up, but might.
        if ext_id == this_id:
            Print('Error: output extension appears in its own dependencies,'
                  ' indicating it transformed its own prior output.')
            continue

        # Skip if already defined.
        if ext_id in existing_deps:
            continue
        
        # Add it, with a tag to indicate it came from the customizer.
        # TODO: optional dependencies?
        root.append( ET.Element('dependency', id = ext_id, from_customizer = 'true' ))

    content_file.Update_Root(root)
    return