Package org.terasology.rendering.logic

Source Code of org.terasology.rendering.logic.SkeletonRenderer

/*
* Copyright 2014 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.rendering.logic;

import com.bulletphysics.linearmath.QuaternionUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.asset.Assets;
import org.terasology.config.Config;
import org.terasology.entitySystem.entity.EntityManager;
import org.terasology.entitySystem.entity.EntityRef;
import org.terasology.entitySystem.entity.lifecycleEvents.OnActivatedComponent;
import org.terasology.entitySystem.event.ReceiveEvent;
import org.terasology.entitySystem.systems.BaseComponentSystem;
import org.terasology.entitySystem.systems.RegisterMode;
import org.terasology.entitySystem.systems.RegisterSystem;
import org.terasology.entitySystem.systems.RenderSystem;
import org.terasology.entitySystem.systems.UpdateSubscriberSystem;
import org.terasology.logic.location.Location;
import org.terasology.logic.location.LocationComponent;
import org.terasology.math.MatrixUtils;
import org.terasology.registry.In;
import org.terasology.rendering.assets.animation.MeshAnimation;
import org.terasology.rendering.assets.animation.MeshAnimationFrame;
import org.terasology.rendering.assets.material.Material;
import org.terasology.rendering.assets.skeletalmesh.Bone;
import org.terasology.rendering.opengl.OpenGLSkeletalMesh;
import org.terasology.rendering.world.WorldRenderer;

import javax.vecmath.Matrix4f;
import javax.vecmath.Quat4f;
import javax.vecmath.Vector3f;
import java.nio.FloatBuffer;
import java.util.List;

import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
import static org.lwjgl.opengl.GL11.glBegin;
import static org.lwjgl.opengl.GL11.glDisable;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.glEnd;
import static org.lwjgl.opengl.GL11.glLineWidth;
import static org.lwjgl.opengl.GL11.glPopMatrix;
import static org.lwjgl.opengl.GL11.glPushMatrix;
import static org.lwjgl.opengl.GL11.glVertex3f;

/**
* @author Immortius
*/
@RegisterSystem(RegisterMode.CLIENT)
public class SkeletonRenderer extends BaseComponentSystem implements RenderSystem, UpdateSubscriberSystem {

    private static final Logger logger = LoggerFactory.getLogger(SkeletonRenderer.class);

    @In
    private EntityManager entityManager;

    @In
    private WorldRenderer worldRenderer;

    @In
    private Config config;

    @ReceiveEvent(components = {SkeletalMeshComponent.class, LocationComponent.class})
    public void newSkeleton(OnActivatedComponent event, EntityRef entity) {
        SkeletalMeshComponent skeleton = entity.getComponent(SkeletalMeshComponent.class);
        if (skeleton.mesh == null) {
            return;
        }

        if (skeleton.boneEntities == null) {
            skeleton.boneEntities = Maps.newHashMap();
            for (Bone bone : skeleton.mesh.getBones()) {
                LocationComponent loc = new LocationComponent();
                EntityRef parent = (bone.getParent() != null) ? skeleton.boneEntities.get(bone.getParent().getName()) : entity;
                EntityRef boneEntity = entityManager.create(loc);
                Location.attachChild(parent, boneEntity);
                loc.setLocalPosition(bone.getLocalPosition());
                loc.setLocalRotation(bone.getLocalRotation());

                if (bone.getParent() == null) {
                    skeleton.rootBone = boneEntity;
                }
                skeleton.boneEntities.put(bone.getName(), boneEntity);
            }
            entity.saveComponent(skeleton);
        }
    }

    @Override
    public void update(float delta) {
        for (EntityRef entity : entityManager.getEntitiesWith(SkeletalMeshComponent.class, LocationComponent.class)) {
            SkeletalMeshComponent skeletalMeshComp = entity.getComponent(SkeletalMeshComponent.class);
            if (skeletalMeshComp.animation != null && skeletalMeshComp.animation.getFrameCount() > 0) {
                skeletalMeshComp.animationTime += delta * skeletalMeshComp.animationRate;
                float framePos = skeletalMeshComp.animationTime / skeletalMeshComp.animation.getTimePerFrame();

                if (skeletalMeshComp.loop) {
                    while ((int) framePos >= skeletalMeshComp.animation.getFrameCount()) {
                        framePos -= skeletalMeshComp.animation.getFrameCount();
                        skeletalMeshComp.animationTime -= skeletalMeshComp.animation.getTimePerFrame() * skeletalMeshComp.animation.getFrameCount();
                    }
                    int frameId = (int) framePos;
                    MeshAnimationFrame frameA = skeletalMeshComp.animation.getFrame(frameId);
                    MeshAnimationFrame frameB = skeletalMeshComp.animation.getFrame((frameId + 1) % skeletalMeshComp.animation.getFrameCount());
                    updateSkeleton(skeletalMeshComp, frameA, frameB, framePos - frameId);
                } else {
                    if ((int) framePos >= skeletalMeshComp.animation.getFrameCount()) {
                        MeshAnimationFrame frame = skeletalMeshComp.animation.getFrame(skeletalMeshComp.animation.getFrameCount() - 1);
                        updateSkeleton(skeletalMeshComp, frame, frame, 1.0f);
                        MeshAnimation animation = skeletalMeshComp.animation;
                        skeletalMeshComp.animationTime = 0;
                        skeletalMeshComp.animation = null;
                        entity.send(new AnimEndEvent(animation));
                    } else {
                        int frameId = (int) framePos;
                        MeshAnimationFrame frameA = skeletalMeshComp.animation.getFrame(frameId);
                        MeshAnimationFrame frameB = (frameId + 1 >= skeletalMeshComp.animation.getFrameCount()) ? frameA : skeletalMeshComp.animation.getFrame(frameId + 1);
                        updateSkeleton(skeletalMeshComp, frameA, frameB, framePos - frameId);
                    }
                }
                entity.saveComponent(skeletalMeshComp);
            }
        }
    }

    private void updateSkeleton(SkeletalMeshComponent skeletalMeshComp, MeshAnimationFrame frameA, MeshAnimationFrame frameB, float interpolationVal) {
        Vector3f newPos = new Vector3f();
        Quat4f newRot = new Quat4f();

        for (int i = 0; i < skeletalMeshComp.animation.getBoneCount(); ++i) {
            EntityRef boneEntity = skeletalMeshComp.boneEntities.get(skeletalMeshComp.animation.getBoneName(i));
            if (boneEntity == null) {
                continue;
            }
            LocationComponent boneLoc = boneEntity.getComponent(LocationComponent.class);
            if (boneLoc != null) {

                newPos.interpolate(frameA.getPosition(i), frameB.getPosition(i), interpolationVal);
                boneLoc.setLocalPosition(newPos);
                newRot.interpolate(frameA.getRotation(i), frameB.getRotation(i), interpolationVal);
                newRot.normalize();
                boneLoc.setLocalRotation(newRot);
                boneEntity.saveComponent(boneLoc);
            }
        }
    }

    @Override
    public void renderOpaque() {
        Vector3f cameraPosition = worldRenderer.getActiveCamera().getPosition();

        Quat4f worldRot = new Quat4f();
        Vector3f worldPos = new Vector3f();
        Quat4f inverseWorldRot = new Quat4f();
        Matrix4f matrixCameraSpace = new Matrix4f();

        FloatBuffer tempMatrixBuffer44 = BufferUtils.createFloatBuffer(16);
        FloatBuffer tempMatrixBuffer33 = BufferUtils.createFloatBuffer(12);

        for (EntityRef entity : entityManager.getEntitiesWith(SkeletalMeshComponent.class, LocationComponent.class)) {
            SkeletalMeshComponent skeletalMesh = entity.getComponent(SkeletalMeshComponent.class);
            if (skeletalMesh.mesh == null || skeletalMesh.material == null || skeletalMesh.boneEntities == null) {
                continue;
            }
            skeletalMesh.material.enable();
            skeletalMesh.material.setFloat("sunlight", 1.0f, true);
            skeletalMesh.material.setFloat("blockLight", 1.0f, true);

            skeletalMesh.material.setMatrix4("projectionMatrix", worldRenderer.getActiveCamera().getProjectionMatrix());
            skeletalMesh.material.bindTextures();

            LocationComponent location = entity.getComponent(LocationComponent.class);

            location.getWorldRotation(worldRot);
            inverseWorldRot.inverse(worldRot);
            location.getWorldPosition(worldPos);

            Vector3f worldPositionCameraSpace = new Vector3f();
            worldPositionCameraSpace.sub(worldPos, cameraPosition);

            worldPos.y -= skeletalMesh.heightOffset;

            float worldScale = location.getWorldScale();
            matrixCameraSpace.set(worldRot, worldPositionCameraSpace, worldScale);

            Matrix4f modelViewMatrix = MatrixUtils.calcModelViewMatrix(worldRenderer.getActiveCamera().getViewMatrix(), matrixCameraSpace);
            MatrixUtils.matrixToFloatBuffer(modelViewMatrix, tempMatrixBuffer44);

            skeletalMesh.material.setMatrix4("worldViewMatrix", tempMatrixBuffer44, true);

            MatrixUtils.matrixToFloatBuffer(MatrixUtils.calcNormalMatrix(modelViewMatrix), tempMatrixBuffer33);
            skeletalMesh.material.setMatrix3("normalMatrix", tempMatrixBuffer33, true);

            skeletalMesh.material.setFloat("sunlight", worldRenderer.getSunlightValueAt(worldPos), true);
            skeletalMesh.material.setFloat("blockLight", worldRenderer.getBlockLightValueAt(worldPos), true);

            // TODO: Add frustum culling here
            List<Vector3f> bonePositions = Lists.newArrayListWithCapacity(skeletalMesh.mesh.getVertexCount());
            List<Quat4f> boneRotations = Lists.newArrayListWithCapacity(skeletalMesh.mesh.getVertexCount());
            for (Bone bone : skeletalMesh.mesh.getBones()) {
                EntityRef boneEntity = skeletalMesh.boneEntities.get(bone.getName());
                if (boneEntity == null) {
                    boneEntity = EntityRef.NULL;
                }
                LocationComponent boneLocation = boneEntity.getComponent(LocationComponent.class);
                if (boneLocation != null) {
                    Vector3f pos = boneLocation.getWorldPosition();
                    pos.sub(worldPos);
                    QuaternionUtil.quatRotate(inverseWorldRot, pos, pos);
                    bonePositions.add(pos);
                    Quat4f rot = new Quat4f();
                    rot.mul(inverseWorldRot, boneLocation.getWorldRotation());
                    boneRotations.add(rot);
                } else {
                    logger.warn("Unable to resolve bone \"{}\"", bone.getName());
                    bonePositions.add(new Vector3f());
                    boneRotations.add(new Quat4f());
                }
            }
            ((OpenGLSkeletalMesh) skeletalMesh.mesh).setScaleTranslate(skeletalMesh.scale, skeletalMesh.translate);
            ((OpenGLSkeletalMesh) skeletalMesh.mesh).render(bonePositions, boneRotations);
        }
    }

    @Override
    public void renderAlphaBlend() {
    }

    @Override
    public void renderOverlay() {
        if (config.getRendering().getDebug().isRenderSkeletons()) {
            glDisable(GL_DEPTH_TEST);
            Vector3f cameraPosition = worldRenderer.getActiveCamera().getPosition();
            Material material = Assets.getMaterial("engine:white");
            material.setFloat("sunlight", 1.0f, true);
            material.setFloat("blockLight", 1.0f, true);
            material.setMatrix4("projectionMatrix", worldRenderer.getActiveCamera().getProjectionMatrix());
            glLineWidth(2);
            Vector3f worldPos = new Vector3f();
            Matrix4f matrixCameraSpace = new Matrix4f();


            FloatBuffer tempMatrixBuffer44 = BufferUtils.createFloatBuffer(16);
            FloatBuffer tempMatrixBuffer33 = BufferUtils.createFloatBuffer(12);

            for (EntityRef entity : entityManager.getEntitiesWith(SkeletalMeshComponent.class, LocationComponent.class)) {
                LocationComponent location = entity.getComponent(LocationComponent.class);

                location.getWorldPosition(worldPos);

                Vector3f worldPositionCameraSpace = new Vector3f();
                worldPositionCameraSpace.sub(worldPos, cameraPosition);

                float worldScale = location.getWorldScale();
                matrixCameraSpace.set(new Quat4f(0, 0, 0, 1), worldPositionCameraSpace, worldScale);

                Matrix4f modelViewMatrix = MatrixUtils.calcModelViewMatrix(worldRenderer.getActiveCamera().getViewMatrix(), matrixCameraSpace);
                MatrixUtils.matrixToFloatBuffer(modelViewMatrix, tempMatrixBuffer44);

                material.setMatrix4("worldViewMatrix", tempMatrixBuffer44, true);

                MatrixUtils.matrixToFloatBuffer(MatrixUtils.calcNormalMatrix(modelViewMatrix), tempMatrixBuffer33);
                material.setMatrix3("normalMatrix", tempMatrixBuffer33, true);

                SkeletalMeshComponent skeletalMesh = entity.getComponent(SkeletalMeshComponent.class);
                renderBone(skeletalMesh.rootBone, worldPos);
            }
            glEnable(GL_DEPTH_TEST);
        }
    }

    @Override
    public void renderFirstPerson() {
    }

    @Override
    public void renderShadows() {
    }

    private void renderBoneOrientation(EntityRef boneEntity) {
        LocationComponent loc = boneEntity.getComponent(LocationComponent.class);
        if (loc == null) {
            return;
        }
        glPushMatrix();
        Vector3f worldPosA = loc.getWorldPosition();
        Quat4f worldRot = loc.getWorldRotation();
        Vector3f offset = new Vector3f(0, 0, 0.1f);
        QuaternionUtil.quatRotate(worldRot, offset, offset);
        offset.add(worldPosA);

        glBegin(GL11.GL_LINES);
        glVertex3f(worldPosA.x, worldPosA.y, worldPosA.z);
        glVertex3f(offset.x, offset.y, offset.z);
        glEnd();

        for (EntityRef child : loc.getChildren()) {
            renderBoneOrientation(child);
        }
        glPopMatrix();
    }

    private void renderBone(EntityRef boneEntity, Vector3f centerPos) {
        LocationComponent loc = boneEntity.getComponent(LocationComponent.class);
        if (loc == null) {
            return;
        }
        LocationComponent parentLoc = loc.getParent().getComponent(LocationComponent.class);
        if (parentLoc != null) {
            Vector3f worldPosA = loc.getWorldPosition();
            worldPosA.sub(centerPos);
            Vector3f worldPosB = parentLoc.getWorldPosition();
            worldPosB.sub(centerPos);

            glBegin(GL11.GL_LINES);
            glVertex3f(worldPosA.x, worldPosA.y, worldPosA.z);
            glVertex3f(worldPosB.x, worldPosB.y, worldPosB.z);
            glEnd();

            for (EntityRef child : loc.getChildren()) {
                renderBone(child, centerPos);
            }
        }
    }
}
TOP

Related Classes of org.terasology.rendering.logic.SkeletonRenderer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.