/*
* 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.logic.location;
import com.google.common.collect.Lists;
import org.terasology.entitySystem.Component;
import org.terasology.entitySystem.entity.EntityRef;
import org.terasology.math.Direction;
import org.terasology.math.geom.Quat4f;
import org.terasology.math.geom.Vector3f;
import org.terasology.network.Replicate;
import org.terasology.network.ReplicationCheck;
import org.terasology.reflection.metadata.FieldMetadata;
import org.terasology.rendering.nui.properties.TextField;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
/**
* Component represent the location and facing of an entity in the world
*
*/
public final class LocationComponent implements Component, ReplicationCheck {
public boolean replicateChanges = true;
// Relative to
@Replicate
EntityRef parent = EntityRef.NULL;
@Replicate
List<EntityRef> children = Lists.newArrayList();
// Standard position/rotation
@Replicate
@TextField
Vector3f position = new Vector3f();
@Replicate
Quat4f rotation = new Quat4f(0, 0, 0, 1);
@Replicate
float scale = 1.0f;
public LocationComponent() {
}
public LocationComponent(Vector3f position) {
this.position.set(position);
}
/**
* @return The position of this component relative to any parent. Can be directly modified to update the component
*/
public Vector3f getLocalPosition() {
return position;
}
public void setLocalPosition(Vector3f newPos) {
position.set(newPos);
}
public Vector3f getLocalDirection() {
Vector3f result = Direction.FORWARD.getVector3f();
getLocalRotation().rotate(result, result);
return result;
}
public Quat4f getLocalRotation() {
return rotation;
}
public void setLocalRotation(Quat4f newQuat) {
rotation.set(newQuat);
}
public void setLocalScale(float value) {
this.scale = value;
}
public float getLocalScale() {
return scale;
}
/**
* @return A new vector containing the world location.
*/
public Vector3f getWorldPosition() {
return getWorldPosition(new Vector3f());
}
public Vector3f getWorldPosition(Vector3f output) {
output.set(position);
LocationComponent parentLoc = parent.getComponent(LocationComponent.class);
while (parentLoc != null) {
output.scale(parentLoc.scale);
parentLoc.getLocalRotation().rotate(output, output);
output.add(parentLoc.position);
parentLoc = parentLoc.parent.getComponent(LocationComponent.class);
}
return output;
}
public Vector3f getWorldDirection() {
Vector3f result = Direction.FORWARD.getVector3f();
getWorldRotation().rotate(result, result);
return result;
}
public Quat4f getWorldRotation() {
return getWorldRotation(new Quat4f(0, 0, 0, 1));
}
public Quat4f getWorldRotation(Quat4f output) {
output.set(rotation);
LocationComponent parentLoc = parent.getComponent(LocationComponent.class);
while (parentLoc != null) {
output.mul(parentLoc.rotation, output);
parentLoc = parentLoc.parent.getComponent(LocationComponent.class);
}
return output;
}
public float getWorldScale() {
float result = scale;
LocationComponent parentLoc = parent.getComponent(LocationComponent.class);
while (parentLoc != null) {
result *= parentLoc.getLocalScale();
parentLoc = parentLoc.parent.getComponent(LocationComponent.class);
}
return result;
}
public void setWorldPosition(Vector3f value) {
this.position.set(value);
LocationComponent parentLoc = parent.getComponent(LocationComponent.class);
if (parentLoc != null) {
this.position.sub(parentLoc.getWorldPosition());
this.position.scale(1f / parentLoc.getWorldScale());
Quat4f rot = new Quat4f(0, 0, 0, 1);
rot.inverse(parentLoc.getWorldRotation());
rot.rotate(this.position, this.position);
}
}
public void setWorldRotation(Quat4f value) {
this.rotation.set(value);
LocationComponent parentLoc = parent.getComponent(LocationComponent.class);
if (parentLoc != null) {
Quat4f worldRot = parentLoc.getWorldRotation();
worldRot.inverse();
this.rotation.mul(worldRot, this.rotation);
}
}
public void setWorldScale(float value) {
this.scale = value;
LocationComponent parentLoc = parent.getComponent(LocationComponent.class);
if (parentLoc != null) {
this.scale /= parentLoc.getWorldScale();
}
}
public EntityRef getParent() {
return parent;
}
public Collection<EntityRef> getChildren() {
return children;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof LocationComponent) {
LocationComponent other = (LocationComponent) o;
return other.scale == scale && Objects.equals(parent, other.parent) && Objects.equals(position, other.position) && Objects.equals(rotation, other.rotation);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(position, rotation, scale, parent);
}
@Override
public boolean shouldReplicate(FieldMetadata<?, ?> field, boolean initial, boolean toOwner) {
return initial || replicateChanges;
}
}