mirror of
https://github.com/Retera/WarsmashModEngine.git
synced 2022-07-31 17:38:59 +02:00
Update with lighting system, and after test playing Projection Revolution.
This commit is contained in:
parent
b0b0745af9
commit
843b04b4a8
@ -1,24 +1,26 @@
|
||||
// This is the Warsmash INI file for Project Revolution
|
||||
// PRSCMOD
|
||||
|
||||
[DataSources]
|
||||
Count=7
|
||||
Count=9
|
||||
Type00=MPQ
|
||||
Path00="E:\Games\Warcraft III Patch 1.22\war3.mpq"
|
||||
Path00="E:\Games\Warcraft III Project Revolution\War3\The Sheep Attack\war3.mpq"
|
||||
Type01=MPQ
|
||||
Path01="E:\Games\Warcraft III Patch 1.22\War3x.mpq"
|
||||
Path01="E:\Games\Warcraft III Project Revolution\War3\The Sheep Attack\War3x.mpq"
|
||||
Type02=MPQ
|
||||
Path02="E:\Games\Warcraft III Patch 1.22\War3xlocal.mpq"
|
||||
Path02="E:\Games\Warcraft III Project Revolution\War3\The Sheep Attack\War3xlocal.mpq"
|
||||
Type03=MPQ
|
||||
Path03="E:\Games\Warcraft III Patch 1.22\War3Patch.mpq"
|
||||
Type04=Folder
|
||||
Path04="..\..\resources"
|
||||
Type05=Folder
|
||||
Path05="E:\Backups\Warsmash\Data"
|
||||
Path03="E:\Games\Warcraft III Project Revolution\War3\The Sheep Attack\war3patch.mpq"
|
||||
Type04=MPQ
|
||||
Path04="E:\Games\Warcraft III Project Revolution\PRSCMOD\Revolution.mpq"
|
||||
Type05=MPQ
|
||||
Path05="E:\Games\Warcraft III Project Revolution\PRSCMOD\Sound.mpq"
|
||||
Type06=Folder
|
||||
Path06="."
|
||||
Path06="E:\Games\Warcraft III Project Revolution\ProjectRevolusmash"
|
||||
Type07=Folder
|
||||
Path07="..\..\resources"
|
||||
Type08=Folder
|
||||
Path08="E:\Games\Warcraft III Project Revolution\PRSCMOD\PR-Maps"
|
||||
|
||||
[Map]
|
||||
//FilePath="CombatUnitTests.w3x"
|
||||
//FilePath="PitchRoll.w3x"
|
||||
FilePath="PeonStartingBase.w3x"
|
||||
//FilePath="PlayerPeasants.w3m"
|
||||
//FilePath="FireLord.w3x"
|
||||
//FilePath="Maps\Campaign\NightElf03.w3m"
|
||||
FilePath="ProjectRevolusmash.w3x"
|
25
core/assets/warsmash127.ini
Normal file
25
core/assets/warsmash127.ini
Normal file
@ -0,0 +1,25 @@
|
||||
[DataSources]
|
||||
Count=7
|
||||
Type00=MPQ
|
||||
Path00="E:\Games\Warcraft III Patch 1.22\war3.mpq"
|
||||
Type01=MPQ
|
||||
Path01="E:\Games\Warcraft III Patch 1.22\War3x.mpq"
|
||||
Type02=MPQ
|
||||
Path02="E:\Games\Warcraft III Patch 1.22\War3xlocal.mpq"
|
||||
Type03=MPQ
|
||||
Path03="E:\Games\Warcraft III Patch 1.22\War3Patch.mpq"
|
||||
Type04=Folder
|
||||
Path04="..\..\resources"
|
||||
Type05=Folder
|
||||
Path05="E:\Backups\Warsmash\Data"
|
||||
Type06=Folder
|
||||
Path06="."
|
||||
|
||||
[Map]
|
||||
//FilePath="CombatUnitTests.w3x"
|
||||
//FilePath="PitchRoll.w3x"
|
||||
FilePath="PeonStartingBase.w3x"
|
||||
//FilePath="DungeonGoldMine.w3m"
|
||||
//FilePath="PlayerPeasants.w3m"
|
||||
//FilePath="FireLord.w3x"
|
||||
//FilePath="Maps\Campaign\NightElf03.w3m"
|
@ -12,6 +12,10 @@ Type04=Folder
|
||||
Path04="."
|
||||
|
||||
[Map]
|
||||
FilePath="PitchRoll.w3x"
|
||||
//FilePath="PitchRoll.w3x"
|
||||
//FilePath="ReforgedGeorgeVacation.w3x"
|
||||
//FilePath="Maps\Campaign\NightElf03.w3m"
|
||||
//FilePath="Maps\Campaign\NightElf03.w3m"
|
||||
//FilePath="PrivateDontShare/Cult 8.w3x"
|
||||
//FilePath="TorchLight2.w3x"
|
||||
FilePath="OrcAssault.w3x"
|
||||
//FilePath="PeonStartingBase.w3x"
|
||||
|
@ -14,6 +14,7 @@ import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input;
|
||||
import com.badlogic.gdx.InputProcessor;
|
||||
import com.badlogic.gdx.audio.Music;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.GL30;
|
||||
@ -227,15 +228,10 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
|
||||
Gdx.input.setInputProcessor(this);
|
||||
|
||||
// final Music music = Gdx.audio
|
||||
// .newMusic(new DataSourceFileHandle(this.viewer.dataSource, "Sound\\Music\\mp3Music\\Undead2.mp3"));
|
||||
// music.setVolume(0.2f);
|
||||
// music.setLooping(true);
|
||||
// music.play();
|
||||
|
||||
this.minimap = new Rectangle(18.75f, 13.75f, 278.75f, 276.25f);
|
||||
final float worldWidth = (this.viewer.terrain.columns - 1);
|
||||
final float worldHeight = this.viewer.terrain.rows - 1;
|
||||
final Rectangle playableMapArea = this.viewer.terrain.getPlayableMapArea();
|
||||
final float worldWidth = playableMapArea.getWidth();
|
||||
final float worldHeight = playableMapArea.getHeight();
|
||||
final float worldSize = Math.max(worldWidth, worldHeight);
|
||||
final float minimapFilledWidth = (worldWidth / worldSize) * this.minimap.width;
|
||||
final float minimapFilledHeight = (worldHeight / worldSize) * this.minimap.height;
|
||||
@ -264,6 +260,16 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
new RootFrameListener() {
|
||||
@Override
|
||||
public void onCreate(final GameUI rootFrame) {
|
||||
WarsmashGdxMapGame.this.viewer.setGameUI(rootFrame);
|
||||
|
||||
final String musicField = rootFrame.getSkinField("Music_V1");
|
||||
final String[] musics = musicField.split(";");
|
||||
final String musicPath = musics[(int) (Math.random() * musics.length)];
|
||||
final Music music = Gdx.audio.newMusic(
|
||||
new DataSourceFileHandle(WarsmashGdxMapGame.this.viewer.dataSource, musicPath));
|
||||
music.setVolume(0.2f);
|
||||
music.setLooping(true);
|
||||
music.play();
|
||||
}
|
||||
});
|
||||
this.meleeUI.main();
|
||||
@ -272,6 +278,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
updateUIScene();
|
||||
|
||||
this.meleeUI.resize();
|
||||
resize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
|
||||
}
|
||||
|
||||
private void updateUIScene() {
|
||||
|
@ -54,6 +54,7 @@ public final class GameUI extends AbstractUIFrame implements UIFrame {
|
||||
private final FreeTypeFontParameter fontParam;
|
||||
private final Map<String, UIFrame> nameToFrame = new HashMap<>();
|
||||
private final Viewport fdfCoordinateResolutionDummyViewport;
|
||||
private final DataTable skinData;
|
||||
|
||||
public GameUI(final DataSource dataSource, final Element skin, final Viewport viewport,
|
||||
final FreeTypeFontGenerator fontGenerator, final Scene uiScene, final War3MapViewer modelViewer) {
|
||||
@ -74,6 +75,23 @@ public final class GameUI extends AbstractUIFrame implements UIFrame {
|
||||
this.fontGenerator = fontGenerator;
|
||||
this.fontParam = new FreeTypeFontParameter();
|
||||
this.fdfCoordinateResolutionDummyViewport = new FitViewport(0.8f, 0.6f);
|
||||
this.skinData = new DataTable(modelViewer.getWorldEditStrings());
|
||||
try {
|
||||
try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("Units\\CommandFunc.txt")) {
|
||||
this.skinData.readTXT(miscDataTxtStream, true);
|
||||
}
|
||||
try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("Units\\CommandStrings.txt")) {
|
||||
this.skinData.readTXT(miscDataTxtStream, true);
|
||||
}
|
||||
if (this.dataSource.has("war3mapSkin.txt")) {
|
||||
try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("war3mapSkin.txt")) {
|
||||
this.skinData.readTXT(miscDataTxtStream, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Element loadSkin(final DataSource dataSource, final String skin) {
|
||||
@ -146,6 +164,10 @@ public final class GameUI extends AbstractUIFrame implements UIFrame {
|
||||
return file;
|
||||
}
|
||||
|
||||
public DataTable getSkinData() {
|
||||
return this.skinData;
|
||||
}
|
||||
|
||||
public UIFrame createFrame(final String name, final UIFrame owner, final int priority, final int createContext) {
|
||||
final FrameDefinition frameDefinition = this.templates.getFrame(name);
|
||||
if (frameDefinition.getFrameClass() == FrameClass.Frame) {
|
||||
|
@ -23,6 +23,9 @@ public class TextureFrame extends AbstractRenderableFrame {
|
||||
|
||||
@Override
|
||||
protected void internalRender(final SpriteBatch batch, final BitmapFont baseFont, final GlyphLayout glyphLayout) {
|
||||
if (this.texture == null) {
|
||||
return;
|
||||
}
|
||||
batch.draw(this.texture, this.renderBounds.x, this.renderBounds.y, this.renderBounds.width,
|
||||
this.renderBounds.height);
|
||||
}
|
||||
@ -42,6 +45,10 @@ public class TextureFrame extends AbstractRenderableFrame {
|
||||
}
|
||||
|
||||
public void setTexture(final Texture texture) {
|
||||
if (texture == null) {
|
||||
this.texture = null;
|
||||
return;
|
||||
}
|
||||
final TextureRegion texRegion;
|
||||
if (this.texCoord != null) {
|
||||
texRegion = new TextureRegion(texture, this.texCoord.getX(), this.texCoord.getZ(), this.texCoord.getY(),
|
||||
|
@ -3,7 +3,6 @@ package com.etheller.warsmash.parsers.w3x.w3i;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
@ -11,7 +10,7 @@ import com.google.common.io.LittleEndianDataOutputStream;
|
||||
* A player.
|
||||
*/
|
||||
public class Player {
|
||||
private War3ID id;
|
||||
private int id;
|
||||
private int type;
|
||||
private int race;
|
||||
private int isFixedStartPosition;
|
||||
@ -23,7 +22,7 @@ public class Player {
|
||||
private long enemyHighPrioritiesFlags;
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream, final int version) throws IOException {
|
||||
this.id = ParseUtils.readWar3ID(stream);
|
||||
this.id = (int) ParseUtils.readUInt32(stream);
|
||||
this.type = stream.readInt();
|
||||
this.race = stream.readInt();
|
||||
this.isFixedStartPosition = stream.readInt();
|
||||
@ -38,7 +37,7 @@ public class Player {
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
ParseUtils.writeWar3ID(stream, this.id);
|
||||
ParseUtils.writeUInt32(stream, this.id);
|
||||
stream.writeInt(this.type);
|
||||
stream.writeInt(this.race);
|
||||
stream.writeInt(this.isFixedStartPosition);
|
||||
@ -52,7 +51,7 @@ public class Player {
|
||||
return 33 + this.name.length();
|
||||
}
|
||||
|
||||
public War3ID getId() {
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ import java.util.Set;
|
||||
import com.etheller.warsmash.util.StringBundle;
|
||||
|
||||
public class DataTable implements ObjectData {
|
||||
private static final boolean DEBUG = false;
|
||||
public static boolean DEBUG = false;
|
||||
|
||||
Map<StringKey, Element> dataTable = new LinkedHashMap<>();
|
||||
|
||||
@ -182,7 +182,7 @@ public class DataTable implements ObjectData {
|
||||
if (input.startsWith("O;")) {
|
||||
continue;
|
||||
}
|
||||
if (input.contains("X1;")) {
|
||||
if (input.contains("X1;") || input.endsWith(";X1")) {
|
||||
rowStartCount++;
|
||||
col = 0;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import com.badlogic.gdx.graphics.Pixmap;
|
||||
import com.badlogic.gdx.graphics.Pixmap.Format;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.viewer5.handlers.tga.TgaFile;
|
||||
|
||||
/**
|
||||
* Uses AWT stuff
|
||||
@ -26,14 +27,27 @@ import com.etheller.warsmash.datasources.DataSource;
|
||||
*/
|
||||
public final class ImageUtils {
|
||||
private static final int BYTES_PER_PIXEL = 4;
|
||||
public static final String DEFAULT_ICON_PATH = "ReplaceableTextures\\CommandButtons\\BTNTemp.blp";
|
||||
|
||||
public static Texture getBLPTexture(final DataSource dataSource, final String path) {
|
||||
try {
|
||||
try (final InputStream resourceAsStream = dataSource.getResourceAsStream(path)) {
|
||||
if (resourceAsStream == null) {
|
||||
throw new IllegalStateException("missing resource: " + path);
|
||||
if ((resourceAsStream == null) || path.endsWith(".tga")) {
|
||||
final String tgaPath = path.substring(0, path.length() - 4) + ".tga";
|
||||
try (final InputStream tgaStream = dataSource.getResourceAsStream(tgaPath)) {
|
||||
if (tgaStream == null) {
|
||||
throw new IllegalStateException("missing resource: " + path);
|
||||
}
|
||||
else {
|
||||
return ImageUtils.getTexture(TgaFile.readTGA(tgaPath, tgaStream));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ImageUtils.getTexture(ImageIO.read(resourceAsStream));
|
||||
final BufferedImage image = ImageIO.read(resourceAsStream);
|
||||
if (image == null) {
|
||||
throw new IllegalStateException("corrupt resource: " + path);
|
||||
}
|
||||
return ImageUtils.getTexture(image);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -49,7 +49,8 @@ public class MappedData {
|
||||
final MappedDataRow mapped = this.map.get(name);
|
||||
|
||||
for (int j = 0, k = header.size(); j < k; j++) {
|
||||
String key = (String) header.get(j);
|
||||
final Object headerObj = header.get(j);
|
||||
String key = headerObj == null ? null : headerObj.toString();
|
||||
|
||||
// UnitBalance.slk doesn't define the name of one row.
|
||||
if (key == null) {
|
||||
|
@ -175,4 +175,15 @@ public class ParseUtils {
|
||||
stream.write(nameBytes);
|
||||
stream.write(0);
|
||||
}
|
||||
|
||||
public static float[] flipRGB(final float[] color) {
|
||||
final float r = color[0];
|
||||
color[0] = color[2];
|
||||
color[2] = r;
|
||||
return color;
|
||||
}
|
||||
|
||||
public static float[] newFlippedRGB(final float[] color) {
|
||||
return new float[] { color[2], color[1], color[0] };
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ public enum RenderMathUtils {
|
||||
public static final float[] FLOAT_VEC3_ZERO = new float[] { 0, 0, 0 };
|
||||
public static final float[] FLOAT_QUAT_DEFAULT = new float[] { 0, 0, 0, 1 };
|
||||
public static final float[] FLOAT_VEC3_ONE = new float[] { 1, 1, 1 };
|
||||
public static final float HALF_PI = (float) (Math.PI / 2.0);
|
||||
|
||||
// copied from ghostwolf and
|
||||
// https://www.blend4web.com/api_doc/libs_gl-matrix2.js.html
|
||||
|
@ -104,7 +104,7 @@ public abstract class ModelViewer {
|
||||
}
|
||||
|
||||
public Scene addSimpleScene() {
|
||||
final Scene scene = new SimpleScene(this);
|
||||
final Scene scene = new SimpleScene(this, createLightManager(true));
|
||||
|
||||
this.scenes.add(scene);
|
||||
|
||||
@ -112,7 +112,7 @@ public abstract class ModelViewer {
|
||||
}
|
||||
|
||||
public WorldScene addWorldScene() {
|
||||
final WorldScene scene = new WorldScene(this);
|
||||
final WorldScene scene = new WorldScene(this, createLightManager(false));
|
||||
|
||||
this.scenes.add(scene);
|
||||
|
||||
@ -367,5 +367,5 @@ public abstract class ModelViewer {
|
||||
System.err.println("error, this, InvalidHandler, FailedToLoad");
|
||||
}
|
||||
|
||||
public abstract SceneLightManager createLightManager();
|
||||
public abstract SceneLightManager createLightManager(boolean simple);
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public abstract class Scene {
|
||||
public boolean alpha = false;
|
||||
private final SceneLightManager lightManager;
|
||||
|
||||
public Scene(final ModelViewer viewer) {
|
||||
public Scene(final ModelViewer viewer, final SceneLightManager lightManager) {
|
||||
final CanvasProvider canvas = viewer.canvas;
|
||||
this.viewer = viewer;
|
||||
this.camera = new Camera();
|
||||
@ -87,7 +87,7 @@ public abstract class Scene {
|
||||
this.visibleCells = 0;
|
||||
this.visibleInstances = 0;
|
||||
|
||||
this.lightManager = this.viewer.createLightManager();
|
||||
this.lightManager = lightManager;
|
||||
}
|
||||
|
||||
public boolean enableAudio() {
|
||||
@ -218,6 +218,8 @@ public abstract class Scene {
|
||||
|
||||
this.emitterObjectUpdater.update(dt);
|
||||
this.updatedParticles = this.emitterObjectUpdater.objects.size();
|
||||
|
||||
this.lightManager.update();
|
||||
}
|
||||
|
||||
protected abstract void innerUpdate(float dt, int frame);
|
||||
|
@ -4,4 +4,6 @@ public interface SceneLightManager {
|
||||
public void add(final SceneLightInstance lightInstance);
|
||||
|
||||
public void remove(final SceneLightInstance lightInstance);
|
||||
|
||||
public void update();
|
||||
}
|
||||
|
@ -55,4 +55,63 @@ public class Shaders {
|
||||
" return vec3(quat_transform(q, v.xy), v.z);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" ";
|
||||
|
||||
public static String lightSystem(final String normalName, final String positionName, final String lightTexture,
|
||||
final String lightCount, final boolean terrain) {
|
||||
return " vec3 lightFactor = vec3(0.0,0.0,0.0);\r\n" + //
|
||||
" float lightIndex = 0.0;\r\n" + //
|
||||
" for(float lightIndex = 0.0; lightIndex < " + lightCount + "; lightIndex += 1.0) {\r\n" + //
|
||||
" float rowPos = (lightIndex + 0.5) / " + lightCount + ";\r\n" + //
|
||||
" vec4 lightPosition = texture2D(" + lightTexture + ", vec2(0.125, rowPos));\r\n" + //
|
||||
" vec3 lightExtra = texture2D(" + lightTexture + ", vec2(0.375, rowPos)).xyz;\r\n" + //
|
||||
" vec4 lightColor = texture2D(" + lightTexture + ", vec2(0.625, rowPos));\r\n" + //
|
||||
" vec4 lightAmbColor = texture2D(" + lightTexture + ", vec2(0.875, rowPos));\r\n" + //
|
||||
" if(lightExtra.x > 1.5) {\r\n" + //
|
||||
" // Ambient light;\r\n" + //
|
||||
" float dist = length(" + positionName + " - vec3(lightPosition." + (terrain ? "xyw" : "xyz")
|
||||
+ "));\r\n" + //
|
||||
" float attenuationStart = lightExtra.y;\r\n" + //
|
||||
" float attenuationEnd = lightExtra.z;\r\n" + //
|
||||
" if( dist <= attenuationEnd ) {\r\n" + //
|
||||
" float attenuationDist = clamp((dist-attenuationStart), 0.001, (attenuationEnd-attenuationStart));\r\n"
|
||||
+ //
|
||||
" float attenuationFactor = 1.0/(attenuationDist);\r\n" + //
|
||||
" lightFactor += attenuationFactor * lightAmbColor.a * lightAmbColor.rgb;\r\n" + //
|
||||
" \r\n" + //
|
||||
" }\r\n" + //
|
||||
" } else if(lightExtra.x > 0.5) {\r\n" + //
|
||||
" // Directional (sun) light;\r\n" + //
|
||||
" vec3 lightDirection = vec3(lightPosition.xyz);\r\n" + //
|
||||
" vec3 lightFactorContribution = lightColor.a * lightColor.rgb * clamp(dot(" + normalName
|
||||
+ ", lightDirection), 0.0, 1.0);\r\n" + //
|
||||
" if(lightFactorContribution.r > 1.0 || lightFactorContribution.g > 1.0 || lightFactorContribution.b > 1.0) {\r\n"
|
||||
+ //
|
||||
" lightFactorContribution = normalize(lightFactorContribution);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" lightFactor += lightFactorContribution + lightAmbColor.a * lightAmbColor.rgb;\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" // Omnidirectional light;\r\n" + //
|
||||
" vec3 deltaBtwn = " + positionName + " - lightPosition.xyz;\r\n" + //
|
||||
" float dist = length(" + positionName + " - vec3(lightPosition." + (terrain ? "xyw" : "xyz")
|
||||
+ "));\r\n" + //
|
||||
" float attenuationStart = lightExtra.y;\r\n" + //
|
||||
" float attenuationEnd = lightExtra.z;\r\n" + //
|
||||
" if( dist <= attenuationEnd ) {\r\n" + //
|
||||
" float attenuationDist = clamp((dist-attenuationStart), 0.001, (attenuationEnd-attenuationStart));\r\n"
|
||||
+ //
|
||||
" float attenuationFactor = 1.0/(attenuationDist);\r\n" + //
|
||||
" vec3 lightDirection = -deltaBtwn;\r\n" + //
|
||||
" vec3 lightFactorContribution = attenuationFactor * lightColor.a * lightColor.rgb * clamp(dot("
|
||||
+ normalName + ", lightDirection), 0.0, 1.0);\r\n" + //
|
||||
" if(lightFactorContribution.r > 1.0 || lightFactorContribution.g > 1.0 || lightFactorContribution.b > 1.0) {\r\n"
|
||||
+ //
|
||||
" lightFactorContribution = normalize(lightFactorContribution);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" lightFactor += lightFactorContribution + attenuationFactor * lightAmbColor.a * lightAmbColor.rgb;\r\n"
|
||||
+ //
|
||||
" \r\n" + //
|
||||
" }\r\n" + //
|
||||
" }\r\n" + //
|
||||
" }\r\n";
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ import java.util.List;
|
||||
public class SimpleScene extends Scene {
|
||||
private final List<ModelInstance> allInstances = new ArrayList<>();
|
||||
|
||||
public SimpleScene(final ModelViewer viewer) {
|
||||
super(viewer);
|
||||
public SimpleScene(final ModelViewer viewer, final SceneLightManager lightManager) {
|
||||
super(viewer, lightManager);
|
||||
this.visibleCells = 1;
|
||||
this.visibleInstances = 0;
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ import com.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
|
||||
public abstract class SkeletalNode extends GenericNode {
|
||||
protected static final Vector3 locationHeap = new Vector3();
|
||||
protected static final Vector3 cameraRayHeap = new Vector3();
|
||||
protected static final Vector3 billboardAxisHeap = new Vector3();
|
||||
protected static final Quaternion rotationHeap = new Quaternion();
|
||||
protected static final Quaternion rotationHeap2 = new Quaternion();
|
||||
protected static final Vector3 scalingHeap = new Vector3();
|
||||
@ -16,9 +17,9 @@ public abstract class SkeletalNode extends GenericNode {
|
||||
public UpdatableObject object;
|
||||
|
||||
public boolean billboarded;
|
||||
public final boolean billboardedX;
|
||||
public final boolean billboardedY;
|
||||
public final boolean billboardedZ;
|
||||
public boolean billboardedX;
|
||||
public boolean billboardedY;
|
||||
public boolean billboardedZ;
|
||||
|
||||
public SkeletalNode() {
|
||||
this.pivot = new Vector3();
|
||||
@ -98,6 +99,36 @@ public abstract class SkeletalNode extends GenericNode {
|
||||
|
||||
this.convertBasis(computedRotation);
|
||||
}
|
||||
else if (this.billboardedX) {
|
||||
final Camera camera = scene.camera;
|
||||
computedRotation = rotationHeap;
|
||||
cameraRayHeap.set(camera.billboardedVectors[6]);
|
||||
computedRotation.set(this.parent.inverseWorldRotation);
|
||||
computedRotation.transform(cameraRayHeap);
|
||||
billboardAxisHeap.set(1, 0, 0);
|
||||
final float angle = (float) Math.atan2(cameraRayHeap.z, cameraRayHeap.y);
|
||||
computedRotation.setFromAxisRad(billboardAxisHeap, angle);
|
||||
}
|
||||
else if (this.billboardedY) {
|
||||
final Camera camera = scene.camera;
|
||||
computedRotation = rotationHeap;
|
||||
cameraRayHeap.set(camera.billboardedVectors[6]);
|
||||
computedRotation.set(this.parent.inverseWorldRotation);
|
||||
computedRotation.transform(cameraRayHeap);
|
||||
billboardAxisHeap.set(0, 1, 0);
|
||||
final float angle = (float) Math.atan2(cameraRayHeap.z, -cameraRayHeap.x);
|
||||
computedRotation.setFromAxisRad(billboardAxisHeap, angle);
|
||||
}
|
||||
else if (this.billboardedZ) {
|
||||
final Camera camera = scene.camera;
|
||||
computedRotation = rotationHeap;
|
||||
cameraRayHeap.set(camera.billboardedVectors[6]);
|
||||
computedRotation.set(this.parent.inverseWorldRotation);
|
||||
computedRotation.transform(cameraRayHeap);
|
||||
billboardAxisHeap.set(0, 0, 1);
|
||||
final float angle = (float) Math.atan2(cameraRayHeap.y, cameraRayHeap.x);
|
||||
computedRotation.setFromAxisRad(billboardAxisHeap, angle);
|
||||
}
|
||||
else {
|
||||
computedRotation = this.localRotation;
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ public class WorldScene extends Scene {
|
||||
|
||||
public Grid grid;
|
||||
|
||||
public WorldScene(final ModelViewer viewer) {
|
||||
super(viewer);
|
||||
public WorldScene(final ModelViewer viewer, final SceneLightManager lightManager) {
|
||||
super(viewer, lightManager);
|
||||
this.grid = new Grid(-100000, -100000, 200000, 200000, 200000, 200000);
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ public class DataTexture {
|
||||
this.reserve(width, height);
|
||||
}
|
||||
|
||||
private void reserve(final int width, final int height) {
|
||||
public void reserve(final int width, final int height) {
|
||||
if ((this.width < width) || (this.height < height)) {
|
||||
final GL20 gl = this.gl;
|
||||
|
||||
|
@ -63,9 +63,9 @@ public class BatchGroup extends GenericGroup {
|
||||
final DataTexture boneTexture = instance.boneTexture;
|
||||
final DataTexture unitLightsTexture = lightManager.getUnitLightsTexture();
|
||||
|
||||
unitLightsTexture.bind(16);
|
||||
shader.setUniformi("u_lightTexture", 16);
|
||||
shader.setUniformf("u_lightCount", unitLightsTexture.getHeight());
|
||||
unitLightsTexture.bind(14);
|
||||
shader.setUniformi("u_lightTexture", 14);
|
||||
shader.setUniformf("u_lightCount", unitLightsTexture.getHeight() - 0.5f);
|
||||
|
||||
// Instances of models with no bones don't have a bone texture.
|
||||
if (boneTexture != null) {
|
||||
@ -103,6 +103,7 @@ public class BatchGroup extends GenericGroup {
|
||||
shader.setUniform4fv("u_geosetColor", geosetColor, 0, geosetColor.length);
|
||||
|
||||
shader.setUniformf("u_layerAlpha", layerAlpha);
|
||||
shader.setUniformf("u_unshaded", layer.unshaded);
|
||||
|
||||
shader.setUniform2fv("u_uvTrans", uvAnim, 0, 2);
|
||||
shader.setUniform2fv("u_uvRot", uvAnim, 2, 2);
|
||||
|
@ -106,7 +106,7 @@ public class Geoset {
|
||||
public void bind(final ShaderProgram shader, final int coordId) {
|
||||
// TODO use indices instead of strings for attributes
|
||||
shader.setVertexAttribute("a_position", 3, GL20.GL_FLOAT, false, 0, this.positionOffset);
|
||||
// shader.setVertexAttribute("a_normal", 3, GL20.GL_FLOAT, false, 0, this.normalOffset);
|
||||
shader.setVertexAttribute("a_normal", 3, GL20.GL_FLOAT, false, 0, this.normalOffset);
|
||||
shader.setVertexAttribute("a_uv", 2, GL20.GL_FLOAT, false, 0, this.uvOffset + (coordId * this.vertices * 8));
|
||||
shader.setVertexAttribute("a_bones", 4, this.openGLSkinType, false, this.skinStride, this.skinOffset);
|
||||
shader.setVertexAttribute("a_boneNumber", 1, this.openGLSkinType, false, this.skinStride,
|
||||
|
@ -18,13 +18,15 @@ public class Light extends GenericObject {
|
||||
case 0:
|
||||
this.type = Type.OMNIDIRECTIONAL;
|
||||
break;
|
||||
case 2:
|
||||
case 1:
|
||||
this.type = Type.DIRECTIONAL;
|
||||
break;
|
||||
default:
|
||||
case 1:
|
||||
case 2:
|
||||
this.type = Type.AMBIENT;
|
||||
break;
|
||||
default:
|
||||
this.type = Type.DIRECTIONAL;
|
||||
break;
|
||||
}
|
||||
this.attenuation = light.getAttenuation();
|
||||
this.color = light.getColor();
|
||||
@ -61,6 +63,11 @@ public class Light extends GenericObject {
|
||||
return this.getVectorValue(out, AnimationMap.KLBC.getWar3id(), sequence, frame, counter, this.ambientColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVisibility(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KLAV.getWar3id(), sequence, frame, counter, 1);
|
||||
}
|
||||
|
||||
public static enum Type {
|
||||
// Omnidirectional light used for in-game sun
|
||||
OMNIDIRECTIONAL,
|
||||
|
@ -2,11 +2,15 @@ package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.SceneLightInstance;
|
||||
import com.etheller.warsmash.viewer5.UpdatableObject;
|
||||
|
||||
public class LightInstance implements UpdatableObject, SceneLightInstance {
|
||||
private static final Matrix4 matrix4Heap = new Matrix4();
|
||||
private static final Vector3 vector3Heap = new Vector3();
|
||||
private static final float[] vectorHeap = new float[3];
|
||||
private static final float[] scalarHeap = new float[1];
|
||||
protected final MdxNode node;
|
||||
@ -41,15 +45,28 @@ public class LightInstance implements UpdatableObject, SceneLightInstance {
|
||||
final float ambientColorRed = vectorHeap[0];
|
||||
final float ambientColorGreen = vectorHeap[1];
|
||||
final float ambientColorBlue = vectorHeap[2];
|
||||
floatBuffer.put(offset, this.node.worldLocation.x);
|
||||
floatBuffer.put(offset + 1, this.node.worldLocation.y);
|
||||
floatBuffer.put(offset + 2, this.node.worldLocation.z);
|
||||
switch (this.light.getType()) {
|
||||
case AMBIENT:
|
||||
case OMNIDIRECTIONAL:
|
||||
floatBuffer.put(offset, this.node.worldLocation.x);
|
||||
floatBuffer.put(offset + 1, this.node.worldLocation.y);
|
||||
floatBuffer.put(offset + 2, this.node.worldLocation.z);
|
||||
break;
|
||||
case DIRECTIONAL:
|
||||
vector3Heap.set(0, 0, 1);
|
||||
this.node.localRotation.transform(vector3Heap);
|
||||
vector3Heap.nor();
|
||||
floatBuffer.put(offset, vector3Heap.x);
|
||||
floatBuffer.put(offset + 1, vector3Heap.y);
|
||||
floatBuffer.put(offset + 2, vector3Heap.z);
|
||||
break;
|
||||
}
|
||||
// I use some padding to make the memory structure of the light be a 4x4 float
|
||||
// grid, when somebody who actually has experience with this stuff comes along
|
||||
// to change this to something smart, maybe they'll remove the padding if it's
|
||||
// not necessary. I'm basing how I implement this on how Ghostwolf did
|
||||
// BoneTexture
|
||||
floatBuffer.put(offset + 3, 0);
|
||||
floatBuffer.put(offset + 3, this.instance.worldLocation.z);
|
||||
floatBuffer.put(offset + 4, this.light.getType().ordinal());
|
||||
floatBuffer.put(offset + 5, attenuationStart);
|
||||
floatBuffer.put(offset + 6, attenuationEnd);
|
||||
@ -66,16 +83,20 @@ public class LightInstance implements UpdatableObject, SceneLightInstance {
|
||||
|
||||
@Override
|
||||
public void update(final float dt, final boolean visible) {
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
public void update(final Scene scene) {
|
||||
if (this.loadedInScene != this.visible) {
|
||||
if (this.visible) {
|
||||
scene.addLight(this);
|
||||
}
|
||||
else {
|
||||
scene.removeLight(this);
|
||||
this.light.getVisibility(scalarHeap, this.instance.sequence, this.instance.frame, this.instance.counter);
|
||||
this.visible = scalarHeap[0] > 0;
|
||||
if (scene != null) {
|
||||
if (this.loadedInScene != this.visible) {
|
||||
if (this.visible) {
|
||||
scene.addLight(this);
|
||||
}
|
||||
else {
|
||||
scene.removeLight(this);
|
||||
}
|
||||
this.loadedInScene = this.visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -242,13 +242,16 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
/// TODO: single-axis billboarding
|
||||
if (genericObject.billboarded != 0) {
|
||||
node.billboarded = true;
|
||||
} // else if (genericObject.billboardedX) {
|
||||
// node.billboardedX = true;
|
||||
// } else if (genericObject.billboardedY) {
|
||||
// node.billboardedY = true;
|
||||
// } else if (genericObject.billboardedZ) {
|
||||
// node.billboardedZ = true;
|
||||
// }
|
||||
}
|
||||
else if (genericObject.billboardedX != 0) {
|
||||
node.billboardedX = true;
|
||||
}
|
||||
else if (genericObject.billboardedY != 0) {
|
||||
node.billboardedY = true;
|
||||
}
|
||||
else if (genericObject.billboardedZ != 0) {
|
||||
node.billboardedZ = true;
|
||||
}
|
||||
|
||||
if (object != null) {
|
||||
node.object = object;
|
||||
@ -724,10 +727,16 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
*
|
||||
* @param ray
|
||||
*/
|
||||
public boolean intersectRayWithCollision(final Ray ray, final Vector3 intersection) {
|
||||
public boolean intersectRayWithCollision(final Ray ray, final Vector3 intersection, final boolean alwaysUseMesh) {
|
||||
final MdxModel mdxModel = (MdxModel) this.model;
|
||||
final List<CollisionShape> collisionShapes = mdxModel.collisionShapes;
|
||||
if (collisionShapes.isEmpty()) {
|
||||
for (final CollisionShape collisionShape : collisionShapes) {
|
||||
final MdxNode mdxNode = this.nodes[collisionShape.index];
|
||||
if (collisionShape.checkIntersect(ray, mdxNode, intersection)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (collisionShapes.isEmpty() || alwaysUseMesh) {
|
||||
for (final Geoset geoset : mdxModel.geosets) {
|
||||
if (!geoset.unselectable) {
|
||||
geoset.getAlpha(alphaHeap, this.sequence, this.frame, this.counter);
|
||||
@ -741,14 +750,6 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (final CollisionShape collisionShape : collisionShapes) {
|
||||
final MdxNode mdxNode = this.nodes[collisionShape.index];
|
||||
if (collisionShape.checkIntersect(ray, mdxNode, intersection)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -24,13 +24,13 @@ public class MdxHandler extends ModelHandler {
|
||||
viewer.addHandler(new BlpHandler());
|
||||
viewer.addHandler(new TgaHandler());
|
||||
|
||||
Shaders.complex = viewer.webGL.createShaderProgram(MdxShaders.vsComplexUnshaded, MdxShaders.fsComplex);
|
||||
Shaders.extended = viewer.webGL.createShaderProgram("#define EXTENDED_BONES\r\n" + MdxShaders.vsComplexUnshaded,
|
||||
Shaders.complex = viewer.webGL.createShaderProgram(MdxShaders.vsComplex, MdxShaders.fsComplex);
|
||||
Shaders.extended = viewer.webGL.createShaderProgram("#define EXTENDED_BONES\r\n" + MdxShaders.vsComplex,
|
||||
MdxShaders.fsComplex);
|
||||
Shaders.complexShadowMap = viewer.webGL.createShaderProgram(MdxShaders.vsComplexUnshaded,
|
||||
Shaders.complexShadowMap = viewer.webGL.createShaderProgram(MdxShaders.vsComplex,
|
||||
MdxShaders.fsComplexShadowMap);
|
||||
Shaders.extendedShadowMap = viewer.webGL.createShaderProgram(
|
||||
"#define EXTENDED_BONES\r\n" + MdxShaders.vsComplexUnshaded, MdxShaders.fsComplexShadowMap);
|
||||
"#define EXTENDED_BONES\r\n" + MdxShaders.vsComplex, MdxShaders.fsComplexShadowMap);
|
||||
Shaders.particles = viewer.webGL.createShaderProgram(MdxShaders.vsParticles, MdxShaders.fsParticles);
|
||||
Shaders.simple = viewer.webGL.createShaderProgram(MdxShaders.vsSimple, MdxShaders.fsSimple);
|
||||
// Shaders.hd = viewer.webGL.createShaderProgram(MdxShaders.vsHd, MdxShaders.fsHd);
|
||||
|
@ -91,6 +91,7 @@ public class MdxShaders {
|
||||
" uniform vec2 u_uvRot;\r\n" + //
|
||||
" uniform float u_uvScale;\r\n" + //
|
||||
" uniform bool u_hasBones;\r\n" + //
|
||||
" uniform bool u_unshaded;\r\n" + //
|
||||
" attribute vec3 a_position;\r\n" + //
|
||||
" attribute vec3 a_normal;\r\n" + //
|
||||
" attribute vec2 a_uv;\r\n" + //
|
||||
@ -103,6 +104,8 @@ public class MdxShaders {
|
||||
" varying vec4 v_color;\r\n" + //
|
||||
" varying vec4 v_uvTransRot;\r\n" + //
|
||||
" varying float v_uvScale;\r\n" + //
|
||||
" uniform sampler2D u_lightTexture;\r\n" + //
|
||||
" uniform float u_lightCount;\r\n" + //
|
||||
Shaders.boneTexture + "\r\n" + //
|
||||
" void transform(inout vec3 position, inout vec3 normal) {\r\n" + //
|
||||
" // For the broken models out there, since the game supports this.\r\n" + //
|
||||
@ -110,8 +113,8 @@ public class MdxShaders {
|
||||
" vec4 position4 = vec4(position, 1.0);\r\n" + //
|
||||
" vec4 normal4 = vec4(normal, 0.0);\r\n" + //
|
||||
" mat4 bone;\r\n" + //
|
||||
" vec4 p;\r\n" + //
|
||||
" vec4 n;\r\n" + //
|
||||
" vec4 p = vec4(0.0,0.0,0.0,0.0);\r\n" + //
|
||||
" vec4 n = vec4(0.0,0.0,0.0,0.0);\r\n" + //
|
||||
" for (int i = 0; i < 4; i++) {\r\n" + //
|
||||
" if (a_bones[i] > 0.0) {\r\n" + //
|
||||
" bone = fetchMatrix(a_bones[i] - 1.0, 0.0);\r\n" + //
|
||||
@ -130,6 +133,8 @@ public class MdxShaders {
|
||||
" #endif\r\n" + //
|
||||
" position = p.xyz / a_boneNumber;\r\n" + //
|
||||
" normal = normalize(n.xyz);\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" position.x += 100.0;\r\n" + //
|
||||
" }\r\n" + //
|
||||
"\r\n" + //
|
||||
" }\r\n" + //
|
||||
@ -144,65 +149,10 @@ public class MdxShaders {
|
||||
" v_uvTransRot = vec4(u_uvTrans, u_uvRot);\r\n" + //
|
||||
" v_uvScale = u_uvScale;\r\n" + //
|
||||
" gl_Position = u_mvp * vec4(position, 1.0);\r\n" + //
|
||||
" }";
|
||||
|
||||
public static final String vsComplexUnshaded = " uniform mat4 u_mvp;\r\n" + //
|
||||
" uniform vec4 u_vertexColor;\r\n" + //
|
||||
" uniform vec4 u_geosetColor;\r\n" + //
|
||||
" uniform float u_layerAlpha;\r\n" + //
|
||||
" uniform vec2 u_uvTrans;\r\n" + //
|
||||
" uniform vec2 u_uvRot;\r\n" + //
|
||||
" uniform float u_uvScale;\r\n" + //
|
||||
" uniform bool u_hasBones;\r\n" + //
|
||||
" attribute vec3 a_position;\r\n" + //
|
||||
" attribute vec2 a_uv;\r\n" + //
|
||||
" attribute vec4 a_bones;\r\n" + //
|
||||
" #ifdef EXTENDED_BONES\r\n" + //
|
||||
" attribute vec4 a_extendedBones;\r\n" + //
|
||||
" #endif\r\n" + //
|
||||
" attribute float a_boneNumber;\r\n" + //
|
||||
" varying vec2 v_uv;\r\n" + //
|
||||
" varying vec4 v_color;\r\n" + //
|
||||
" varying vec4 v_uvTransRot;\r\n" + //
|
||||
" varying float v_uvScale;\r\n" + //
|
||||
Shaders.boneTexture + "\r\n" + //
|
||||
" void transform(inout vec3 position) {\r\n" + //
|
||||
" // For the broken models out there, since the game supports this.\r\n" + //
|
||||
" if (a_boneNumber > 0.0) {\r\n" + //
|
||||
" vec4 position4 = vec4(position, 1.0);\r\n" + //
|
||||
" mat4 bone;\r\n" + //
|
||||
" vec4 p = vec4(0.0,0.0,0.0,0.0);\r\n" + //
|
||||
" for (int i = 0; i < 4; i++) {\r\n" + //
|
||||
" if (a_bones[i] > 0.0) {\r\n" + //
|
||||
" bone = fetchMatrix(a_bones[i] - 1.0, 0.0);\r\n" + //
|
||||
" p += bone * position4;\r\n" + //
|
||||
" }\r\n" + //
|
||||
" }\r\n" + //
|
||||
" #ifdef EXTENDED_BONES\r\n" + //
|
||||
" for (int i = 0; i < 4; i++) {\r\n" + //
|
||||
" if (a_extendedBones[i] > 0.0) {\r\n" + //
|
||||
" bone = fetchMatrix(a_extendedBones[i] - 1.0, 0.0);\r\n" + //
|
||||
" p += bone * position4;\r\n" + //
|
||||
" }\r\n" + //
|
||||
" }\r\n" + //
|
||||
" #endif\r\n" + //
|
||||
" position = p.xyz / a_boneNumber;\r\n" + //
|
||||
// " position.x *= fetchMatrix(0.0, 0.0)[0][0];\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" position.x += 100.0;\r\n" + //
|
||||
" if(!u_unshaded) {\r\n" + //
|
||||
Shaders.lightSystem("normal", "position", "u_lightTexture", "u_lightCount", false) + "\r\n" + //
|
||||
" v_color.xyz *= clamp(lightFactor, 0.0, 1.0);\r\n" + //
|
||||
" }\r\n" + //
|
||||
"\r\n" + //
|
||||
" }\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" vec3 position = a_position;\r\n" + //
|
||||
" if (u_hasBones) {\r\n" + //
|
||||
" transform(position);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" v_uv = a_uv;\r\n" + //
|
||||
" v_color = u_vertexColor * u_geosetColor.bgra * vec4(1.0, 1.0, 1.0, u_layerAlpha);\r\n" + //
|
||||
" v_uvTransRot = vec4(u_uvTrans, u_uvRot);\r\n" + //
|
||||
" v_uvScale = u_uvScale;\r\n" + //
|
||||
" gl_Position = u_mvp * vec4(position, 1.0);\r\n" + //
|
||||
" }";
|
||||
|
||||
public static final String fsComplex = Shaders.quatTransform + "\r\n\r\n" + //
|
||||
@ -231,9 +181,6 @@ public class MdxShaders {
|
||||
" if (u_filterMode >= 5.0 && color.a < 0.02) {\r\n" + //
|
||||
" discard;\r\n" + //
|
||||
" }\r\n" + //
|
||||
" // if (!u_unshaded) {\r\n" + //
|
||||
" // color *= clamp(dot(v_normal, lightDirection) + 0.45, 0.0, 1.0);\r\n" + //
|
||||
" // }\r\n" + //
|
||||
" gl_FragColor = color;\r\n" + //
|
||||
" }";
|
||||
|
||||
@ -245,6 +192,7 @@ public class MdxShaders {
|
||||
" varying vec4 v_color;\r\n" + //
|
||||
" varying vec4 v_uvTransRot;\r\n" + //
|
||||
" varying float v_uvScale;\r\n" + //
|
||||
" varying vec3 v_normal;\r\n" + //
|
||||
// " layout(location = 0) out float fragmentdepth;\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" vec2 uv = v_uv;\r\n" + //
|
||||
|
@ -12,7 +12,7 @@ public class MdxViewer extends ModelViewer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SceneLightManager createLightManager() {
|
||||
public SceneLightManager createLightManager(final boolean simple) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
@ -3,7 +3,9 @@ package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
||||
import com.etheller.warsmash.parsers.mdlx.timeline.Timeline;
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
|
||||
public final class SdSequence<TYPE> {
|
||||
@ -30,9 +32,9 @@ public final class SdSequence<TYPE> {
|
||||
|
||||
final int interpolationType = sd.interpolationType;
|
||||
final long[] frames = timeline.getFrames();
|
||||
final TYPE[] values = timeline.getValues();
|
||||
final TYPE[] inTans = timeline.getInTans();
|
||||
final TYPE[] outTans = timeline.getOutTans();
|
||||
final TYPE[] values = getValues(timeline);
|
||||
final TYPE[] inTans = getInTans(timeline);
|
||||
final TYPE[] outTans = getOutTans(timeline);
|
||||
final TYPE defval = sd.defval;
|
||||
|
||||
// When using a global sequence, where the first key is outside of the
|
||||
@ -47,7 +49,12 @@ public final class SdSequence<TYPE> {
|
||||
// This fixes problems spread over many models, e.g. HeroMountainKing
|
||||
// (compare in WE and in Magos).
|
||||
if (isGlobalSequence && (frames.length > 0) && (frames[0] > end)) {
|
||||
framesBuilder.add(frames[0]);
|
||||
if (start == end) {
|
||||
framesBuilder.add(start);
|
||||
}
|
||||
else {
|
||||
framesBuilder.add(frames[0]);
|
||||
}
|
||||
valuesBuilder.add(values[0]);
|
||||
}
|
||||
|
||||
@ -127,6 +134,33 @@ public final class SdSequence<TYPE> {
|
||||
this.outTans = outTansBuilder.toArray(arrayDescriptor.create(outTansBuilder.size()));
|
||||
}
|
||||
|
||||
private TYPE[] getValues(final Timeline<TYPE> timeline) {
|
||||
final TYPE[] values = timeline.getValues();
|
||||
return fixTimelineArray(timeline, values);
|
||||
}
|
||||
|
||||
private TYPE[] getOutTans(final Timeline<TYPE> timeline) {
|
||||
final TYPE[] outTans = timeline.getOutTans();
|
||||
return fixTimelineArray(timeline, outTans);
|
||||
}
|
||||
|
||||
private TYPE[] getInTans(final Timeline<TYPE> timeline) {
|
||||
final TYPE[] inTans = timeline.getInTans();
|
||||
return fixTimelineArray(timeline, inTans);
|
||||
}
|
||||
|
||||
private TYPE[] fixTimelineArray(final Timeline<TYPE> timeline, final TYPE[] values) {
|
||||
if (timeline.getName().equals(AnimationMap.KLAC.getWar3id())
|
||||
|| timeline.getName().equals(AnimationMap.KLBC.getWar3id())) {
|
||||
final float[][] flippedColorData = new float[values.length][3];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
flippedColorData[i] = ParseUtils.newFlippedRGB((float[]) values[i]);
|
||||
}
|
||||
return (TYPE[]) flippedColorData;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
// private TYPE[] makeArray(final int size) {
|
||||
// return (TYPE[]) new Object[size];
|
||||
// }
|
||||
|
@ -0,0 +1,26 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||
|
||||
public class Destructable extends Doodad {
|
||||
|
||||
private final float life;
|
||||
|
||||
public Destructable(final War3MapViewer map, final MdxModel model, final MutableGameObject row,
|
||||
final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad, final WorldEditorDataType type,
|
||||
final float maxPitch, final float maxRoll, final float life) {
|
||||
super(map, model, row, doodad, type, maxPitch, maxRoll);
|
||||
this.life = life;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrimaryTag getAnimation() {
|
||||
if (this.life <= 0) {
|
||||
return PrimaryTag.DEATH;
|
||||
}
|
||||
return super.getAnimation();
|
||||
}
|
||||
}
|
@ -6,7 +6,10 @@ import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.viewer5.ModelInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||
|
||||
public class Doodad {
|
||||
private static final int SAMPLE_RADIUS = 4;
|
||||
@ -30,6 +33,7 @@ public class Doodad {
|
||||
}
|
||||
else {
|
||||
instance = model.addInstance();
|
||||
((MdxComplexInstance) instance).setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP);
|
||||
}
|
||||
|
||||
instance.move(doodad.getLocation());
|
||||
@ -50,8 +54,8 @@ public class Doodad {
|
||||
final float pitchSampleBackwardY = y - (SAMPLE_RADIUS * (float) Math.sin(facingRadians));
|
||||
final float pitchSampleGroundHeight1 = map.terrain.getGroundHeight(pitchSampleBackwardX, pitchSampleBackwardY);
|
||||
final float pitchSampleGorundHeight2 = map.terrain.getGroundHeight(pitchSampleForwardX, pitchSampleForwardY);
|
||||
pitch = Math.min(maxPitch,
|
||||
(float) Math.atan2(pitchSampleGorundHeight2 - pitchSampleGroundHeight1, SAMPLE_RADIUS * 2));
|
||||
pitch = Math.max(-maxPitch, Math.min(maxPitch,
|
||||
(float) Math.atan2(pitchSampleGorundHeight2 - pitchSampleGroundHeight1, SAMPLE_RADIUS * 2)));
|
||||
final double leftOfFacingAngle = facingRadians + (Math.PI / 2);
|
||||
final float rollSampleForwardX = x + (SAMPLE_RADIUS * (float) Math.cos(leftOfFacingAngle));
|
||||
final float rollSampleForwardY = y + (SAMPLE_RADIUS * (float) Math.sin(leftOfFacingAngle));
|
||||
@ -59,8 +63,8 @@ public class Doodad {
|
||||
final float rollSampleBackwardY = y - (SAMPLE_RADIUS * (float) Math.sin(leftOfFacingAngle));
|
||||
final float rollSampleGroundHeight1 = map.terrain.getGroundHeight(rollSampleBackwardX, rollSampleBackwardY);
|
||||
final float rollSampleGroundHeight2 = map.terrain.getGroundHeight(rollSampleForwardX, rollSampleForwardY);
|
||||
roll = Math.min(maxRoll,
|
||||
(float) Math.atan2(rollSampleGroundHeight2 - rollSampleGroundHeight1, SAMPLE_RADIUS * 2));
|
||||
roll = Math.max(-maxRoll, Math.min(maxRoll,
|
||||
(float) Math.atan2(rollSampleGroundHeight2 - rollSampleGroundHeight1, SAMPLE_RADIUS * 2)));
|
||||
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, facingRadians));
|
||||
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_Y, -pitch));
|
||||
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_X, roll));
|
||||
@ -86,4 +90,8 @@ public class Doodad {
|
||||
this.instance = instance;
|
||||
this.row = row;
|
||||
}
|
||||
|
||||
public PrimaryTag getAnimation() {
|
||||
return PrimaryTag.STAND;
|
||||
}
|
||||
}
|
||||
|
@ -26,10 +26,12 @@ public class SplatModel {
|
||||
public final float[] color;
|
||||
private final List<float[]> locations;
|
||||
private final List<SplatMover> splatInstances;
|
||||
private final boolean unshaded;
|
||||
|
||||
public SplatModel(final GL30 gl, final Texture texture, final List<float[]> locations, final float[] centerOffset,
|
||||
final List<Consumer<SplatMover>> unitMapping) {
|
||||
final List<Consumer<SplatMover>> unitMapping, final boolean unshaded) {
|
||||
this.texture = texture;
|
||||
this.unshaded = unshaded;
|
||||
this.batches = new ArrayList<>();
|
||||
this.color = new float[] { 1, 1, 1, 1 };
|
||||
|
||||
@ -205,6 +207,7 @@ public class SplatModel {
|
||||
|
||||
gl.glActiveTexture(GL30.GL_TEXTURE1);
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.texture.getGlHandle());
|
||||
shader.setUniformi("u_show_lighting", this.unshaded ? 0 : 1);
|
||||
shader.setUniform4fv("u_color", this.color, 0, 4);
|
||||
|
||||
for (final Batch b : this.batches) {
|
||||
|
@ -1,107 +1,13 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.viewer5.SceneLightInstance;
|
||||
import com.etheller.warsmash.viewer5.SceneLightManager;
|
||||
import com.etheller.warsmash.viewer5.gl.DataTexture;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.LightInstance;
|
||||
|
||||
public class W3xSceneLightManager implements SceneLightManager {
|
||||
public final List<LightInstance> lights;
|
||||
private FloatBuffer lightDataCopyHeap;
|
||||
private final DataTexture unitLightsTexture;
|
||||
private final DataTexture terrainLightsTexture;
|
||||
private final War3MapViewer viewer;
|
||||
private int terrainLightCount;
|
||||
private int unitLightCount;
|
||||
public interface W3xSceneLightManager {
|
||||
public DataTexture getUnitLightsTexture();
|
||||
|
||||
public W3xSceneLightManager(final War3MapViewer viewer) {
|
||||
this.viewer = viewer;
|
||||
this.lights = new ArrayList<>();
|
||||
this.unitLightsTexture = new DataTexture(viewer.gl, 4, 4, 1);
|
||||
this.terrainLightsTexture = new DataTexture(viewer.gl, 4, 4, 1);
|
||||
this.lightDataCopyHeap = ByteBuffer.allocateDirect(16 * 1 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
}
|
||||
public int getUnitLightCount();
|
||||
|
||||
@Override
|
||||
public void add(final SceneLightInstance lightInstance) {
|
||||
// TODO redesign to avoid cast
|
||||
final LightInstance mdxLight = (LightInstance) lightInstance;
|
||||
this.lights.add(mdxLight);
|
||||
rebind();
|
||||
}
|
||||
public DataTexture getTerrainLightsTexture();
|
||||
|
||||
@Override
|
||||
public void remove(final SceneLightInstance lightInstance) {
|
||||
// TODO redesign to avoid cast
|
||||
final LightInstance mdxLight = (LightInstance) lightInstance;
|
||||
this.lights.remove(mdxLight);
|
||||
rebind();
|
||||
}
|
||||
|
||||
private void rebind() {
|
||||
final int numberOfLights = this.lights.size() + 1;
|
||||
final int bytesNeeded = numberOfLights * 4 * 16;
|
||||
if (bytesNeeded > (this.lightDataCopyHeap.capacity() * 4)) {
|
||||
this.lightDataCopyHeap = ByteBuffer.allocateDirect(bytesNeeded).order(ByteOrder.nativeOrder())
|
||||
.asFloatBuffer();
|
||||
}
|
||||
|
||||
this.unitLightCount = 0;
|
||||
this.lightDataCopyHeap.clear();
|
||||
int offset = 0;
|
||||
if (this.viewer.dncUnit != null) {
|
||||
if (!this.viewer.dncUnit.lights.isEmpty()) {
|
||||
this.viewer.dncUnit.lights.get(0).bind(0, this.lightDataCopyHeap);
|
||||
offset += 16;
|
||||
this.unitLightCount++;
|
||||
}
|
||||
}
|
||||
for (final LightInstance light : this.lights) {
|
||||
light.bind(offset, this.lightDataCopyHeap);
|
||||
offset += 16;
|
||||
this.unitLightCount++;
|
||||
}
|
||||
this.lightDataCopyHeap.flip();
|
||||
this.unitLightsTexture.bindAndUpdate(this.lightDataCopyHeap, 16, this.unitLightCount);
|
||||
|
||||
this.terrainLightCount = 0;
|
||||
this.lightDataCopyHeap.clear();
|
||||
offset = 0;
|
||||
if (this.viewer.dncTerrain != null) {
|
||||
if (!this.viewer.dncTerrain.lights.isEmpty()) {
|
||||
this.viewer.dncTerrain.lights.get(0).bind(0, this.lightDataCopyHeap);
|
||||
offset += 16;
|
||||
this.terrainLightCount++;
|
||||
}
|
||||
}
|
||||
for (final LightInstance light : this.lights) {
|
||||
light.bind(offset, this.lightDataCopyHeap);
|
||||
offset += 16;
|
||||
this.terrainLightCount++;
|
||||
}
|
||||
this.lightDataCopyHeap.flip();
|
||||
this.terrainLightsTexture.bindAndUpdate(this.lightDataCopyHeap, 16, this.terrainLightCount);
|
||||
}
|
||||
|
||||
public DataTexture getUnitLightsTexture() {
|
||||
return this.unitLightsTexture;
|
||||
}
|
||||
|
||||
public int getUnitLightCount() {
|
||||
return this.unitLightCount;
|
||||
}
|
||||
|
||||
public DataTexture getTerrainLightsTexture() {
|
||||
return this.terrainLightsTexture;
|
||||
}
|
||||
|
||||
public int getTerrainLightCount() {
|
||||
return this.terrainLightCount;
|
||||
}
|
||||
public int getTerrainLightCount();
|
||||
}
|
||||
|
@ -0,0 +1,90 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.viewer5.SceneLightInstance;
|
||||
import com.etheller.warsmash.viewer5.SceneLightManager;
|
||||
import com.etheller.warsmash.viewer5.gl.DataTexture;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.LightInstance;
|
||||
|
||||
public class W3xScenePortraitLightManager implements SceneLightManager, W3xSceneLightManager {
|
||||
public final List<LightInstance> lights;
|
||||
private FloatBuffer lightDataCopyHeap;
|
||||
private final DataTexture unitLightsTexture;
|
||||
private final War3MapViewer viewer;
|
||||
private int unitLightCount;
|
||||
|
||||
public W3xScenePortraitLightManager(final War3MapViewer viewer) {
|
||||
this.viewer = viewer;
|
||||
this.lights = new ArrayList<>();
|
||||
this.unitLightsTexture = new DataTexture(viewer.gl, 4, 4, 1);
|
||||
this.lightDataCopyHeap = ByteBuffer.allocateDirect(16 * 1 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(final SceneLightInstance lightInstance) {
|
||||
// TODO redesign to avoid cast
|
||||
final LightInstance mdxLight = (LightInstance) lightInstance;
|
||||
this.lights.add(mdxLight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(final SceneLightInstance lightInstance) {
|
||||
// TODO redesign to avoid cast
|
||||
final LightInstance mdxLight = (LightInstance) lightInstance;
|
||||
this.lights.remove(mdxLight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
final int numberOfLights = this.lights.size() + 1;
|
||||
final int bytesNeeded = numberOfLights * 4 * 16;
|
||||
if (bytesNeeded > (this.lightDataCopyHeap.capacity() * 4)) {
|
||||
this.lightDataCopyHeap = ByteBuffer.allocateDirect(bytesNeeded).order(ByteOrder.nativeOrder())
|
||||
.asFloatBuffer();
|
||||
this.unitLightsTexture.reserve(4, numberOfLights);
|
||||
}
|
||||
|
||||
this.unitLightCount = 0;
|
||||
this.lightDataCopyHeap.clear();
|
||||
int offset = 0;
|
||||
if (this.viewer.dncTarget != null) {
|
||||
if (!this.viewer.dncTarget.lights.isEmpty()) {
|
||||
this.viewer.dncTarget.lights.get(0).bind(0, this.lightDataCopyHeap);
|
||||
offset += 16;
|
||||
this.unitLightCount++;
|
||||
}
|
||||
}
|
||||
for (final LightInstance light : this.lights) {
|
||||
light.bind(offset, this.lightDataCopyHeap);
|
||||
offset += 16;
|
||||
this.unitLightCount++;
|
||||
}
|
||||
this.lightDataCopyHeap.limit(offset);
|
||||
this.unitLightsTexture.bindAndUpdate(this.lightDataCopyHeap, 4, this.unitLightCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataTexture getUnitLightsTexture() {
|
||||
return this.unitLightsTexture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUnitLightCount() {
|
||||
return this.unitLightCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataTexture getTerrainLightsTexture() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTerrainLightCount() {
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.viewer5.SceneLightInstance;
|
||||
import com.etheller.warsmash.viewer5.SceneLightManager;
|
||||
import com.etheller.warsmash.viewer5.gl.DataTexture;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.LightInstance;
|
||||
|
||||
public class W3xSceneWorldLightManager implements SceneLightManager, W3xSceneLightManager {
|
||||
public final List<LightInstance> lights;
|
||||
private FloatBuffer lightDataCopyHeap;
|
||||
private final DataTexture unitLightsTexture;
|
||||
private final DataTexture terrainLightsTexture;
|
||||
private final War3MapViewer viewer;
|
||||
private int terrainLightCount;
|
||||
private int unitLightCount;
|
||||
|
||||
public W3xSceneWorldLightManager(final War3MapViewer viewer) {
|
||||
this.viewer = viewer;
|
||||
this.lights = new ArrayList<>();
|
||||
this.unitLightsTexture = new DataTexture(viewer.gl, 4, 4, 1);
|
||||
this.terrainLightsTexture = new DataTexture(viewer.gl, 4, 4, 1);
|
||||
this.lightDataCopyHeap = ByteBuffer.allocateDirect(16 * 1 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(final SceneLightInstance lightInstance) {
|
||||
// TODO redesign to avoid cast
|
||||
final LightInstance mdxLight = (LightInstance) lightInstance;
|
||||
this.lights.add(mdxLight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(final SceneLightInstance lightInstance) {
|
||||
// TODO redesign to avoid cast
|
||||
final LightInstance mdxLight = (LightInstance) lightInstance;
|
||||
this.lights.remove(mdxLight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
final int numberOfLights = this.lights.size() + 1;
|
||||
final int bytesNeeded = numberOfLights * 4 * 16;
|
||||
if (bytesNeeded > (this.lightDataCopyHeap.capacity() * 4)) {
|
||||
this.lightDataCopyHeap = ByteBuffer.allocateDirect(bytesNeeded).order(ByteOrder.nativeOrder())
|
||||
.asFloatBuffer();
|
||||
this.unitLightsTexture.reserve(4, numberOfLights);
|
||||
this.terrainLightsTexture.reserve(4, numberOfLights);
|
||||
}
|
||||
|
||||
this.unitLightCount = 0;
|
||||
this.lightDataCopyHeap.clear();
|
||||
int offset = 0;
|
||||
if (this.viewer.dncUnit != null) {
|
||||
if (!this.viewer.dncUnit.lights.isEmpty()) {
|
||||
this.viewer.dncUnit.lights.get(0).bind(0, this.lightDataCopyHeap);
|
||||
offset += 16;
|
||||
this.unitLightCount++;
|
||||
}
|
||||
}
|
||||
for (final LightInstance light : this.lights) {
|
||||
light.bind(offset, this.lightDataCopyHeap);
|
||||
offset += 16;
|
||||
this.unitLightCount++;
|
||||
}
|
||||
this.lightDataCopyHeap.limit(offset);
|
||||
this.unitLightsTexture.bindAndUpdate(this.lightDataCopyHeap, 4, this.unitLightCount);
|
||||
|
||||
this.terrainLightCount = 0;
|
||||
this.lightDataCopyHeap.clear();
|
||||
offset = 0;
|
||||
if (this.viewer.dncTerrain != null) {
|
||||
if (!this.viewer.dncTerrain.lights.isEmpty()) {
|
||||
this.viewer.dncTerrain.lights.get(0).bind(0, this.lightDataCopyHeap);
|
||||
offset += 16;
|
||||
this.terrainLightCount++;
|
||||
}
|
||||
}
|
||||
for (final LightInstance light : this.lights) {
|
||||
light.bind(offset, this.lightDataCopyHeap);
|
||||
offset += 16;
|
||||
this.terrainLightCount++;
|
||||
}
|
||||
this.lightDataCopyHeap.limit(offset);
|
||||
this.terrainLightsTexture.bindAndUpdate(this.lightDataCopyHeap, 4, this.terrainLightCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataTexture getUnitLightsTexture() {
|
||||
return this.unitLightsTexture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUnitLightCount() {
|
||||
return this.unitLightCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataTexture getTerrainLightsTexture() {
|
||||
return this.terrainLightsTexture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTerrainLightCount() {
|
||||
return this.terrainLightCount;
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
import com.etheller.warsmash.viewer5.Shaders;
|
||||
|
||||
public class W3xShaders {
|
||||
public static final class UberSplat {
|
||||
private UberSplat() {
|
||||
@ -12,12 +14,15 @@ public class W3xShaders {
|
||||
" uniform vec2 u_size;\r\n" + //
|
||||
" uniform vec2 u_shadowPixel;\r\n" + //
|
||||
" uniform vec2 u_centerOffset;\r\n" + //
|
||||
" uniform sampler2D lightTexture;\r\n" + //
|
||||
" uniform float lightCount;\r\n" + //
|
||||
" attribute vec3 a_position;\r\n" + //
|
||||
" attribute vec2 a_uv;\r\n" + //
|
||||
" varying vec2 v_uv;\r\n" + //
|
||||
" varying vec2 v_suv;\r\n" + //
|
||||
" varying vec3 v_normal;\r\n" + //
|
||||
" varying float a_positionHeight;\r\n" + //
|
||||
" varying vec3 shadeColor;\r\n" + //
|
||||
" const float normalDist = 0.25;\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" vec2 halfPixel = u_pixel * 0.5;\r\n" + //
|
||||
@ -34,8 +39,11 @@ public class W3xShaders {
|
||||
" v_normal = normalize(vec3(hL - hR, hD - hU, normalDist * 2.0));\r\n" + //
|
||||
" v_uv = a_uv;\r\n" + //
|
||||
" v_suv = base / u_size;\r\n" + //
|
||||
" gl_Position = u_mvp * vec4(a_position.xy, height * 128.0 + a_position.z, 1.0);\r\n" + //
|
||||
" vec3 myposition = vec3(a_position.xy, height * 128.0 + a_position.z);\r\n" + //
|
||||
" gl_Position = u_mvp * vec4(myposition.xyz, 1.0);\r\n" + //
|
||||
" a_positionHeight = a_position.z;\r\n" + //
|
||||
Shaders.lightSystem("v_normal", "myposition", "lightTexture", "lightCount", true) + "\r\n" + //
|
||||
" shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" ";
|
||||
|
||||
@ -43,10 +51,12 @@ public class W3xShaders {
|
||||
" uniform sampler2D u_texture;\r\n" + //
|
||||
" uniform sampler2D u_shadowMap;\r\n" + //
|
||||
" uniform vec4 u_color;\r\n" + //
|
||||
" uniform bool u_show_lighting;\r\n" + //
|
||||
" varying vec2 v_uv;\r\n" + //
|
||||
" varying vec2 v_suv;\r\n" + //
|
||||
" varying vec3 v_normal;\r\n" + //
|
||||
" varying float a_positionHeight;\r\n" + //
|
||||
" varying vec3 shadeColor;\r\n" + //
|
||||
// " const vec3 lightDirection = normalize(vec3(-0.3, -0.3, 0.25));\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" if (any(bvec4(lessThan(v_uv, vec2(0.0)), greaterThan(v_uv, vec2(1.0))))) {\r\n" + //
|
||||
@ -59,6 +69,9 @@ public class W3xShaders {
|
||||
" if (a_positionHeight <= 4.0) {;\r\n" + //
|
||||
" color.xyz *= 1.0 - shadow;\r\n" + //
|
||||
" };\r\n" + //
|
||||
" if (u_show_lighting) {;\r\n" + //
|
||||
" color.xyz *= shadeColor;\r\n" + //
|
||||
" };\r\n" + //
|
||||
" gl_FragColor = color;\r\n" + //
|
||||
" }\r\n" + //
|
||||
" ";
|
||||
|
@ -33,6 +33,7 @@ import com.etheller.warsmash.datasources.CompoundDataSource;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.datasources.MpqDataSource;
|
||||
import com.etheller.warsmash.datasources.SubdirDataSource;
|
||||
import com.etheller.warsmash.parsers.fdf.GameUI;
|
||||
import com.etheller.warsmash.parsers.w3x.War3Map;
|
||||
import com.etheller.warsmash.parsers.w3x.doo.War3MapDoo;
|
||||
import com.etheller.warsmash.parsers.w3x.objectdata.Warcraft3MapObjectData;
|
||||
@ -147,7 +148,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
|
||||
public Terrain terrain;
|
||||
public int renderPathing = 0;
|
||||
public int renderLighting = 0;
|
||||
public int renderLighting = 1;
|
||||
|
||||
public List<SplatModel> selModels = new ArrayList<>();
|
||||
public List<RenderUnit> selected = new ArrayList<>();
|
||||
@ -158,6 +159,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
private MdxComplexInstance confirmationInstance;
|
||||
public MdxComplexInstance dncUnit;
|
||||
public MdxComplexInstance dncTerrain;
|
||||
public MdxComplexInstance dncTarget;
|
||||
public CSimulation simulation;
|
||||
private float updateTime;
|
||||
|
||||
@ -172,6 +174,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
private final List<SelectionCircleSize> selectionCircleSizes = new ArrayList<>();
|
||||
|
||||
private final Map<CUnit, RenderUnit> unitToRenderPeer = new HashMap<>();
|
||||
private GameUI gameUI;
|
||||
|
||||
public War3MapViewer(final DataSource dataSource, final CanvasProvider canvas) {
|
||||
super(dataSource, canvas);
|
||||
@ -260,6 +263,11 @@ public class War3MapViewer extends ModelViewer {
|
||||
try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("UI\\SoundInfo\\MiscData.txt")) {
|
||||
this.miscData.readTXT(miscDataTxtStream, true);
|
||||
}
|
||||
if (this.dataSource.has("war3mapMisc.txt")) {
|
||||
try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("war3mapMisc.txt")) {
|
||||
this.miscData.readTXT(miscDataTxtStream, true);
|
||||
}
|
||||
}
|
||||
this.unitGlobalStrings = new DataTable(worldEditStrings);
|
||||
try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("Units\\UnitGlobalStrings.txt")) {
|
||||
this.unitGlobalStrings.readTXT(miscDataTxtStream, true);
|
||||
@ -339,8 +347,8 @@ public class War3MapViewer extends ModelViewer {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
setDataSource(tilesetSource);
|
||||
final WorldEditStrings worldEditStrings = new WorldEditStrings(this.dataSource);
|
||||
loadSLKs(worldEditStrings);
|
||||
this.worldEditStrings = new WorldEditStrings(this.dataSource);
|
||||
loadSLKs(this.worldEditStrings);
|
||||
|
||||
this.solverParams.tileset = Character.toLowerCase(tileset);
|
||||
|
||||
@ -351,8 +359,8 @@ public class War3MapViewer extends ModelViewer {
|
||||
final StandardObjectData standardObjectData = new StandardObjectData(this.dataSource);
|
||||
this.worldEditData = standardObjectData.getWorldEditData();
|
||||
|
||||
this.terrain = new Terrain(terrainData, terrainPathing, w3iFile, this.webGL, this.dataSource, worldEditStrings,
|
||||
this, this.worldEditData);
|
||||
this.terrain = new Terrain(terrainData, terrainPathing, w3iFile, this.webGL, this.dataSource,
|
||||
this.worldEditStrings, this, this.worldEditData);
|
||||
|
||||
final float[] centerOffset = terrainData.getCenterOffset();
|
||||
final int[] mapSize = terrainData.getMapSize();
|
||||
@ -595,7 +603,13 @@ public class War3MapViewer extends ModelViewer {
|
||||
model = (MdxModel) this.load(fileVar, this.mapPathSolver, this.solverParams);
|
||||
}
|
||||
|
||||
this.doodads.add(new Doodad(this, model, row, doodad, type, maxPitch, maxRoll));
|
||||
if (type == WorldEditorDataType.DESTRUCTIBLES) {
|
||||
this.doodads
|
||||
.add(new Destructable(this, model, row, doodad, type, maxPitch, maxRoll, doodad.getLife()));
|
||||
}
|
||||
else {
|
||||
this.doodads.add(new Doodad(this, model, row, doodad, type, maxPitch, maxRoll));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -763,16 +777,18 @@ public class War3MapViewer extends ModelViewer {
|
||||
final float shadowY = row.getFieldAsFloat(UNIT_SHADOW_Y, 0);
|
||||
final float shadowWidth = row.getFieldAsFloat(UNIT_SHADOW_W, 0);
|
||||
final float shadowHeight = row.getFieldAsFloat(UNIT_SHADOW_H, 0);
|
||||
if (!this.terrain.splats.containsKey(texture)) {
|
||||
final Splat splat = new Splat();
|
||||
splat.opacity = 0.5f;
|
||||
this.terrain.splats.put(texture, splat);
|
||||
if (this.mapMpq.has(texture)) {
|
||||
if (!this.terrain.splats.containsKey(texture)) {
|
||||
final Splat splat = new Splat();
|
||||
splat.opacity = 0.5f;
|
||||
this.terrain.splats.put(texture, splat);
|
||||
}
|
||||
final float x = unit.getLocation()[0] - shadowX;
|
||||
final float y = unit.getLocation()[1] - shadowY;
|
||||
this.terrain.splats.get(texture).locations
|
||||
.add(new float[] { x, y, x + shadowWidth, y + shadowHeight, 3 });
|
||||
unitShadowSplat = this.terrain.splats.get(texture);
|
||||
}
|
||||
final float x = unit.getLocation()[0] - shadowX;
|
||||
final float y = unit.getLocation()[1] - shadowY;
|
||||
this.terrain.splats.get(texture).locations
|
||||
.add(new float[] { x, y, x + shadowWidth, y + shadowHeight, 3 });
|
||||
unitShadowSplat = this.terrain.splats.get(texture);
|
||||
}
|
||||
|
||||
final String buildingShadow = row.getFieldAsString(BUILDING_SHADOW, 0);
|
||||
@ -890,8 +906,11 @@ public class War3MapViewer extends ModelViewer {
|
||||
final ModelInstance instance = item.instance;
|
||||
if ((instance instanceof MdxComplexInstance) && (instance != this.confirmationInstance)) {
|
||||
final MdxComplexInstance mdxComplexInstance = (MdxComplexInstance) instance;
|
||||
if (mdxComplexInstance.sequenceEnded || (mdxComplexInstance.sequence == -1)) {
|
||||
SequenceUtils.randomStandSequence(mdxComplexInstance);
|
||||
if ((mdxComplexInstance.sequence == -1)
|
||||
|| (mdxComplexInstance.sequenceEnded && (((MdxModel) mdxComplexInstance.model).sequences
|
||||
.get(mdxComplexInstance.sequence).getFlags() == 0))) {
|
||||
SequenceUtils.randomSequence(mdxComplexInstance, item.getAnimation(), SequenceUtils.EMPTY,
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -904,8 +923,13 @@ public class War3MapViewer extends ModelViewer {
|
||||
}
|
||||
this.dncTerrain.setFrameByRatio(
|
||||
this.simulation.getGameTimeOfDay() / this.simulation.getGameplayConstants().getGameDayHours());
|
||||
this.dncTerrain.update(rawDeltaTime, null);
|
||||
this.dncUnit.setFrameByRatio(
|
||||
this.simulation.getGameTimeOfDay() / this.simulation.getGameplayConstants().getGameDayHours());
|
||||
this.dncUnit.update(rawDeltaTime, null);
|
||||
this.dncTarget.setFrameByRatio(
|
||||
this.simulation.getGameTimeOfDay() / this.simulation.getGameplayConstants().getGameDayHours());
|
||||
this.dncTarget.update(rawDeltaTime, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -995,7 +1019,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
final String path = entry.getKey();
|
||||
final Splat locations = entry.getValue();
|
||||
final SplatModel model = new SplatModel(Gdx.gl30, (Texture) load(path, PathSolver.DEFAULT, null),
|
||||
locations.locations, this.terrain.centerOffset, locations.unitMapping);
|
||||
locations.locations, this.terrain.centerOffset, locations.unitMapping, true);
|
||||
model.color[0] = 0;
|
||||
model.color[1] = 1;
|
||||
model.color[2] = 0;
|
||||
@ -1037,7 +1061,8 @@ public class War3MapViewer extends ModelViewer {
|
||||
for (final RenderUnit unit : this.units) {
|
||||
final MdxComplexInstance instance = unit.instance;
|
||||
if (instance.isVisible(this.worldScene.camera)
|
||||
&& instance.intersectRayWithCollision(gdxRayHeap, intersectionHeap)
|
||||
&& instance.intersectRayWithCollision(gdxRayHeap, intersectionHeap,
|
||||
unit.getSimulationUnit().getUnitType().isBuilding())
|
||||
&& !unit.getSimulationUnit().isDead()) {
|
||||
entity = unit;
|
||||
}
|
||||
@ -1075,8 +1100,8 @@ public class War3MapViewer extends ModelViewer {
|
||||
RenderUnit entity = null;
|
||||
for (final RenderUnit unit : this.units) {
|
||||
final MdxComplexInstance instance = unit.instance;
|
||||
if (instance.isVisible(this.worldScene.camera)
|
||||
&& instance.intersectRayWithCollision(gdxRayHeap, intersectionHeap)) {
|
||||
if (instance.isVisible(this.worldScene.camera) && instance.intersectRayWithCollision(gdxRayHeap,
|
||||
intersectionHeap, unit.getSimulationUnit().getUnitType().isBuilding())) {
|
||||
entity = unit;
|
||||
}
|
||||
}
|
||||
@ -1149,6 +1174,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
private static final int MAXIMUM_ACCEPTED = 1 << 30;
|
||||
private float selectionCircleScaleFactor;
|
||||
private DataTable worldEditData;
|
||||
private WorldEditStrings worldEditStrings;
|
||||
|
||||
/**
|
||||
* Returns a power of two size for the given target capacity.
|
||||
@ -1255,6 +1281,12 @@ public class War3MapViewer extends ModelViewer {
|
||||
this.dncUnit = (MdxComplexInstance) unitDNCModel.addInstance();
|
||||
this.dncUnit.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP);
|
||||
this.dncUnit.setSequence(0);
|
||||
final MdxModel targetDNCModel = (MdxModel) load(
|
||||
mdx("Environment\\DNC\\DNCLordaeron\\DNCLordaeronTarget\\DNCLordaeronTarget.mdl"), PathSolver.DEFAULT,
|
||||
null);
|
||||
this.dncTarget = (MdxComplexInstance) targetDNCModel.addInstance();
|
||||
this.dncTarget.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP);
|
||||
this.dncTarget.setSequence(0);
|
||||
}
|
||||
|
||||
private static String mdx(String mdxPath) {
|
||||
@ -1268,7 +1300,27 @@ public class War3MapViewer extends ModelViewer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SceneLightManager createLightManager() {
|
||||
return new W3xSceneLightManager(this);
|
||||
public SceneLightManager createLightManager(final boolean simple) {
|
||||
if (simple) {
|
||||
return new W3xScenePortraitLightManager(this);
|
||||
}
|
||||
else {
|
||||
return new W3xSceneWorldLightManager(this);
|
||||
}
|
||||
}
|
||||
|
||||
public WorldEditStrings getWorldEditStrings() {
|
||||
return this.worldEditStrings;
|
||||
}
|
||||
|
||||
public void setGameUI(final GameUI gameUI) {
|
||||
this.gameUI = gameUI;
|
||||
for (final RenderUnit unit : this.units) {
|
||||
unit.initAbilityUI(this);
|
||||
}
|
||||
}
|
||||
|
||||
public GameUI getGameUI() {
|
||||
return this.gameUI;
|
||||
}
|
||||
}
|
||||
|
@ -87,18 +87,18 @@ public class CliffMesh {
|
||||
this.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.uvBuffer);
|
||||
this.gl.glVertexAttribPointer(1, 2, GL20.GL_FLOAT, false, 0, 0);
|
||||
|
||||
// this.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.normalBuffer);
|
||||
// this.gl.glVertexAttribPointer(2, 3, GL20.GL_FLOAT, false, 0, 0);
|
||||
this.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.normalBuffer);
|
||||
this.gl.glVertexAttribPointer(2, 3, GL20.GL_FLOAT, false, 0, 0);
|
||||
|
||||
this.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.instanceBuffer);
|
||||
this.gl.glVertexAttribPointer(2, 4, GL20.GL_FLOAT, false, 0, 0);
|
||||
this.gl.glVertexAttribDivisor(2, 1);
|
||||
this.gl.glVertexAttribPointer(3, 4, GL20.GL_FLOAT, false, 0, 0);
|
||||
this.gl.glVertexAttribDivisor(3, 1);
|
||||
|
||||
this.gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
||||
this.gl.glDrawElementsInstanced(GL20.GL_TRIANGLES, this.indices, GL30.GL_UNSIGNED_SHORT, 0,
|
||||
this.renderJobs.remaining() / 4);
|
||||
|
||||
this.gl.glVertexAttribDivisor(2, 0); // ToDo use vao
|
||||
this.gl.glVertexAttribDivisor(3, 0); // ToDo use vao
|
||||
|
||||
this.renderJobs.clear();
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import javax.imageio.ImageIO;
|
||||
import com.badlogic.gdx.graphics.GL30;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.util.ImageUtils;
|
||||
import com.etheller.warsmash.viewer5.handlers.tga.TgaFile;
|
||||
|
||||
public class GroundTexture {
|
||||
public int id;
|
||||
@ -19,43 +20,61 @@ public class GroundTexture {
|
||||
public GroundTexture(final String path, final DataSource dataSource, final GL30 gl) throws IOException {
|
||||
if (path.toLowerCase().endsWith(".blp")) {
|
||||
try (InputStream stream = dataSource.getResourceAsStream(path)) {
|
||||
final BufferedImage image = ImageIO.read(stream);
|
||||
if (image == null) {
|
||||
throw new IllegalStateException("Missing ground texture: " + path);
|
||||
}
|
||||
final Buffer buffer = ImageUtils.getTextureBuffer(ImageUtils.forceBufferedImagesRGB(image));
|
||||
final int width = image.getWidth();
|
||||
final int height = image.getHeight();
|
||||
|
||||
this.tileSize = (int) (height * 0.25);
|
||||
this.extended = (width > height);
|
||||
|
||||
this.id = gl.glGenTexture();
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, this.id);
|
||||
gl.glTexImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, GL30.GL_RGBA8, this.tileSize, this.tileSize,
|
||||
this.extended ? 32 : 16, 0, GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, null);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_MIN_FILTER, GL30.GL_LINEAR_MIPMAP_LINEAR);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_WRAP_S, GL30.GL_CLAMP_TO_EDGE);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_WRAP_T, GL30.GL_CLAMP_TO_EDGE);
|
||||
|
||||
gl.glPixelStorei(GL30.GL_UNPACK_ROW_LENGTH, width);
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
buffer.position(((y * this.tileSize * width) + (x * this.tileSize)) * 4);
|
||||
gl.glTexSubImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, 0, 0, (y * 4) + x, this.tileSize, this.tileSize,
|
||||
1, GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
if (this.extended) {
|
||||
buffer.position(((y * this.tileSize * width) + ((x + 4) * this.tileSize)) * 4);
|
||||
gl.glTexSubImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, 0, 0, (y * 4) + x + 16, this.tileSize,
|
||||
this.tileSize, 1, GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, buffer);
|
||||
if (stream == null) {
|
||||
final String tgaPath = path.substring(0, path.length() - 4) + ".tga";
|
||||
try (final InputStream tgaStream = dataSource.getResourceAsStream(tgaPath)) {
|
||||
if (tgaStream != null) {
|
||||
final BufferedImage tgaData = TgaFile.readTGA(tgaPath, tgaStream);
|
||||
loadImage(path, gl, tgaData);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Missing ground texture: " + path);
|
||||
}
|
||||
}
|
||||
}
|
||||
gl.glPixelStorei(GL30.GL_UNPACK_ROW_LENGTH, 0);
|
||||
gl.glGenerateMipmap(GL30.GL_TEXTURE_2D_ARRAY);
|
||||
else {
|
||||
final BufferedImage image = ImageIO.read(stream);
|
||||
loadImage(path, gl, image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void loadImage(final String path, final GL30 gl, final BufferedImage image) {
|
||||
if (image == null) {
|
||||
throw new IllegalStateException("Missing ground texture: " + path);
|
||||
}
|
||||
final Buffer buffer = ImageUtils.getTextureBuffer(ImageUtils.forceBufferedImagesRGB(image));
|
||||
final int width = image.getWidth();
|
||||
final int height = image.getHeight();
|
||||
|
||||
this.tileSize = (int) (height * 0.25);
|
||||
this.extended = (width > height);
|
||||
|
||||
this.id = gl.glGenTexture();
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, this.id);
|
||||
gl.glTexImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, GL30.GL_RGBA8, this.tileSize, this.tileSize,
|
||||
this.extended ? 32 : 16, 0, GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, null);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_MIN_FILTER, GL30.GL_LINEAR_MIPMAP_LINEAR);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_WRAP_S, GL30.GL_CLAMP_TO_EDGE);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_WRAP_T, GL30.GL_CLAMP_TO_EDGE);
|
||||
|
||||
gl.glPixelStorei(GL30.GL_UNPACK_ROW_LENGTH, width);
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
buffer.position(((y * this.tileSize * width) + (x * this.tileSize)) * 4);
|
||||
gl.glTexSubImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, 0, 0, (y * 4) + x, this.tileSize, this.tileSize, 1,
|
||||
GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
if (this.extended) {
|
||||
buffer.position(((y * this.tileSize * width) + ((x + 4) * this.tileSize)) * 4);
|
||||
gl.glTexSubImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, 0, 0, (y * 4) + x + 16, this.tileSize,
|
||||
this.tileSize, 1, GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
gl.glPixelStorei(GL30.GL_UNPACK_ROW_LENGTH, 0);
|
||||
gl.glGenerateMipmap(GL30.GL_TEXTURE_2D_ARRAY);
|
||||
}
|
||||
}
|
||||
|
@ -41,12 +41,15 @@ import com.etheller.warsmash.viewer5.Camera;
|
||||
import com.etheller.warsmash.viewer5.PathSolver;
|
||||
import com.etheller.warsmash.viewer5.RawOpenGLTextureResource;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.gl.DataTexture;
|
||||
import com.etheller.warsmash.viewer5.gl.Extensions;
|
||||
import com.etheller.warsmash.viewer5.gl.WebGL;
|
||||
import com.etheller.warsmash.viewer5.handlers.tga.TgaFile;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.DynamicShadowManager;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel.SplatMover;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.Variations;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.W3xSceneLightManager;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.W3xShaders;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
|
||||
|
||||
@ -174,9 +177,16 @@ public class Terrain {
|
||||
|
||||
final char tileset = w3eFile.getTileset();
|
||||
final Element waterInfo = this.waterTable.get(tileset + "Sha");
|
||||
this.waterHeightOffset = waterInfo.getFieldFloatValue("height");
|
||||
this.waterTextureCount = waterInfo.getFieldValue("numTex");
|
||||
this.waterIncreasePerFrame = waterInfo.getFieldValue("texRate") / 60f;
|
||||
if (waterInfo != null) {
|
||||
this.waterHeightOffset = waterInfo.getFieldFloatValue("height");
|
||||
this.waterTextureCount = waterInfo.getFieldValue("numTex");
|
||||
this.waterIncreasePerFrame = waterInfo.getFieldValue("texRate") / 60f;
|
||||
}
|
||||
else {
|
||||
this.waterHeightOffset = 0;
|
||||
this.waterTextureCount = 0;
|
||||
this.waterIncreasePerFrame = 0;
|
||||
}
|
||||
|
||||
loadWaterColor(this.minShallowColor, "Smin", waterInfo);
|
||||
loadWaterColor(this.maxShallowColor, "Smax", waterInfo);
|
||||
@ -213,6 +223,9 @@ public class Terrain {
|
||||
// Ground textures
|
||||
for (final War3ID groundTile : w3eFile.getGroundTiles()) {
|
||||
final Element terrainTileInfo = this.terrainTable.get(groundTile.asStringValue());
|
||||
if (terrainTileInfo == null) {
|
||||
throw new RuntimeException("No terrain info for: " + groundTile.asStringValue());
|
||||
}
|
||||
final String dir = terrainTileInfo.getField("dir");
|
||||
final String file = terrainTileInfo.getField("file");
|
||||
this.groundTextures.add(new GroundTexture(dir + "\\" + file + texturesExt, dataSource, Gdx.gl30));
|
||||
@ -231,9 +244,25 @@ public class Terrain {
|
||||
final String texDir = cliffInfo.getField("texDir");
|
||||
final String texFile = cliffInfo.getField("texFile");
|
||||
try (InputStream imageStream = dataSource.getResourceAsStream(texDir + "\\" + texFile + texturesExt)) {
|
||||
final BufferedImage image = ImageIO.read(imageStream);
|
||||
if (image == null) {
|
||||
throw new IllegalStateException("Missing cliff texture: " + texDir + "\\" + texFile + texturesExt);
|
||||
final BufferedImage image;
|
||||
if (imageStream == null) {
|
||||
final String tgaPath = texDir + "\\" + texFile + ".tga";
|
||||
try (final InputStream tgaStream = dataSource.getResourceAsStream(tgaPath)) {
|
||||
if (tgaStream != null) {
|
||||
image = TgaFile.readTGA(tgaPath, tgaStream);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"Missing cliff texture: " + texDir + "\\" + texFile + texturesExt);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
image = ImageIO.read(imageStream);
|
||||
if (image == null) {
|
||||
throw new IllegalStateException(
|
||||
"Missing cliff texture: " + texDir + "\\" + texFile + texturesExt);
|
||||
}
|
||||
}
|
||||
this.cliffTextures.add(new UnloadedTexture(image.getWidth(), image.getHeight(),
|
||||
ImageUtils.getTextureBuffer(ImageUtils.forceBufferedImagesRGB(image)),
|
||||
@ -796,7 +825,7 @@ public class Terrain {
|
||||
private static void loadWaterColor(final float[] out, final String prefix, final Element waterInfo) {
|
||||
for (int i = 0; i < colorTags.length; i++) {
|
||||
final String colorTag = colorTags[i];
|
||||
out[i] = waterInfo.getFieldFloatValue(prefix + "_" + colorTag) / 255f;
|
||||
out[i] = waterInfo == null ? 0.0f : waterInfo.getFieldFloatValue(prefix + "_" + colorTag) / 255f;
|
||||
}
|
||||
}
|
||||
|
||||
@ -856,6 +885,13 @@ public class Terrain {
|
||||
gl.glUniform1f(this.groundShader.getUniformLocation("centerOffsetX"), this.centerOffset[0]);
|
||||
gl.glUniform1f(this.groundShader.getUniformLocation("centerOffsetY"), this.centerOffset[1]);
|
||||
|
||||
final W3xSceneLightManager lightManager = (W3xSceneLightManager) this.viewer.worldScene.getLightManager();
|
||||
final DataTexture unitLightsTexture = lightManager.getTerrainLightsTexture();
|
||||
|
||||
unitLightsTexture.bind(21);
|
||||
gl.glUniform1i(this.groundShader.getUniformLocation("lightTexture"), 21);
|
||||
gl.glUniform1f(this.groundShader.getUniformLocation("lightCount"), unitLightsTexture.getHeight() - 0.5f);
|
||||
|
||||
gl.glUniformMatrix4fv(this.groundShader.getUniformLocation("DepthBiasMVP"), 1, false,
|
||||
dynamicShadowManager.getDepthBiasMVP().val, 0);
|
||||
|
||||
@ -949,6 +985,13 @@ public class Terrain {
|
||||
gl.glActiveTexture(GL30.GL_TEXTURE2);
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.shadowMap);
|
||||
|
||||
final W3xSceneLightManager lightManager = (W3xSceneLightManager) this.viewer.worldScene.getLightManager();
|
||||
final DataTexture terrainLightsTexture = lightManager.getTerrainLightsTexture();
|
||||
|
||||
terrainLightsTexture.bind(21);
|
||||
gl.glUniform1i(shader.getUniformLocation("lightTexture"), 21);
|
||||
gl.glUniform1f(shader.getUniformLocation("lightCount"), terrainLightsTexture.getHeight() - 0.5f);
|
||||
|
||||
// Render the cliffs
|
||||
for (final SplatModel splat : this.uberSplatModels) {
|
||||
splat.render(gl, shader);
|
||||
@ -976,6 +1019,13 @@ public class Terrain {
|
||||
gl.glUniform1f(this.waterShader.getUniformLocation("centerOffsetY"), this.centerOffset[1]);
|
||||
gl.glUniform4fv(9, 1, this.shaderMapBounds, 0);
|
||||
|
||||
final W3xSceneLightManager lightManager = (W3xSceneLightManager) this.viewer.worldScene.getLightManager();
|
||||
final DataTexture unitLightsTexture = lightManager.getTerrainLightsTexture();
|
||||
|
||||
unitLightsTexture.bind(21);
|
||||
gl.glUniform1i(this.waterShader.getUniformLocation("lightTexture"), 21);
|
||||
gl.glUniform1f(this.waterShader.getUniformLocation("lightCount"), unitLightsTexture.getHeight() - 0.5f);
|
||||
|
||||
gl.glActiveTexture(GL30.GL_TEXTURE0);
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.waterHeight);
|
||||
gl.glActiveTexture(GL30.GL_TEXTURE1);
|
||||
@ -1025,6 +1075,13 @@ public class Terrain {
|
||||
gl.glUniform1i(1, this.viewer.renderPathing);
|
||||
gl.glUniform1i(2, this.viewer.renderLighting);
|
||||
|
||||
final W3xSceneLightManager lightManager = (W3xSceneLightManager) this.viewer.worldScene.getLightManager();
|
||||
final DataTexture unitLightsTexture = lightManager.getTerrainLightsTexture();
|
||||
|
||||
unitLightsTexture.bind(21);
|
||||
gl.glUniform1i(this.cliffShader.getUniformLocation("lightTexture"), 21);
|
||||
gl.glUniform1f(this.cliffShader.getUniformLocation("lightCount"), unitLightsTexture.getHeight() - 0.5f);
|
||||
|
||||
this.cliffShader.setUniformi("shadowMap", 2);
|
||||
gl.glActiveTexture(GL30.GL_TEXTURE2);
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.shadowMap);
|
||||
@ -1266,7 +1323,7 @@ public class Terrain {
|
||||
|
||||
final SplatModel splatModel = new SplatModel(Gdx.gl30,
|
||||
(Texture) this.viewer.load(path, PathSolver.DEFAULT, null), splat.locations, this.centerOffset,
|
||||
splat.unitMapping);
|
||||
splat.unitMapping, false);
|
||||
splatModel.color[3] = splat.opacity;
|
||||
this.uberSplatModels.add(splatModel);
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.environment;
|
||||
|
||||
import com.etheller.warsmash.viewer5.Shaders;
|
||||
|
||||
/**
|
||||
* Mostly copied from HiveWE!
|
||||
*/
|
||||
@ -12,8 +14,8 @@ public class TerrainShaders {
|
||||
"\r\n" + //
|
||||
"layout (location = 0) in vec3 vPosition;\r\n" + //
|
||||
"layout (location = 1) in vec2 vUV;\r\n" + //
|
||||
// "layout (location = 2) in vec3 vNormal;\r\n" + //
|
||||
"layout (location = 2) in vec4 vOffset;\r\n" + //
|
||||
"layout (location = 2) in vec3 vNormal;\r\n" + //
|
||||
"layout (location = 3) in vec4 vOffset;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 0) uniform mat4 MVP;\r\n" + //
|
||||
"\r\n" + //
|
||||
@ -21,12 +23,15 @@ public class TerrainShaders {
|
||||
"layout (binding = 3) uniform sampler2D shadowMap;\r\n" + //
|
||||
"layout (location = 3) uniform float centerOffsetX;\r\n" + //
|
||||
"layout (location = 4) uniform float centerOffsetY;\r\n" + //
|
||||
"layout (location = 5) uniform sampler2D lightTexture;\r\n" + //
|
||||
"layout (location = 6) uniform float lightCount;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 0) out vec3 UV;\r\n" + //
|
||||
"layout (location = 1) out vec3 Normal;\r\n" + //
|
||||
"layout (location = 2) out vec2 pathing_map_uv;\r\n" + //
|
||||
"layout (location = 3) out vec3 position;\r\n" + //
|
||||
"layout (location = 4) out vec2 v_suv;\r\n" + //
|
||||
"layout (location = 5) out vec3 shadeColor;\r\n" + //
|
||||
"\r\n" + //
|
||||
"void main() {\r\n" + //
|
||||
" pathing_map_uv = (vec2(vPosition.x + 128, vPosition.y) / 128 + vOffset.xy) * 4;\r\n" + //
|
||||
@ -52,9 +57,11 @@ public class TerrainShaders {
|
||||
" float hR = texelFetch(height_texture, height_pos + off.xz, 0).r;\r\n" + //
|
||||
" float hD = texelFetch(height_texture, height_pos - off.zy, 0).r;\r\n" + //
|
||||
" float hU = texelFetch(height_texture, height_pos + off.zy, 0).r;\r\n" + //
|
||||
" vec3 terrain_normal = normalize(vec3(hL - hR, hD - hU, 2.0));\r\n" + //
|
||||
" vec3 terrain_normal = normalize(vNormal);//vec3(hL - hR, hD - hU, 2.0)+vNormal);\r\n" + //
|
||||
"\r\n" + //
|
||||
" Normal = terrain_normal;\r\n" + //
|
||||
Shaders.lightSystem("Normal", "myposition.xyz", "lightTexture", "lightCount", true) + "\r\n" + //
|
||||
" shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + //
|
||||
"}";
|
||||
|
||||
public static final String frag = "#version 450 core\r\n" + //
|
||||
@ -70,6 +77,7 @@ public class TerrainShaders {
|
||||
"layout (location = 1) in vec3 Normal;\r\n" + //
|
||||
"layout (location = 2) in vec2 pathing_map_uv;\r\n" + //
|
||||
"layout (location = 4) in vec2 v_suv;\r\n" + //
|
||||
"layout (location = 5) in vec3 shadeColor;\r\n" + //
|
||||
"\r\n" + //
|
||||
"out vec4 color;\r\n" + //
|
||||
"\r\n" + //
|
||||
@ -79,10 +87,7 @@ public class TerrainShaders {
|
||||
" float shadow = texture2D(shadowMap, v_suv).r;\r\n" + //
|
||||
" color.rgb *= (1.0 - shadow);\r\n" + //
|
||||
" if (show_lighting) {\r\n" + //
|
||||
" vec3 light_direction = vec3(-0.3, -0.3, 0.25);\r\n" + //
|
||||
" light_direction = normalize(light_direction);\r\n" + //
|
||||
"\r\n" + //
|
||||
" color.rgb *= clamp(dot(Normal, light_direction) + 0.45, 0, 1);\r\n" + //
|
||||
" color.rgb *= shadeColor;\r\n" + //
|
||||
" }\r\n" + //
|
||||
"\r\n" + //
|
||||
" uvec4 byte = texelFetch(pathing_map_static, ivec2(pathing_map_uv), 0);\r\n" + //
|
||||
@ -128,14 +133,16 @@ public class TerrainShaders {
|
||||
"layout (binding = 2) uniform usampler2D terrain_texture_list;\r\n" + //
|
||||
"layout (location = 4) uniform float centerOffsetX;\r\n" + //
|
||||
"layout (location = 5) uniform float centerOffsetY;\r\n" + //
|
||||
"layout (location = 7) uniform sampler2D lightTexture;\r\n" + //
|
||||
"layout (location = 8) uniform float lightCount;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 0) out vec2 UV;\r\n" + //
|
||||
"layout (location = 1) out flat uvec4 texture_indices;\r\n" + //
|
||||
"layout (location = 2) out vec2 pathing_map_uv;\r\n" + //
|
||||
"layout (location = 3) out vec3 normal;\r\n" + //
|
||||
"layout (location = 4) out vec3 position;\r\n" + //
|
||||
"layout (location = 5) out vec3 ShadowCoord;\r\n" + //
|
||||
"layout (location = 6) out vec2 v_suv;\r\n" + //
|
||||
"layout (location = 7) out vec3 shadeColor;\r\n" + //
|
||||
"\r\n" + //
|
||||
"void main() { \r\n" + //
|
||||
" ivec2 size = textureSize(terrain_texture_list, 0);\r\n" + //
|
||||
@ -149,15 +156,16 @@ public class TerrainShaders {
|
||||
" float hR = texelFetch(height_texture, height_pos + off.xz, 0).r;\r\n" + //
|
||||
" float hD = texelFetch(height_texture, height_pos - off.zy, 0).r;\r\n" + //
|
||||
" float hU = texelFetch(height_texture, height_pos + off.zy, 0).r;\r\n" + //
|
||||
" normal = normalize(vec3(hL - hR, hD - hU, 2.0));\r\n" + //
|
||||
" vec3 normal = normalize(vec3(hL - hR, hD - hU, 2.0));\r\n" + //
|
||||
"\r\n" + //
|
||||
" UV = vec2(vPosition.x, 1 - vPosition.y);\r\n" + //
|
||||
" texture_indices = texelFetch(terrain_texture_list, pos, 0);\r\n" + //
|
||||
" pathing_map_uv = (vPosition + pos) * 4; \r\n" + //
|
||||
"\r\n" + //
|
||||
" // Cliff culling\r\n" + //
|
||||
" position = vec3((vPosition.x + pos.x)*128.0 + centerOffsetX, (vPosition.y + pos.y)*128.0 + centerOffsetY, height.r*128.0);\r\n"
|
||||
" vec3 positionWorld = vec3((vPosition.x + pos.x)*128.0 + centerOffsetX, (vPosition.y + pos.y)*128.0 + centerOffsetY, height.r*128.0);\r\n"
|
||||
+ //
|
||||
" position = positionWorld;\r\n" + //
|
||||
" gl_Position = ((texture_indices.a & 32768) == 0) ? MVP * vec4(position.xyz, 1) : vec4(2.0, 0.0, 0.0, 1.0);\r\n"
|
||||
+ //
|
||||
" ShadowCoord = (((texture_indices.a & 32768) == 0) ? DepthBiasMVP * vec4(position.xyz, 1) : vec4(2.0, 0.0, 0.0, 1.0)).xyz;\r\n"
|
||||
@ -165,6 +173,8 @@ public class TerrainShaders {
|
||||
" v_suv = (vPosition + pos) / size;\r\n" + //
|
||||
" position.x = (position.x - centerOffsetX) / (size.x * 128.0);\r\n" + //
|
||||
" position.y = (position.y - centerOffsetY) / (size.y * 128.0);\r\n" + //
|
||||
Shaders.lightSystem("normal", "positionWorld", "lightTexture", "lightCount", true) + "\r\n" + //
|
||||
" shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + //
|
||||
"}";
|
||||
|
||||
public static final String frag = "#version 450 core\r\n" + //
|
||||
@ -197,10 +207,10 @@ public class TerrainShaders {
|
||||
"layout (location = 0) in vec2 UV;\r\n" + //
|
||||
"layout (location = 1) in flat uvec4 texture_indices;\r\n" + //
|
||||
"layout (location = 2) in vec2 pathing_map_uv;\r\n" + //
|
||||
"layout (location = 3) in vec3 normal;\r\n" + //
|
||||
"layout (location = 4) in vec3 position;\r\n" + //
|
||||
"layout (location = 5) in vec3 ShadowCoord;\r\n" + //
|
||||
"layout (location = 6) in vec2 v_suv;\r\n" + //
|
||||
"layout (location = 7) in vec3 shadeColor;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 0) out vec4 color;\r\n" + //
|
||||
// "layout (location = 1) out vec4 position;\r\n" + //
|
||||
@ -263,14 +273,12 @@ public class TerrainShaders {
|
||||
// " if ( texture2D(shadowMap, ShadowCoord.xy).z > ShadowCoord.z ) {\r\n" + //
|
||||
// " visibility = 0.5;\r\n" + //
|
||||
// " }\r\n" + //
|
||||
" color = vec4(color.xyz * (1.0 - shadow), 1.0);\r\n" + //
|
||||
"\r\n" + //
|
||||
// " if (show_lighting) {\r\n" + //
|
||||
// " vec3 light_direction = vec3(-0.3, -0.3, 0.25);\r\n" + //
|
||||
// " light_direction = normalize(light_direction);\r\n" + //
|
||||
// "\r\n" + //
|
||||
// " color.rgb *= clamp(dot(normal, light_direction) + 0.45, 0, 1);\r\n" + //
|
||||
// " }\r\n" + //
|
||||
" if (show_lighting) {\r\n" + //
|
||||
" color = vec4(color.xyz * (1.0 - shadow) * shadeColor, 1.0);\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" color = vec4(color.xyz * (1.0 - shadow), 1.0);\r\n" + //
|
||||
" }\r\n" + //
|
||||
// "\r\n" + //
|
||||
// " if (show_pathing_map) {\r\n" + //
|
||||
// " uint byte_static = texelFetch(pathing_map_static, ivec2(pathing_map_uv), 0).r;\r\n" + //
|
||||
@ -415,10 +423,13 @@ public class TerrainShaders {
|
||||
"layout (location = 3) uniform vec4 deep_color_min;\r\n" + //
|
||||
"layout (location = 4) uniform vec4 deep_color_max;\r\n" + //
|
||||
"layout (location = 5) uniform float water_offset;\r\n" + //
|
||||
"layout (location = 10) uniform sampler2D lightTexture;\r\n" + //
|
||||
"layout (location = 11) uniform float lightCount;\r\n" + //
|
||||
"\r\n" + //
|
||||
"out vec2 UV;\r\n" + //
|
||||
"out vec4 Color;\r\n" + //
|
||||
"out vec2 position;\r\n" + //
|
||||
"out vec3 shadeColor;\r\n" + //
|
||||
"\r\n" + //
|
||||
"const float min_depth = 10.f / 128;\r\n" + //
|
||||
"const float deeplevel = 64.f / 128;\r\n" + //
|
||||
@ -437,8 +448,9 @@ public class TerrainShaders {
|
||||
"\r\n" + //
|
||||
" position = vec2((vPosition.x + pos.x)*128.0 + centerOffsetX, (vPosition.y + pos.y)*128.0 + centerOffsetY);\r\n"
|
||||
+ //
|
||||
" gl_Position = is_water ? MVP * vec4(position.xy, water_height*128.0, 1) : vec4(2.0, 0.0, 0.0, 1.0);\r\n"
|
||||
+ //
|
||||
" vec4 myposition = vec4(position.xy, water_height*128.0, 1);\r\n" + //
|
||||
" vec3 Normal = vec3(0,0,1);\r\n" + //
|
||||
" gl_Position = is_water ? MVP * myposition : vec4(2.0, 0.0, 0.0, 1.0);\r\n" + //
|
||||
"\r\n" + //
|
||||
" UV = vec2((vPosition.x + pos.x%2)/2.0, (vPosition.y + pos.y%2)/2.0);\r\n" + //
|
||||
"\r\n" + //
|
||||
@ -451,6 +463,8 @@ public class TerrainShaders {
|
||||
" value = clamp(value - deeplevel, 0.f, maxdepth - deeplevel) / (maxdepth - deeplevel);\r\n" + //
|
||||
" Color = deep_color_min * (1.f - value) + deep_color_max * value;\r\n" + //
|
||||
" }\r\n" + //
|
||||
Shaders.lightSystem("Normal", "myposition.xyz", "lightTexture", "lightCount", true) + "\r\n" + //
|
||||
" shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + //
|
||||
" }";
|
||||
|
||||
public static final String frag = "#version 450 core\r\n" + //
|
||||
@ -465,13 +479,14 @@ public class TerrainShaders {
|
||||
"in vec2 UV;\r\n" + //
|
||||
"in vec4 Color;\r\n" + //
|
||||
"in vec2 position;\r\n" + //
|
||||
"in vec3 shadeColor;\r\n" + //
|
||||
"\r\n" + //
|
||||
"out vec4 outColor;\r\n" + //
|
||||
"\r\n" + //
|
||||
"void main() {\r\n" + //
|
||||
" vec2 d2 = min(position - mapBounds.xy, mapBounds.zw - position);\r\n" + //
|
||||
" float d1 = clamp(min(d2.x, d2.y) / 64.0 + 1.0, 0.0, 1.0) * 0.8 + 0.2;;\r\n" + //
|
||||
" outColor = texture(water_textures, vec3(UV, current_texture)) * vec4(Color.rgb * d1, Color.a);\r\n"
|
||||
" outColor = texture(water_textures, vec3(UV, current_texture)) * vec4(Color.rgb * d1 * shadeColor, Color.a);\r\n"
|
||||
+ //
|
||||
"}";
|
||||
}
|
||||
|
@ -8,7 +8,9 @@ import java.util.Queue;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
import com.etheller.warsmash.parsers.fdf.GameUI;
|
||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
||||
import com.etheller.warsmash.units.Element;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.util.ImageUtils;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
@ -34,6 +36,12 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityP
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityStop;
|
||||
|
||||
public class RenderUnit {
|
||||
private static final War3ID ABILITY_MOVE = War3ID.fromString("Amov");
|
||||
private static final War3ID ABILITY_STOP = War3ID.fromString("Astp");
|
||||
private static final War3ID ABILITY_HOLD_POSITION = War3ID.fromString("Ahol");
|
||||
private static final War3ID ABILITY_PATROL = War3ID.fromString("Apat");
|
||||
private static final War3ID ABILITY_ATTACK = War3ID.fromString("Aatk");
|
||||
|
||||
private static final Quaternion tempQuat = new Quaternion();
|
||||
private static final War3ID RED = War3ID.fromString("uclr");
|
||||
private static final War3ID GREEN = War3ID.fromString("uclg");
|
||||
@ -133,33 +141,44 @@ public class RenderUnit {
|
||||
this.row = row;
|
||||
this.soundset = soundset;
|
||||
|
||||
for (final CAbility ability : simulationUnit.getAbilities()) {
|
||||
}
|
||||
|
||||
public void initAbilityUI(final War3MapViewer map) {
|
||||
for (final CAbility ability : this.simulationUnit.getAbilities()) {
|
||||
if (ability instanceof CAbilityMove) {
|
||||
final GameUI gameUI = map.getGameUI();
|
||||
final Element moveCommand = gameUI.getSkinData().get("CmdMove");
|
||||
final String moveCommandArt = gameUI.getSkinField(moveCommand.getField("Art"));
|
||||
this.commandCardIcons.add(new CommandCardIcon(0, 0,
|
||||
ImageUtils.getBLPTexture(map.dataSource, "ReplaceableTextures\\CommandButtons\\BTNMove.blp"),
|
||||
ability.getOrderId()));
|
||||
ImageUtils.getBLPTexture(map.dataSource, moveCommandArt), ability.getOrderId()));
|
||||
}
|
||||
else if (ability instanceof CAbilityAttack) {
|
||||
final GameUI gameUI = map.getGameUI();
|
||||
final Element command = gameUI.getSkinData().get("CmdAttack");
|
||||
final String commandArt = gameUI.getSkinField(command.getField("Art"));
|
||||
this.commandCardIcons.add(new CommandCardIcon(3, 0,
|
||||
ImageUtils.getBLPTexture(map.dataSource, "ReplaceableTextures\\CommandButtons\\BTNAttack.blp"),
|
||||
ability.getOrderId()));
|
||||
ImageUtils.getBLPTexture(map.dataSource, commandArt), ability.getOrderId()));
|
||||
}
|
||||
else if (ability instanceof CAbilityHoldPosition) {
|
||||
this.commandCardIcons
|
||||
.add(new CommandCardIcon(2, 0,
|
||||
ImageUtils.getBLPTexture(map.dataSource,
|
||||
"ReplaceableTextures\\CommandButtons\\BTNHoldPosition.blp"),
|
||||
ability.getOrderId()));
|
||||
final GameUI gameUI = map.getGameUI();
|
||||
final Element command = gameUI.getSkinData().get("CmdHoldPos");
|
||||
final String commandArt = gameUI.getSkinField(command.getField("Art"));
|
||||
this.commandCardIcons.add(new CommandCardIcon(2, 0,
|
||||
ImageUtils.getBLPTexture(map.dataSource, commandArt), ability.getOrderId()));
|
||||
}
|
||||
else if (ability instanceof CAbilityPatrol) {
|
||||
final GameUI gameUI = map.getGameUI();
|
||||
final Element command = gameUI.getSkinData().get("CmdPatrol");
|
||||
final String commandArt = gameUI.getSkinField(command.getField("Art"));
|
||||
this.commandCardIcons.add(new CommandCardIcon(0, 1,
|
||||
ImageUtils.getBLPTexture(map.dataSource, "ReplaceableTextures\\CommandButtons\\BTNPatrol.blp"),
|
||||
ability.getOrderId()));
|
||||
ImageUtils.getBLPTexture(map.dataSource, commandArt), ability.getOrderId()));
|
||||
}
|
||||
else if (ability instanceof CAbilityStop) {
|
||||
final GameUI gameUI = map.getGameUI();
|
||||
final Element command = gameUI.getSkinData().get("CmdStop");
|
||||
final String commandArt = gameUI.getSkinField(command.getField("Art"));
|
||||
this.commandCardIcons.add(new CommandCardIcon(1, 0,
|
||||
ImageUtils.getBLPTexture(map.dataSource, "ReplaceableTextures\\CommandButtons\\BTNStop.blp"),
|
||||
ability.getOrderId()));
|
||||
ImageUtils.getBLPTexture(map.dataSource, commandArt), ability.getOrderId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -225,7 +244,7 @@ public class RenderUnit {
|
||||
}
|
||||
if (boneCorpse && !this.boneCorpse) {
|
||||
this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.BONE,
|
||||
map.simulation.getGameplayConstants().getBoneDecayTime(), true);
|
||||
this.simulationUnit.getEndingDecayTime(map.simulation), true);
|
||||
}
|
||||
else if (corpse && !this.corpse) {
|
||||
this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.FLESH,
|
||||
|
@ -23,6 +23,7 @@ public class CGameplayConstants {
|
||||
private final float duskTimeGameHours;
|
||||
private final float gameDayHours;
|
||||
private final float gameDayLength;
|
||||
private final float structureDecayTime;
|
||||
|
||||
public CGameplayConstants(final DataTable parsedDataTable) {
|
||||
final Element miscData = parsedDataTable.get("Misc");
|
||||
@ -31,6 +32,7 @@ public class CGameplayConstants {
|
||||
this.maxCollisionRadius = miscData.getFieldFloatValue("MaxCollisionRadius");
|
||||
this.decayTime = miscData.getFieldFloatValue("DecayTime");
|
||||
this.boneDecayTime = miscData.getFieldFloatValue("BoneDecayTime");
|
||||
this.structureDecayTime = miscData.getFieldFloatValue("StructureDecayTime");
|
||||
this.bulletDeathTime = miscData.getFieldFloatValue("BulletDeathTime");
|
||||
this.closeEnoughRange = miscData.getFieldFloatValue("CloseEnoughRange");
|
||||
|
||||
@ -105,4 +107,8 @@ public class CGameplayConstants {
|
||||
public float getDuskTimeGameHours() {
|
||||
return this.duskTimeGameHours;
|
||||
}
|
||||
|
||||
public float getStructureDecayTime() {
|
||||
return this.structureDecayTime;
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ public class CSimulation {
|
||||
for (int i = 0; i < WarsmashConstants.MAX_PLAYERS; i++) {
|
||||
if (i < playerInfos.size()) {
|
||||
final Player playerInfo = playerInfos.get(i);
|
||||
this.players.add(new CPlayer(playerInfo.getId().getValue(), CMapControl.values()[playerInfo.getType()],
|
||||
this.players.add(new CPlayer(playerInfo.getId(), CMapControl.values()[playerInfo.getType()],
|
||||
playerInfo.getName(), CRace.parseRace(playerInfo.getRace()), playerInfo.getStartLocation()));
|
||||
}
|
||||
else {
|
||||
|
@ -16,10 +16,13 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitStateListener.
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CAttackOrder;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CAllianceType;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer;
|
||||
|
||||
public class CUnit extends CWidget {
|
||||
private static final Rectangle tempRect = new Rectangle();
|
||||
private War3ID typeId;
|
||||
private float facing; // degrees
|
||||
private float mana;
|
||||
@ -51,6 +54,8 @@ public class CUnit extends CWidget {
|
||||
// questionable -- it already was -- but I meant for those to inform us
|
||||
// which fields shouldn't be persisted if we do game state save later
|
||||
private transient CUnitStateNotifier stateNotifier = new CUnitStateNotifier();
|
||||
private final float acquisitionRange;
|
||||
private transient static AutoAttackTargetFinderEnum autoAttackTargetFinderEnum = new AutoAttackTargetFinderEnum();
|
||||
|
||||
public CUnit(final int handleId, final int playerIndex, final float x, final float y, final float life,
|
||||
final War3ID typeId, final float facing, final float mana, final int maximumLife, final int maximumMana,
|
||||
@ -67,6 +72,7 @@ public class CUnit extends CWidget {
|
||||
this.flyHeight = unitType.getDefaultFlyingHeight();
|
||||
this.unitType = unitType;
|
||||
this.classifications.addAll(unitType.getClassifications());
|
||||
this.acquisitionRange = unitType.getDefaultAcquisitionRange();
|
||||
}
|
||||
|
||||
public void setUnitAnimationListener(final CUnitAnimationListener unitAnimationListener) {
|
||||
@ -172,9 +178,8 @@ public class CUnit extends CWidget {
|
||||
this.deathTurnTick = gameTurnTick;
|
||||
}
|
||||
}
|
||||
else if (game
|
||||
.getGameTurnTick() > (this.deathTurnTick + (int) (game.getGameplayConstants().getBoneDecayTime()
|
||||
/ WarsmashConstants.SIMULATION_STEP_TIME))) {
|
||||
else if (game.getGameTurnTick() > (this.deathTurnTick
|
||||
+ (int) (getEndingDecayTime(game) / WarsmashConstants.SIMULATION_STEP_TIME))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -189,9 +194,33 @@ public class CUnit extends CWidget {
|
||||
this.unitAnimationListener.playAnimation(true, PrimaryTag.STAND, SequenceUtils.EMPTY, 1.0f, true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// check to auto acquire targets
|
||||
if (!this.unitType.getAttacks().isEmpty()) {
|
||||
if (this.collisionRectangle != null) {
|
||||
tempRect.set(this.collisionRectangle);
|
||||
}
|
||||
else {
|
||||
tempRect.set(this.getX(), this.getY(), 0, 0);
|
||||
}
|
||||
final float halfSize = this.acquisitionRange;
|
||||
tempRect.x -= halfSize;
|
||||
tempRect.y -= halfSize;
|
||||
tempRect.width += halfSize * 2;
|
||||
tempRect.height += halfSize * 2;
|
||||
game.getWorldCollision().enumUnitsInRect(tempRect, autoAttackTargetFinderEnum.reset(game, this));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public float getEndingDecayTime(final CSimulation game) {
|
||||
if (this.unitType.isBuilding()) {
|
||||
return game.getGameplayConstants().getStructureDecayTime();
|
||||
}
|
||||
return game.getGameplayConstants().getBoneDecayTime();
|
||||
}
|
||||
|
||||
public void order(final COrder order, final boolean queue) {
|
||||
if (isDead()) {
|
||||
return;
|
||||
@ -349,8 +378,23 @@ public class CUnit extends CWidget {
|
||||
this.life -= trueDamage;
|
||||
simulation.unitDamageEvent(this, weaponType, this.unitType.getArmorType());
|
||||
this.stateNotifier.lifeChanged();
|
||||
if (!wasDead && isDead() && !this.unitType.isBuilding()) {
|
||||
kill(simulation);
|
||||
if (isDead()) {
|
||||
if (!wasDead && !this.unitType.isBuilding()) {
|
||||
kill(simulation);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this.currentOrder == null) {
|
||||
if (!simulation.getPlayer(getPlayerIndex()).hasAlliance(source.getPlayerIndex(),
|
||||
CAllianceType.PASSIVE)) {
|
||||
for (final CUnitAttack attack : this.unitType.getAttacks()) {
|
||||
if (source.canBeTargetedBy(simulation, this, attack.getTargetsAllowed())) {
|
||||
this.order(new CAttackOrder(this, attack, source), false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -478,4 +522,35 @@ public class CUnit extends CWidget {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isMovementDisabled() {
|
||||
return this.unitType.isBuilding();
|
||||
}
|
||||
|
||||
private static final class AutoAttackTargetFinderEnum implements CUnitEnumFunction {
|
||||
private CSimulation game;
|
||||
private CUnit source;
|
||||
|
||||
private AutoAttackTargetFinderEnum reset(final CSimulation game, final CUnit source) {
|
||||
this.game = game;
|
||||
this.source = source;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean call(final CUnit unit) {
|
||||
if (!this.game.getPlayer(this.source.getPlayerIndex()).hasAlliance(unit.getPlayerIndex(),
|
||||
CAllianceType.PASSIVE)) {
|
||||
for (final CUnitAttack attack : this.source.unitType.getAttacks()) {
|
||||
if (this.source.canReach(unit, this.source.acquisitionRange)
|
||||
&& unit.canBeTargetedBy(this.game, this.source, attack.getTargetsAllowed())
|
||||
&& (this.source.distance(unit) >= this.source.getUnitType().getMinimumAttackRange())) {
|
||||
this.source.order(new CAttackOrder(this.source, attack, unit), false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,11 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation;
|
||||
|
||||
public interface CUnitEnumFunction {
|
||||
/**
|
||||
* Operates on a unit, returning true if we should stop execution.
|
||||
*
|
||||
* @param unit
|
||||
* @return
|
||||
*/
|
||||
boolean call(CUnit unit);
|
||||
}
|
||||
|
@ -33,12 +33,15 @@ public class CUnitType {
|
||||
// game data? can we store it in a cleaner way?
|
||||
private final BufferedImage buildingPathingPixelMap;
|
||||
private final EnumSet<CTargetType> targetedAs;
|
||||
private final float defaultAcquisitionRange;
|
||||
private final float minimumAttackRange;
|
||||
|
||||
public CUnitType(final String name, final boolean isBldg, final MovementType movementType,
|
||||
final float defaultFlyingHeight, final float collisionSize,
|
||||
final EnumSet<CUnitClassification> classifications, final List<CUnitAttack> attacks, final String armorType,
|
||||
final boolean raise, final boolean decay, final CDefenseType defenseType, final float impactZ,
|
||||
final BufferedImage buildingPathingPixelMap, final float deathTime, final EnumSet<CTargetType> targetedAs) {
|
||||
final BufferedImage buildingPathingPixelMap, final float deathTime, final EnumSet<CTargetType> targetedAs,
|
||||
final float defaultAcquisitionRange, final float minimumAttackRange) {
|
||||
this.name = name;
|
||||
this.building = isBldg;
|
||||
this.movementType = movementType;
|
||||
@ -54,6 +57,8 @@ public class CUnitType {
|
||||
this.buildingPathingPixelMap = buildingPathingPixelMap;
|
||||
this.deathTime = deathTime;
|
||||
this.targetedAs = targetedAs;
|
||||
this.defaultAcquisitionRange = defaultAcquisitionRange;
|
||||
this.minimumAttackRange = minimumAttackRange;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@ -115,4 +120,12 @@ public class CUnitType {
|
||||
public EnumSet<CTargetType> getTargetedAs() {
|
||||
return this.targetedAs;
|
||||
}
|
||||
|
||||
public float getDefaultAcquisitionRange() {
|
||||
return this.defaultAcquisitionRange;
|
||||
}
|
||||
|
||||
public float getMinimumAttackRange() {
|
||||
return this.minimumAttackRange;
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,10 @@ public enum CAttackType implements CodeKeyType {
|
||||
}
|
||||
|
||||
public static CAttackType parseAttackType(final String attackTypeString) {
|
||||
return valueOf(attackTypeString.toUpperCase());
|
||||
final String upperCaseAttackType = attackTypeString.toUpperCase();
|
||||
if ("SEIGE".equals(upperCaseAttackType)) {
|
||||
return SIEGE;
|
||||
}
|
||||
return valueOf(upperCaseAttackType);
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,10 @@ public enum CDefenseType implements CodeKeyType {
|
||||
}
|
||||
|
||||
public static CDefenseType parseDefenseType(final String typeString) {
|
||||
return valueOf(typeString.toUpperCase());
|
||||
final String upperCaseTypeString = typeString.toUpperCase();
|
||||
if (upperCaseTypeString.equals("HEAVY")) {
|
||||
return LARGE;
|
||||
}
|
||||
return valueOf(upperCaseTypeString);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,27 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.data;
|
||||
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.util.ImageUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
|
||||
public class CAbilityData {
|
||||
private static final War3ID ABILITY_ICON = War3ID.fromString("aart");
|
||||
private final MutableObjectData abilityData;
|
||||
|
||||
public CAbilityData(final MutableObjectData abilityData) {
|
||||
this.abilityData = abilityData;
|
||||
}
|
||||
|
||||
public String getIconPath(final War3ID id, final int level) {
|
||||
final MutableGameObject mutableGameObject = this.abilityData.get(id);
|
||||
if (mutableGameObject == null) {
|
||||
return ImageUtils.DEFAULT_ICON_PATH;
|
||||
}
|
||||
final String iconPath = mutableGameObject.getFieldAsString(ABILITY_ICON, level);
|
||||
if ((iconPath == null) || "".equals(iconPath)) {
|
||||
return ImageUtils.DEFAULT_ICON_PATH;
|
||||
}
|
||||
return iconPath;
|
||||
}
|
||||
}
|
||||
|
@ -103,6 +103,9 @@ public class CUnitData {
|
||||
private static final War3ID ATTACK2_WEAPON_SOUND = War3ID.fromString("ucs2");
|
||||
private static final War3ID ATTACK2_WEAPON_TYPE = War3ID.fromString("ua2w");
|
||||
|
||||
private static final War3ID ACQUISITION_RANGE = War3ID.fromString("uacq");
|
||||
private static final War3ID MINIMUM_ATTACK_RANGE = War3ID.fromString("uamn");
|
||||
|
||||
private static final War3ID PROJECTILE_IMPACT_Z = War3ID.fromString("uimz");
|
||||
|
||||
private static final War3ID DEATH_TYPE = War3ID.fromString("udea");
|
||||
@ -160,6 +163,8 @@ public class CUnitData {
|
||||
final boolean isBldg = unitType.getFieldAsBoolean(IS_BLDG, 0);
|
||||
final PathingGrid.MovementType movementType = PathingGrid.getMovementType(movetp);
|
||||
final String unitName = unitType.getFieldAsString(NAME, 0);
|
||||
final float acquisitionRange = unitType.getFieldAsFloat(ACQUISITION_RANGE, 0);
|
||||
final float minimumAttackRange = unitType.getFieldAsFloat(MINIMUM_ATTACK_RANGE, 0);
|
||||
final EnumSet<CTargetType> targetedAs = CTargetType
|
||||
.parseTargetTypeSet(unitType.getFieldAsString(TARGETED_AS, 0));
|
||||
final String classificationString = unitType.getFieldAsString(CLASSIFICATION, 0);
|
||||
@ -269,7 +274,7 @@ public class CUnitData {
|
||||
final float deathTime = unitType.getFieldAsFloat(DEATH_TIME, 0);
|
||||
unitTypeInstance = new CUnitType(unitName, isBldg, movementType, moveHeight, collisionSize, classifications,
|
||||
attacks, armorType, raise, decay, defenseType, impactZ, buildingPathingPixelMap, deathTime,
|
||||
targetedAs);
|
||||
targetedAs, acquisitionRange, minimumAttackRange);
|
||||
this.unitIdToUnitType.put(typeId, unitTypeInstance);
|
||||
}
|
||||
return unitTypeInstance;
|
||||
|
@ -29,11 +29,16 @@ public class CAttackOrder implements COrder {
|
||||
}
|
||||
|
||||
private void createMoveOrder(final CUnit unit, final CWidget target) {
|
||||
if ((target instanceof CUnit) && !(((CUnit) target).getUnitType().isBuilding())) {
|
||||
this.moveOrder = new CMoveOrder(unit, (CUnit) target);
|
||||
if (!unit.isMovementDisabled()) { // TODO: Check mobility instead
|
||||
if ((target instanceof CUnit) && !(((CUnit) target).getUnitType().isBuilding())) {
|
||||
this.moveOrder = new CMoveOrder(unit, (CUnit) target);
|
||||
}
|
||||
else {
|
||||
this.moveOrder = new CMoveOrder(unit, target.getX(), target.getY());
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.moveOrder = new CMoveOrder(unit, target.getX(), target.getY());
|
||||
this.moveOrder = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +56,11 @@ public class CAttackOrder implements COrder {
|
||||
*/)) {
|
||||
range += this.unitAttack.getRangeMotionBuffer();
|
||||
}
|
||||
if (!this.unit.canReach(this.target, range)) {
|
||||
if (!this.unit.canReach(this.target, range)
|
||||
|| (this.unit.distance(this.target) < this.unit.getUnitType().getMinimumAttackRange())) {
|
||||
if (this.moveOrder == null) {
|
||||
return true;
|
||||
}
|
||||
if (this.moveOrder.update(simulation)) {
|
||||
return true; // we just cant reach them
|
||||
}
|
||||
@ -61,46 +70,51 @@ public class CAttackOrder implements COrder {
|
||||
return false;
|
||||
}
|
||||
this.wasInRange = true;
|
||||
final float prevX = this.unit.getX();
|
||||
final float prevY = this.unit.getY();
|
||||
final float deltaY = this.target.getY() - prevY;
|
||||
final float deltaX = this.target.getX() - prevX;
|
||||
final double goalAngleRad = Math.atan2(deltaY, deltaX);
|
||||
float goalAngle = (float) Math.toDegrees(goalAngleRad);
|
||||
if (goalAngle < 0) {
|
||||
goalAngle += 360;
|
||||
}
|
||||
float facing = this.unit.getFacing();
|
||||
float delta = goalAngle - facing;
|
||||
final float propulsionWindow = simulation.getGameplayConstants().getAttackHalfAngle();
|
||||
final float turnRate = simulation.getUnitData().getTurnRate(this.unit.getTypeId());
|
||||
|
||||
if (delta < -180) {
|
||||
delta = 360 + delta;
|
||||
}
|
||||
if (delta > 180) {
|
||||
delta = -360 + delta;
|
||||
}
|
||||
final float absDelta = Math.abs(delta);
|
||||
|
||||
if ((absDelta <= 1.0) && (absDelta != 0)) {
|
||||
this.unit.setFacing(goalAngle);
|
||||
}
|
||||
else {
|
||||
float angleToAdd = Math.signum(delta) * (float) Math.toDegrees(turnRate);
|
||||
if (absDelta < Math.abs(angleToAdd)) {
|
||||
angleToAdd = delta;
|
||||
if (!this.unit.isMovementDisabled()) {
|
||||
final float prevX = this.unit.getX();
|
||||
final float prevY = this.unit.getY();
|
||||
final float deltaY = this.target.getY() - prevY;
|
||||
final float deltaX = this.target.getX() - prevX;
|
||||
final double goalAngleRad = Math.atan2(deltaY, deltaX);
|
||||
float goalAngle = (float) Math.toDegrees(goalAngleRad);
|
||||
if (goalAngle < 0) {
|
||||
goalAngle += 360;
|
||||
}
|
||||
float facing = this.unit.getFacing();
|
||||
float delta = goalAngle - facing;
|
||||
final float propulsionWindow = simulation.getGameplayConstants().getAttackHalfAngle();
|
||||
final float turnRate = simulation.getUnitData().getTurnRate(this.unit.getTypeId());
|
||||
|
||||
if (delta < -180) {
|
||||
delta = 360 + delta;
|
||||
}
|
||||
if (delta > 180) {
|
||||
delta = -360 + delta;
|
||||
}
|
||||
final float absDelta = Math.abs(delta);
|
||||
|
||||
if ((absDelta <= 1.0) && (absDelta != 0)) {
|
||||
this.unit.setFacing(goalAngle);
|
||||
}
|
||||
else {
|
||||
float angleToAdd = Math.signum(delta) * (float) Math.toDegrees(turnRate);
|
||||
if (absDelta < Math.abs(angleToAdd)) {
|
||||
angleToAdd = delta;
|
||||
}
|
||||
facing += angleToAdd;
|
||||
this.unit.setFacing(facing);
|
||||
}
|
||||
if (absDelta < propulsionWindow) {
|
||||
this.wasWithinPropWindow = true;
|
||||
}
|
||||
else {
|
||||
// If this happens, the unit is facing the wrong way, and has to turn before
|
||||
// moving.
|
||||
this.wasWithinPropWindow = false;
|
||||
}
|
||||
facing += angleToAdd;
|
||||
this.unit.setFacing(facing);
|
||||
}
|
||||
if (absDelta < propulsionWindow) {
|
||||
this.wasWithinPropWindow = true;
|
||||
}
|
||||
else {
|
||||
// If this happens, the unit is facing the wrong way, and has to turn before
|
||||
// moving.
|
||||
this.wasWithinPropWindow = false;
|
||||
this.wasWithinPropWindow = true;
|
||||
}
|
||||
|
||||
final int cooldownEndTime = this.unit.getCooldownEndTime();
|
||||
@ -108,9 +122,22 @@ public class CAttackOrder implements COrder {
|
||||
if (this.wasWithinPropWindow) {
|
||||
if (this.damagePointLaunchTime != 0) {
|
||||
if (currentTurnTick >= this.damagePointLaunchTime) {
|
||||
final int minDamage = this.unitAttack.getMinDamage();
|
||||
final int maxDamage = this.unitAttack.getMaxDamage();
|
||||
final int damage = simulation.getSeededRandom().nextInt(maxDamage - minDamage) + minDamage;
|
||||
int minDamage = this.unitAttack.getMinDamage();
|
||||
final int maxDamage = Math.max(0, this.unitAttack.getMaxDamage());
|
||||
if (minDamage > maxDamage) {
|
||||
minDamage = maxDamage;
|
||||
}
|
||||
final int damage;
|
||||
if (maxDamage == 0) {
|
||||
damage = 0;
|
||||
}
|
||||
else if (minDamage == maxDamage) {
|
||||
damage = minDamage;
|
||||
}
|
||||
else {
|
||||
damage = simulation.getSeededRandom().nextInt(maxDamage - minDamage) + minDamage;
|
||||
}
|
||||
System.out.println(damage + " from " + minDamage + " to " + maxDamage);
|
||||
this.unitAttack.launch(simulation, this.unit, this.target, damage);
|
||||
this.damagePointLaunchTime = 0;
|
||||
}
|
||||
|
@ -86,6 +86,7 @@ public class MeleeUI implements CUnitStateListener {
|
||||
|
||||
// TODO remove this & replace with FDF
|
||||
private final Texture activeButtonTexture;
|
||||
private UIFrame inventoryCover;
|
||||
|
||||
public MeleeUI(final DataSource dataSource, final Viewport uiViewport, final FreeTypeFontGenerator fontGenerator,
|
||||
final Scene uiScene, final War3MapViewer war3MapViewer, final RootFrameListener rootFrameListener) {
|
||||
@ -110,6 +111,7 @@ public class MeleeUI implements CUnitStateListener {
|
||||
// =================================
|
||||
this.rootFrame = new GameUI(this.dataSource, GameUI.loadSkin(this.dataSource, 0), this.uiViewport,
|
||||
this.fontGenerator, this.uiScene, this.war3MapViewer);
|
||||
this.rootFrameListener.onCreate(this.rootFrame);
|
||||
try {
|
||||
this.rootFrame.loadTOCFile("UI\\FrameDef\\FrameDef.toc");
|
||||
}
|
||||
@ -142,11 +144,10 @@ public class MeleeUI implements CUnitStateListener {
|
||||
this.resourceBarLumberText = (StringFrame) this.rootFrame.getFrameByName("ResourceBarLumberText", 0);
|
||||
this.resourceBarLumberText.setText("150");
|
||||
this.resourceBarSupplyText = (StringFrame) this.rootFrame.getFrameByName("ResourceBarSupplyText", 0);
|
||||
this.resourceBarSupplyText.setText("153/100");
|
||||
this.resourceBarSupplyText.setColor(Color.RED);
|
||||
this.resourceBarSupplyText.setText("12/100");
|
||||
this.resourceBarUpkeepText = (StringFrame) this.rootFrame.getFrameByName("ResourceBarUpkeepText", 0);
|
||||
this.resourceBarUpkeepText.setText("High Upkeep");
|
||||
this.resourceBarUpkeepText.setColor(Color.RED);
|
||||
this.resourceBarUpkeepText.setText("No Upkeep");
|
||||
this.resourceBarUpkeepText.setColor(Color.GREEN);
|
||||
|
||||
// Create the Time Indicator (clock)
|
||||
this.timeIndicator = (SpriteFrame) this.rootFrame.createFrame("TimeOfDayIndicator", this.rootFrame, 0, 0);
|
||||
@ -195,6 +196,8 @@ public class MeleeUI implements CUnitStateListener {
|
||||
this.armorInfoPanelIconValue = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconValue", 0);
|
||||
this.armorInfoPanelIconLevel = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconLevel", 0);
|
||||
|
||||
this.inventoryCover = this.rootFrame.createSimpleFrame("SmashConsoleInventoryCover", this.rootFrame, 0);
|
||||
|
||||
this.rootFrame.positionBounds(this.uiViewport);
|
||||
selectUnit(null);
|
||||
}
|
||||
|
@ -84,9 +84,9 @@ public class DesktopLauncher {
|
||||
config.gles30ContextMajorVersion = 3;
|
||||
config.gles30ContextMinorVersion = 3;
|
||||
config.samples = 16;
|
||||
config.vSyncEnabled = false;
|
||||
config.foregroundFPS = 0;
|
||||
config.backgroundFPS = 0;
|
||||
// config.vSyncEnabled = false;
|
||||
// config.foregroundFPS = 0;
|
||||
// config.backgroundFPS = 0;
|
||||
if ((arg.length > 0) && "-windowed".equals(arg[0])) {
|
||||
config.fullscreen = false;
|
||||
}
|
||||
|
@ -1,2 +1,3 @@
|
||||
UI\FrameDef\SmashUI\TimeOfDayIndicator.fdf
|
||||
UI\FrameDef\SmashUI\UnitPortrait.fdf
|
||||
UI\FrameDef\SmashUI\InventoryCover.fdf
|
||||
|
14
resources/UI/FrameDef/SmashUI/InventoryCover.fdf
Normal file
14
resources/UI/FrameDef/SmashUI/InventoryCover.fdf
Normal file
@ -0,0 +1,14 @@
|
||||
Frame "SIMPLEFRAME" "SmashConsoleInventoryCover" {
|
||||
DecorateFileNames,
|
||||
SetAllPoints,
|
||||
|
||||
// The top of the UI console
|
||||
Texture "SmashConsoleInventoryCoverTexture" {
|
||||
File "ConsoleInventoryCoverTexture",
|
||||
Width 0.128,
|
||||
Height 0.256,
|
||||
AlphaMode "ALPHAKEY",
|
||||
Anchor BOTTOMLEFT,0.472,0.0,
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user