예제 #1
0
    public void Bind([MaybeNull] WorldCamera camera, RenderingDevice device, IList <Light3d> lights,
                     MdfRenderOverrides overrides = null)
    {
        device.SetMaterial(mDeviceMaterial);

        BindShader(camera, device, lights, overrides);
    }
예제 #2
0
    public void DrawQuad(IGameViewport viewport, ReadOnlySpan <ShapeVertex3d> corners,
                         IMdfRenderMaterial material,
                         PackedLinearColorA color)
    {
        _discVertexBuffer.Resource.Update(corners);
        _discBufferBinding.Resource.Bind();

        MdfRenderOverrides overrides = new MdfRenderOverrides();

        overrides.overrideDiffuse = true;
        overrides.overrideColor   = color;
        material.Bind(viewport, _device, null, overrides);

        _device.SetIndexBuffer(_discIndexBuffer);
        _device.DrawIndexed(PrimitiveType.TriangleList, 4, 2 * 3);
    }
예제 #3
0
    public void Render(IGameViewport viewport, IAnimatedModel model, AnimatedModelParams animParams, IList <Light3d> lights,
                       MdfRenderOverrides materialOverrides = null)
    {
        var renderData = GetOrCreateState(model);

        var materialIds = model.GetSubmeshes();

        for (var i = 0; i < materialIds.Length; ++i)
        {
            var material = materialIds[i];
            var submesh  = animParams.rotation3d ? model.GetSubmeshForParticles(animParams, i) : model.GetSubmesh(animParams, i);

            // Usually this should not happen, since it means there's
            // an unbound replacement material
            if (material == null)
            {
                continue;
            }

            material.Bind(viewport, mDevice, lights, materialOverrides);

            // Do we have to recalculate the normals?
            if (material.GetSpec().recalculateNormals)
            {
                RecalcNormals(
                    submesh.VertexCount,
                    submesh.Positions,
                    submesh.Normals,
                    submesh.PrimitiveCount,
                    submesh.Indices
                    );
            }

            var submeshData = GetSubmeshData(renderData, i, submesh);
            submeshData.binding.Resource.Bind();

            mDevice.SetIndexBuffer(submeshData.idxBuffer);
            mDevice.DrawIndexed(PrimitiveType.TriangleList,
                                submesh.VertexCount,
                                submesh.PrimitiveCount * 3);
        }
    }
예제 #4
0
    public void DrawRectangleWithMaterial(Span <Vertex2d> corners, IMdfRenderMaterial material)
    {
        MdfRenderOverrides overrides = new MdfRenderOverrides();

        overrides.ignoreLighting = true;
        overrides.uiProjection   = true;
        material?.Bind((WorldCamera)null, _device, Array.Empty <Light3d>(), overrides);

        _device.SetDepthStencilState(noDepthState);

        foreach (ref var vertex in corners)
        {
            vertex.normal = new Vector4(0, 0, -1, 0);
        }

        // Copy the vertices
        _device.UpdateBuffer <Vertex2d>(vertexBuffer, corners);

        mdfBufferBinding.Bind();

        _device.SetIndexBuffer(indexBuffer);

        _device.DrawIndexed(PrimitiveType.TriangleList, 4, 6);
    }
예제 #5
0
    public override void Render(IGameViewport viewport, PartSysEmitter emitter)
    {
        var it = emitter.NewIterator();

        var animParams = AnimatedModelParams.Default;

        animParams.rotation3d = true;

        // Lazily initialize render state
        if (!emitter.HasRenderState())
        {
            // Resolve the mesh filename
            var baseName = ResolveBasename(emitter.GetSpec().GetMeshName());
            var skmName  = baseName + ".skm";
            var skaName  = baseName + ".ska";

            try
            {
                var animId = new EncodedAnimId(NormalAnimType.ItemIdle); // This seems to be item_idle
                var model  = _modelFactory.FromFilenames(skmName, skaName, animId, animParams);

                emitter.SetRenderState(
                    new ModelEmitterRenderState(model)
                    );
            }
            catch (Exception e)
            {
                Logger.Error("Unable to load model {0} for particle system {1}: {2}",
                             baseName, emitter.GetSpec().GetParent().GetName(), e);

                emitter.SetRenderState(new ModelEmitterRenderState(null));
            }
        }

        var renderState = (ModelEmitterRenderState)emitter.GetRenderState();

        if (renderState.Model == null)
        {
            return; // The loader above was unable to load the model for this emitter
        }

        var overrides = new MdfRenderOverrides
        {
            ignoreLighting  = true,
            overrideDiffuse = true
        };

        var yaw   = emitter.GetParamState(PartSysParamId.part_yaw);
        var pitch = emitter.GetParamState(PartSysParamId.part_pitch);
        var roll  = emitter.GetParamState(PartSysParamId.part_roll);

        while (it.HasNext())
        {
            var particleIdx = it.Next();
            var age         = emitter.GetParticleAge(particleIdx);

            overrides.overrideColor = GeneralEmitterRenderState.GetParticleColor(emitter, particleIdx);

            // Yes, this is *actually* swapped for Y / Z
            var particleState = emitter.GetParticleState();
            animParams.offsetX = particleState.GetState(ParticleStateField.PSF_POS_VAR_X, particleIdx);
            animParams.offsetY = particleState.GetState(ParticleStateField.PSF_POS_VAR_Z, particleIdx);
            animParams.offsetZ = particleState.GetState(ParticleStateField.PSF_POS_VAR_Y, particleIdx);

            if (yaw != null)
            {
                animParams.rotationYaw = Angles.ToRadians(yaw.GetValue(emitter, particleIdx, age));
            }

            if (pitch != null)
            {
                animParams.rotationPitch = Angles.ToRadians(pitch.GetValue(emitter, particleIdx, age));
            }

            if (roll != null)
            {
                animParams.rotationRoll = Angles.ToRadians(roll.GetValue(emitter, particleIdx, age));
            }

            renderState.Model.SetTime(animParams, age);

            _modelRenderer.Render(viewport, renderState.Model, animParams, new List <Light3d>(), overrides);
        }
    }
예제 #6
0
    public void RenderObject(IGameViewport viewport, GameObject obj, bool showInvisible)
    {
        mTotalLastFrame++;

        var type  = obj.type;
        var flags = obj.GetFlags();

        // Dont render destroyed or disabled objects
        const ObjectFlag dontDrawFlags = ObjectFlag.OFF | ObjectFlag.DESTROYED | ObjectFlag.DONTDRAW;

        if ((flags & dontDrawFlags) != 0)
        {
            return;
        }

        // Hide invisible objects we're supposed to show them
        if ((flags & ObjectFlag.INVISIBLE) != 0 && !showInvisible)
        {
            return;
        }

        // Dont draw secret doors that haven't been found yet
        var secretDoorFlags = obj.GetSecretDoorFlags();

        if (secretDoorFlags.HasFlag(SecretDoorFlag.SECRET_DOOR))
        {
            var found = secretDoorFlags.HasFlag(SecretDoorFlag.SECRET_DOOR_FOUND);
            if (!found && type != ObjectType.portal)
            {
                return;
            }
        }

        var animatedModel = obj.GetOrCreateAnimHandle();

        var animParams = obj.GetAnimParams();

        locXY worldLoc;

        GameObject parent = null;

        if (type.IsEquipment())
        {
            parent = GameSystems.Item.GetParent(obj);
        }

        var alpha = GetAlpha(obj);

        if (parent != null)
        {
            var parentAlpha = GetAlpha(parent);
            alpha = (alpha + parentAlpha) / 2;

            worldLoc = parent.GetLocation();
        }
        else
        {
            worldLoc = obj.GetLocation();
        }

        if (alpha == 0)
        {
            return;
        }

        // Handle fog occlusion of the world position
        if (type != ObjectType.container &&
            (type == ObjectType.projectile ||
             type.IsCritter() ||
             type.IsEquipment()) &&
            (GameSystems.MapFogging.GetFogStatus(worldLoc, animParams.offsetX, animParams.offsetY) & 1) == 0)
        {
            return;
        }

        LocAndOffsets worldPosFull;

        worldPosFull.off_x    = animParams.offsetX;
        worldPosFull.off_y    = animParams.offsetY;
        worldPosFull.location = worldLoc;

        var radius       = obj.GetRadius();
        var renderHeight = obj.GetRenderHeight(true);

        if (!IsObjectOnScreen(viewport.Camera, worldPosFull, animParams.offsetZ, radius, renderHeight))
        {
            return;
        }

        if (Globals.Config.drawObjCylinders)
        {
            Tig.ShapeRenderer3d.DrawCylinder(
                viewport,
                worldPosFull.ToInches3D(animParams.offsetZ),
                radius,
                renderHeight
                );
        }

        var lightSearchRadius = 0.0f;

        if (!flags.HasFlag(ObjectFlag.DONTLIGHT))
        {
            lightSearchRadius = radius;
        }

        LocAndOffsets locAndOffsets;

        locAndOffsets.location = worldLoc;
        locAndOffsets.off_x    = animParams.offsetX;
        locAndOffsets.off_y    = animParams.offsetY;
        var lights = FindLights(locAndOffsets, lightSearchRadius);

        if (type == ObjectType.weapon)
        {
            int glowType;
            if (flags.HasFlag(ObjectFlag.INVENTORY) && parent != null)
            {
                glowType = GameSystems.D20.GetWeaponGlowType(parent, obj);
            }
            else
            {
                glowType = GameSystems.D20.GetWeaponGlowType(null, obj);
            }

            if (glowType != 0 && glowType <= mGlowMaterials.Length)
            {
                var glowMaterial = mGlowMaterials[glowType - 1];
                if (glowMaterial.IsValid)
                {
                    RenderObjectHighlight(viewport, obj, glowMaterial);
                }
            }
        }

        if (GameSystems.ItemHighlight.ShowHighlights &&
            (type.IsEquipment() &&
             !(flags.HasFlag(ObjectFlag.INVENTORY) || flags.HasFlag(ObjectFlag.CLICK_THROUGH)) ||
             GameSystems.Critter.IsLootableCorpse(obj) ||
             type == ObjectType.portal))
        {
            RenderObjectHighlight(viewport, obj, mHighlightMaterial);

            // Add a single light with full ambient color to make the object appear fully lit
            lights.Clear();
            Light3d fullBrightLight = new Light3d();
            fullBrightLight.ambient = LinearColor.White;
            fullBrightLight.color   = new LinearColor(0, 0, 0);
            fullBrightLight.dir     = Vector4.UnitZ;
            fullBrightLight.type    = Light3dType.Directional;
            lights.Add(fullBrightLight);
        }

        mRenderedLastFrame++;
        MdfRenderOverrides overrides = new MdfRenderOverrides();

        overrides.alpha = alpha / 255.0f;
        mAasRenderer.Render(viewport, animatedModel, animParams, lights, overrides);

        Light3d globalLight = new Light3d();

        if (lights.Count > 0)
        {
            globalLight = lights[0];
        }

        if (type.IsCritter())
        {
            if (alpha > 16)
            {
                if (mShadowType == ShadowType.ShadowMap)
                {
                    RenderShadowMapShadow(viewport, obj, animParams, animatedModel, globalLight, alpha);
                }
                else if (mShadowType == ShadowType.Geometry)
                {
                    mAasRenderer.RenderGeometryShadow(
                        viewport.Camera,
                        animatedModel,
                        animParams,
                        globalLight,
                        alpha / 255.0f);
                }
                else if (mShadowType == ShadowType.Blob)
                {
                    RenderBlobShadow(viewport, obj, animatedModel, ref animParams, alpha);
                }
            }

            /*
             * This renders the equipment in a critter's hand separately, but
             * I am not certain *why* exactly. I thought this would have been
             * handled by addmeshes, but it might be that there's a distinct
             * difference between addmeshes that are skinned onto the mobile's
             * skeleton and equipment that is unskinned and just positioned
             * in the player's hands.
             */
            var weaponPrim = GameSystems.Critter.GetWornItem(obj, EquipSlot.WeaponPrimary);
            if (weaponPrim != null)
            {
                RenderObject(viewport, weaponPrim, showInvisible);
            }

            var weaponSec = GameSystems.Critter.GetWornItem(obj, EquipSlot.WeaponSecondary);
            if (weaponSec != null)
            {
                RenderObject(viewport, weaponSec, showInvisible);
            }

            var shield = GameSystems.Critter.GetWornItem(obj, EquipSlot.Shield);
            if (shield != null)
            {
                RenderObject(viewport, shield, showInvisible);
            }
        }
        else if (type.IsEquipment() && mShadowType == ShadowType.Geometry)
        {
            mAasRenderer.RenderGeometryShadow(
                viewport.Camera,
                animatedModel,
                animParams,
                globalLight,
                alpha / 255.0f);
        }

        RenderMirrorImages(
            viewport,
            obj,
            animParams,
            animatedModel,
            lights);

        if (mGrappleController.IsGiantFrog(obj))
        {
            mGrappleController.AdvanceAndRender(
                viewport,
                obj,
                animParams,
                animatedModel,
                lights,
                alpha / 255.0f);
        }
    }
예제 #7
0
    public void RenderObjectHighlight(IGameViewport viewport, GameObject obj,
                                      ResourceRef <IMdfRenderMaterial> material)
    {
        mTotalLastFrame++;

        var type  = obj.type;
        var flags = obj.GetFlags();

        // Dont render destroyed or disabled objects
        const ObjectFlag dontDrawFlags = ObjectFlag.OFF | ObjectFlag.DESTROYED | ObjectFlag.DONTDRAW;

        if ((flags & dontDrawFlags) != 0)
        {
            return;
        }

        // Hide invisible objects we're supposed to show them
        if (flags.HasFlag(ObjectFlag.INVISIBLE))
        {
            return;
        }

        // Dont draw secret doors that haven't been found yet
        var secretDoorFlags = obj.GetSecretDoorFlags();

        if (secretDoorFlags.HasFlag(SecretDoorFlag.SECRET_DOOR))
        {
            var found = secretDoorFlags.HasFlag(SecretDoorFlag.SECRET_DOOR_FOUND);
            if (!found && type != ObjectType.portal)
            {
                return;
            }
        }

        var animatedModel = obj.GetOrCreateAnimHandle();
        var animParams    = obj.GetAnimParams();

        locXY worldLoc;

        GameObject parent = null;

        if (type.IsEquipment())
        {
            parent = GameSystems.Item.GetParent(obj);
        }

        var alpha = GetAlpha(obj);

        if (parent != null)
        {
            var parentAlpha = GetAlpha(parent);
            alpha = (alpha + parentAlpha) / 2;

            worldLoc = parent.GetLocation();
        }
        else
        {
            worldLoc = obj.GetLocation();
        }

        if (alpha == 0)
        {
            return;
        }

        // Handle fog occlusion of the world position
        if (type != ObjectType.container &&
            (type == ObjectType.projectile ||
             type.IsCritter() ||
             type.IsEquipment()) &&
            (GameSystems.MapFogging.GetFogStatus(worldLoc, animParams.offsetX, animParams.offsetY) & 1) == 0)
        {
            return;
        }

        LocAndOffsets worldPosFull;

        worldPosFull.off_x    = animParams.offsetX;
        worldPosFull.off_y    = animParams.offsetY;
        worldPosFull.location = worldLoc;

        var radius       = obj.GetRadius();
        var renderHeight = obj.GetRenderHeight(true);

        if (!IsObjectOnScreen(viewport.Camera, worldPosFull, animParams.offsetZ, radius, renderHeight))
        {
            return;
        }

        var lightSearchRadius = 0.0f;

        if (!flags.HasFlag(ObjectFlag.DONTLIGHT))
        {
            lightSearchRadius = radius;
        }

        LocAndOffsets locAndOffsets;

        locAndOffsets.location = worldLoc;
        locAndOffsets.off_x    = animParams.offsetX;
        locAndOffsets.off_y    = animParams.offsetY;
        var lights = FindLights(locAndOffsets, lightSearchRadius);

        mRenderedLastFrame++;

        MdfRenderOverrides overrides = new MdfRenderOverrides();

        overrides.alpha = alpha / 255.0f;
        material.Resource.Bind(viewport, mDevice, lights, overrides);
        mAasRenderer.RenderWithoutMaterial(animatedModel, animParams);
    }
예제 #8
0
    public void RenderOccludedObject(IGameViewport viewport, GameObject obj)
    {
        mTotalLastFrame++;

        var type  = obj.type;
        var flags = obj.GetFlags();

        // Dont render destroyed or disabled objects
        const ObjectFlag dontDrawFlags = ObjectFlag.OFF | ObjectFlag.DESTROYED | ObjectFlag.DONTDRAW;

        if ((flags & dontDrawFlags) != 0)
        {
            return;
        }

        if (flags.HasFlag(ObjectFlag.INVISIBLE) || flags.HasFlag(ObjectFlag.INVENTORY))
        {
            return;
        }

        switch (type)
        {
        case ObjectType.scenery:
        case ObjectType.trap:
            return;

        case ObjectType.pc:
        case ObjectType.npc:
            if (GameSystems.Critter.IsConcealed(obj))
            {
                return;
            }

            break;

        default:
            break;
        }

        // Dont draw secret doors that haven't been found yet
        var secretDoorFlags = obj.GetSecretDoorFlags();

        if (secretDoorFlags.HasFlag(SecretDoorFlag.SECRET_DOOR))
        {
            var found = ((secretDoorFlags & SecretDoorFlag.SECRET_DOOR_FOUND) != 0);
            if (!found && type != ObjectType.portal)
            {
                return;
            }
        }

        var animatedModel = obj.GetOrCreateAnimHandle();

        var animParams = obj.GetAnimParams();

        locXY worldLoc;

        GameObject parent = null;

        if (type.IsEquipment())
        {
            parent = GameSystems.Item.GetParent(obj);
        }

        var alpha = GetAlpha(obj);

        if (parent != null)
        {
            var parentAlpha = GetAlpha(parent);
            alpha = (alpha + parentAlpha) / 2;

            worldLoc = parent.GetLocation();
        }
        else
        {
            worldLoc = obj.GetLocation();
        }

        if (alpha == 0)
        {
            return;
        }

        // Handle fog occlusion of the world position, but handle it differently for portals
        if (type != ObjectType.portal)
        {
            var fogStatus = GameSystems.MapFogging.GetFogStatus(worldLoc, animParams.offsetX, animParams.offsetY);
            if ((fogStatus & 0xB0) == 0 || (fogStatus & 1) == 0)
            {
                return;
            }
        }
        else
        {
            LocAndOffsets loc;
            loc.location = worldLoc;
            loc.off_x    = animParams.offsetX - locXY.INCH_PER_SUBTILE;
            loc.off_y    = animParams.offsetY - locXY.INCH_PER_SUBTILE;
            loc.Normalize();

            var fogStatus = GameSystems.MapFogging.GetFogStatus(loc.location, loc.off_x, loc.off_y);
            if ((fogStatus & 0xB0) == 0 || (fogStatus & 1) == 0)
            {
                return;
            }
        }

        LocAndOffsets worldPosFull;

        worldPosFull.off_x    = animParams.offsetX;
        worldPosFull.off_y    = animParams.offsetY;
        worldPosFull.location = worldLoc;

        var radius       = obj.GetRadius();
        var renderHeight = obj.GetRenderHeight(true);

        if (!IsObjectOnScreen(viewport.Camera, worldPosFull, animParams.offsetZ, radius, renderHeight))
        {
            return;
        }

        var lightSearchRadius = 0.0f;

        if (!flags.HasFlag(ObjectFlag.DONTLIGHT))
        {
            lightSearchRadius = radius;
        }

        LocAndOffsets locAndOffsets;

        locAndOffsets.location = worldLoc;
        locAndOffsets.off_x    = animParams.offsetX;
        locAndOffsets.off_y    = animParams.offsetY;
        var lights = FindLights(locAndOffsets, lightSearchRadius);

        mRenderedLastFrame++;
        MdfRenderOverrides overrides = new MdfRenderOverrides();

        overrides.alpha = alpha / 255.0f;

        if (type != ObjectType.portal)
        {
            mOccludedMaterial.Resource.Bind(viewport, mDevice, lights, overrides);
            mAasRenderer.RenderWithoutMaterial(animatedModel, animParams);

            if (type.IsCritter())
            {
                /*
                 * This renders the equipment in a critter's hand separately, but
                 * I am not certain *why* exactly. I thought this would have been
                 * handled by addmeshes, but it might be that there's a distinct
                 * difference between addmeshes that are skinned onto the mobile's
                 * skeleton and equipment that is unskinned and just positioned
                 * in the player's hands.
                 */
                var weaponPrim = GameSystems.Critter.GetWornItem(obj, EquipSlot.WeaponPrimary);
                if (weaponPrim != null)
                {
                    RenderOccludedObject(viewport, weaponPrim);
                }

                var weaponSec = GameSystems.Critter.GetWornItem(obj, EquipSlot.WeaponSecondary);
                if (weaponSec != null)
                {
                    RenderOccludedObject(viewport, weaponSec);
                }

                var shield = GameSystems.Critter.GetWornItem(obj, EquipSlot.Shield);
                if (shield != null)
                {
                    RenderOccludedObject(viewport, shield);
                }
            }
        }
        else
        {
            if (GameSystems.ItemHighlight.ShowHighlights)
            {
                overrides.ignoreLighting = true;
            }

            mAasRenderer.Render(viewport, animatedModel, animParams, lights, overrides);
        }
    }
예제 #9
0
    private void BindShader([MaybeNull] WorldCamera camera,
                            RenderingDevice device,
                            IList <Light3d> lights,
                            MdfRenderOverrides overrides)
    {
        // Fill out the globals for the shader
        var globals = new MdfGlobalConstants();

        Matrix4x4 viewProj;

        if (overrides != null && overrides.uiProjection)
        {
            viewProj = device.UiProjection;
        }
        else
        {
            viewProj = camera?.GetViewProj() ?? device.UiProjection;
        }

        // Should we use a separate world matrix?
        if (overrides != null && overrides.useWorldMatrix)
        {
            // Build a world * view * proj matrix
            var worldViewProj = overrides.worldMatrix * viewProj;
            globals.viewProj = worldViewProj;
        }
        else
        {
            globals.viewProj = viewProj;
        }

        // Set material diffuse color for shader
        Vector4 color;

        // TODO: This is a bug, it should check overrideDiffuse
        if (overrides != null && overrides.overrideColor != default)
        {
            color = overrides.overrideColor.ToRGBA();
        }
        else
        {
            color = new PackedLinearColorA(mSpec.diffuse).ToRGBA();
        }

        globals.matDiffuse = color;
        if (overrides != null && overrides.alpha != 1.0f)
        {
            globals.matDiffuse.W *= overrides.alpha;
        }

        // Set time for UV animation in minutes as a floating point number
        var timeInSec = (float)(device.GetLastFrameStart() - device.GetDeviceCreated()).Seconds;

        globals.uvAnimTime.X = timeInSec / 60.0f;
        // Clamp to [0, 1]
        if (globals.uvAnimTime.X > 1)
        {
            globals.uvAnimTime.X -= MathF.Floor(globals.uvAnimTime.X);
        }

        // Swirl is more complicated due to cos/sin involvement
        // This means speedU is in "full rotations every 60 seconds" . RPM
        var uvRotations = globals.UvRotations;

        for (var i = 0; i < mSpec.samplers.Count; ++i)
        {
            var sampler = mSpec.samplers[i];
            if (sampler.uvType != MdfUvType.Swirl)
            {
                continue;
            }

            ref var uvRot = ref uvRotations[i];
            uvRot.X = MathF.Cos(sampler.speedU * globals.uvAnimTime.X * MathF.PI * 2) * 0.1f;
            uvRot.Y = MathF.Sin(sampler.speedV * globals.uvAnimTime.X * MathF.PI * 2) * 0.1f;
        }