mirror of
https://github.com/GTNewHorizons/CodeChickenCore.git
synced 2026-11-11 07:47:47 +00:00
Initial Commit
This commit is contained in:
1 parent
397135f687
commit
0f32dd8620
82 files changed
+10693
No files matched your search
@@ -1,2 +1,4 @@
|
||||
CodeChickenLib
|
||||
==============
|
||||
Formerly known as CodeChickenCore-Public, this is a library of systems to help make various aspects of minecraft modding easier.
|
||||
It contains libraries for 3D math and transformations, model rendering, packets, config, colours, asm and a few other things.
|
||||
@@ -0,0 +1,332 @@
|
||||
package codechicken.lib.asm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.FieldNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.LabelNode;
|
||||
import org.objectweb.asm.tree.LocalVariableNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
import org.objectweb.asm.tree.TryCatchBlockNode;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import codechicken.lib.asm.InstructionComparator.InsnListSection;
|
||||
|
||||
public class ASMHelper
|
||||
{
|
||||
public static class CodeBlock
|
||||
{
|
||||
public Label start = new Label();
|
||||
public Label end = new Label();
|
||||
}
|
||||
|
||||
public static class ForBlock extends CodeBlock
|
||||
{
|
||||
public Label cmp = new Label();
|
||||
public Label inc = new Label();
|
||||
public Label body = new Label();
|
||||
}
|
||||
|
||||
public static abstract class MethodAltercator
|
||||
{
|
||||
public final ObfMapping method;
|
||||
|
||||
public MethodAltercator(ObfMapping method)
|
||||
{
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public abstract void alter(MethodNode mv);
|
||||
}
|
||||
|
||||
public static abstract class MethodWriter
|
||||
{
|
||||
public final int access;
|
||||
public final ObfMapping method;
|
||||
public final String[] exceptions;
|
||||
|
||||
public MethodWriter(int access, ObfMapping method)
|
||||
{
|
||||
this(access, method, null);
|
||||
}
|
||||
|
||||
public MethodWriter(int access, ObfMapping method, String[] exceptions)
|
||||
{
|
||||
this.access = access;
|
||||
this.method = method;
|
||||
this.exceptions = exceptions;
|
||||
}
|
||||
|
||||
public abstract void write(MethodNode mv);
|
||||
}
|
||||
|
||||
public static class MethodInjector
|
||||
{
|
||||
public final ObfMapping method;
|
||||
public final InsnList needle;
|
||||
public final InsnList injection;
|
||||
public final boolean before;
|
||||
|
||||
public MethodInjector(ObfMapping method, InsnList needle, InsnList injection, boolean before)
|
||||
{
|
||||
this.method = method;
|
||||
this.needle = needle;
|
||||
this.injection = injection;
|
||||
this.before = before;
|
||||
}
|
||||
}
|
||||
|
||||
public static MethodNode findMethod(ObfMapping methodmap, ClassNode cnode)
|
||||
{
|
||||
for(MethodNode mnode : cnode.methods)
|
||||
if(methodmap.matches(mnode))
|
||||
return mnode;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static FieldNode findField(ObfMapping fieldmap, ClassNode cnode)
|
||||
{
|
||||
for(FieldNode fnode : cnode.fields)
|
||||
if(fieldmap.matches(fnode))
|
||||
return fnode;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ClassNode createClassNode(byte[] bytes)
|
||||
{
|
||||
return createClassNode(bytes, 0);
|
||||
}
|
||||
|
||||
public static ClassNode createClassNode(byte[] bytes, int flags)
|
||||
{
|
||||
ClassNode cnode = new ClassNode();
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
reader.accept(cnode, flags);
|
||||
return cnode;
|
||||
}
|
||||
|
||||
public static byte[] createBytes(ClassNode cnode, int flags)
|
||||
{
|
||||
ClassWriter cw = new CC_ClassWriter(flags);
|
||||
cnode.accept(cw);
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
public static byte[] writeMethods(String name, byte[] bytes, Multimap<String, MethodWriter> writers)
|
||||
{
|
||||
if(writers.containsKey(name))
|
||||
{
|
||||
ClassNode cnode = createClassNode(bytes);
|
||||
|
||||
for(MethodWriter mw : writers.get(name))
|
||||
{
|
||||
MethodNode mv = findMethod(mw.method, cnode);
|
||||
if(mv == null)
|
||||
mv = (MethodNode) cnode.visitMethod(mw.access, mw.method.s_name, mw.method.s_desc, null, mw.exceptions);
|
||||
|
||||
mv.access = mw.access;
|
||||
mv.instructions.clear();
|
||||
mw.write(mv);
|
||||
}
|
||||
|
||||
bytes = createBytes(cnode, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static byte[] injectMethods(String name, byte[] bytes, Multimap<String, MethodInjector> injectors)
|
||||
{
|
||||
if(injectors.containsKey(name))
|
||||
{
|
||||
ClassNode cnode = createClassNode(bytes);
|
||||
|
||||
for(MethodInjector injector : injectors.get(name))
|
||||
{
|
||||
MethodNode method = findMethod(injector.method, cnode);
|
||||
if(method == null)
|
||||
throw new RuntimeException("Method not found: "+injector.method);
|
||||
System.out.println("Injecting into " + injector.method + "\n" + printInsnList(injector.injection));
|
||||
|
||||
List<AbstractInsnNode> callNodes;
|
||||
if(injector.before)
|
||||
callNodes = InstructionComparator.insnListFindStart(method.instructions, injector.needle);
|
||||
else
|
||||
callNodes = InstructionComparator.insnListFindEnd(method.instructions, injector.needle);
|
||||
|
||||
if(callNodes.size() == 0)
|
||||
{
|
||||
throw new RuntimeException("Needle not found in Haystack: " + injector.method+"\n" + printInsnList(injector.needle));
|
||||
}
|
||||
|
||||
for(AbstractInsnNode node : callNodes)
|
||||
{
|
||||
if(injector.before)
|
||||
{
|
||||
System.out.println("Injected before: "+printInsn(node));
|
||||
method.instructions.insertBefore(node, cloneInsnList(injector.injection));
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Injected after: "+printInsn(node));
|
||||
method.instructions.insert(node, cloneInsnList(injector.injection));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bytes = createBytes(cnode, ClassWriter.COMPUTE_FRAMES);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static String printInsnList(InsnList list)
|
||||
{
|
||||
InsnListPrinter p = new InsnListPrinter();
|
||||
p.visitInsnList(list);
|
||||
return p.textString();
|
||||
}
|
||||
|
||||
public static String printInsn(AbstractInsnNode insn)
|
||||
{
|
||||
InsnListPrinter p = new InsnListPrinter();
|
||||
p.visitInsn(insn);
|
||||
return p.textString();
|
||||
}
|
||||
|
||||
public static Map<LabelNode, LabelNode> cloneLabels(InsnList insns)
|
||||
{
|
||||
HashMap<LabelNode, LabelNode> labelMap = new HashMap<LabelNode, LabelNode>();
|
||||
for(AbstractInsnNode insn = insns.getFirst(); insn != null; insn = insn.getNext())
|
||||
if(insn.getType() == 8)
|
||||
labelMap.put((LabelNode) insn, new LabelNode());
|
||||
return labelMap;
|
||||
}
|
||||
|
||||
public static InsnList cloneInsnList(InsnList insns)
|
||||
{
|
||||
return cloneInsnList(cloneLabels(insns), insns);
|
||||
}
|
||||
|
||||
public static InsnList cloneInsnList(Map<LabelNode, LabelNode> labelMap, InsnList insns)
|
||||
{
|
||||
InsnList clone = new InsnList();
|
||||
for(AbstractInsnNode insn = insns.getFirst(); insn != null; insn = insn.getNext())
|
||||
clone.add(insn.clone(labelMap));
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public static List<TryCatchBlockNode> cloneTryCatchBlocks(Map<LabelNode, LabelNode> labelMap, List<TryCatchBlockNode> tcblocks)
|
||||
{
|
||||
ArrayList<TryCatchBlockNode> clone = new ArrayList<TryCatchBlockNode>();
|
||||
for(TryCatchBlockNode node : tcblocks)
|
||||
clone.add(new TryCatchBlockNode(
|
||||
labelMap.get(node.start),
|
||||
labelMap.get(node.end),
|
||||
labelMap.get(node.handler),
|
||||
node.type));
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public static List<LocalVariableNode> cloneLocals(Map<LabelNode, LabelNode> labelMap, List<LocalVariableNode> locals)
|
||||
{
|
||||
ArrayList<LocalVariableNode> clone = new ArrayList<LocalVariableNode>();
|
||||
for(LocalVariableNode node : locals)
|
||||
clone.add(new LocalVariableNode(
|
||||
node.name, node.desc, node.signature,
|
||||
labelMap.get(node.start),
|
||||
labelMap.get(node.end),
|
||||
node.index));
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public static void copy(MethodNode src, MethodNode dst)
|
||||
{
|
||||
Map<LabelNode, LabelNode> labelMap = cloneLabels(src.instructions);
|
||||
dst.instructions = cloneInsnList(labelMap, src.instructions);
|
||||
dst.tryCatchBlocks = cloneTryCatchBlocks(labelMap, src.tryCatchBlocks);
|
||||
if(src.localVariables != null)
|
||||
dst.localVariables = cloneLocals(labelMap, src.localVariables);
|
||||
dst.visibleAnnotations = src.visibleAnnotations;
|
||||
dst.invisibleAnnotations = src.invisibleAnnotations;
|
||||
dst.visitMaxs(src.maxStack, src.maxLocals);
|
||||
}
|
||||
|
||||
public static byte[] alterMethods(String name, byte[] bytes, HashMultimap<String, MethodAltercator> altercators)
|
||||
{
|
||||
if(altercators.containsKey(name))
|
||||
{
|
||||
ClassNode cnode = createClassNode(bytes);
|
||||
|
||||
for(MethodAltercator injector : altercators.get(name))
|
||||
{
|
||||
MethodNode method = findMethod(injector.method, cnode);
|
||||
if(method == null)
|
||||
throw new RuntimeException("Method not found: "+injector.method);
|
||||
|
||||
injector.alter(method);
|
||||
}
|
||||
|
||||
bytes = createBytes(cnode, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
public static String printInsnList(InsnListSection subsection)
|
||||
{
|
||||
InsnListPrinter p = new InsnListPrinter();
|
||||
p.visitInsnList(subsection);
|
||||
return p.textString();
|
||||
}
|
||||
|
||||
public static int getLocal(List<LocalVariableNode> list, String name)
|
||||
{
|
||||
int found = -1;
|
||||
for(LocalVariableNode node : list)
|
||||
{
|
||||
if(node.name.equals(name))
|
||||
{
|
||||
if(found >= 0)
|
||||
throw new RuntimeException("Duplicate local variable: "+name+" not coded to handle this scenario.");
|
||||
|
||||
found = node.index;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
public static void replaceMethodCode(MethodNode original, MethodNode replacement)
|
||||
{
|
||||
original.instructions.clear();
|
||||
if(original.localVariables != null)
|
||||
original.localVariables.clear();
|
||||
if(original.tryCatchBlocks != null)
|
||||
original.tryCatchBlocks.clear();
|
||||
replacement.accept(original);
|
||||
}
|
||||
|
||||
public static void removeBlock(InsnList insns, InsnListSection block)
|
||||
{
|
||||
AbstractInsnNode insn = block.first;
|
||||
while(true)
|
||||
{
|
||||
AbstractInsnNode next = insn.getNext();
|
||||
insns.remove(insn);
|
||||
if(insn == block.last)
|
||||
break;
|
||||
insn = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
package codechicken.lib.asm;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.IincInsnNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.InsnNode;
|
||||
import org.objectweb.asm.tree.IntInsnNode;
|
||||
import org.objectweb.asm.tree.JumpInsnNode;
|
||||
import org.objectweb.asm.tree.LabelNode;
|
||||
import org.objectweb.asm.tree.LdcInsnNode;
|
||||
import org.objectweb.asm.tree.LineNumberNode;
|
||||
import org.objectweb.asm.tree.MultiANewArrayInsnNode;
|
||||
import org.objectweb.asm.tree.VarInsnNode;
|
||||
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
import static org.objectweb.asm.tree.AbstractInsnNode.*;
|
||||
|
||||
public class ASMReader
|
||||
{
|
||||
public static class ASMBlock
|
||||
{
|
||||
public InsnList insns = new InsnList();
|
||||
private HashMap<String, LabelNode> labels = new HashMap<String, LabelNode>();
|
||||
|
||||
public LabelNode getOrAdd(String s)
|
||||
{
|
||||
LabelNode l = get(s);
|
||||
if(l == null)
|
||||
labels.put(s, l = new LabelNode());
|
||||
return l;
|
||||
}
|
||||
|
||||
public LabelNode get(String s)
|
||||
{
|
||||
return labels.get(s);
|
||||
}
|
||||
|
||||
public void replace(String s, LabelNode l)
|
||||
{
|
||||
LabelNode old = get(s);
|
||||
if(old != null)
|
||||
{
|
||||
Map<LabelNode, LabelNode> map = ImmutableMap.of(old, l);
|
||||
for(AbstractInsnNode insn = insns.getFirst(); insn != null; insn = insn.getNext())
|
||||
{
|
||||
switch(insn.getType())
|
||||
{
|
||||
case JUMP_INSN:
|
||||
case FRAME:
|
||||
case LOOKUPSWITCH_INSN:
|
||||
case TABLESWITCH_INSN:
|
||||
case LABEL:
|
||||
insns.insert(insn, insn.clone(map));
|
||||
insns.remove(insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
labels.put(s, l);
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, Integer> opCodes = new HashMap<String, Integer>();
|
||||
public static byte[] TYPE;
|
||||
|
||||
static
|
||||
{
|
||||
opCodes.put("NOP", NOP);
|
||||
opCodes.put("ACONST_NULL", ACONST_NULL);
|
||||
opCodes.put("ICONST_M1", ICONST_M1);
|
||||
opCodes.put("ICONST_0", ICONST_0);
|
||||
opCodes.put("ICONST_1", ICONST_1);
|
||||
opCodes.put("ICONST_2", ICONST_2);
|
||||
opCodes.put("ICONST_3", ICONST_3);
|
||||
opCodes.put("ICONST_4", ICONST_4);
|
||||
opCodes.put("ICONST_5", ICONST_5);
|
||||
opCodes.put("LCONST_0", LCONST_0);
|
||||
opCodes.put("LCONST_1", LCONST_1);
|
||||
opCodes.put("FCONST_0", FCONST_0);
|
||||
opCodes.put("FCONST_1", FCONST_1);
|
||||
opCodes.put("FCONST_2", FCONST_2);
|
||||
opCodes.put("DCONST_0", DCONST_0);
|
||||
opCodes.put("DCONST_1", DCONST_1);
|
||||
opCodes.put("BIPUSH", BIPUSH);
|
||||
opCodes.put("SIPUSH", SIPUSH);
|
||||
opCodes.put("LDC", LDC);
|
||||
opCodes.put("ILOAD", ILOAD);
|
||||
opCodes.put("LLOAD", LLOAD);
|
||||
opCodes.put("FLOAD", FLOAD);
|
||||
opCodes.put("DLOAD", DLOAD);
|
||||
opCodes.put("ALOAD", ALOAD);
|
||||
opCodes.put("IALOAD", IALOAD);
|
||||
opCodes.put("LALOAD", LALOAD);
|
||||
opCodes.put("FALOAD", FALOAD);
|
||||
opCodes.put("DALOAD", DALOAD);
|
||||
opCodes.put("AALOAD", AALOAD);
|
||||
opCodes.put("BALOAD", BALOAD);
|
||||
opCodes.put("CALOAD", CALOAD);
|
||||
opCodes.put("SALOAD", SALOAD);
|
||||
opCodes.put("ISTORE", ISTORE);
|
||||
opCodes.put("LSTORE", LSTORE);
|
||||
opCodes.put("FSTORE", FSTORE);
|
||||
opCodes.put("DSTORE", DSTORE);
|
||||
opCodes.put("ASTORE", ASTORE);
|
||||
opCodes.put("IASTORE", IASTORE);
|
||||
opCodes.put("LASTORE", LASTORE);
|
||||
opCodes.put("FASTORE", FASTORE);
|
||||
opCodes.put("DASTORE", DASTORE);
|
||||
opCodes.put("AASTORE", AASTORE);
|
||||
opCodes.put("BASTORE", BASTORE);
|
||||
opCodes.put("CASTORE", CASTORE);
|
||||
opCodes.put("SASTORE", SASTORE);
|
||||
opCodes.put("POP", POP);
|
||||
opCodes.put("POP2", POP2);
|
||||
opCodes.put("DUP", DUP);
|
||||
opCodes.put("DUP_X1", DUP_X1);
|
||||
opCodes.put("DUP_X2", DUP_X2);
|
||||
opCodes.put("DUP2", DUP2);
|
||||
opCodes.put("DUP2_X1", DUP2_X1);
|
||||
opCodes.put("DUP2_X2", DUP2_X2);
|
||||
opCodes.put("SWAP", SWAP);
|
||||
opCodes.put("IADD", IADD);
|
||||
opCodes.put("LADD", LADD);
|
||||
opCodes.put("FADD", FADD);
|
||||
opCodes.put("DADD", DADD);
|
||||
opCodes.put("ISUB", ISUB);
|
||||
opCodes.put("LSUB", LSUB);
|
||||
opCodes.put("FSUB", FSUB);
|
||||
opCodes.put("DSUB", DSUB);
|
||||
opCodes.put("IMUL", IMUL);
|
||||
opCodes.put("LMUL", LMUL);
|
||||
opCodes.put("FMUL", FMUL);
|
||||
opCodes.put("DMUL", DMUL);
|
||||
opCodes.put("IDIV", IDIV);
|
||||
opCodes.put("LDIV", LDIV);
|
||||
opCodes.put("FDIV", FDIV);
|
||||
opCodes.put("DDIV", DDIV);
|
||||
opCodes.put("IREM", IREM);
|
||||
opCodes.put("LREM", LREM);
|
||||
opCodes.put("FREM", FREM);
|
||||
opCodes.put("DREM", DREM);
|
||||
opCodes.put("INEG", INEG);
|
||||
opCodes.put("LNEG", LNEG);
|
||||
opCodes.put("FNEG", FNEG);
|
||||
opCodes.put("DNEG", DNEG);
|
||||
opCodes.put("ISHL", ISHL);
|
||||
opCodes.put("LSHL", LSHL);
|
||||
opCodes.put("ISHR", ISHR);
|
||||
opCodes.put("LSHR", LSHR);
|
||||
opCodes.put("IUSHR", IUSHR);
|
||||
opCodes.put("LUSHR", LUSHR);
|
||||
opCodes.put("IAND", IAND);
|
||||
opCodes.put("LAND", LAND);
|
||||
opCodes.put("IOR", IOR);
|
||||
opCodes.put("LOR", LOR);
|
||||
opCodes.put("IXOR", IXOR);
|
||||
opCodes.put("LXOR", LXOR);
|
||||
opCodes.put("IINC", IINC);
|
||||
opCodes.put("I2L", I2L);
|
||||
opCodes.put("I2F", I2F);
|
||||
opCodes.put("I2D", I2D);
|
||||
opCodes.put("L2I", L2I);
|
||||
opCodes.put("L2F", L2F);
|
||||
opCodes.put("L2D", L2D);
|
||||
opCodes.put("F2I", F2I);
|
||||
opCodes.put("F2L", F2L);
|
||||
opCodes.put("F2D", F2D);
|
||||
opCodes.put("D2I", D2I);
|
||||
opCodes.put("D2L", D2L);
|
||||
opCodes.put("D2F", D2F);
|
||||
opCodes.put("I2B", I2B);
|
||||
opCodes.put("I2C", I2C);
|
||||
opCodes.put("I2S", I2S);
|
||||
opCodes.put("LCMP", LCMP);
|
||||
opCodes.put("FCMPL", FCMPL);
|
||||
opCodes.put("FCMPG", FCMPG);
|
||||
opCodes.put("DCMPL", DCMPL);
|
||||
opCodes.put("DCMPG", DCMPG);
|
||||
opCodes.put("IFEQ", IFEQ);
|
||||
opCodes.put("IFNE", IFNE);
|
||||
opCodes.put("IFLT", IFLT);
|
||||
opCodes.put("IFGE", IFGE);
|
||||
opCodes.put("IFGT", IFGT);
|
||||
opCodes.put("IFLE", IFLE);
|
||||
opCodes.put("IF_ICMPEQ", IF_ICMPEQ);
|
||||
opCodes.put("IF_ICMPNE", IF_ICMPNE);
|
||||
opCodes.put("IF_ICMPLT", IF_ICMPLT);
|
||||
opCodes.put("IF_ICMPGE", IF_ICMPGE);
|
||||
opCodes.put("IF_ICMPGT", IF_ICMPGT);
|
||||
opCodes.put("IF_ICMPLE", IF_ICMPLE);
|
||||
opCodes.put("IF_ACMPEQ", IF_ACMPEQ);
|
||||
opCodes.put("IF_ACMPNE", IF_ACMPNE);
|
||||
opCodes.put("GOTO", GOTO);
|
||||
opCodes.put("JSR", JSR);
|
||||
opCodes.put("RET", RET);
|
||||
opCodes.put("TABLESWITCH", TABLESWITCH);
|
||||
opCodes.put("LOOKUPSWITCH", LOOKUPSWITCH);
|
||||
opCodes.put("IRETURN", IRETURN);
|
||||
opCodes.put("LRETURN", LRETURN);
|
||||
opCodes.put("FRETURN", FRETURN);
|
||||
opCodes.put("DRETURN", DRETURN);
|
||||
opCodes.put("ARETURN", ARETURN);
|
||||
opCodes.put("RETURN", RETURN);
|
||||
opCodes.put("GETSTATIC", GETSTATIC);
|
||||
opCodes.put("PUTSTATIC", PUTSTATIC);
|
||||
opCodes.put("GETFIELD", GETFIELD);
|
||||
opCodes.put("PUTFIELD", PUTFIELD);
|
||||
opCodes.put("INVOKEVIRTUAL", INVOKEVIRTUAL);
|
||||
opCodes.put("INVOKESPECIAL", INVOKESPECIAL);
|
||||
opCodes.put("INVOKESTATIC", INVOKESTATIC);
|
||||
opCodes.put("INVOKEINTERFACE", INVOKEINTERFACE);
|
||||
opCodes.put("INVOKEDYNAMIC", INVOKEDYNAMIC);
|
||||
opCodes.put("NEW", NEW);
|
||||
opCodes.put("NEWARRAY", NEWARRAY);
|
||||
opCodes.put("ANEWARRAY", ANEWARRAY);
|
||||
opCodes.put("ARRAYLENGTH", ARRAYLENGTH);
|
||||
opCodes.put("ATHROW", ATHROW);
|
||||
opCodes.put("CHECKCAST", CHECKCAST);
|
||||
opCodes.put("INSTANCEOF", INSTANCEOF);
|
||||
opCodes.put("MONITORENTER", MONITORENTER);
|
||||
opCodes.put("MONITOREXIT", MONITOREXIT);
|
||||
opCodes.put("MULTIANEWARRAY", MULTIANEWARRAY);
|
||||
opCodes.put("IFNULL", IFNULL);
|
||||
opCodes.put("IFNONNULL", IFNONNULL);
|
||||
|
||||
//derived from classWriter, mapped to AbstractInsnNode
|
||||
TYPE = new byte[200];
|
||||
String s = "AAAAAAAAAAAAAAAABBJ__CCCCC____________________AAAAAAAACC"
|
||||
+ "CCC____________________AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
+ "AAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAHHHHHHHHHHHHHHHHCLMAA"
|
||||
+ "AAAAEEEEFFFFGDBDAADDAA_NHH";
|
||||
for (int i = 0; i < s.length(); i++)
|
||||
TYPE[i] = (byte) (s.charAt(i) - 'A');
|
||||
}
|
||||
|
||||
public static Map<String, ASMBlock> loadResource(String res)
|
||||
{
|
||||
return loadResource(ASMHelper.class.getResourceAsStream(res), res);
|
||||
}
|
||||
|
||||
public static Map<String, ASMBlock> loadResource(InputStream in, String res)
|
||||
{
|
||||
HashMap<String, ASMBlock> blocks = new HashMap<String, ASMBlock>();
|
||||
String current = "unnamed";
|
||||
ASMBlock block = new ASMBlock();
|
||||
try
|
||||
{
|
||||
BufferedReader r = new BufferedReader(new InputStreamReader(in));
|
||||
String line;
|
||||
while((line = r.readLine()) != null)
|
||||
{
|
||||
{
|
||||
int hpos = line.indexOf('#');
|
||||
if(hpos >= 0) line = line.substring(0, hpos);
|
||||
}
|
||||
line = line.trim();
|
||||
if(line.length() == 0) continue;
|
||||
if(line.startsWith("list "))
|
||||
{
|
||||
if(block.insns.size() > 0) blocks.put(current, block);
|
||||
current = line.substring(5);
|
||||
block = new ASMBlock();
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
AbstractInsnNode insn = null;
|
||||
String[] split = line.replace(" : ", ":").split(" ");
|
||||
Integer i_opcode = opCodes.get(split[0]);
|
||||
if(i_opcode == null)
|
||||
{
|
||||
if(split[0].equals("LINENUMBER"))
|
||||
insn = new LineNumberNode(Integer.parseInt(split[1]), block.getOrAdd(split[2]));
|
||||
else if(split[0].startsWith("L"))
|
||||
insn = block.getOrAdd(split[0]);
|
||||
else
|
||||
throw new Exception("Unknown opcode " + split[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
int opcode = i_opcode;
|
||||
switch(TYPE[opcode])
|
||||
{
|
||||
case INSN:
|
||||
insn = new InsnNode(opcode);
|
||||
break;
|
||||
case INT_INSN:
|
||||
insn = new IntInsnNode(opcode, Integer.parseInt(split[1]));
|
||||
break;
|
||||
case VAR_INSN:
|
||||
insn = new VarInsnNode(opcode, Integer.parseInt(split[1]));
|
||||
break;
|
||||
case TYPE_INSN:
|
||||
insn = new ObfMapping(split[1]).toInsn(opcode);
|
||||
break;
|
||||
case FIELD_INSN:
|
||||
case METHOD_INSN:
|
||||
insn = ObfMapping.fromDesc(split[1]).toInsn(opcode);
|
||||
break;
|
||||
case INVOKE_DYNAMIC_INSN:
|
||||
throw new Exception("Found INVOKEDYNAMIC while reading");
|
||||
case JUMP_INSN:
|
||||
insn = new JumpInsnNode(opcode, block.getOrAdd(split[1]));
|
||||
break;
|
||||
case LDC_INSN:
|
||||
String cst = split[1];
|
||||
if(cst.contains(""))
|
||||
insn = new LdcInsnNode(cst.substring(1, cst.length()-1));
|
||||
else if(cst.endsWith("L"))
|
||||
insn = new LdcInsnNode(Long.valueOf(cst.substring(0, cst.length()-1)));
|
||||
else if(cst.endsWith("F"))
|
||||
insn = new LdcInsnNode(Float.valueOf(cst.substring(0, cst.length()-1)));
|
||||
else if(cst.endsWith("D"))
|
||||
insn = new LdcInsnNode(Double.valueOf(cst.substring(0, cst.length()-1)));
|
||||
else
|
||||
insn = new LdcInsnNode(Integer.valueOf(cst));
|
||||
break;
|
||||
case IINC_INSN:
|
||||
insn = new IincInsnNode(opcode, Integer.parseInt(split[1]));
|
||||
break;
|
||||
case LABEL:
|
||||
throw new Exception("Use L# for labels");
|
||||
case TABLESWITCH_INSN:
|
||||
case LOOKUPSWITCH_INSN:
|
||||
throw new Exception("I don't know how to deal with this insn type");
|
||||
case MULTIANEWARRAY_INSN:
|
||||
insn = new MultiANewArrayInsnNode(split[1], Integer.parseInt(split[2]));
|
||||
break;
|
||||
case FRAME:
|
||||
throw new Exception("Use ClassWriter.COMPUTE_FRAMES");
|
||||
}
|
||||
}
|
||||
|
||||
if(insn != null)
|
||||
block.insns.add(insn);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
System.err.println("Error while reading ASM Block "+
|
||||
current+" from "+res+", line: "+line);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
r.close();
|
||||
if(block.insns.size() > 0) blocks.put(current, block);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException("Failed to read ASM resource: "+res, e);
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package codechicken.lib.asm;
|
||||
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
|
||||
|
||||
public class CC_ClassWriter extends ClassWriter
|
||||
{
|
||||
public CC_ClassWriter(int flags)
|
||||
{
|
||||
super(flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCommonSuperClass(String type1, String type2)
|
||||
{
|
||||
String c = type1.replace('/', '.');
|
||||
String d = type2.replace('/', '.');
|
||||
if(ClassHeirachyManager.classExtends(d, c))
|
||||
return type1;
|
||||
if(ClassHeirachyManager.classExtends(c, d))
|
||||
return type2;
|
||||
do
|
||||
{
|
||||
c = ClassHeirachyManager.getSuperClass(c);
|
||||
}
|
||||
while(!ClassHeirachyManager.classExtends(d, c));
|
||||
return c.replace('.', '/');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package codechicken.lib.asm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
import net.minecraft.launchwrapper.LaunchClassLoader;
|
||||
|
||||
/**
|
||||
* This is added as a class transformer if CodeChickenCore is installed. Adding it as a class transformer will speed evaluation up slightly by automatically caching superclasses when they are first loaded.
|
||||
*/
|
||||
public class ClassHeirachyManager implements IClassTransformer
|
||||
{
|
||||
public static HashSet<String> knownClasses = new HashSet<String>();
|
||||
public static HashMap<String, ArrayList<String>> superclasses = new HashMap<String, ArrayList<String>>();
|
||||
private static LaunchClassLoader cl = (LaunchClassLoader)ClassHeirachyManager.class.getClassLoader();
|
||||
|
||||
/**
|
||||
* Returns true if clazz extends, either directly or indirectly, superclass.
|
||||
* @param clazz The class in question
|
||||
* @param superclass The class being extended
|
||||
* @param bytes The bytes for the clazz. Only needed if not already defined.
|
||||
* @return
|
||||
*/
|
||||
public static boolean classExtends(String clazz, String superclass, byte[] bytes)
|
||||
{
|
||||
if(!knownClasses.contains(clazz))
|
||||
new ClassHeirachyManager().transform(clazz, clazz, bytes);
|
||||
|
||||
return classExtends(clazz, superclass);
|
||||
}
|
||||
|
||||
public static boolean classExtends(String clazz, String superclass)
|
||||
{
|
||||
if(clazz.equals(superclass))
|
||||
return true;
|
||||
|
||||
if(clazz.equals("java.lang.Object"))
|
||||
return false;
|
||||
|
||||
declareClass(clazz);
|
||||
|
||||
if(!superclasses.containsKey(clazz))//just can't handle this
|
||||
return false;
|
||||
|
||||
for(String s : superclasses.get(clazz))
|
||||
if(classExtends(s, superclass))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void declareClass(String clazz)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(!knownClasses.contains(clazz))
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] bytes = cl.getClassBytes(clazz);
|
||||
if(bytes != null)
|
||||
new ClassHeirachyManager().transform(clazz, clazz, bytes);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
}
|
||||
|
||||
if(!knownClasses.contains(clazz))
|
||||
{
|
||||
Class<?> aclass = Class.forName(clazz);
|
||||
|
||||
knownClasses.add(clazz);
|
||||
if(aclass.isInterface())
|
||||
addSuperclass(clazz, "java.lang.Object");
|
||||
else
|
||||
addSuperclass(clazz, aclass.getSuperclass().getName());
|
||||
for(Class<?> iclass : aclass.getInterfaces())
|
||||
addSuperclass(clazz, iclass.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(ClassNotFoundException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] transform(String name, String tname, byte[] bytes)
|
||||
{
|
||||
if (bytes == null) return null;
|
||||
if(!knownClasses.contains(name))
|
||||
{
|
||||
ClassNode node = ASMHelper.createClassNode(bytes);
|
||||
|
||||
knownClasses.add(name);
|
||||
addSuperclass(name, node.superName.replace('/', '.'));
|
||||
for(String iclass : node.interfaces)
|
||||
addSuperclass(name, iclass.replace('/', '.'));
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static void addSuperclass(String name, String superclass)
|
||||
{
|
||||
ArrayList<String> supers = superclasses.get(name);
|
||||
if(supers == null)
|
||||
superclasses.put(name, supers = new ArrayList<String>());
|
||||
supers.add(superclass);
|
||||
supers.add(new ObfMapping(superclass).toRuntime().javaClass());
|
||||
}
|
||||
|
||||
public static String getSuperClass(String c)
|
||||
{
|
||||
declareClass(c);
|
||||
if(!knownClasses.contains(c))
|
||||
return "java.lang.Object";
|
||||
return superclasses.get(c).get(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package codechicken.lib.asm;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.FieldInsnNode;
|
||||
import org.objectweb.asm.tree.FrameNode;
|
||||
import org.objectweb.asm.tree.IincInsnNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.IntInsnNode;
|
||||
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
|
||||
import org.objectweb.asm.tree.JumpInsnNode;
|
||||
import org.objectweb.asm.tree.LabelNode;
|
||||
import org.objectweb.asm.tree.LdcInsnNode;
|
||||
import org.objectweb.asm.tree.LineNumberNode;
|
||||
import org.objectweb.asm.tree.LookupSwitchInsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MultiANewArrayInsnNode;
|
||||
import org.objectweb.asm.tree.TableSwitchInsnNode;
|
||||
import org.objectweb.asm.tree.TypeInsnNode;
|
||||
import org.objectweb.asm.tree.VarInsnNode;
|
||||
import org.objectweb.asm.util.Textifier;
|
||||
|
||||
import codechicken.lib.asm.InstructionComparator.InsnListSection;
|
||||
|
||||
public class InsnListPrinter extends Textifier
|
||||
{
|
||||
private boolean buildingLabelMap = false;
|
||||
|
||||
public void visitInsnList(InsnList list)
|
||||
{
|
||||
text.clear();
|
||||
if(labelNames == null)
|
||||
labelNames = new HashMap<Label, String>();
|
||||
else
|
||||
labelNames.clear();
|
||||
|
||||
buildingLabelMap = true;
|
||||
for(AbstractInsnNode insn = list.getFirst(); insn != null; insn = insn.getNext())
|
||||
if(insn.getType() == 8)
|
||||
visitLabel(((LabelNode)insn).getLabel());
|
||||
|
||||
text.clear();
|
||||
buildingLabelMap = false;
|
||||
|
||||
for(AbstractInsnNode insn = list.getFirst(); insn != null; insn = insn.getNext())
|
||||
_visitInsn(insn);
|
||||
}
|
||||
|
||||
public void visitInsnList(InsnListSection subsection)
|
||||
{
|
||||
text.clear();
|
||||
if(labelNames == null)
|
||||
labelNames = new HashMap<Label, String>();
|
||||
else
|
||||
labelNames.clear();
|
||||
|
||||
buildingLabelMap = true;
|
||||
{
|
||||
AbstractInsnNode insn = subsection.first;
|
||||
while(true)
|
||||
{
|
||||
if(insn.getType() == 8)
|
||||
visitLabel(((LabelNode)insn).getLabel());
|
||||
if(insn == subsection.last)
|
||||
break;
|
||||
insn = insn.getNext();
|
||||
}
|
||||
}
|
||||
|
||||
text.clear();
|
||||
buildingLabelMap = false;
|
||||
|
||||
{
|
||||
AbstractInsnNode insn = subsection.first;
|
||||
while(true)
|
||||
{
|
||||
_visitInsn(insn);
|
||||
if(insn == subsection.last)
|
||||
break;
|
||||
insn = insn.getNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void visitInsn(AbstractInsnNode insn)
|
||||
{
|
||||
text.clear();
|
||||
if(labelNames == null)
|
||||
labelNames = new HashMap<Label, String>();
|
||||
else
|
||||
labelNames.clear();
|
||||
|
||||
_visitInsn(insn);
|
||||
}
|
||||
|
||||
private void _visitInsn(AbstractInsnNode insn)
|
||||
{
|
||||
switch(insn.getType())
|
||||
{
|
||||
case 0:
|
||||
visitInsn(insn.getOpcode());
|
||||
break;
|
||||
case 1:
|
||||
IntInsnNode iinsn = (IntInsnNode)insn;
|
||||
visitIntInsn(iinsn.getOpcode(), iinsn.operand);
|
||||
break;
|
||||
case 2:
|
||||
VarInsnNode vinsn = (VarInsnNode)insn;
|
||||
visitVarInsn(vinsn.getOpcode(), vinsn.var);
|
||||
break;
|
||||
case 3:
|
||||
TypeInsnNode tinsn = (TypeInsnNode)insn;
|
||||
visitTypeInsn(tinsn.getOpcode(), tinsn.desc);
|
||||
break;
|
||||
case 4:
|
||||
FieldInsnNode finsn = (FieldInsnNode)insn;
|
||||
visitFieldInsn(finsn.getOpcode(), finsn.owner, finsn.name, finsn.desc);
|
||||
break;
|
||||
case 5:
|
||||
MethodInsnNode minsn = (MethodInsnNode)insn;
|
||||
visitMethodInsn(minsn.getOpcode(), minsn.owner, minsn.name, minsn.desc);
|
||||
break;
|
||||
case 6:
|
||||
InvokeDynamicInsnNode idinsn = (InvokeDynamicInsnNode)insn;
|
||||
visitInvokeDynamicInsn(idinsn.name, idinsn.desc, idinsn.bsm, idinsn.bsmArgs);
|
||||
break;
|
||||
case 7:
|
||||
JumpInsnNode jinsn = (JumpInsnNode)insn;
|
||||
visitJumpInsn(jinsn.getOpcode(), jinsn.label.getLabel());
|
||||
break;
|
||||
case 8:
|
||||
LabelNode linsn = (LabelNode)insn;
|
||||
visitLabel(linsn.getLabel());
|
||||
break;
|
||||
case 9:
|
||||
LdcInsnNode ldcinsn = (LdcInsnNode)insn;
|
||||
visitLdcInsn(ldcinsn.cst);
|
||||
break;
|
||||
case 10:
|
||||
IincInsnNode iiinsn = (IincInsnNode)insn;
|
||||
visitIincInsn(iiinsn.var, iiinsn.incr);
|
||||
break;
|
||||
case 11:
|
||||
TableSwitchInsnNode tsinsn = (TableSwitchInsnNode)insn;
|
||||
Label[] tslables = new Label[tsinsn.labels.size()];
|
||||
for(int i = 0; i < tslables.length; i++)
|
||||
tslables[i] = tsinsn.labels.get(i).getLabel();
|
||||
visitTableSwitchInsn(tsinsn.min, tsinsn.max, tsinsn.dflt.getLabel(), tslables);
|
||||
break;
|
||||
case 12:
|
||||
LookupSwitchInsnNode lsinsn = (LookupSwitchInsnNode)insn;
|
||||
Label[] lslables = new Label[lsinsn.labels.size()];
|
||||
for(int i = 0; i < lslables.length; i++)
|
||||
lslables[i] = lsinsn.labels.get(i).getLabel();
|
||||
int[] lskeys = new int[lsinsn.keys.size()];
|
||||
for(int i = 0; i < lskeys.length; i++)
|
||||
lskeys[i] = lsinsn.keys.get(i);
|
||||
visitLookupSwitchInsn(lsinsn.dflt.getLabel(), lskeys, lslables);
|
||||
break;
|
||||
case 13:
|
||||
MultiANewArrayInsnNode ainsn = (MultiANewArrayInsnNode)insn;
|
||||
visitMultiANewArrayInsn(ainsn.desc, ainsn.dims);
|
||||
break;
|
||||
case 14:
|
||||
FrameNode fnode = (FrameNode)insn;
|
||||
switch(fnode.type)
|
||||
{
|
||||
case -1:
|
||||
case 0:
|
||||
visitFrame(fnode.type, fnode.local.size(), fnode.local.toArray(), fnode.stack.size(), fnode.stack.toArray());
|
||||
break;
|
||||
case 1:
|
||||
visitFrame(fnode.type, fnode.local.size(), fnode.local.toArray(), 0, null);
|
||||
break;
|
||||
case 2:
|
||||
visitFrame(fnode.type, fnode.local.size(), null, 0, null);
|
||||
break;
|
||||
case 3:
|
||||
visitFrame(fnode.type, 0, null, 0, null);
|
||||
break;
|
||||
case 4:
|
||||
visitFrame(fnode.type, 0, null, 1, fnode.stack.toArray());
|
||||
}
|
||||
break;
|
||||
case 15:
|
||||
LineNumberNode lnode = (LineNumberNode)insn;
|
||||
visitLineNumber(lnode.line, lnode.start.getLabel());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLabel(Label label)
|
||||
{
|
||||
if(!buildingLabelMap && !labelNames.containsKey(label))
|
||||
{
|
||||
labelNames.put(label, "LEXT"+labelNames.size());
|
||||
}
|
||||
super.visitLabel(label);
|
||||
}
|
||||
|
||||
public String textString()
|
||||
{
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
print(pw);
|
||||
pw.flush();
|
||||
return sw.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
package codechicken.lib.asm;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.FieldInsnNode;
|
||||
import org.objectweb.asm.tree.IincInsnNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.IntInsnNode;
|
||||
import org.objectweb.asm.tree.JumpInsnNode;
|
||||
import org.objectweb.asm.tree.LabelNode;
|
||||
import org.objectweb.asm.tree.LdcInsnNode;
|
||||
import org.objectweb.asm.tree.LineNumberNode;
|
||||
import org.objectweb.asm.tree.LookupSwitchInsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.TableSwitchInsnNode;
|
||||
import org.objectweb.asm.tree.TypeInsnNode;
|
||||
import org.objectweb.asm.tree.VarInsnNode;
|
||||
|
||||
import static org.objectweb.asm.tree.AbstractInsnNode.*;
|
||||
|
||||
import codechicken.lib.asm.ObfMapping;
|
||||
|
||||
public class InstructionComparator
|
||||
{
|
||||
public static class InsnListSection
|
||||
{
|
||||
public InsnListSection(AbstractInsnNode first, AbstractInsnNode last)
|
||||
{
|
||||
this.first = first;
|
||||
this.last = last;
|
||||
}
|
||||
|
||||
public InsnListSection(InsnList haystack, int start, int end)
|
||||
{
|
||||
this(haystack.get(start), haystack.get(end));
|
||||
}
|
||||
|
||||
public AbstractInsnNode first;
|
||||
public AbstractInsnNode last;
|
||||
}
|
||||
|
||||
public static boolean varInsnEqual(VarInsnNode insn1, VarInsnNode insn2)
|
||||
{
|
||||
if(insn1.var == -1 || insn2.var == -1)
|
||||
return true;
|
||||
|
||||
return insn1.var == insn2.var;
|
||||
}
|
||||
|
||||
public static boolean methodInsnEqual(AbstractInsnNode absnode, int Opcode, ObfMapping method)
|
||||
{
|
||||
if(!(absnode instanceof MethodInsnNode) || absnode.getOpcode() != Opcode)
|
||||
return false;
|
||||
|
||||
return method.matches((MethodInsnNode)absnode);
|
||||
}
|
||||
|
||||
public static boolean methodInsnEqual(MethodInsnNode insn1, MethodInsnNode insn2)
|
||||
{
|
||||
return insn1.owner.equals(insn2.owner) && insn1.name.equals(insn2.name) && insn1.desc.equals(insn2.desc);
|
||||
}
|
||||
|
||||
public static boolean fieldInsnEqual(FieldInsnNode insn1, FieldInsnNode insn2)
|
||||
{
|
||||
return insn1.owner.equals(insn2.owner) && insn1.name.equals(insn2.name) && insn1.desc.equals(insn2.desc);
|
||||
}
|
||||
|
||||
public static boolean ldcInsnEqual(LdcInsnNode insn1, LdcInsnNode insn2)
|
||||
{
|
||||
if(insn1.cst.equals("~") || insn2.cst.equals("~"))
|
||||
return true;
|
||||
|
||||
return insn1.cst.equals(insn2.cst);
|
||||
}
|
||||
|
||||
public static boolean typeInsnEqual(TypeInsnNode insn1, TypeInsnNode insn2)
|
||||
{
|
||||
if(insn1.desc.equals("~") || insn2.desc.equals("~"))
|
||||
return true;
|
||||
|
||||
return insn1.desc.equals(insn2.desc);
|
||||
}
|
||||
|
||||
public static boolean iincInsnEqual(IincInsnNode node1, IincInsnNode node2)
|
||||
{
|
||||
return node1.var == node2.var && node1.incr == node2.incr;
|
||||
}
|
||||
|
||||
public static boolean intInsnEqual(IntInsnNode node1, IntInsnNode node2)
|
||||
{
|
||||
if(node1.operand == -1 || node2.operand == -1)
|
||||
return true;
|
||||
|
||||
return node1.operand == node2.operand;
|
||||
}
|
||||
|
||||
public static boolean insnEqual(AbstractInsnNode node1, AbstractInsnNode node2)
|
||||
{
|
||||
if(node1.getOpcode() != node2.getOpcode())
|
||||
return false;
|
||||
|
||||
switch(node2.getType())
|
||||
{
|
||||
case VAR_INSN:
|
||||
return varInsnEqual((VarInsnNode) node1, (VarInsnNode) node2);
|
||||
case TYPE_INSN:
|
||||
return typeInsnEqual((TypeInsnNode) node1, (TypeInsnNode) node2);
|
||||
case FIELD_INSN:
|
||||
return fieldInsnEqual((FieldInsnNode) node1, (FieldInsnNode) node2);
|
||||
case METHOD_INSN:
|
||||
return methodInsnEqual((MethodInsnNode) node1, (MethodInsnNode) node2);
|
||||
case LDC_INSN:
|
||||
return ldcInsnEqual((LdcInsnNode) node1, (LdcInsnNode) node2);
|
||||
case IINC_INSN:
|
||||
return iincInsnEqual((IincInsnNode) node1, (IincInsnNode) node2);
|
||||
case INT_INSN:
|
||||
return intInsnEqual((IntInsnNode)node1, (IntInsnNode)node2);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static InsnList getImportantList(InsnList list)
|
||||
{
|
||||
if(list.size() == 0)
|
||||
return list;
|
||||
|
||||
HashMap<LabelNode, LabelNode> labels = new HashMap<LabelNode, LabelNode>();
|
||||
for(AbstractInsnNode insn = list.getFirst(); insn != null; insn = insn.getNext())
|
||||
{
|
||||
if(insn instanceof LabelNode)
|
||||
labels.put((LabelNode)insn, (LabelNode)insn);
|
||||
}
|
||||
|
||||
InsnList importantNodeList = new InsnList();
|
||||
for(AbstractInsnNode insn = list.getFirst(); insn != null; insn = insn.getNext())
|
||||
{
|
||||
if(insn instanceof LabelNode || insn instanceof LineNumberNode)
|
||||
continue;
|
||||
|
||||
importantNodeList.add(insn.clone(labels));
|
||||
}
|
||||
return importantNodeList;
|
||||
}
|
||||
|
||||
public static boolean insnListMatches(InsnList haystack, InsnList needle, int start)
|
||||
{
|
||||
if(haystack.size()-start < needle.size())
|
||||
return false;
|
||||
|
||||
for(int i = 0; i < needle.size(); i++)
|
||||
{
|
||||
if(!insnEqual(haystack.get(i+start), needle.get(i)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static List<Integer> insnListFind(InsnList haystack, InsnList needle)
|
||||
{
|
||||
LinkedList<Integer> list = new LinkedList<Integer>();
|
||||
for(int start = 0; start <= haystack.size()-needle.size(); start++)
|
||||
if(insnListMatches(haystack, needle, start))
|
||||
list.add(start);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<AbstractInsnNode> insnListFindStart(InsnList haystack, InsnList needle)
|
||||
{
|
||||
LinkedList<AbstractInsnNode> callNodes = new LinkedList<AbstractInsnNode>();
|
||||
for(int callPoint : insnListFind(haystack, needle))
|
||||
callNodes.add(haystack.get(callPoint));
|
||||
return callNodes;
|
||||
}
|
||||
|
||||
public static List<AbstractInsnNode> insnListFindEnd(InsnList haystack, InsnList needle)
|
||||
{
|
||||
LinkedList<AbstractInsnNode> callNodes = new LinkedList<AbstractInsnNode>();
|
||||
for(int callPoint : insnListFind(haystack, needle))
|
||||
callNodes.add(haystack.get(callPoint+needle.size()-1));
|
||||
return callNodes;
|
||||
}
|
||||
|
||||
public static List<InsnListSection> insnListFindL(InsnList haystack, InsnList needle)
|
||||
{
|
||||
HashSet<LabelNode> controlFlowLabels = new HashSet<LabelNode>();
|
||||
|
||||
for(AbstractInsnNode insn = haystack.getFirst(); insn != null; insn = insn.getNext())
|
||||
{
|
||||
switch(insn.getType())
|
||||
{
|
||||
case 8:
|
||||
case 15:
|
||||
break;
|
||||
case 7:
|
||||
JumpInsnNode jinsn = (JumpInsnNode)insn;
|
||||
controlFlowLabels.add(jinsn.label);
|
||||
break;
|
||||
case 11:
|
||||
TableSwitchInsnNode tsinsn = (TableSwitchInsnNode)insn;
|
||||
for(LabelNode label : tsinsn.labels)
|
||||
controlFlowLabels.add(label);
|
||||
break;
|
||||
case 12:
|
||||
LookupSwitchInsnNode lsinsn = (LookupSwitchInsnNode)insn;
|
||||
for(LabelNode label : lsinsn.labels)
|
||||
controlFlowLabels.add(label);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LinkedList<InsnListSection> list = new LinkedList<InsnListSection>();
|
||||
nextsection: for(int start = 0; start <= haystack.size()-needle.size(); start++)
|
||||
{
|
||||
InsnListSection section = insnListMatchesL(haystack, needle, start, controlFlowLabels);
|
||||
if(section != null)
|
||||
{
|
||||
for(InsnListSection asection : list)
|
||||
if(asection.last == section.last)
|
||||
continue nextsection;
|
||||
|
||||
list.add(section);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static InsnListSection insnListMatchesL(InsnList haystack, InsnList needle, int start, HashSet<LabelNode> controlFlowLabels)
|
||||
{
|
||||
int h = start, n = 0;
|
||||
for(;h < haystack.size() && n < needle.size(); h++)
|
||||
{
|
||||
AbstractInsnNode insn = haystack.get(h);
|
||||
if(insn.getType() == 15)
|
||||
continue;
|
||||
if(insn.getType() == 8 && !controlFlowLabels.contains(insn))
|
||||
continue;
|
||||
|
||||
if(!insnEqual(haystack.get(h), needle.get(n)))
|
||||
return null;
|
||||
n++;
|
||||
}
|
||||
if(n != needle.size())
|
||||
return null;
|
||||
|
||||
return new InsnListSection(haystack, start, h-1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package codechicken.lib.asm;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.minecraft.launchwrapper.LaunchClassLoader;
|
||||
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.commons.Remapper;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.FieldInsnNode;
|
||||
import org.objectweb.asm.tree.FieldNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
import org.objectweb.asm.tree.TypeInsnNode;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
|
||||
|
||||
public class ObfMapping
|
||||
{
|
||||
/**
|
||||
* CCC will deal with this.
|
||||
*/
|
||||
public static Remapper runtimeMapper = FMLDeobfuscatingRemapper.INSTANCE;
|
||||
public static Remapper mcpMapper = null;
|
||||
|
||||
public static final boolean obfuscated;
|
||||
|
||||
static
|
||||
{
|
||||
boolean obf = true;
|
||||
try
|
||||
{
|
||||
obf = ((LaunchClassLoader)ObfMapping.class.getClassLoader()).getClassBytes("net.minecraft.world.World") == null;
|
||||
}
|
||||
catch(IOException iox) {}
|
||||
obfuscated = obf;
|
||||
}
|
||||
|
||||
public String s_owner;
|
||||
public String s_name;
|
||||
public String s_desc;
|
||||
|
||||
public boolean runtime;
|
||||
|
||||
public ObfMapping(String owner)
|
||||
{
|
||||
this(owner, null, null);
|
||||
}
|
||||
|
||||
public ObfMapping(String owner, String name, String desc)
|
||||
{
|
||||
this.s_owner = owner;
|
||||
this.s_name = name;
|
||||
this.s_desc = desc;
|
||||
|
||||
if(s_owner.contains("."))
|
||||
throw new IllegalArgumentException(s_owner);
|
||||
|
||||
if(mcpMapper != null)
|
||||
map(mcpMapper);
|
||||
}
|
||||
|
||||
public ObfMapping(ObfMapping descmap, String subclass)
|
||||
{
|
||||
this(subclass, descmap.s_name, descmap.s_desc);
|
||||
}
|
||||
|
||||
public static ObfMapping fromDesc(String s)
|
||||
{
|
||||
int lastDot = s.lastIndexOf('.');
|
||||
if(lastDot < 0)
|
||||
return new ObfMapping(s, "", "");
|
||||
int sep = s.indexOf('(');//methods
|
||||
int sep_end = sep;
|
||||
if(sep < 0) {
|
||||
sep = s.indexOf(' ');//some stuffs
|
||||
sep_end = sep+1;
|
||||
}
|
||||
if(sep < 0) {
|
||||
sep = s.indexOf(':');//fields
|
||||
sep_end = sep+1;
|
||||
}
|
||||
if(sep < 0)
|
||||
return new ObfMapping(s.substring(0, lastDot), s.substring(lastDot + 1), "");
|
||||
|
||||
return new ObfMapping(s.substring(0, lastDot), s.substring(lastDot+1, sep), s.substring(sep_end));
|
||||
}
|
||||
|
||||
public ObfMapping subclass(String subclass)
|
||||
{
|
||||
return new ObfMapping(this, subclass);
|
||||
}
|
||||
|
||||
public boolean matches(MethodNode node)
|
||||
{
|
||||
return s_name.equals(node.name) && s_desc.equals(node.desc);
|
||||
}
|
||||
|
||||
public boolean matches(MethodInsnNode node)
|
||||
{
|
||||
return s_owner.equals(node.owner) && s_name.equals(node.name) && s_desc.equals(node.desc);
|
||||
}
|
||||
|
||||
public AbstractInsnNode toInsn(int opcode)
|
||||
{
|
||||
if(isClass())
|
||||
return new TypeInsnNode(opcode, s_owner);
|
||||
else if(isMethod())
|
||||
return new MethodInsnNode(opcode, s_owner, s_name, s_desc);
|
||||
else
|
||||
return new FieldInsnNode(opcode, s_owner, s_name, s_desc);
|
||||
}
|
||||
|
||||
public void visitTypeInsn(MethodVisitor mv, int opcode)
|
||||
{
|
||||
mv.visitTypeInsn(opcode, s_owner);
|
||||
}
|
||||
|
||||
public void visitMethodInsn(MethodVisitor mv, int opcode)
|
||||
{
|
||||
mv.visitMethodInsn(opcode, s_owner, s_name, s_desc);
|
||||
}
|
||||
|
||||
public void visitFieldInsn(MethodVisitor mv, int opcode)
|
||||
{
|
||||
mv.visitFieldInsn(opcode, s_owner, s_name, s_desc);
|
||||
}
|
||||
|
||||
public boolean isClass(String name)
|
||||
{
|
||||
return name.replace('.', '/').equals(s_owner);
|
||||
}
|
||||
|
||||
public boolean matches(String name, String desc)
|
||||
{
|
||||
return s_name.equals(name) && s_desc.equals(desc);
|
||||
}
|
||||
|
||||
public boolean matches(FieldNode node)
|
||||
{
|
||||
return s_name.equals(node.name) && s_desc.equals(node.desc);
|
||||
}
|
||||
|
||||
public boolean matches(FieldInsnNode node)
|
||||
{
|
||||
return s_owner.equals(node.owner) && s_name.equals(node.name) && s_desc.equals(node.desc);
|
||||
}
|
||||
|
||||
public String javaClass()
|
||||
{
|
||||
return s_owner.replace('/', '.');
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if(!(obj instanceof ObfMapping))
|
||||
return false;
|
||||
|
||||
ObfMapping desc = (ObfMapping)obj;
|
||||
return s_owner.equals(desc.s_owner) && s_name.equals(desc.s_name) && s_desc.equals(desc.s_desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(s_desc, s_name, s_owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
if(s_name.length() == 0)
|
||||
return "["+s_owner+"]";
|
||||
if(s_desc.length() == 0)
|
||||
return "["+s_owner+"."+s_name+"]";
|
||||
return "["+s_owner+"."+s_name+s_desc+"]";
|
||||
}
|
||||
|
||||
public boolean isClass()
|
||||
{
|
||||
return s_name.length() == 0;
|
||||
}
|
||||
|
||||
public boolean isMethod()
|
||||
{
|
||||
return s_desc.contains("(");
|
||||
}
|
||||
|
||||
public boolean isField()
|
||||
{
|
||||
return !isClass() && !isMethod();
|
||||
}
|
||||
|
||||
public ObfMapping map(Remapper mapper)
|
||||
{
|
||||
if(isMethod())
|
||||
s_name = mapper.mapMethodName(s_owner, s_name, s_desc);
|
||||
else if(isField())
|
||||
s_name = mapper.mapFieldName(s_owner, s_name, s_desc);
|
||||
|
||||
s_owner = mapper.mapType(s_owner);
|
||||
|
||||
if(!isClass())
|
||||
s_desc = mapper.mapDesc(s_desc);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ObfMapping toRuntime()
|
||||
{
|
||||
if(!runtime)
|
||||
map(runtimeMapper);
|
||||
|
||||
runtime = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ObfMapping copy()
|
||||
{
|
||||
return new ObfMapping(s_owner, s_name, s_desc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package codechicken.lib.colour;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.lib.math.MathHelper;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public abstract class Colour
|
||||
{
|
||||
public byte r;
|
||||
public byte g;
|
||||
public byte b;
|
||||
public byte a;
|
||||
|
||||
public Colour(int r, int g, int b, int a)
|
||||
{
|
||||
this.r = (byte) r;
|
||||
this.g = (byte) g;
|
||||
this.b = (byte) b;
|
||||
this.a = (byte) a;
|
||||
}
|
||||
|
||||
public Colour(Colour colour)
|
||||
{
|
||||
r = colour.r;
|
||||
g = colour.g;
|
||||
b = colour.b;
|
||||
a = colour.a;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void glColour()
|
||||
{
|
||||
GL11.glColor4ub(r, g, b, a);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void glColour(int a)
|
||||
{
|
||||
GL11.glColor4ub(r, g, b, (byte) a);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Deprecated
|
||||
public void glColour(byte a)
|
||||
{
|
||||
GL11.glColor4ub(r, g, b, a);
|
||||
}
|
||||
|
||||
public abstract int pack();
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return getClass().getSimpleName() + "[0x" + Integer.toHexString(pack()).toUpperCase() + "]";
|
||||
}
|
||||
|
||||
public Colour add(Colour colour2)
|
||||
{
|
||||
a += colour2.a;
|
||||
r += colour2.r;
|
||||
g += colour2.g;
|
||||
b += colour2.b;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Colour sub(Colour colour2)
|
||||
{
|
||||
int ia = (a & 0xFF) - (colour2.a & 0xFF);
|
||||
int ir = (r & 0xFF) - (colour2.r & 0xFF);
|
||||
int ig = (g & 0xFF) - (colour2.g & 0xFF);
|
||||
int ib = (b & 0xFF) - (colour2.b & 0xFF);
|
||||
a = (byte) (ia < 0 ? 0 : ia);
|
||||
r = (byte) (ir < 0 ? 0 : ir);
|
||||
g = (byte) (ig < 0 ? 0 : ig);
|
||||
b = (byte) (ib < 0 ? 0 : ib);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Colour invert()
|
||||
{
|
||||
a = (byte) (0xFF - (a & 0xFF));
|
||||
r = (byte) (0xFF - (r & 0xFF));
|
||||
g = (byte) (0xFF - (g & 0xFF));
|
||||
b = (byte) (0xFF - (b & 0xFF));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Colour multiply(Colour colour2)
|
||||
{
|
||||
a = (byte) ((a & 0xFF) * ((colour2.a & 0xFF) / 255D));
|
||||
r = (byte) ((r & 0xFF) * ((colour2.r & 0xFF) / 255D));
|
||||
g = (byte) ((g & 0xFF) * ((colour2.g & 0xFF) / 255D));
|
||||
b = (byte) ((b & 0xFF) * ((colour2.b & 0xFF) / 255D));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Colour scale(double d)
|
||||
{
|
||||
a = (byte) ((a & 0xFF) * d);
|
||||
r = (byte) ((r & 0xFF) * d);
|
||||
g = (byte) ((g & 0xFF) * d);
|
||||
b = (byte) ((b & 0xFF) * d);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Colour interpolate(Colour colour2, double d)
|
||||
{
|
||||
return this.add(colour2.copy().sub(this).scale(d));
|
||||
}
|
||||
|
||||
public Colour multiplyC(double d)
|
||||
{
|
||||
r = (byte) MathHelper.clip((r & 0xFF) * d, 0, 255);
|
||||
g = (byte) MathHelper.clip((g & 0xFF) * d, 0, 255);
|
||||
b = (byte) MathHelper.clip((b & 0xFF) * d, 0, 255);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public abstract Colour copy();
|
||||
|
||||
public int rgb()
|
||||
{
|
||||
return (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF);
|
||||
}
|
||||
|
||||
public int argb()
|
||||
{
|
||||
return (a & 0xFF) << 24 | (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF);
|
||||
}
|
||||
|
||||
public int rgba()
|
||||
{
|
||||
return (r & 0xFF) << 24 | (g & 0xFF) << 16 | (b & 0xFF) << 8 | (a & 0xFF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package codechicken.lib.colour;
|
||||
|
||||
public class ColourARGB extends Colour
|
||||
{
|
||||
public ColourARGB(int colour)
|
||||
{
|
||||
super((colour >> 16) & 0xFF, (colour >> 8) & 0xFF, colour & 0xFF, (colour >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
public ColourARGB(int a, int r, int g, int b)
|
||||
{
|
||||
super(r, g, b, a);
|
||||
}
|
||||
|
||||
public ColourARGB(ColourARGB colour)
|
||||
{
|
||||
super(colour);
|
||||
}
|
||||
|
||||
public ColourARGB copy()
|
||||
{
|
||||
return new ColourARGB(this);
|
||||
}
|
||||
|
||||
public int pack()
|
||||
{
|
||||
return pack(this);
|
||||
}
|
||||
|
||||
public static int pack(Colour colour)
|
||||
{
|
||||
return (colour.a&0xFF) << 24 | (colour.r&0xFF) << 16 | (colour.g&0xFF) << 8 | (colour.b&0xFF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package codechicken.lib.colour;
|
||||
|
||||
public class ColourRGBA extends Colour
|
||||
{
|
||||
public ColourRGBA(int colour)
|
||||
{
|
||||
super((colour >> 24) & 0xFF, (colour >> 16) & 0xFF, (colour >> 8) & 0xFF, colour & 0xFF);
|
||||
}
|
||||
|
||||
public ColourRGBA(double r, double g, double b, double a)
|
||||
{
|
||||
super((int) (255 * r), (int) (255 * g), (int) (255 * b), (int) (255 * a));
|
||||
}
|
||||
|
||||
public ColourRGBA(int r, int g, int b, int a)
|
||||
{
|
||||
super(r, g, b, a);
|
||||
}
|
||||
|
||||
public ColourRGBA(ColourRGBA colour)
|
||||
{
|
||||
super(colour);
|
||||
}
|
||||
|
||||
public int pack()
|
||||
{
|
||||
return pack(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Colour copy()
|
||||
{
|
||||
return new ColourRGBA(this);
|
||||
}
|
||||
|
||||
public static int pack(Colour colour)
|
||||
{
|
||||
return (colour.r&0xFF) << 24 | (colour.g&0xFF) << 16 | (colour.b&0xFF) << 8 | (colour.a&0xFF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package codechicken.lib.colour;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import codechicken.lib.math.MathHelper;
|
||||
import codechicken.lib.render.TextureUtils;
|
||||
|
||||
public class CustomGradient
|
||||
{
|
||||
public int[] gradient;
|
||||
|
||||
public CustomGradient(ResourceLocation textureFile)
|
||||
{
|
||||
BufferedImage img = TextureUtils.loadBufferedImage(textureFile);
|
||||
int[] data = new int[img.getWidth()];
|
||||
img.getRGB(0, 0, img.getWidth(), 1, data, 0, img.getWidth());
|
||||
gradient = new int[img.getWidth()];
|
||||
for(int i = 0; i < data.length; i++)
|
||||
gradient[i] = (data[i]<<8)|(((data[i])>>24)&0xFF);
|
||||
}
|
||||
|
||||
public ColourRGBA getColour(double position)
|
||||
{
|
||||
return new ColourRGBA(getColourI(position));
|
||||
}
|
||||
|
||||
public int getColourI(double position)
|
||||
{
|
||||
int off = (int)MathHelper.clip(gradient.length*position, 0, gradient.length-1);
|
||||
return gradient[off];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package codechicken.lib.config;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
public class ConfigFile extends ConfigTagParent
|
||||
{
|
||||
public ConfigFile(File file)
|
||||
{
|
||||
if(!file.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
file.createNewFile();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
this.file = file;
|
||||
newlinemode = 2;
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
private void loadConfig()
|
||||
{
|
||||
loading = true;
|
||||
BufferedReader reader;
|
||||
try
|
||||
{
|
||||
reader = new BufferedReader(new FileReader(file));
|
||||
|
||||
while(true)
|
||||
{
|
||||
reader.mark(2000);
|
||||
String line = reader.readLine();
|
||||
if(line != null && line.startsWith("#"))
|
||||
{
|
||||
if(comment == null || comment.equals(""))
|
||||
comment = line.substring(1);
|
||||
else
|
||||
comment = comment+"\n"+line.substring(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
loadChildren(reader);
|
||||
reader.close();
|
||||
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
loading = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFile setComment(String header)
|
||||
{
|
||||
super.setComment(header);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFile setSortMode(int mode)
|
||||
{
|
||||
super.setSortMode(mode);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameQualifier()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
public static String readLine(BufferedReader reader) throws IOException
|
||||
{
|
||||
String line = reader.readLine();
|
||||
if(line != null)
|
||||
return line.replace("\t", "");
|
||||
return line;
|
||||
}
|
||||
|
||||
public static String formatLine(String line)
|
||||
{
|
||||
line = line.replace("\t", "");
|
||||
if(line.startsWith("#"))
|
||||
{
|
||||
return line;
|
||||
}
|
||||
else if(line.contains("="))
|
||||
{
|
||||
line = line.substring(0, line.indexOf("=")).replace(" ", "")+line.substring(line.indexOf("="));
|
||||
return line;
|
||||
}
|
||||
else
|
||||
{
|
||||
line = line.replace(" ", "");
|
||||
return line;
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeLine(PrintWriter writer, String line, int tabs)
|
||||
{
|
||||
for(int i = 0; i < tabs; i++)
|
||||
writer.print('\t');
|
||||
|
||||
writer.println(line);
|
||||
}
|
||||
|
||||
public void saveConfig()
|
||||
{
|
||||
if(loading)
|
||||
return;
|
||||
|
||||
PrintWriter writer;
|
||||
try
|
||||
{
|
||||
writer = new PrintWriter(file);
|
||||
}
|
||||
catch(FileNotFoundException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
writeComment(writer, 0);
|
||||
ConfigFile.writeLine(writer, "", 0);
|
||||
saveTagTree(writer, 0, "");
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
|
||||
public boolean isLoading()
|
||||
{
|
||||
return loading;
|
||||
}
|
||||
|
||||
public File file;
|
||||
private boolean loading;
|
||||
|
||||
public static final byte[] lineend = new byte[]{0xD, 0xA};
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
package codechicken.lib.config;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import codechicken.lib.colour.Colour;
|
||||
import codechicken.lib.colour.ColourRGBA;
|
||||
|
||||
public class ConfigTag extends ConfigTagParent
|
||||
{
|
||||
public ConfigTag(ConfigTagParent parent, String name)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.name = name;
|
||||
qualifiedname = parent.getNameQualifier()+name;
|
||||
newline = parent.newlinemode == 2;
|
||||
parent.addChild(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameQualifier()
|
||||
{
|
||||
return qualifiedname+".";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveConfig()
|
||||
{
|
||||
parent.saveConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the tag is loaded from a config file as opposed to constructed by a mod
|
||||
* @return this
|
||||
*/
|
||||
public ConfigTag onLoaded()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setValue(String value)
|
||||
{
|
||||
this.value = value;
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
public void setDefaultValue(String defaultvalue)
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
value = defaultvalue;
|
||||
saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
public void setIntValue(int i)
|
||||
{
|
||||
setValue(Integer.toString(i));
|
||||
}
|
||||
|
||||
public void setBooleanValue(boolean b)
|
||||
{
|
||||
setValue(Boolean.toString(b));
|
||||
}
|
||||
|
||||
public void setHexValue(int i)
|
||||
{
|
||||
setValue("0x"+Long.toString(((long)i) << 32 >>> 32, 16));
|
||||
}
|
||||
|
||||
public void setColourRGB(Colour c)
|
||||
{
|
||||
String s = Long.toString(((long)c.rgb()) << 32 >>> 32, 16);
|
||||
while(s.length() < 6)
|
||||
s = "0"+s;
|
||||
setValue("0x"+s.toUpperCase());
|
||||
}
|
||||
|
||||
public String getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getValue(String defaultvalue)
|
||||
{
|
||||
setDefaultValue(defaultvalue);
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getIntValue()
|
||||
{
|
||||
return Integer.parseInt(getValue());
|
||||
}
|
||||
|
||||
public int getIntValue(int defaultvalue)
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
setIntValue(defaultvalue);
|
||||
}
|
||||
try
|
||||
{
|
||||
return getIntValue();
|
||||
}
|
||||
catch(NumberFormatException nfe)
|
||||
{
|
||||
setIntValue(defaultvalue);
|
||||
return getIntValue();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getBooleanValue()
|
||||
{
|
||||
String value = getValue();
|
||||
if(value != null && (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes")))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if(value != null && (value.equalsIgnoreCase("false") || value.equalsIgnoreCase("no")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
throw new NumberFormatException(qualifiedname+".value="+value);
|
||||
}
|
||||
|
||||
public boolean getBooleanValue(boolean defaultvalue)
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
setBooleanValue(defaultvalue);
|
||||
}
|
||||
try
|
||||
{
|
||||
return getBooleanValue();
|
||||
}
|
||||
catch(NumberFormatException nfe)
|
||||
{
|
||||
setBooleanValue(defaultvalue);
|
||||
return getBooleanValue();
|
||||
}
|
||||
}
|
||||
|
||||
public int getHexValue()
|
||||
{
|
||||
return (int) Long.parseLong(getValue().replace("0x", ""), 16);
|
||||
}
|
||||
|
||||
public int getHexValue(int defaultvalue)
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
setHexValue(defaultvalue);
|
||||
}
|
||||
try
|
||||
{
|
||||
return getHexValue();
|
||||
}
|
||||
catch(NumberFormatException nfe)
|
||||
{
|
||||
setHexValue(defaultvalue);
|
||||
return getHexValue();
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern patternRGB = Pattern.compile("(\\d+),(\\d+),(\\d+)");
|
||||
public Colour getColourRGB()
|
||||
{
|
||||
Matcher matcherRGB = patternRGB.matcher(getValue().replaceAll("\\s", ""));
|
||||
if(matcherRGB.matches())
|
||||
{
|
||||
return new ColourRGBA(
|
||||
Integer.parseInt(matcherRGB.group(1)),
|
||||
Integer.parseInt(matcherRGB.group(2)),
|
||||
Integer.parseInt(matcherRGB.group(3)),
|
||||
0xFF);
|
||||
}
|
||||
|
||||
return new ColourRGBA(getHexValue()<<8|0xFF);
|
||||
}
|
||||
|
||||
public Colour getColourRGB(Colour defaultvalue)
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
setColourRGB(defaultvalue);
|
||||
}
|
||||
try
|
||||
{
|
||||
return getColourRGB();
|
||||
}
|
||||
catch(NumberFormatException nfe)
|
||||
{
|
||||
setColourRGB(defaultvalue);
|
||||
return getColourRGB();
|
||||
}
|
||||
}
|
||||
|
||||
public void save(PrintWriter writer, int tabs, String bracequalifier, boolean first)
|
||||
{
|
||||
String vname;
|
||||
if(qualifiedname.contains(".") && bracequalifier.length() > 0)
|
||||
vname = qualifiedname.substring(bracequalifier.length() + 1).replace(' ', '_');
|
||||
else
|
||||
vname = qualifiedname;
|
||||
vname.replace(' ', '_');
|
||||
|
||||
if(newline && !first)
|
||||
ConfigFile.writeLine(writer, "", tabs);
|
||||
|
||||
writeComment(writer, tabs);
|
||||
if(value != null)
|
||||
ConfigFile.writeLine(writer, vname+"="+value, tabs);
|
||||
|
||||
if(!hasChildTags())
|
||||
return;
|
||||
|
||||
if(brace)
|
||||
{
|
||||
if(value == null)
|
||||
ConfigFile.writeLine(writer, vname, tabs);
|
||||
ConfigFile.writeLine(writer, "{", tabs);
|
||||
saveTagTree(writer, tabs+1, qualifiedname.replace(' ', '_'));
|
||||
ConfigFile.writeLine(writer, "}", tabs);
|
||||
}
|
||||
else
|
||||
{
|
||||
saveTagTree(writer, tabs, bracequalifier);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigTag setComment(String comment)
|
||||
{
|
||||
super.setComment(comment);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigTag setSortMode(int mode)
|
||||
{
|
||||
super.setSortMode(mode);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigTag setNewLine(boolean b)
|
||||
{
|
||||
newline = b;
|
||||
saveConfig();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigTag useBraces()
|
||||
{
|
||||
brace = true;
|
||||
if(parent.newlinemode == 1)
|
||||
newline = true;
|
||||
|
||||
saveConfig();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigTag setPosition(int pos)
|
||||
{
|
||||
position = pos;
|
||||
saveConfig();
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean containsTag(String tagname)
|
||||
{
|
||||
return getTag(tagname, false) != null;
|
||||
}
|
||||
|
||||
public int getId(String name, int defaultValue)
|
||||
{
|
||||
return getTag(name).getIntValue(defaultValue);
|
||||
}
|
||||
|
||||
public int getId(String name)
|
||||
{
|
||||
int ret = getId(name, IDBase);
|
||||
IDBase = ret+1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int getAcheivementId(String name, int defaultValue)
|
||||
{
|
||||
return getTag(name).getIntValue(defaultValue);
|
||||
}
|
||||
|
||||
public ConfigTag setBaseID(int i)
|
||||
{
|
||||
IDBase = i;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigTagParent parent;
|
||||
public String name;
|
||||
public String qualifiedname;
|
||||
public String value;
|
||||
public boolean brace;
|
||||
public boolean newline;
|
||||
public int position = Integer.MAX_VALUE;
|
||||
|
||||
private int IDBase;
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
package codechicken.lib.config;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public abstract class ConfigTagParent
|
||||
{
|
||||
public static class TagOrderComparator implements Comparator<ConfigTag>
|
||||
{
|
||||
int sortMode;
|
||||
|
||||
public TagOrderComparator(int sortMode)
|
||||
{
|
||||
this.sortMode = sortMode;
|
||||
}
|
||||
|
||||
public int compare(ConfigTag o1, ConfigTag o2)
|
||||
{
|
||||
if(o1.position != o2.position)
|
||||
return compareInt(o1.position, o2.position);
|
||||
if(o1.brace != o2.brace)
|
||||
return o1.brace ? 1 : -1;//braced one goes after
|
||||
switch(sortMode)
|
||||
{
|
||||
case 1:
|
||||
if(o1.value == o2.value)
|
||||
return 0;
|
||||
if(o1.value == null)
|
||||
return 1;
|
||||
if(o2.value == null)
|
||||
return -1;
|
||||
return o1.value.compareTo(o2.value);
|
||||
default:
|
||||
return o1.name.compareTo(o2.name);
|
||||
}
|
||||
}
|
||||
|
||||
private int compareInt(int a, int b)
|
||||
{
|
||||
return a == b ? 0 : a < b ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
private TreeMap<String, ConfigTag> childtags = new TreeMap<String, ConfigTag>();
|
||||
public String comment;
|
||||
/**
|
||||
* 0 = name, 1 = value
|
||||
*/
|
||||
public int sortMode = 0;
|
||||
/**
|
||||
* The mode for determining when child tags should leave a blank line between them and the one above
|
||||
* 0 = never, 1 = when braced, 2 = always
|
||||
*/
|
||||
public int newlinemode = 1;
|
||||
|
||||
public abstract void saveConfig();
|
||||
|
||||
public abstract String getNameQualifier();
|
||||
|
||||
public ConfigTagParent setComment(String comment)
|
||||
{
|
||||
this.comment = comment;
|
||||
saveConfig();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigTagParent setSortMode(int mode)
|
||||
{
|
||||
sortMode = mode;
|
||||
saveConfig();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigTagParent setNewLineMode(int mode)
|
||||
{
|
||||
newlinemode = mode;
|
||||
for(Entry<String, ConfigTag> entry : childtags.entrySet())
|
||||
{
|
||||
ConfigTag tag = entry.getValue();
|
||||
if(newlinemode == 0)
|
||||
tag.newline = false;
|
||||
else if(newlinemode == 1)
|
||||
tag.newline = tag.brace;
|
||||
else if(newlinemode == 2)
|
||||
tag.newline = true;
|
||||
}
|
||||
saveConfig();
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, ConfigTag> childTagMap()
|
||||
{
|
||||
return childtags;
|
||||
}
|
||||
|
||||
public boolean hasChildTags()
|
||||
{
|
||||
return !childtags.isEmpty();
|
||||
}
|
||||
|
||||
public boolean containsTag(String tagname)
|
||||
{
|
||||
return getTag(tagname, false) != null;
|
||||
}
|
||||
|
||||
public ConfigTag getNewTag(String tagname)
|
||||
{
|
||||
return new ConfigTag(this, tagname);
|
||||
}
|
||||
|
||||
public ConfigTag getTag(String tagname, boolean create)
|
||||
{
|
||||
tagname = tagname.replace('_', ' ');
|
||||
int dotpos = tagname.indexOf(".");
|
||||
String basetagname = dotpos == -1 ? tagname : tagname.substring(0, dotpos);
|
||||
ConfigTag basetag = childtags.get(basetagname);
|
||||
if(basetag == null)
|
||||
{
|
||||
if(!create)
|
||||
return null;
|
||||
|
||||
basetag = getNewTag(basetagname);
|
||||
saveConfig();
|
||||
}
|
||||
if(dotpos == -1)
|
||||
return basetag;
|
||||
|
||||
return basetag.getTag(tagname.substring(dotpos+1), create);
|
||||
}
|
||||
|
||||
public ConfigTag getTag(String tagname)
|
||||
{
|
||||
return getTag(tagname, true);
|
||||
}
|
||||
|
||||
public boolean removeTag(String tagname)
|
||||
{
|
||||
ConfigTag tag = getTag(tagname, false);
|
||||
if(tag == null)
|
||||
return false;
|
||||
|
||||
int dotpos = tagname.lastIndexOf(".");
|
||||
String lastpart = dotpos == -1 ? tagname : tagname.substring(dotpos+1, tagname.length());
|
||||
if(tag.parent != null)
|
||||
return tag.parent.childtags.remove(lastpart) != null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addChild(ConfigTag tag)
|
||||
{
|
||||
childtags.put(tag.name, tag);
|
||||
}
|
||||
|
||||
public <T extends ConfigTag> ArrayList<T> getSortedTagList()
|
||||
{
|
||||
ArrayList<T> taglist = new ArrayList<T>(childtags.size());
|
||||
for(Entry<String, ConfigTag> tag : childtags.entrySet())
|
||||
taglist.add((T)tag.getValue());
|
||||
|
||||
Collections.sort(taglist, new TagOrderComparator(sortMode));
|
||||
return taglist;
|
||||
}
|
||||
|
||||
public void loadChildren(BufferedReader reader)
|
||||
{
|
||||
String comment = "";
|
||||
String bracequalifier="";
|
||||
try
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
String line = ConfigFile.readLine(reader);
|
||||
if(line == null)
|
||||
break;
|
||||
if(line.startsWith("#"))
|
||||
{
|
||||
if(comment == null || comment.equals(""))
|
||||
comment = line.substring(1);
|
||||
else
|
||||
comment = comment+"\n"+line.substring(1);
|
||||
}
|
||||
else if(line.contains("="))
|
||||
{
|
||||
String qualifiedname = line.substring(0, line.indexOf("="));
|
||||
getTag(qualifiedname)
|
||||
.onLoaded()
|
||||
.setComment(comment)
|
||||
.setValue(line.substring(line.indexOf("=")+1));
|
||||
comment = "";
|
||||
bracequalifier = qualifiedname;
|
||||
}
|
||||
else if(line.equals("{"))
|
||||
{
|
||||
getTag(bracequalifier).setComment(comment).useBraces().loadChildren(reader);
|
||||
comment = "";
|
||||
bracequalifier = "";
|
||||
}
|
||||
else if(line.equals("}"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
bracequalifier = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveTagTree(PrintWriter writer, int tabs, String bracequalifier)
|
||||
{
|
||||
boolean first = true;
|
||||
for(ConfigTag tag : getSortedTagList())
|
||||
{
|
||||
tag.save(writer, tabs, bracequalifier, first);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void writeComment(PrintWriter writer, int tabs)
|
||||
{
|
||||
if(comment != null && !comment.equals(""))
|
||||
{
|
||||
String[] comments = comment.split("\n");
|
||||
for(int i = 0; i < comments.length; i++)
|
||||
ConfigFile.writeLine(writer, "#"+comments[i], tabs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package codechicken.lib.config;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class SimpleProperties
|
||||
{
|
||||
public HashMap<String, String> propertyMap = new HashMap<String, String>();
|
||||
public File propertyFile;
|
||||
public boolean saveOnChange = false;
|
||||
public String encoding;
|
||||
|
||||
private boolean loading = false;
|
||||
|
||||
public SimpleProperties(File file, boolean saveOnChange, String encoding)
|
||||
{
|
||||
propertyFile = file;
|
||||
this.saveOnChange = saveOnChange;
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
public SimpleProperties(File file, boolean saveOnChange)
|
||||
{
|
||||
this(file, saveOnChange, Charset.defaultCharset().name());
|
||||
}
|
||||
|
||||
public SimpleProperties(File file)
|
||||
{
|
||||
this(file, true);
|
||||
}
|
||||
|
||||
public void load()
|
||||
{
|
||||
clear();
|
||||
loading = true;
|
||||
|
||||
try
|
||||
{
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(propertyFile), encoding));
|
||||
while(true)
|
||||
{
|
||||
String read = reader.readLine();
|
||||
if(read == null)
|
||||
break;
|
||||
|
||||
int equalIndex = read.indexOf('=');
|
||||
if(equalIndex == -1)
|
||||
continue;
|
||||
|
||||
setProperty(read.substring(0, equalIndex), read.substring(equalIndex+1));
|
||||
}
|
||||
reader.close();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
loading = false;
|
||||
}
|
||||
|
||||
public void save()
|
||||
{
|
||||
try
|
||||
{
|
||||
PrintStream writer = new PrintStream(propertyFile);
|
||||
|
||||
for(Entry<String, String> entry : propertyMap.entrySet())
|
||||
{
|
||||
writer.println(entry.getKey()+"="+entry.getValue());
|
||||
}
|
||||
|
||||
writer.close();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
propertyMap.clear();
|
||||
}
|
||||
|
||||
public boolean hasProperty(String key)
|
||||
{
|
||||
return propertyMap.containsKey(key);
|
||||
}
|
||||
|
||||
public void removeProperty(String key)
|
||||
{
|
||||
if(propertyMap.remove(key) != null && saveOnChange && !loading)
|
||||
save();
|
||||
}
|
||||
|
||||
public void setProperty(String key, int value)
|
||||
{
|
||||
setProperty(key, Integer.toString(value));
|
||||
}
|
||||
|
||||
public void setProperty(String key, boolean value)
|
||||
{
|
||||
setProperty(key, Boolean.toString(value));
|
||||
}
|
||||
|
||||
public void setProperty(String key, String value)
|
||||
{
|
||||
propertyMap.put(key, value);
|
||||
if(saveOnChange && !loading)
|
||||
save();
|
||||
}
|
||||
|
||||
public int getProperty(String property, int defaultvalue)
|
||||
{
|
||||
try {
|
||||
return Integer.parseInt(getProperty(property, Integer.toString(defaultvalue)));
|
||||
} catch(NumberFormatException nfe)
|
||||
{return defaultvalue;}
|
||||
}
|
||||
|
||||
public boolean getProperty(String property, boolean defaultvalue)
|
||||
{
|
||||
try {
|
||||
return Boolean.parseBoolean(getProperty(property, Boolean.toString(defaultvalue)));
|
||||
} catch(NumberFormatException nfe)
|
||||
{return defaultvalue;}
|
||||
}
|
||||
|
||||
public String getProperty(String property, String defaultvalue)
|
||||
{
|
||||
String value = propertyMap.get(property);
|
||||
if(value == null)
|
||||
{
|
||||
setProperty(property, defaultvalue);
|
||||
return defaultvalue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getProperty(String property)
|
||||
{
|
||||
return propertyMap.get(property);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package codechicken.lib.data;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
|
||||
public interface MCDataInput
|
||||
{
|
||||
public long readLong();
|
||||
public int readInt();
|
||||
public short readShort();
|
||||
public int readUnsignedShort();
|
||||
public byte readByte();
|
||||
public int readUnsignedByte();
|
||||
public double readDouble();
|
||||
public float readFloat();
|
||||
public boolean readBoolean();
|
||||
public char readChar();
|
||||
public byte[] readByteArray(int length);
|
||||
public String readString();
|
||||
public BlockCoord readCoord();
|
||||
public NBTTagCompound readNBTTagCompound();
|
||||
public ItemStack readItemStack();
|
||||
public FluidStack readFluidStack();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package codechicken.lib.data;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
public class MCDataInputStream extends InputStream
|
||||
{
|
||||
private MCDataInput in;
|
||||
|
||||
public MCDataInputStream(MCDataInput in)
|
||||
{
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read()
|
||||
{
|
||||
return in.readByte()&0xFF;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package codechicken.lib.data;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
|
||||
public interface MCDataOutput
|
||||
{
|
||||
public MCDataOutput writeLong(long l);
|
||||
public MCDataOutput writeInt(int i);
|
||||
public MCDataOutput writeShort(int s);
|
||||
public MCDataOutput writeByte(int b);
|
||||
public MCDataOutput writeDouble(double d);
|
||||
public MCDataOutput writeFloat(float f);
|
||||
public MCDataOutput writeBoolean(boolean b);
|
||||
public MCDataOutput writeChar(char c);
|
||||
public MCDataOutput writeByteArray(byte[] array);
|
||||
public MCDataOutput writeString(String s);
|
||||
public MCDataOutput writeCoord(int x, int y, int z);
|
||||
public MCDataOutput writeCoord(BlockCoord coord);
|
||||
public MCDataOutput writeNBTTagCompound(NBTTagCompound tag);
|
||||
public MCDataOutput writeItemStack(ItemStack stack);
|
||||
public MCDataOutput writeFluidStack(FluidStack liquid);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package codechicken.lib.data;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class MCDataOutputStream extends OutputStream
|
||||
{
|
||||
private MCDataOutput out;
|
||||
|
||||
public MCDataOutputStream(MCDataOutput out)
|
||||
{
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b)
|
||||
{
|
||||
out.writeByte(b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package codechicken.lib.data;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompressedStreamTools;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
|
||||
public class MCOutputStreamWrapper implements MCDataOutput
|
||||
{
|
||||
public DataOutputStream dataout;
|
||||
|
||||
public MCOutputStreamWrapper(DataOutputStream out)
|
||||
{
|
||||
dataout = out;
|
||||
}
|
||||
|
||||
public MCOutputStreamWrapper writeBoolean(boolean b)
|
||||
{
|
||||
try
|
||||
{
|
||||
dataout.writeBoolean(b);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MCOutputStreamWrapper writeByte(int b)
|
||||
{
|
||||
try
|
||||
{
|
||||
dataout.writeByte(b);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MCOutputStreamWrapper writeShort(int s)
|
||||
{
|
||||
try
|
||||
{
|
||||
dataout.writeShort(s);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MCOutputStreamWrapper writeInt(int i)
|
||||
{
|
||||
try
|
||||
{
|
||||
dataout.writeInt(i);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MCOutputStreamWrapper writeFloat(float f)
|
||||
{
|
||||
try
|
||||
{
|
||||
dataout.writeFloat(f);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MCOutputStreamWrapper writeDouble(double d)
|
||||
{
|
||||
try
|
||||
{
|
||||
dataout.writeDouble(d);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MCOutputStreamWrapper writeLong(long l)
|
||||
{
|
||||
try
|
||||
{
|
||||
dataout.writeLong(l);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MCOutputStreamWrapper writeChar(char c)
|
||||
{
|
||||
try
|
||||
{
|
||||
dataout.writeChar(c);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MCOutputStreamWrapper writeByteArray(byte[] barray)
|
||||
{
|
||||
try
|
||||
{
|
||||
dataout.write(barray);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MCOutputStreamWrapper writeCoord(int x, int y, int z)
|
||||
{
|
||||
writeInt(x);
|
||||
writeInt(y);
|
||||
writeInt(z);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MCOutputStreamWrapper writeCoord(BlockCoord coord)
|
||||
{
|
||||
writeInt(coord.x);
|
||||
writeInt(coord.y);
|
||||
writeInt(coord.z);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MCOutputStreamWrapper writeString(String s)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(s.length() > 65535)
|
||||
throw new IOException("String length: "+s.length()+"too long.");
|
||||
dataout.writeShort(s.length());
|
||||
dataout.writeChars(s);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MCOutputStreamWrapper writeItemStack(ItemStack stack)
|
||||
{
|
||||
writeItemStack(stack, false);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MCOutputStreamWrapper writeItemStack(ItemStack stack, boolean large)
|
||||
{
|
||||
if (stack == null)
|
||||
{
|
||||
writeShort(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeShort(stack.itemID);
|
||||
if(large)
|
||||
writeInt(stack.stackSize);
|
||||
else
|
||||
writeByte(stack.stackSize);
|
||||
writeShort(stack.getItemDamage());
|
||||
writeNBTTagCompound(stack.stackTagCompound);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MCOutputStreamWrapper writeNBTTagCompound(NBTTagCompound compound)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (compound == null)
|
||||
{
|
||||
writeShort(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] bytes = CompressedStreamTools.compress(compound);
|
||||
writeShort((short)bytes.length);
|
||||
writeByteArray(bytes);
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MCOutputStreamWrapper writeFluidStack(FluidStack fluid)
|
||||
{
|
||||
if (fluid == null)
|
||||
{
|
||||
writeShort(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeShort(fluid.fluidID);
|
||||
writeInt(fluid.amount);
|
||||
writeNBTTagCompound(fluid.tag);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
package codechicken.lib.data;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.nbt.NBTTagByte;
|
||||
import net.minecraft.nbt.NBTTagByteArray;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagDouble;
|
||||
import net.minecraft.nbt.NBTTagFloat;
|
||||
import net.minecraft.nbt.NBTTagInt;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.nbt.NBTTagLong;
|
||||
import net.minecraft.nbt.NBTTagShort;
|
||||
import net.minecraft.nbt.NBTTagString;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
|
||||
public class NBTDataWrapper implements MCDataInput, MCDataOutput
|
||||
{
|
||||
private NBTTagList readList;
|
||||
private int readTag = 0;
|
||||
private NBTTagList writeList;
|
||||
|
||||
public NBTDataWrapper(NBTTagList input)
|
||||
{
|
||||
readList = input;
|
||||
}
|
||||
|
||||
public NBTDataWrapper()
|
||||
{
|
||||
writeList = new NBTTagList();
|
||||
}
|
||||
|
||||
public NBTTagList toTag()
|
||||
{
|
||||
return writeList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTDataWrapper writeLong(long l)
|
||||
{
|
||||
writeList.appendTag(new NBTTagLong(null, l));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTDataWrapper writeInt(int i)
|
||||
{
|
||||
writeList.appendTag(new NBTTagInt(null, i));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTDataWrapper writeShort(int s)
|
||||
{
|
||||
writeList.appendTag(new NBTTagShort(null, (short) s));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTDataWrapper writeByte(int b)
|
||||
{
|
||||
writeList.appendTag(new NBTTagByte(null, (byte) b));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTDataWrapper writeDouble(double d)
|
||||
{
|
||||
writeList.appendTag(new NBTTagDouble(null, d));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTDataWrapper writeFloat(float f)
|
||||
{
|
||||
writeList.appendTag(new NBTTagFloat(null, f));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTDataWrapper writeBoolean(boolean b)
|
||||
{
|
||||
writeList.appendTag(new NBTTagByte(null, (byte) (b ? 1 : 0)));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTDataWrapper writeChar(char c)
|
||||
{
|
||||
writeList.appendTag(new NBTTagShort(null, (short)c));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTDataWrapper writeByteArray(byte[] array)
|
||||
{
|
||||
writeList.appendTag(new NBTTagByteArray(null, array));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTDataWrapper writeString(String s)
|
||||
{
|
||||
writeList.appendTag(new NBTTagString(null, s));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTDataWrapper writeCoord(int x, int y, int z)
|
||||
{
|
||||
writeInt(x);
|
||||
writeInt(y);
|
||||
writeInt(z);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTDataWrapper writeCoord(BlockCoord coord)
|
||||
{
|
||||
writeCoord(coord.x, coord.y, coord.z);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTDataWrapper writeNBTTagCompound(NBTTagCompound tag)
|
||||
{
|
||||
writeList.appendTag(tag);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTDataWrapper writeItemStack(ItemStack stack)
|
||||
{
|
||||
writeList.appendTag(stack.writeToNBT(new NBTTagCompound()));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTDataWrapper writeFluidStack(FluidStack liquid)
|
||||
{
|
||||
writeList.appendTag(liquid.writeToNBT(new NBTTagCompound()));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong()
|
||||
{
|
||||
return ((NBTTagLong)readTag()).data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt()
|
||||
{
|
||||
return ((NBTTagInt)readTag()).data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readShort()
|
||||
{
|
||||
return ((NBTTagShort)readTag()).data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedShort()
|
||||
{
|
||||
return ((NBTTagShort)readTag()).data & 0xFFFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte()
|
||||
{
|
||||
return ((NBTTagByte)readTag()).data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedByte()
|
||||
{
|
||||
return ((NBTTagByte)readTag()).data & 0xFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readDouble()
|
||||
{
|
||||
return ((NBTTagDouble)readTag()).data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float readFloat()
|
||||
{
|
||||
return ((NBTTagFloat)readTag()).data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readBoolean()
|
||||
{
|
||||
return ((NBTTagByte)readTag()).data != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char readChar()
|
||||
{
|
||||
return (char)((NBTTagShort)readTag()).data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readByteArray(int length)
|
||||
{
|
||||
return ((NBTTagByteArray)readTag()).byteArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readString()
|
||||
{
|
||||
return ((NBTTagString)readTag()).data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockCoord readCoord()
|
||||
{
|
||||
return new BlockCoord(readInt(), readInt(), readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTTagCompound readNBTTagCompound()
|
||||
{
|
||||
return (NBTTagCompound)readTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack readItemStack()
|
||||
{
|
||||
return ItemStack.loadItemStackFromNBT(readNBTTagCompound());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidStack readFluidStack()
|
||||
{
|
||||
return FluidStack.loadFluidStackFromNBT(readNBTTagCompound());
|
||||
}
|
||||
|
||||
private NBTBase readTag()
|
||||
{
|
||||
return readList.tagAt(readTag++);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package codechicken.lib.lighting;
|
||||
|
||||
import codechicken.lib.render.CCModel;
|
||||
import codechicken.lib.render.Vertex5;
|
||||
|
||||
public class CCRBModel extends CCModel
|
||||
{
|
||||
public LC[] lightCoefficents;
|
||||
|
||||
/**
|
||||
* Lighting sides and coefficients are computed for each vertex of the model. All faces must be axis planar and all verts are assumed to be in the range (0,0,0) to (1,1,1)
|
||||
*/
|
||||
public CCRBModel(CCModel m)
|
||||
{
|
||||
super(m.vertexMode);
|
||||
verts = new Vertex5[m.verts.length];
|
||||
copy(m, 0, this, 0, m.verts.length);
|
||||
if(normals == null)
|
||||
computeNormals();
|
||||
if(colours == null)
|
||||
setColour(-1);
|
||||
computeLighting();
|
||||
}
|
||||
|
||||
private void computeLighting()
|
||||
{
|
||||
lightCoefficents = new LC[verts.length];
|
||||
for(int k = 0; k < verts.length; k++)
|
||||
lightCoefficents[k] = LC.compute(verts[k].vec, normals[k]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package codechicken.lib.lighting;
|
||||
|
||||
import codechicken.lib.render.CCModel;
|
||||
import codechicken.lib.vec.Rotation;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
|
||||
public class LC
|
||||
{
|
||||
public LC(int s, float e, float f, float g, float h)
|
||||
{
|
||||
side = s;
|
||||
fa = e;
|
||||
fb = f;
|
||||
fc = g;
|
||||
fd = h;
|
||||
}
|
||||
|
||||
public int side;
|
||||
public float fa;
|
||||
public float fb;
|
||||
public float fc;
|
||||
public float fd;
|
||||
|
||||
public static LC compute(Vector3 vec, Vector3 normal)
|
||||
{
|
||||
int side = CCModel.findSide(normal);
|
||||
if(side < 0)
|
||||
return new LC(12, 1, 0, 0, 0);
|
||||
return compute(vec, side);
|
||||
}
|
||||
|
||||
public static LC compute(Vector3 vec, int side)
|
||||
{
|
||||
boolean offset = false;
|
||||
switch(side)
|
||||
{
|
||||
case 0: offset = vec.y <= 0; break;
|
||||
case 1: offset = vec.y >= 1; break;
|
||||
case 2: offset = vec.z <= 0; break;
|
||||
case 3: offset = vec.z >= 1; break;
|
||||
case 4: offset = vec.x <= 0; break;
|
||||
case 5: offset = vec.x >= 1; break;
|
||||
}
|
||||
if(!offset)
|
||||
side+=6;
|
||||
return computeO(vec, side);
|
||||
}
|
||||
|
||||
public static LC computeO(Vector3 vec, int side)
|
||||
{
|
||||
Vector3 v1 = Rotation.axes[((side&0xE)+3)%6];
|
||||
Vector3 v2 = Rotation.axes[((side&0xE)+5)%6];
|
||||
float d1 = (float) vec.scalarProject(v1);
|
||||
float d2 = 1-d1;
|
||||
float d3 = (float) vec.scalarProject(v2);
|
||||
float d4 = 1-d3;
|
||||
return new LC(side, d2*d4, d2*d3, d1*d4, d1*d3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package codechicken.lib.lighting;
|
||||
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
|
||||
public class LazyLightMatrix
|
||||
{
|
||||
private BlockCoord pos = new BlockCoord();
|
||||
private IBlockAccess access;
|
||||
private boolean computed = false;
|
||||
private LightMatrix lightMatrix = new LightMatrix();
|
||||
|
||||
public LightMatrix lightMatrix()
|
||||
{
|
||||
if(computed)
|
||||
return lightMatrix;
|
||||
computed = true;
|
||||
lightMatrix.computeAt(access, pos.x, pos.y, pos.z);
|
||||
return lightMatrix;
|
||||
}
|
||||
|
||||
public void setPos(IBlockAccess access, int x, int y, int z)
|
||||
{
|
||||
computed = false;
|
||||
this.access = access;
|
||||
pos.set(x, y, z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
package codechicken.lib.lighting;
|
||||
|
||||
import codechicken.lib.render.CCModel;
|
||||
import codechicken.lib.render.CCRenderState;
|
||||
import codechicken.lib.render.IVertexModifier;
|
||||
import codechicken.lib.render.UV;
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
/**
|
||||
* Note that when using the class as a vertex transformer, the vertices are assumed to be within the BB (x, y, z) -> (x+1, y+1, z+1)
|
||||
*/
|
||||
public class LightMatrix implements IVertexModifier
|
||||
{
|
||||
public int computed = 0;
|
||||
public float[][] ao = new float[13][4];
|
||||
public int[][] brightness = new int[13][4];
|
||||
public BlockCoord pos = new BlockCoord();
|
||||
|
||||
private float[] aSamples = new float[27];
|
||||
private int[] bSamples = new int[27];
|
||||
private Vector3 v_temp = new Vector3();
|
||||
|
||||
/**
|
||||
* The 9 positions in the sample array for each side, sides >= 6 are centered on sample 13 (the block itself)
|
||||
*/
|
||||
public static final int[][] ssamplem = new int[][]{
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8},
|
||||
{18,19,20,21,22,23,24,25,26},
|
||||
{ 0, 9,18, 1,10,19, 2,11,20},
|
||||
{ 6,15,24, 7,16,25, 8,17,26},
|
||||
{ 0, 3, 6, 9,12,15,18,21,24},
|
||||
{ 2, 5, 8,11,14,17,20,23,26},
|
||||
{ 9,10,11,12,13,14,15,16,17},
|
||||
{ 9,10,11,12,13,14,15,16,17},
|
||||
{ 3,12,21, 4,13,22, 5,14,23},
|
||||
{ 3,12,21, 4,13,22, 5,14,23},
|
||||
{ 1, 4, 7,10,13,16,19,22,25},
|
||||
{ 1, 4, 7,10,13,16,19,22,25},
|
||||
{13,13,13,13,13,13,13,13,13}};
|
||||
public static final int[][] qsamplem = new int[][]{//the positions in the side sample array for each corner
|
||||
{0,1,3,4},
|
||||
{5,1,2,4},
|
||||
{6,7,3,4},
|
||||
{5,7,8,4}};
|
||||
public static final float[] sideao = new float[]{
|
||||
0.5F, 1F, 0.8F, 0.8F, 0.6F, 0.6F,
|
||||
0.5F, 1F, 0.8F, 0.8F, 0.6F, 0.6F,
|
||||
1F};
|
||||
|
||||
/*static
|
||||
{
|
||||
int[][] os = new int[][]{
|
||||
{0,-1,0},
|
||||
{0, 1,0},
|
||||
{0,0,-1},
|
||||
{0,0, 1},
|
||||
{-1,0,0},
|
||||
{ 1,0,0}};
|
||||
|
||||
for(int s = 0; s < 12; s++)
|
||||
{
|
||||
int[] d0 = s < 6 ? new int[]{os[s][0]+1, os[s][1]+1, os[s][2]+1} : new int[]{1, 1, 1};
|
||||
int[] d1 = os[((s&0xE)+3)%6];
|
||||
int[] d2 = os[((s&0xE)+5)%6];
|
||||
for(int a = -1; a <= 1; a++)
|
||||
for(int b = -1; b <= 1; b++)
|
||||
ssamplem[s][(a+1)*3+b+1] = (d0[1]+d1[1]*a+d2[1]*b)*9+(d0[2]+d1[2]*a+d2[2]*b)*3+(d0[0]+d1[0]*a+d2[0]*b);
|
||||
}
|
||||
System.out.println(Arrays.deepToString(ssamplem));
|
||||
}*/
|
||||
|
||||
public void computeAt(IBlockAccess a, int x, int y, int z)
|
||||
{
|
||||
pos.set(x, y, z);
|
||||
computed = 0;
|
||||
//inc x, inc z, inc y
|
||||
sample( 0, aSamples, bSamples, a, x-1, y-1, z-1);
|
||||
sample( 1, aSamples, bSamples, a, x , y-1, z-1);
|
||||
sample( 2, aSamples, bSamples, a, x+1, y-1, z-1);
|
||||
sample( 3, aSamples, bSamples, a, x-1, y-1, z );
|
||||
sample( 4, aSamples, bSamples, a, x , y-1, z );
|
||||
sample( 5, aSamples, bSamples, a, x+1, y-1, z );
|
||||
sample( 6, aSamples, bSamples, a, x-1, y-1, z+1);
|
||||
sample( 7, aSamples, bSamples, a, x , y-1, z+1);
|
||||
sample( 8, aSamples, bSamples, a, x+1, y-1, z+1);
|
||||
sample( 9, aSamples, bSamples, a, x-1, y , z-1);
|
||||
sample(10, aSamples, bSamples, a, x , y , z-1);
|
||||
sample(11, aSamples, bSamples, a, x+1, y , z-1);
|
||||
sample(12, aSamples, bSamples, a, x-1, y , z );
|
||||
sample(13, aSamples, bSamples, a, x , y , z );
|
||||
sample(14, aSamples, bSamples, a, x+1, y , z );
|
||||
sample(15, aSamples, bSamples, a, x-1, y , z+1);
|
||||
sample(16, aSamples, bSamples, a, x , y , z+1);
|
||||
sample(17, aSamples, bSamples, a, x+1, y , z+1);
|
||||
sample(18, aSamples, bSamples, a, x-1, y+1, z-1);
|
||||
sample(19, aSamples, bSamples, a, x , y+1, z-1);
|
||||
sample(20, aSamples, bSamples, a, x+1, y+1, z-1);
|
||||
sample(21, aSamples, bSamples, a, x-1, y+1, z );
|
||||
sample(22, aSamples, bSamples, a, x , y+1, z );
|
||||
sample(23, aSamples, bSamples, a, x+1, y+1, z );
|
||||
sample(24, aSamples, bSamples, a, x-1, y+1, z+1);
|
||||
sample(25, aSamples, bSamples, a, x , y+1, z+1);
|
||||
sample(26, aSamples, bSamples, a, x+1, y+1, z+1);
|
||||
}
|
||||
|
||||
public int[] brightness(int side)
|
||||
{
|
||||
sideSample(side);
|
||||
return brightness[side];
|
||||
}
|
||||
|
||||
public float[] ao(int side)
|
||||
{
|
||||
sideSample(side);
|
||||
return ao[side];
|
||||
}
|
||||
|
||||
public void sideSample(int side)
|
||||
{
|
||||
if((computed&1<<side) == 0)
|
||||
{
|
||||
int[] ssample = ssamplem[side];
|
||||
for(int q = 0; q < 4; q++)
|
||||
{
|
||||
int[] qsample = qsamplem[q];
|
||||
if(Minecraft.isAmbientOcclusionEnabled())
|
||||
interp(side, q, ssample[qsample[0]], ssample[qsample[1]], ssample[qsample[2]], ssample[qsample[3]]);
|
||||
else
|
||||
interp(side, q, ssample[4], ssample[4], ssample[4], ssample[4]);
|
||||
}
|
||||
computed|=1<<side;
|
||||
}
|
||||
}
|
||||
|
||||
private void sample(int i, float[] aSamples, int[] bSamples, IBlockAccess a, int x, int y, int z)
|
||||
{
|
||||
int bid = a.getBlockId(x, y, z);
|
||||
Block b = Block.blocksList[bid];
|
||||
if(b == null)
|
||||
{
|
||||
bSamples[i] = a.getLightBrightnessForSkyBlocks(x, y, z, 0);
|
||||
aSamples[i] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bSamples[i] = a.getLightBrightnessForSkyBlocks(x, y, z, b.getLightValue(a, x, y, z));
|
||||
aSamples[i] = b.getAmbientOcclusionLightValue(a, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
private void interp(int s, int q, int a, int b, int c, int d)
|
||||
{
|
||||
ao[s][q] = interpAO(aSamples[a], aSamples[b], aSamples[c], aSamples[d])*sideao[s];
|
||||
brightness[s][q] = interpBrightness(bSamples[a], bSamples[b], bSamples[c], bSamples[d]);
|
||||
}
|
||||
|
||||
public static float interpAO(float a, float b, float c, float d)
|
||||
{
|
||||
return (a+b+c+d)/4F;
|
||||
}
|
||||
|
||||
public static int interpBrightness(int a, int b, int c, int d)
|
||||
{
|
||||
if(a == 0)
|
||||
a = d;
|
||||
if(b == 0)
|
||||
b = d;
|
||||
if(c == 0)
|
||||
c = d;
|
||||
return (a+b+c+d)>>2 & 0xFF00FF;
|
||||
}
|
||||
|
||||
public void setColour(Tessellator tess, LC lc, int c)
|
||||
{
|
||||
float[] a = ao(lc.side);
|
||||
float f = (a[0]*lc.fa + a[1]*lc.fb + a[2]*lc.fc + a[3]*lc.fd);
|
||||
CCRenderState.vertexColour((int)((c>>>24)*f), (int)((c>>16&0xFF)*f), (int)((c>>8&0xFF)*f), (c&0xFF));
|
||||
}
|
||||
|
||||
public void setBrightness(Tessellator tess, LC lc)
|
||||
{
|
||||
int[] b = brightness(lc.side);
|
||||
tess.setBrightness((int)(b[0]*lc.fa + b[1]*lc.fb + b[2]*lc.fc+b[3]*lc.fd) & 0xFF00FF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyModifiers(CCModel m, Tessellator tess, Vector3 vec, UV uv, Vector3 normal, int i)
|
||||
{
|
||||
LC lc;
|
||||
if(m instanceof CCRBModel)
|
||||
lc = ((CCRBModel)m).lightCoefficents[i];
|
||||
else
|
||||
lc = LC.compute(v_temp.set(vec).add(-pos.x, -pos.y, -pos.z), normal);
|
||||
|
||||
setColour(tess, lc, (m == null || m.colours == null) ? -1 : m.colours[i]);
|
||||
setBrightness(tess, lc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsNormals()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package codechicken.lib.lighting;
|
||||
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import codechicken.lib.render.CCModel;
|
||||
import codechicken.lib.render.CCRenderState;
|
||||
import codechicken.lib.render.IVertexModifier;
|
||||
import codechicken.lib.render.UV;
|
||||
import codechicken.lib.vec.Rotation;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
|
||||
public class LightModel implements IVertexModifier
|
||||
{
|
||||
public static class Light
|
||||
{
|
||||
public Vector3 ambient = new Vector3();
|
||||
public Vector3 diffuse = new Vector3();
|
||||
public Vector3 position;
|
||||
|
||||
public Light(Vector3 pos)
|
||||
{
|
||||
position = pos.copy().normalize();
|
||||
}
|
||||
|
||||
public Light setDiffuse(Vector3 vec)
|
||||
{
|
||||
diffuse.set(vec);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Light setAmbient(Vector3 vec)
|
||||
{
|
||||
ambient.set(vec);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static LightModel standardLightModel;
|
||||
static
|
||||
{
|
||||
standardLightModel = new LightModel()
|
||||
.setAmbient(new Vector3(0.4, 0.4, 0.4))
|
||||
.addLight(new Light(new Vector3(0.2, 1, -0.7))
|
||||
.setDiffuse(new Vector3(0.6, 0.6, 0.6)))
|
||||
.addLight(new Light(new Vector3(-0.2, 1, 0.7))
|
||||
.setDiffuse(new Vector3(0.6, 0.6, 0.6)));
|
||||
}
|
||||
|
||||
private Vector3 ambient = new Vector3();
|
||||
private Light[] lights = new Light[8];
|
||||
private int lightCount;
|
||||
|
||||
public LightModel addLight(Light light)
|
||||
{
|
||||
lights[lightCount++] = light;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LightModel setAmbient(Vector3 vec)
|
||||
{
|
||||
ambient.set(vec);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param colour The pre-lighting vertex colour. RGBA format
|
||||
* @param normal The normal at the vertex
|
||||
* @return The lighting applied colour
|
||||
*/
|
||||
public int apply(int colour, Vector3 normal)
|
||||
{
|
||||
Vector3 n_colour = ambient.copy();
|
||||
for(int l = 0; l < lightCount; l++)
|
||||
{
|
||||
Light light = lights[l];
|
||||
double n_l = light.position.dotProduct(normal);
|
||||
double f = n_l > 0 ? 1 : 0;
|
||||
n_colour.x += light.ambient.x + f*light.diffuse.x*n_l;
|
||||
n_colour.y += light.ambient.y + f*light.diffuse.y*n_l;
|
||||
n_colour.z += light.ambient.z + f*light.diffuse.z*n_l;
|
||||
}
|
||||
|
||||
if(n_colour.x > 1)
|
||||
n_colour.x = 1;
|
||||
if(n_colour.y > 1)
|
||||
n_colour.y = 1;
|
||||
if(n_colour.z > 1)
|
||||
n_colour.z = 1;
|
||||
|
||||
n_colour.multiply((colour >>> 24)/255D, (colour >> 16 & 0xFF)/255D, (colour >> 8 & 0xFF)/255D);
|
||||
return (int)(n_colour.x*255)<<24 | (int)(n_colour.y*255)<<16 | (int)(n_colour.z*255)<<8 | colour&0xFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyModifiers(CCModel m, Tessellator tess, Vector3 vec, UV uv, Vector3 normal, int i)
|
||||
{
|
||||
CCRenderState.setColour(apply((m == null || m.colours == null) ? -1 : m.colours[i], normal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsNormals()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public PlanarLightModel reducePlanar()
|
||||
{
|
||||
int[] colours = new int[6];
|
||||
for(int i = 0; i < 6; i++)
|
||||
colours[i] = apply(-1, Rotation.axes[i]);
|
||||
return new PlanarLightModel(colours);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package codechicken.lib.lighting;
|
||||
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import codechicken.lib.colour.Colour;
|
||||
import codechicken.lib.colour.ColourRGBA;
|
||||
import codechicken.lib.render.CCModel;
|
||||
import codechicken.lib.render.CCRenderState;
|
||||
import codechicken.lib.render.IVertexModifier;
|
||||
import codechicken.lib.render.UV;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
|
||||
/**
|
||||
* Faster precomputed version of LightModel that only works for axis planar sides
|
||||
*/
|
||||
public class PlanarLightModel implements IVertexModifier
|
||||
{
|
||||
public ColourRGBA[] colours;
|
||||
|
||||
public PlanarLightModel(int[] colours)
|
||||
{
|
||||
this.colours = new ColourRGBA[6];
|
||||
for(int i = 0; i < 6; i++)
|
||||
this.colours[i] = new ColourRGBA(colours[i]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyModifiers(CCModel m, Tessellator tess, Vector3 vec, UV uv, Vector3 normal, int i)
|
||||
{
|
||||
ColourRGBA light = colours[CCModel.findSide(normal)];
|
||||
int colour = (m == null || m.colours == null) ? -1 : m.colours[i];
|
||||
Colour res = new ColourRGBA(colour).multiply(light);
|
||||
CCRenderState.vertexColour(res.r&0xFF, res.g&0xFF, res.b&0xFF, res.a&0xFF) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsNormals()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package codechicken.lib.math;
|
||||
|
||||
public class MathHelper
|
||||
{
|
||||
public static final double phi = 1.618033988749894;
|
||||
public static final double pi = Math.PI;
|
||||
public static final double todeg = 57.29577951308232;
|
||||
public static final double torad = 0.017453292519943;
|
||||
public static final double sqrt2 = 1.414213562373095;
|
||||
|
||||
public static double[] SIN_TABLE = new double[65536];
|
||||
static
|
||||
{
|
||||
for (int i = 0; i < 65536; ++i)
|
||||
SIN_TABLE[i] = Math.sin(i / 65536D * 2 * Math.PI);
|
||||
|
||||
SIN_TABLE[0] = 0;
|
||||
SIN_TABLE[16384] = 1;
|
||||
SIN_TABLE[32768] = 0;
|
||||
SIN_TABLE[49152] = 1;
|
||||
}
|
||||
|
||||
public static double sin(double d)
|
||||
{
|
||||
return SIN_TABLE[(int)((float)d * 10430.378F) & 65535];
|
||||
}
|
||||
|
||||
public static double cos(double d)
|
||||
{
|
||||
return SIN_TABLE[(int)((float)d * 10430.378F + 16384.0F) & 65535];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param a The value
|
||||
* @param b The value to approach
|
||||
* @param max The maximum step
|
||||
* @return the closed value to b no less than max from a
|
||||
*/
|
||||
public static float approachLinear(float a, float b, float max)
|
||||
{
|
||||
return (a > b) ?
|
||||
(a - b < max ? b : a-max) :
|
||||
(b - a < max ? b : a+max);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param a The value
|
||||
* @param b The value to approach
|
||||
* @param max The maximum step
|
||||
* @return the closed value to b no less than max from a
|
||||
*/
|
||||
public static double approachLinear(double a, double b, double max)
|
||||
{
|
||||
return (a > b) ?
|
||||
(a - b < max ? b : a-max) :
|
||||
(b - a < max ? b : a+max);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param a The first value
|
||||
* @param b The second value
|
||||
* @param d The interpolation factor, between 0 and 1
|
||||
* @return a+(b-a)*d
|
||||
*/
|
||||
public static float interpolate(float a, float b, float d)
|
||||
{
|
||||
return a+(b-a)*d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param a The first value
|
||||
* @param b The second value
|
||||
* @param d The interpolation factor, between 0 and 1
|
||||
* @return a+(b-a)*d
|
||||
*/
|
||||
public static double interpolate(double a, double b, double d)
|
||||
{
|
||||
return a+(b-a)*d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param a The value
|
||||
* @param b The value to approach
|
||||
* @param ratio The ratio to reduce the difference by
|
||||
* @return a+(b-a)*ratio
|
||||
*/
|
||||
public static double approachExp(double a, double b, double ratio)
|
||||
{
|
||||
return a+(b-a)*ratio;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param a The value
|
||||
* @param b The value to approach
|
||||
* @param ratio The ratio to reduce the difference by
|
||||
* @param cap The maximum amount to advance by
|
||||
* @return a+(b-a)*ratio
|
||||
*/
|
||||
public static double approachExp(double a, double b, double ratio, double cap)
|
||||
{
|
||||
double d = (b-a)*ratio;
|
||||
if(Math.abs(d) > cap)
|
||||
d = Math.signum(d)*cap;
|
||||
return a+d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param a The value
|
||||
* @param b The value to approach
|
||||
* @param ratio The ratio to reduce the difference by
|
||||
* @param c The value to retreat from
|
||||
* @param kick The difference when a == c
|
||||
* @return
|
||||
*/
|
||||
public static double retreatExp(double a, double b, double c, double ratio, double kick)
|
||||
{
|
||||
double d = (Math.abs(c-a)+kick)*ratio;
|
||||
if(d > Math.abs(b-a))
|
||||
return b;
|
||||
return a+Math.signum(b-a)*d;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value The value
|
||||
* @param min The min value
|
||||
* @param max The max value
|
||||
* @return The clipped value between min and max
|
||||
*/
|
||||
public static double clip(double value, double min, double max)
|
||||
{
|
||||
if(value > max)
|
||||
value = max;
|
||||
if(value < min)
|
||||
value = min;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a <= x <= b
|
||||
*/
|
||||
public static boolean between(double a, double x, double b)
|
||||
{
|
||||
return a <= x && x <= b;
|
||||
}
|
||||
|
||||
public static int approachExpI(int a, int b, double ratio)
|
||||
{
|
||||
int r = (int)Math.round(approachExp(a, b, ratio));
|
||||
return r == a ? b : r;
|
||||
}
|
||||
|
||||
public static int retreatExpI(int a, int b, int c, double ratio, int kick)
|
||||
{
|
||||
int r = (int)Math.round(retreatExp(a, b, c, ratio, kick));
|
||||
return r == a ? b : r;
|
||||
}
|
||||
|
||||
public static int floor_double(double d)
|
||||
{
|
||||
return net.minecraft.util.MathHelper.floor_double(d);
|
||||
}
|
||||
|
||||
public static int roundAway(double d)
|
||||
{
|
||||
return (int) (d < 0 ? Math.floor(d) : Math.ceil(d));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package codechicken.lib.packet;
|
||||
|
||||
public interface ICustomPacketTile
|
||||
{
|
||||
public void handleDescriptionPacket(PacketCustom packet);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package codechicken.lib.packet;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import net.minecraft.network.packet.NetHandler;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
|
||||
public class MetaPacket extends Packet
|
||||
{
|
||||
public ArrayList<Packet> packets = new ArrayList<Packet>();
|
||||
|
||||
public MetaPacket(Packet... packets)
|
||||
{
|
||||
for(Packet p : packets)
|
||||
this.packets.add(p);
|
||||
}
|
||||
|
||||
public MetaPacket(Collection<? extends Packet> packets)
|
||||
{
|
||||
this.packets.addAll(packets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readPacketData(DataInput datain)
|
||||
{
|
||||
throw new IllegalStateException("Meta packets can't be read");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writePacketData(DataOutput dataout) throws IOException
|
||||
{
|
||||
for(Packet p : packets)
|
||||
p.writePacketData(dataout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processPacket(NetHandler nethandler)
|
||||
{
|
||||
for(Packet p : packets)//Memory connection
|
||||
p.processPacket(nethandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPacketSize()
|
||||
{
|
||||
int size = 0;
|
||||
for(Packet p : packets)
|
||||
size+=p.getPacketSize();
|
||||
return size;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,50 @@
|
||||
package codechicken.lib.raytracer;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.util.Vec3;
|
||||
|
||||
public class ExtendedMOP extends MovingObjectPosition
|
||||
{
|
||||
public Object data;
|
||||
|
||||
public ExtendedMOP(Entity entity, Object data)
|
||||
{
|
||||
super(entity);
|
||||
setData(data);
|
||||
}
|
||||
|
||||
public ExtendedMOP(int x, int y, int z, int side, Vec3 hit, Object data)
|
||||
{
|
||||
super(x, y, z, side, hit);
|
||||
setData(data);
|
||||
}
|
||||
|
||||
public ExtendedMOP(MovingObjectPosition mop, Object data)
|
||||
{
|
||||
super(0, 0, 0, 0, mop.hitVec);
|
||||
typeOfHit = mop.typeOfHit;
|
||||
blockX = mop.blockX;
|
||||
blockY = mop.blockY;
|
||||
blockZ = mop.blockZ;
|
||||
sideHit = mop.sideHit;
|
||||
subHit = mop.subHit;
|
||||
setData(data);
|
||||
}
|
||||
|
||||
public void setData(Object data)
|
||||
{
|
||||
if(data instanceof Integer)
|
||||
subHit = ((Integer) data).intValue();
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getData(MovingObjectPosition mop)
|
||||
{
|
||||
if(mop instanceof ExtendedMOP)
|
||||
return (T)((ExtendedMOP)mop).data;
|
||||
|
||||
return (T)Integer.valueOf(mop.subHit);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package codechicken.lib.raytracer;
|
||||
|
||||
import codechicken.lib.vec.Cuboid6;
|
||||
|
||||
public class IndexedCuboid6 extends Cuboid6
|
||||
{
|
||||
public Object data;
|
||||
|
||||
public IndexedCuboid6(Object data, Cuboid6 cuboid)
|
||||
{
|
||||
super(cuboid);
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package codechicken.lib.raytracer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import codechicken.lib.math.MathHelper;
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.lib.vec.Cuboid6;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.util.EnumMovingObjectType;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class RayTracer
|
||||
{
|
||||
private Vector3 vec = new Vector3();
|
||||
private Vector3 vec2 = new Vector3();
|
||||
|
||||
private Vector3 s_vec = new Vector3();
|
||||
private double s_dist;
|
||||
private int s_side;
|
||||
private IndexedCuboid6 c_cuboid;
|
||||
|
||||
public static RayTracer instance = new RayTracer();
|
||||
|
||||
private void traceSide(int side, Vector3 start, Vector3 end, Cuboid6 cuboid)
|
||||
{
|
||||
vec.set(start);
|
||||
Vector3 hit = null;
|
||||
switch(side)
|
||||
{
|
||||
case 0:
|
||||
hit = vec.XZintercept(end, cuboid.min.y);
|
||||
break;
|
||||
case 1:
|
||||
hit = vec.XZintercept(end, cuboid.max.y);
|
||||
break;
|
||||
case 2:
|
||||
hit = vec.XYintercept(end, cuboid.min.z);
|
||||
break;
|
||||
case 3:
|
||||
hit = vec.XYintercept(end, cuboid.max.z);
|
||||
break;
|
||||
case 4:
|
||||
hit = vec.YZintercept(end, cuboid.min.x);
|
||||
break;
|
||||
case 5:
|
||||
hit = vec.YZintercept(end, cuboid.max.x);
|
||||
break;
|
||||
}
|
||||
if(hit == null)
|
||||
return;
|
||||
|
||||
switch(side)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
if(!MathHelper.between(cuboid.min.x, hit.x, cuboid.max.x) || !MathHelper.between(cuboid.min.z, hit.z, cuboid.max.z)) return;
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
if(!MathHelper.between(cuboid.min.x, hit.x, cuboid.max.x) || !MathHelper.between(cuboid.min.y, hit.y, cuboid.max.y)) return;
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
if(!MathHelper.between(cuboid.min.y, hit.y, cuboid.max.y) || !MathHelper.between(cuboid.min.z, hit.z, cuboid.max.z)) return;
|
||||
break;
|
||||
}
|
||||
|
||||
double dist = vec2.set(hit).subtract(start).magSquared();
|
||||
if(dist < s_dist)
|
||||
{
|
||||
s_side = side;
|
||||
s_dist = dist;
|
||||
s_vec.set(vec);
|
||||
}
|
||||
}
|
||||
|
||||
public MovingObjectPosition rayTraceCuboid(Vector3 start, Vector3 end, Cuboid6 cuboid)
|
||||
{
|
||||
s_dist = Double.MAX_VALUE;
|
||||
s_side = -1;
|
||||
|
||||
for(int i = 0; i < 6; i++)
|
||||
traceSide(i, start, end, cuboid);
|
||||
|
||||
if(s_side < 0)
|
||||
return null;
|
||||
|
||||
MovingObjectPosition mop = new MovingObjectPosition(0, 0, 0, s_side, s_vec.toVec3D());
|
||||
mop.typeOfHit = null;
|
||||
return mop;
|
||||
}
|
||||
|
||||
public MovingObjectPosition rayTraceCuboid(Vector3 start, Vector3 end, Cuboid6 cuboid, BlockCoord pos)
|
||||
{
|
||||
MovingObjectPosition mop = rayTraceCuboid(start, end, cuboid);
|
||||
if(mop != null)
|
||||
{
|
||||
mop.typeOfHit = EnumMovingObjectType.TILE;
|
||||
mop.blockX = pos.x;
|
||||
mop.blockY = pos.y;
|
||||
mop.blockZ = pos.z;
|
||||
}
|
||||
return mop;
|
||||
}
|
||||
|
||||
public MovingObjectPosition rayTraceCuboid(Vector3 start, Vector3 end, Cuboid6 cuboid, Entity e)
|
||||
{
|
||||
MovingObjectPosition mop = rayTraceCuboid(start, end, cuboid);
|
||||
if(mop != null)
|
||||
{
|
||||
mop.typeOfHit = EnumMovingObjectType.ENTITY;
|
||||
mop.entityHit = e;
|
||||
}
|
||||
return mop;
|
||||
}
|
||||
|
||||
public MovingObjectPosition rayTraceCuboids(Vector3 start, Vector3 end, List<IndexedCuboid6> cuboids)
|
||||
{
|
||||
double c_dist = Double.MAX_VALUE;
|
||||
MovingObjectPosition c_hit = null;
|
||||
|
||||
for(IndexedCuboid6 cuboid : cuboids)
|
||||
{
|
||||
MovingObjectPosition mop = rayTraceCuboid(start, end, cuboid);
|
||||
if(mop != null && s_dist < c_dist)
|
||||
{
|
||||
mop = new ExtendedMOP(mop, cuboid.data);
|
||||
c_dist = s_dist;
|
||||
c_hit = mop;
|
||||
c_cuboid = cuboid;
|
||||
}
|
||||
}
|
||||
|
||||
return c_hit;
|
||||
}
|
||||
|
||||
public MovingObjectPosition rayTraceCuboids(Vector3 start, Vector3 end, List<IndexedCuboid6> cuboids, BlockCoord pos, Block block)
|
||||
{
|
||||
MovingObjectPosition mop = rayTraceCuboids(start, end, cuboids);
|
||||
if(mop != null)
|
||||
{
|
||||
mop.typeOfHit = EnumMovingObjectType.TILE;
|
||||
mop.blockX = pos.x;
|
||||
mop.blockY = pos.y;
|
||||
mop.blockZ = pos.z;
|
||||
if(block != null)
|
||||
c_cuboid.add(new Vector3(-pos.x, -pos.y, -pos.z)).setBlockBounds(block);
|
||||
}
|
||||
return mop;
|
||||
}
|
||||
|
||||
public static MovingObjectPosition retraceBlock(World world, EntityPlayer player, int x, int y, int z)
|
||||
{
|
||||
Vec3 headVec = Vec3.createVectorHelper(player.posX, (player.posY + 1.62) - player.yOffset, player.posZ);
|
||||
Vec3 lookVec = player.getLook(1.0F);
|
||||
double reach = world.isRemote ? getBlockReachDistance_client() : getBlockReachDistance_server((EntityPlayerMP) player);
|
||||
Vec3 endVec = headVec.addVector(lookVec.xCoord * reach, lookVec.yCoord * reach, lookVec.zCoord * reach);
|
||||
return Block.blocksList[world.getBlockId(x, y, z)].collisionRayTrace(world, x, y, z, headVec, endVec);
|
||||
}
|
||||
|
||||
private static double getBlockReachDistance_server(EntityPlayerMP player)
|
||||
{
|
||||
return player.theItemInWorldManager.getBlockReachDistance();
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
private static double getBlockReachDistance_client()
|
||||
{
|
||||
return Minecraft.getMinecraft().playerController.getBlockReachDistance();
|
||||
}
|
||||
|
||||
public static MovingObjectPosition reTrace(World world, EntityPlayer player)
|
||||
{
|
||||
return reTrace(world, player, world.isRemote ? getBlockReachDistance_client() : getBlockReachDistance_server((EntityPlayerMP) player));
|
||||
}
|
||||
|
||||
public static MovingObjectPosition reTrace(World world, EntityPlayer player, double reach)
|
||||
{
|
||||
Vec3 headVec = Vec3.createVectorHelper(player.posX, (player.posY + 1.62) - player.yOffset, player.posZ);
|
||||
Vec3 lookVec = player.getLook(1);
|
||||
Vec3 endVec = headVec.addVector(lookVec.xCoord * reach, lookVec.yCoord * reach, lookVec.zCoord * reach);
|
||||
return world.rayTraceBlocks_do_do(headVec, endVec, true, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,968 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import codechicken.lib.lighting.CCRBModel;
|
||||
import codechicken.lib.lighting.LightModel;
|
||||
import codechicken.lib.vec.Cuboid6;
|
||||
import codechicken.lib.vec.RedundantTransformation;
|
||||
import codechicken.lib.vec.Transformation;
|
||||
import codechicken.lib.vec.TransformationList;
|
||||
import codechicken.lib.vec.Translation;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
|
||||
import static codechicken.lib.vec.Rotation.*;
|
||||
|
||||
public class CCModel
|
||||
{
|
||||
private static class PositionNormalEntry
|
||||
{
|
||||
public Vector3 pos;
|
||||
public LinkedList<Vector3> normals = new LinkedList<Vector3>();
|
||||
|
||||
public PositionNormalEntry(Vector3 position)
|
||||
{
|
||||
pos = position;
|
||||
}
|
||||
|
||||
public boolean positionEqual(Vector3 v)
|
||||
{
|
||||
return pos.x == v.x && pos.y == v.y && pos.z == v.z;
|
||||
}
|
||||
|
||||
public PositionNormalEntry addNormal(Vector3 normal)
|
||||
{
|
||||
normals.add(normal);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public final int vertexMode;
|
||||
public final int vp;
|
||||
public Vertex5[] verts;
|
||||
public Vector3[] normals;
|
||||
public int[] colours;
|
||||
|
||||
protected CCModel(int vertexMode)
|
||||
{
|
||||
if(vertexMode != 7 && vertexMode != 4)
|
||||
throw new IllegalArgumentException("Models must be GL_QUADS or GL_TRIANGLES");
|
||||
|
||||
this.vertexMode = vertexMode;
|
||||
vp = vertexMode == 7 ? 4 : 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Each pixel corresponds to one unit of position when generating the model
|
||||
* @param i Vertex index to start generating at
|
||||
* @param x1 The minX bound of the box
|
||||
* @param y1 The minY bound of the box
|
||||
* @param z1 The minZ bound of the box
|
||||
* @param w The width of the box
|
||||
* @param h The height of the box
|
||||
* @param d The depth of the box
|
||||
* @param tx The distance of the top left corner of the texture map from the left in pixels
|
||||
* @param ty The distance of the top left corner of the texture map from the top in pixels
|
||||
* @param tw The width of the texture in pixels
|
||||
* @param th The height of the texture in pixels
|
||||
* @param f The scale of the model, pixels per block, normally 16
|
||||
* @return The generated model
|
||||
*/
|
||||
public CCModel generateBox(int i, double x1, double y1, double z1, double w, double h, double d, double tx, double ty, double tw, double th, double f)
|
||||
{
|
||||
double u1, v1, u2, v2;
|
||||
double x2 = x1+w;
|
||||
double y2 = y1+h;
|
||||
double z2 = z1+d;
|
||||
x1 /= f; x2 /= f; y1 /= f; y2 /= f; z1 /= f; z2 /= f;
|
||||
|
||||
//bottom face
|
||||
u1 = (tx + d + w) / tw; v1 = (ty + d) / th;
|
||||
u2 = (tx + d*2 + w) / tw; v2 = ty / th;
|
||||
verts[i++] = new Vertex5(x1, y1, z2, u1, v2);
|
||||
verts[i++] = new Vertex5(x1, y1, z1, u1, v1);
|
||||
verts[i++] = new Vertex5(x2, y1, z1, u2, v1);
|
||||
verts[i++] = new Vertex5(x2, y1, z2, u2, v2);
|
||||
|
||||
//top face
|
||||
u1 = (tx + d) / tw; v1 = (ty + d) / th;
|
||||
u2 = (tx + d + w) / tw; v2 = ty / th;
|
||||
verts[i++] = new Vertex5(x2, y2, z2, u2, v2);
|
||||
verts[i++] = new Vertex5(x2, y2, z1, u2, v1);
|
||||
verts[i++] = new Vertex5(x1, y2, z1, u1, v1);
|
||||
verts[i++] = new Vertex5(x1, y2, z2, u1, v2);
|
||||
|
||||
//front face
|
||||
u1 = (tx + d + w) / tw; v1 = (ty + d) / th;
|
||||
u2 = (tx + d) / tw; v2 = (ty + d + h) / th;
|
||||
verts[i++] = new Vertex5(x1, y2, z1, u2, v1);
|
||||
verts[i++] = new Vertex5(x2, y2, z1, u1, v1);
|
||||
verts[i++] = new Vertex5(x2, y1, z1, u1, v2);
|
||||
verts[i++] = new Vertex5(x1, y1, z1, u2, v2);
|
||||
|
||||
//back face
|
||||
u1 = (tx + d*2 + w*2) / tw; v1 = (ty + d) / th;
|
||||
u2 = (tx + d*2 + w) / tw; v2 = (ty + d + h) / th;
|
||||
verts[i++] = new Vertex5(x1, y2, z2, u1, v1);
|
||||
verts[i++] = new Vertex5(x1, y1, z2, u1, v2);
|
||||
verts[i++] = new Vertex5(x2, y1, z2, u2, v2);
|
||||
verts[i++] = new Vertex5(x2, y2, z2, u2, v1);
|
||||
|
||||
//left face
|
||||
u1 = (tx + d) / tw; v1 = (ty + d) / th;
|
||||
u2 = (tx) / tw; v2 = (ty + d + h) / th;
|
||||
verts[i++] = new Vertex5(x1, y2, z2, u2, v1);
|
||||
verts[i++] = new Vertex5(x1, y2, z1, u1, v1);
|
||||
verts[i++] = new Vertex5(x1, y1, z1, u1, v2);
|
||||
verts[i++] = new Vertex5(x1, y1, z2, u2, v2);
|
||||
|
||||
//right face
|
||||
u1 = (tx + d*2 + w) / tw; v1 = (ty + d) / th;
|
||||
u2 = (tx + d + w) / tw; v2 = (ty + d + h) / th;
|
||||
verts[i++] = new Vertex5(x2, y1, z2, u1, v2);
|
||||
verts[i++] = new Vertex5(x2, y1, z1, u2, v2);
|
||||
verts[i++] = new Vertex5(x2, y2, z1, u2, v1);
|
||||
verts[i++] = new Vertex5(x2, y2, z2, u1, v1);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a box, uv mapped to be the same as a minecraft block with the same bounds
|
||||
* @param i The vertex index to start generating at
|
||||
* @param bounds The bounds of the block, 0 to 1
|
||||
* @return The generated model. When rendering an icon will need to be supplied for the UV transformation.
|
||||
*/
|
||||
public CCModel generateBlock(int i, Cuboid6 bounds)
|
||||
{
|
||||
return generateBlock(i, bounds.min.x, bounds.min.y, bounds.min.z, bounds.max.x, bounds.max.y, bounds.max.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a box, uv mapped to be the same as a minecraft block with the same bounds
|
||||
* @param i The vertex index to start generating at
|
||||
* @param x1 minX
|
||||
* @param y1 minY
|
||||
* @param z1 minZ
|
||||
* @param x2 maxX
|
||||
* @param y2 maxY
|
||||
* @param z2 maxZ
|
||||
* @return The generated model. When rendering an icon will need to be supplied for the UV transformation.
|
||||
*/
|
||||
public CCModel generateBlock(int i, double x1, double y1, double z1, double x2, double y2, double z2)
|
||||
{
|
||||
double u1, v1, u2, v2;
|
||||
|
||||
//bottom face
|
||||
u1 = x1; v1 = z1;
|
||||
u2 = x2; v2 = z2;
|
||||
verts[i++] = new Vertex5(x1, y1, z2, u1, v2);
|
||||
verts[i++] = new Vertex5(x1, y1, z1, u1, v1);
|
||||
verts[i++] = new Vertex5(x2, y1, z1, u2, v1);
|
||||
verts[i++] = new Vertex5(x2, y1, z2, u2, v2);
|
||||
|
||||
//top face
|
||||
u1 = x1+2; v1 = z1;
|
||||
u2 = x2+2; v2 = z2;
|
||||
verts[i++] = new Vertex5(x2, y2, z2, u2, v2);
|
||||
verts[i++] = new Vertex5(x2, y2, z1, u2, v1);
|
||||
verts[i++] = new Vertex5(x1, y2, z1, u1, v1);
|
||||
verts[i++] = new Vertex5(x1, y2, z2, u1, v2);
|
||||
|
||||
//east face
|
||||
u1 = x1+4; v1 = 1-y2;
|
||||
u2 = x2+4; v2 = 1-y1;
|
||||
verts[i++] = new Vertex5(x1, y1, z1, u1, v2);
|
||||
verts[i++] = new Vertex5(x1, y2, z1, u1, v1);
|
||||
verts[i++] = new Vertex5(x2, y2, z1, u2, v1);
|
||||
verts[i++] = new Vertex5(x2, y1, z1, u2, v2);
|
||||
|
||||
//west face
|
||||
u1 = x1+6; v1 = 1-y2;
|
||||
u2 = x2+6; v2 = 1-y1;
|
||||
verts[i++] = new Vertex5(x2, y1, z2, u2, v2);
|
||||
verts[i++] = new Vertex5(x2, y2, z2, u2, v1);
|
||||
verts[i++] = new Vertex5(x1, y2, z2, u1, v1);
|
||||
verts[i++] = new Vertex5(x1, y1, z2, u1, v2);
|
||||
|
||||
//north face
|
||||
u1 = z1+8; v1 = 1-y2;
|
||||
u2 = z2+8; v2 = 1-y1;
|
||||
verts[i++] = new Vertex5(x1, y1, z2, u2, v2);
|
||||
verts[i++] = new Vertex5(x1, y2, z2, u2, v1);
|
||||
verts[i++] = new Vertex5(x1, y2, z1, u1, v1);
|
||||
verts[i++] = new Vertex5(x1, y1, z1, u1, v2);
|
||||
|
||||
//south face
|
||||
u1 = z1+10; v1 = 1-y2;
|
||||
u2 = z2+10; v2 = 1-y1;
|
||||
verts[i++] = new Vertex5(x2, y1, z1, u1, v2);
|
||||
verts[i++] = new Vertex5(x2, y2, z1, u1, v1);
|
||||
verts[i++] = new Vertex5(x2, y2, z2, u2, v1);
|
||||
verts[i++] = new Vertex5(x2, y1, z2, u2, v2);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public CCModel computeNormals()
|
||||
{
|
||||
return computeNormals(0, verts.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the normals of all faces in the model.
|
||||
* Uses the cross product of the vectors along 2 sides of the face
|
||||
* @param start The first vertex to generate normals for
|
||||
* @param length The number of vertices to generate normals for. Note this must be a multiple of 3 for triangles or 4 for quads
|
||||
* @return The model
|
||||
*/
|
||||
public CCModel computeNormals(int start, int length)
|
||||
{
|
||||
if(length%vp != 0 || start%vp != 0)
|
||||
throw new IllegalArgumentException("Cannot generate normals across polygons");
|
||||
|
||||
if(normals == null)
|
||||
normals = new Vector3[verts.length];
|
||||
|
||||
for(int k = 0; k < length; k+=vp)
|
||||
{
|
||||
int i = k + start;
|
||||
Vector3 diff1 = verts[i+1].vec.copy().subtract(verts[i].vec);
|
||||
Vector3 diff2 = verts[i+vp-1].vec.copy().subtract(verts[i].vec);
|
||||
normals[i] = diff1.crossProduct(diff2).normalize();
|
||||
for(int d = 1; d < vp; d++)
|
||||
normals[i+d] = normals[i].copy();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes lighting using the normals and the colour 0xFFFFFF.
|
||||
* Per vert colouring will be added when needed.
|
||||
* Make sure you have generated your normals on the model first.
|
||||
* If you rotate your model after this, the lighting will no longer be valid
|
||||
* @param light The light model to calculate
|
||||
* @return The model
|
||||
*/
|
||||
public CCModel computeLighting(LightModel light)
|
||||
{
|
||||
if(colours == null)
|
||||
setColour(-1);
|
||||
for(int k = 0; k < verts.length; k++)
|
||||
colours[k] = light.apply(colours[k], normals[k]);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CCModel setColour(int c)
|
||||
{
|
||||
if(colours == null)
|
||||
colours = new int[verts.length];
|
||||
for(int k = 0; k < verts.length; k++)
|
||||
colours[k] = c;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Warning, only use this if you NEED to be identical to MC's light model, it's hideous.
|
||||
*/
|
||||
public CCRBModel withMCLighting()
|
||||
{
|
||||
return new CCRBModel(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Averages all normals at the same position to produce a smooth lighting effect.
|
||||
* @return The model
|
||||
*/
|
||||
public CCModel smoothNormals()
|
||||
{
|
||||
ArrayList<PositionNormalEntry> map = new ArrayList<PositionNormalEntry>();
|
||||
nextvert: for(int k = 0; k < verts.length; k++)
|
||||
{
|
||||
Vector3 vec = verts[k].vec;
|
||||
for(PositionNormalEntry e : map)
|
||||
if(e.positionEqual(vec))
|
||||
{
|
||||
e.addNormal(normals[k]);
|
||||
continue nextvert;
|
||||
}
|
||||
|
||||
map.add(new PositionNormalEntry(vec).addNormal(normals[k]));
|
||||
}
|
||||
|
||||
for(PositionNormalEntry e : map)
|
||||
{
|
||||
if(e.normals.size() <= 1)
|
||||
continue;
|
||||
|
||||
Vector3 new_n = new Vector3();
|
||||
for(Vector3 n : e.normals)
|
||||
new_n.add(n);
|
||||
|
||||
new_n.normalize();
|
||||
for(Vector3 n : e.normals)
|
||||
n.set(new_n);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public CCModel apply(Transformation t)
|
||||
{
|
||||
for(int k = 0; k < verts.length; k++)
|
||||
t.apply(verts[k].vec);
|
||||
|
||||
if(normals != null)
|
||||
for(int k = 0; k < normals.length; k++)
|
||||
t.applyN(normals[k]);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void render()
|
||||
{
|
||||
render(0, verts.length, null, null, null);
|
||||
}
|
||||
|
||||
public void render(double x, double y, double z, double u, double v)
|
||||
{
|
||||
render(new Translation(new Vector3(x, y, z)), new UVTranslation(u, v));
|
||||
}
|
||||
|
||||
public void render(double x, double y, double z, IUVTransformation u)
|
||||
{
|
||||
render(new Translation(new Vector3(x, y, z)), u);
|
||||
}
|
||||
|
||||
public void render(Transformation t, double u, double v)
|
||||
{
|
||||
render(t, new UVTranslation(u, v));
|
||||
}
|
||||
|
||||
public void render(Transformation t, IUVTransformation u)
|
||||
{
|
||||
render(t, u, ColourModifier.instance);
|
||||
}
|
||||
|
||||
public void render(Transformation t, IUVTransformation u, IVertexModifier m)
|
||||
{
|
||||
render(0, verts.length, t, u, m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders vertices start through start+length-1 of the model
|
||||
* @param start The first vertex to render
|
||||
* @param length The number of vertices to render
|
||||
* @param t The transformation to apply to the mat
|
||||
* @param u The u texture offset
|
||||
* @param v The v texture offset
|
||||
*/
|
||||
public void render(int start, int length, Transformation t, IUVTransformation u, IVertexModifier m)
|
||||
{
|
||||
boolean drawNormal = CCRenderState.useNormals() && normals != null;
|
||||
boolean computeNormal = drawNormal || m != null && m.needsNormals();
|
||||
Vector3 normal = new Vector3();
|
||||
Vertex5 vert;
|
||||
Vector3 vec = new Vector3();
|
||||
UV uv = new UV();
|
||||
Tessellator tess = Tessellator.instance;
|
||||
for(int k = 0; k < length; k++)
|
||||
{
|
||||
int i = start+k;
|
||||
if(computeNormal)
|
||||
{
|
||||
if(t != null)
|
||||
t.applyN(normal.set(normals[i]));
|
||||
else
|
||||
normal = normals[i];
|
||||
|
||||
if(drawNormal)
|
||||
tess.setNormal((float)normal.x, (float)normal.y, (float)normal.z);
|
||||
}
|
||||
|
||||
vert = verts[i];
|
||||
if(t != null)
|
||||
t.apply(vec.set(vert.vec));
|
||||
else
|
||||
vec = vert.vec;
|
||||
|
||||
if(u != null)
|
||||
u.transform(uv.set(vert.uv));
|
||||
else
|
||||
uv = vert.uv;
|
||||
|
||||
if(m != null)
|
||||
m.applyModifiers(this, tess, vec, uv, normal, i);
|
||||
|
||||
tess.addVertexWithUV(vec.x, vec.y, vec.z, uv.u, uv.v);
|
||||
}
|
||||
}
|
||||
|
||||
public static CCModel quadModel(int numVerts)
|
||||
{
|
||||
return newModel(7, numVerts);
|
||||
}
|
||||
|
||||
public static CCModel triModel(int numVerts)
|
||||
{
|
||||
return newModel(4, numVerts);
|
||||
}
|
||||
|
||||
public static CCModel newModel(int vertexMode, int numVerts)
|
||||
{
|
||||
CCModel model = newModel(vertexMode);
|
||||
model.verts = new Vertex5[numVerts];
|
||||
return model;
|
||||
}
|
||||
|
||||
public static CCModel newModel(int vertexMode)
|
||||
{
|
||||
return new CCModel(vertexMode);
|
||||
}
|
||||
|
||||
public static double[] parseDoubles(String s, String token)
|
||||
{
|
||||
String[] as = s.split(token);
|
||||
double[] values = new double[as.length];
|
||||
for(int i = 0; i < as.length; i++)
|
||||
values[i] = Double.parseDouble(as[i]);
|
||||
return values;
|
||||
}
|
||||
|
||||
public static void illegalAssert(boolean b, String err)
|
||||
{
|
||||
if(!b) throw new IllegalArgumentException(err);
|
||||
}
|
||||
|
||||
public static void assertMatch(Matcher m, String s)
|
||||
{
|
||||
m.reset(s);
|
||||
illegalAssert(m.matches(), "Malformed line: "+s);
|
||||
}
|
||||
|
||||
private static final Pattern vertPattern = Pattern.compile("v(?: ([\\d\\.+-]+))+");
|
||||
private static final Pattern uvwPattern = Pattern.compile("vt(?: ([\\d\\.+-]+))+");
|
||||
private static final Pattern normalPattern = Pattern.compile("vn(?: ([\\d\\.+-]+))+");
|
||||
private static final Pattern polyPattern = Pattern.compile("f(?: ((?:\\d*)(?:/\\d*)?(?:/\\d*)?))+");
|
||||
public static final Matcher vertMatcher = vertPattern.matcher("");
|
||||
public static final Matcher uvwMatcher = uvwPattern.matcher("");
|
||||
public static final Matcher normalMatcher = normalPattern.matcher("");
|
||||
public static final Matcher polyMatcher = polyPattern.matcher("");
|
||||
|
||||
/**
|
||||
* Parses vertices, texture coords, normals and polygons from a WaveFront Obj file
|
||||
* @param input An input stream to a obj file
|
||||
* @param vertexMode The vertex mode to create the model for (GL_TRIANGLES or GL_QUADS)
|
||||
* @param coordSystem The cooridnate system transformation to apply
|
||||
* @return A map of group names to models
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Map<String, CCModel> parseObjModels(InputStream input, int vertexMode, Transformation coordSystem) throws IOException
|
||||
{
|
||||
if(coordSystem == null)
|
||||
coordSystem = new RedundantTransformation();
|
||||
int vp = vertexMode == 7 ? 4 : 3;
|
||||
|
||||
HashMap<String, CCModel> modelMap = new HashMap<String, CCModel>();
|
||||
ArrayList<Vector3> verts = new ArrayList<Vector3>();
|
||||
ArrayList<Vector3> uvs = new ArrayList<Vector3>();
|
||||
ArrayList<Vector3> normals = new ArrayList<Vector3>();
|
||||
ArrayList<int[]> polys = new ArrayList<int[]>();
|
||||
String modelName = "unnamed";
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
|
||||
|
||||
String line;
|
||||
while((line = reader.readLine()) != null)
|
||||
{
|
||||
line = line.replaceAll("\\s+", " ").trim();
|
||||
if(line.startsWith("#") || line.length() == 0)
|
||||
continue;
|
||||
|
||||
if(line.startsWith("v "))
|
||||
{
|
||||
assertMatch(vertMatcher, line);
|
||||
double[] values = parseDoubles(line.substring(2), " ");
|
||||
illegalAssert(values.length >= 3, "Vertices must have x, y and z components");
|
||||
Vector3 vert = new Vector3(values[0], values[1], values[2]);
|
||||
coordSystem.apply(vert);
|
||||
verts.add(vert);
|
||||
continue;
|
||||
}
|
||||
if(line.startsWith("vt "))
|
||||
{
|
||||
assertMatch(uvwMatcher, line);
|
||||
double[] values = parseDoubles(line.substring(3), " ");
|
||||
illegalAssert(values.length >= 2, "Tex Coords must have u, and v components");
|
||||
uvs.add(new Vector3(values[0], 1-values[1], 0));
|
||||
continue;
|
||||
}
|
||||
if(line.startsWith("vn "))
|
||||
{
|
||||
assertMatch(normalMatcher, line);
|
||||
double[] values = parseDoubles(line.substring(3), " ");
|
||||
illegalAssert(values.length >= 3, "Normals must have x, y and z components");
|
||||
Vector3 norm = new Vector3(values[0], values[1], values[2]).normalize();
|
||||
coordSystem.applyN(norm);
|
||||
normals.add(norm);
|
||||
continue;
|
||||
}
|
||||
if(line.startsWith("f "))
|
||||
{
|
||||
assertMatch(polyMatcher, line);
|
||||
String[] av = line.substring(2).split(" ");
|
||||
illegalAssert(av.length >= 3, "Polygons must have at least 3 vertices");
|
||||
int[][] polyVerts = new int[av.length][3];
|
||||
for(int i = 0; i < av.length; i++)
|
||||
{
|
||||
String[] as = av[i].split("/");
|
||||
for(int p = 0; p < as.length; p++)
|
||||
if(as[p].length() > 0)
|
||||
polyVerts[i][p] = Integer.parseInt(as[p]);
|
||||
}
|
||||
if(vp == 3)
|
||||
triangulate(polys, polyVerts);
|
||||
else
|
||||
quadulate(polys, polyVerts);
|
||||
}
|
||||
if(line.startsWith("g "))
|
||||
{
|
||||
if(!polys.isEmpty())
|
||||
{
|
||||
modelMap.put(modelName, createModel(verts, uvs, normals, vertexMode, polys));
|
||||
polys.clear();
|
||||
}
|
||||
modelName = line.substring(2);
|
||||
}
|
||||
}
|
||||
|
||||
if(!polys.isEmpty())
|
||||
modelMap.put(modelName, createModel(verts, uvs, normals, vertexMode, polys));
|
||||
|
||||
return modelMap;
|
||||
}
|
||||
|
||||
public static void triangulate(List<int[]> polys, int[][] polyVerts)
|
||||
{
|
||||
for(int i = 2; i < polyVerts.length; i++)
|
||||
{
|
||||
polys.add(polyVerts[0]);
|
||||
polys.add(polyVerts[i]);
|
||||
polys.add(polyVerts[i-1]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void quadulate(List<int[]> polys, int[][] polyVerts)
|
||||
{
|
||||
if(polyVerts.length == 4)
|
||||
{
|
||||
polys.add(polyVerts[0]);
|
||||
polys.add(polyVerts[3]);
|
||||
polys.add(polyVerts[2]);
|
||||
polys.add(polyVerts[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 2; i < polyVerts.length; i++)
|
||||
{
|
||||
polys.add(polyVerts[0]);
|
||||
polys.add(polyVerts[i]);
|
||||
polys.add(polyVerts[i-1]);
|
||||
polys.add(polyVerts[i-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses vertices, texture coords, normals and polygons from a WaveFront Obj file
|
||||
* @param s The name of the obj resource
|
||||
* @return A map of group names to models
|
||||
*/
|
||||
public static Map<String, CCModel> parseObjModels(ResourceLocation res)
|
||||
{
|
||||
return parseObjModels(res, 4, null);
|
||||
}
|
||||
/**
|
||||
* Parses vertices, texture coords, normals and polygons from a WaveFront Obj file
|
||||
* @param res The resource for the obj file
|
||||
* @param coordSystem The cooridnate system transformation to apply
|
||||
* @return A map of group names to models
|
||||
*/
|
||||
public static Map<String, CCModel> parseObjModels(ResourceLocation res, Transformation coordSystem)
|
||||
{
|
||||
try
|
||||
{
|
||||
return parseObjModels(
|
||||
Minecraft.getMinecraft().func_110442_L().func_110536_a(res).func_110527_b(),
|
||||
4, coordSystem);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException("failed to load model: "+res, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses vertices, texture coords, normals and polygons from a WaveFront Obj file
|
||||
* @param s The name of the obj resource
|
||||
* @param vertexMode The vertex mode to create the model for (GL_TRIANGLES or GL_QUADS)
|
||||
* @param coordSystem The cooridnate system transformation to apply
|
||||
* @return A map of group names to models
|
||||
*/
|
||||
public static Map<String, CCModel> parseObjModels(ResourceLocation res, int vertexMode, Transformation coordSystem)
|
||||
{
|
||||
try
|
||||
{
|
||||
return parseObjModels(
|
||||
Minecraft.getMinecraft().func_110442_L().func_110536_a(res).func_110527_b(),
|
||||
vertexMode, coordSystem);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new RuntimeException("failed to load model: "+res, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static CCModel createModel(List<Vector3> verts, List<Vector3> uvs, List<Vector3> normals, int vertexMode, List<int[]> polys)
|
||||
{
|
||||
int vp = vertexMode == 7 ? 4 : 3;
|
||||
if(polys.size() < vp || polys.size()%vp != 0)
|
||||
throw new IllegalArgumentException("Invalid number of vertices for model: "+polys.size());
|
||||
|
||||
boolean hasNormals = polys.get(0)[2] > 0;
|
||||
CCModel model = CCModel.newModel(vertexMode, polys.size());
|
||||
if(hasNormals)
|
||||
model.normals = new Vector3[polys.size()];
|
||||
|
||||
for(int i = 0; i < polys.size(); i++)
|
||||
{
|
||||
int[] ai = polys.get(i);
|
||||
Vector3 vert = verts.get(ai[0]-1).copy();
|
||||
Vector3 uv = ai[1] <= 0 ? new Vector3() : uvs.get(ai[1]-1).copy();
|
||||
if(ai[2] > 0 != hasNormals)
|
||||
throw new IllegalArgumentException("Normals are an all or nothing deal here.");
|
||||
|
||||
model.verts[i] = new Vertex5(vert, uv.x, uv.y);
|
||||
if(hasNormals)
|
||||
model.normals[i] = normals.get(ai[2]-1).copy();
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Brings the UV coordinates of each face closer to the center UV by d.
|
||||
* Useful for fixing texture seams
|
||||
*/
|
||||
public CCModel shrinkUVs(double d)
|
||||
{
|
||||
for(int k = 0; k < verts.length; k+=vp)
|
||||
{
|
||||
UV uv = new UV();
|
||||
for(int i = 0; i < vp; i++)
|
||||
{
|
||||
uv.add(verts[k+i].uv);
|
||||
}
|
||||
uv.mul(1D/vp);
|
||||
for(int i = 0; i < vp; i++)
|
||||
{
|
||||
Vertex5 vert = verts[k+i];
|
||||
vert.uv.u += vert.uv.u < uv.u ? d : -d;
|
||||
vert.uv.v += vert.uv.v < uv.v ? d : -d;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param side1 The side of this model
|
||||
* @param side2 The side of the new model
|
||||
* @param point The point to rotate around
|
||||
* @return A copy of this model rotated to the appropriate side
|
||||
*/
|
||||
public CCModel sidedCopy(int side1, int side2, Vector3 point)
|
||||
{
|
||||
CCModel model = newModel(vertexMode, verts.length);
|
||||
copy(this, 0, model, 0, model.verts.length);
|
||||
return model.apply(new TransformationList(sideRotations[side1].inverse(), sideRotations[side2]).at(point));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies length vertices and normals
|
||||
*/
|
||||
public static void copy(CCModel src, int srcpos, CCModel dest, int destpos, int length)
|
||||
{
|
||||
for(int k = 0; k < length; k++)
|
||||
dest.verts[destpos+k] = src.verts[srcpos+k].copy();
|
||||
|
||||
if(src.normals != null)
|
||||
{
|
||||
if(dest.normals == null)
|
||||
dest.normals = new Vector3[dest.verts.length];
|
||||
|
||||
for(int k = 0; k < length; k++)
|
||||
dest.normals[destpos+k] = src.normals[srcpos+k].copy();
|
||||
}
|
||||
|
||||
if(src.colours != null)
|
||||
{
|
||||
if(dest.colours == null)
|
||||
dest.colours = new int[dest.verts.length];
|
||||
System.arraycopy(src.colours, srcpos, dest.colours, destpos, length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate models rotated to the other 5 sides of the block
|
||||
* @param models An array of 6 models
|
||||
* @param side The side of this model
|
||||
* @param point The rotation point
|
||||
*/
|
||||
public static void generateSidedModels(CCModel[] models, int side, Vector3 point)
|
||||
{
|
||||
for(int s = 0; s < 6; s++)
|
||||
{
|
||||
if(s == side)
|
||||
continue;
|
||||
|
||||
models[s] = models[side].sidedCopy(side, s, point);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate models rotated to the other 3 horizontal of the block
|
||||
* @param models An array of 4 models
|
||||
* @param side The side of this model
|
||||
* @param point The rotation point
|
||||
*/
|
||||
public static void generateSidedModelsH(CCModel[] models, int side, Vector3 point)
|
||||
{
|
||||
for(int s = 2; s < 6; s++)
|
||||
{
|
||||
if(s == side)
|
||||
continue;
|
||||
|
||||
models[s] = models[side].sidedCopy(side, s, point);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates copies of faces with clockwise vertices
|
||||
* @return The model
|
||||
*/
|
||||
public CCModel generateBackface(int srcpos, int destpos, int length)
|
||||
{
|
||||
if(srcpos+length > destpos)
|
||||
throw new IllegalArgumentException("Overlapping src and dest arrays");
|
||||
if(srcpos%vp != 0 || destpos%vp != 0 || length%vp != 0)
|
||||
throw new IllegalArgumentException("Vertices do not align with polygons");
|
||||
|
||||
int[][] o = new int[][]{{0, 0}, {1, vp-1}, {2, vp-2}, {3, vp-3}};
|
||||
for(int i = 0; i < length; i++)
|
||||
{
|
||||
int b = (i/vp)*vp;
|
||||
int d = i%vp;
|
||||
int di = destpos+b+o[d][1];
|
||||
int si = srcpos+b+o[d][0];
|
||||
verts[di] = new Vertex5(verts[si]);
|
||||
if(normals != null && normals[si] != null)
|
||||
normals[di] = normals[si].copy().negate();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates sided copies of vertices into this model.
|
||||
* Assumes that your model has been generated at vertex side*(numVerts/6)
|
||||
*/
|
||||
public CCModel generateSidedParts(int side, Vector3 point)
|
||||
{
|
||||
if(verts.length%(6*vp) != 0)
|
||||
throw new IllegalArgumentException("Invalid number of vertices for sided part generation");
|
||||
int length = verts.length/6;
|
||||
|
||||
for(int s = 0; s < 6; s++)
|
||||
{
|
||||
if(s == side)
|
||||
continue;
|
||||
|
||||
generateSidedPart(side, s, point, length*side, length*s, length);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates sided copies of vertices into this model.
|
||||
* Assumes that your model has been generated at vertex side*(numVerts/4)
|
||||
*/
|
||||
public CCModel generateSidedPartsH(int side, Vector3 point)
|
||||
{
|
||||
if(verts.length%(4*vp) != 0)
|
||||
throw new IllegalArgumentException("Invalid number of vertices for sided part generation");
|
||||
int length = verts.length/4;
|
||||
|
||||
for(int s = 2; s < 6; s++)
|
||||
{
|
||||
if(s == side)
|
||||
continue;
|
||||
|
||||
generateSidedPart(side, s, point, length*(side-2), length*(s-2), length);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a sided copy of verts into this model
|
||||
*/
|
||||
public CCModel generateSidedPart(int side1, int side2, Vector3 point, int srcpos, int destpos, int length)
|
||||
{
|
||||
return generateRotatedPart(new TransformationList(sideRotations[side1].inverse(), sideRotations[side2]), point, srcpos, destpos, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a rotated copy of verts into this model
|
||||
*/
|
||||
public CCModel generateRotatedPart(Transformation t, Vector3 point, int srcpos, int destpos, int length)
|
||||
{
|
||||
for(int k = 0; k < length; k++)
|
||||
{
|
||||
verts[destpos+k] = verts[srcpos+k].copy();
|
||||
verts[destpos+k].vec
|
||||
.subtract(point)
|
||||
.apply(t)
|
||||
.add(point);
|
||||
}
|
||||
|
||||
if(normals != null)
|
||||
for(int k = 0; k < length; k++)
|
||||
normals[destpos+k] = normals[srcpos+k].copy().apply(t);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public static CCModel combine(Collection<CCModel> models)
|
||||
{
|
||||
if(models.isEmpty())
|
||||
return null;
|
||||
|
||||
int numVerts = 0;
|
||||
int vertexMode = -1;
|
||||
for(CCModel model : models)
|
||||
{
|
||||
if(vertexMode == -1)
|
||||
vertexMode = model.vertexMode;
|
||||
if(vertexMode != model.vertexMode)
|
||||
throw new IllegalArgumentException("Cannot combine models with different vertex modes");
|
||||
|
||||
numVerts+=model.verts.length;
|
||||
}
|
||||
|
||||
CCModel c_model = newModel(vertexMode, numVerts);
|
||||
int i = 0;
|
||||
for(CCModel model : models)
|
||||
{
|
||||
copy(model, 0, c_model, i, model.verts.length);
|
||||
i+=model.verts.length;
|
||||
}
|
||||
|
||||
return c_model;
|
||||
}
|
||||
|
||||
public CCModel twoFacedCopy()
|
||||
{
|
||||
CCModel model = newModel(vertexMode, verts.length*2);
|
||||
copy(this, 0, model, 0, verts.length);
|
||||
model.generateBackface(0, verts.length, verts.length);
|
||||
return model;
|
||||
}
|
||||
|
||||
public CCModel copy()
|
||||
{
|
||||
CCModel model = newModel(vertexMode, verts.length);
|
||||
copy(this, 0, model, 0, verts.length);
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The average of all vertices, for bones.
|
||||
*/
|
||||
public Vector3 collapse()
|
||||
{
|
||||
Vector3 v = new Vector3();
|
||||
for(Vertex5 vert : verts)
|
||||
v.add(vert.vec);
|
||||
v.multiply(1/(double)verts.length);
|
||||
return v;
|
||||
}
|
||||
|
||||
public CCModel zOffset(Cuboid6 offsets)
|
||||
{
|
||||
for(int k = 0; k < verts.length; k++)
|
||||
{
|
||||
Vertex5 vert = verts[k];
|
||||
Vector3 normal = normals[k];
|
||||
switch(findSide(normal))
|
||||
{
|
||||
case 0:
|
||||
vert.vec.y += offsets.min.y;
|
||||
break;
|
||||
case 1:
|
||||
vert.vec.y += offsets.max.y;
|
||||
break;
|
||||
case 2:
|
||||
vert.vec.z += offsets.min.z;
|
||||
break;
|
||||
case 3:
|
||||
vert.vec.z += offsets.max.z;
|
||||
break;
|
||||
case 4:
|
||||
vert.vec.x += offsets.min.x;
|
||||
break;
|
||||
case 5:
|
||||
vert.vec.x += offsets.max.x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public static int findSide(Vector3 normal)
|
||||
{
|
||||
if(normal.y <=-0.99) return 0;
|
||||
if(normal.y >= 0.99) return 1;
|
||||
if(normal.z <=-0.99) return 2;
|
||||
if(normal.z >= 0.99) return 3;
|
||||
if(normal.x <=-0.99) return 4;
|
||||
if(normal.x >= 0.99) return 5;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A Cuboid6 containing all the verts in this model
|
||||
*/
|
||||
public Cuboid6 bounds()
|
||||
{
|
||||
Vector3 vec1 = verts[0].vec;
|
||||
Cuboid6 c = new Cuboid6(vec1.copy(), vec1.copy());
|
||||
for(int i = 1; i < verts.length; i++)
|
||||
c.enclose(verts[i].vec);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import codechicken.lib.vec.Matrix4;
|
||||
import codechicken.lib.vec.Quat;
|
||||
import codechicken.lib.vec.Rotation;
|
||||
import codechicken.lib.vec.Scale;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
|
||||
import static codechicken.lib.math.MathHelper.phi;
|
||||
|
||||
public class CCModelLibrary
|
||||
{
|
||||
public static CCModel icosahedron4;
|
||||
public static CCModel icosahedron7;
|
||||
|
||||
private static int i;
|
||||
|
||||
static
|
||||
{
|
||||
generateIcosahedron();
|
||||
}
|
||||
|
||||
private static void generateIcosahedron()
|
||||
{
|
||||
Vector3[] verts = new Vector3[12];
|
||||
|
||||
verts[0] = new Vector3(-1, phi, 0);
|
||||
verts[1] = new Vector3( 1, phi, 0);
|
||||
verts[2] = new Vector3( 1,-phi, 0);
|
||||
verts[3] = new Vector3(-1,-phi, 0);
|
||||
|
||||
verts[4] = new Vector3(0,-1, phi);
|
||||
verts[5] = new Vector3(0, 1, phi);
|
||||
verts[6] = new Vector3(0, 1,-phi);
|
||||
verts[7] = new Vector3(0,-1,-phi);
|
||||
|
||||
verts[8] = new Vector3( phi, 0,-1);
|
||||
verts[9] = new Vector3( phi, 0, 1);
|
||||
verts[10] = new Vector3(-phi, 0, 1);
|
||||
verts[11] = new Vector3(-phi, 0,-1);
|
||||
|
||||
Quat quat = Quat.aroundAxis(0, 0, 1, Math.atan(1/phi));
|
||||
for(Vector3 vec : verts)
|
||||
quat.rotate(vec);
|
||||
|
||||
icosahedron4 = CCModel.newModel(4, 60);
|
||||
icosahedron7 = CCModel.newModel(7, 80);
|
||||
|
||||
i = 0;
|
||||
//top
|
||||
addIcosahedronTriangle(verts[1], 0.5, 0, verts[0], 0, 0.25, verts[5], 1, 0.25);
|
||||
addIcosahedronTriangle(verts[1], 0.5, 0, verts[5], 0, 0.25, verts[9], 1, 0.25);
|
||||
addIcosahedronTriangle(verts[1], 0.5, 0, verts[9], 0, 0.25, verts[8], 1, 0.25);
|
||||
addIcosahedronTriangle(verts[1], 0.5, 0, verts[8], 0, 0.25, verts[6], 1, 0.25);
|
||||
addIcosahedronTriangle(verts[1], 0.5, 0, verts[6], 0, 0.25, verts[0], 1, 0.25);
|
||||
//centre 1vert top
|
||||
addIcosahedronTriangle(verts[0], 0.5, 0.25, verts[11],0, 0.75, verts[10],1, 0.75);
|
||||
addIcosahedronTriangle(verts[5], 0.5, 0.25, verts[10],0, 0.75, verts[4], 1, 0.75);
|
||||
addIcosahedronTriangle(verts[9], 0.5, 0.25, verts[4], 0, 0.75, verts[2], 1, 0.75);
|
||||
addIcosahedronTriangle(verts[8], 0.5, 0.25, verts[2], 0, 0.75, verts[7], 1, 0.75);
|
||||
addIcosahedronTriangle(verts[6], 0.5, 0.25, verts[7], 0, 0.75, verts[11],1, 0.75);
|
||||
//centre 1vert bottom
|
||||
addIcosahedronTriangle(verts[2], 0.5, 0.75, verts[8], 0, 0.25, verts[9], 1, 0.25);
|
||||
addIcosahedronTriangle(verts[7], 0.5, 0.75, verts[6], 0, 0.25, verts[8], 1, 0.25);
|
||||
addIcosahedronTriangle(verts[11],0.5, 0.75, verts[0], 0, 0.25, verts[6], 1, 0.25);
|
||||
addIcosahedronTriangle(verts[10],0.5, 0.75, verts[5], 0, 0.25, verts[0], 1, 0.25);
|
||||
addIcosahedronTriangle(verts[4], 0.5, 0.75, verts[9], 0, 0.25, verts[5], 1, 0.25);
|
||||
//bottom
|
||||
addIcosahedronTriangle(verts[3], 0.5, 1, verts[2], 0, 0.75, verts[4], 1, 0.75);
|
||||
addIcosahedronTriangle(verts[3], 0.5, 1, verts[7], 0, 0.75, verts[2], 1, 0.75);
|
||||
addIcosahedronTriangle(verts[3], 0.5, 1, verts[11],0, 0.75, verts[7], 1, 0.75);
|
||||
addIcosahedronTriangle(verts[3], 0.5, 1, verts[10],0, 0.75, verts[11],1, 0.75);
|
||||
addIcosahedronTriangle(verts[3], 0.5, 1, verts[4], 0, 0.75, verts[10],1, 0.75);
|
||||
|
||||
icosahedron4.computeNormals().smoothNormals();
|
||||
icosahedron7.computeNormals().smoothNormals();
|
||||
}
|
||||
|
||||
private static void addIcosahedronTriangle(Vector3 vec1, double u1, double v1,
|
||||
Vector3 vec2, double u2, double v2,
|
||||
Vector3 vec3, double u3, double v3)
|
||||
{
|
||||
icosahedron4.verts[i*3] = icosahedron7.verts[i*4] = new Vertex5(vec1, u1, v1);
|
||||
icosahedron4.verts[i*3+1] = icosahedron7.verts[i*4+1] = new Vertex5(vec2, u2, v2);
|
||||
icosahedron4.verts[i*3+2] = icosahedron7.verts[i*4+2] = icosahedron7.verts[i*4+3] = new Vertex5(vec3, u3, v3);
|
||||
i++;
|
||||
}
|
||||
|
||||
public static Matrix4 getRenderMatrix(Vector3 position, Rotation rotation, double scale)
|
||||
{
|
||||
return new Matrix4().translate(position).apply(new Scale(scale)).apply(rotation);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
public class CCRenderState
|
||||
{
|
||||
private static boolean useNormals;
|
||||
private static boolean hasBrightness;
|
||||
private static int brightness;
|
||||
private static boolean useModelColours;
|
||||
private static int colour;
|
||||
private static boolean hasColour;
|
||||
private static int alpha;
|
||||
private static boolean alphaOverride;
|
||||
|
||||
public static void useNormals(boolean b)
|
||||
{
|
||||
useNormals = b;
|
||||
}
|
||||
|
||||
public static boolean useNormals()
|
||||
{
|
||||
return useNormals;
|
||||
}
|
||||
|
||||
public static void useModelColours(boolean b)
|
||||
{
|
||||
useModelColours = b;
|
||||
}
|
||||
|
||||
public static boolean useModelColours()
|
||||
{
|
||||
return useModelColours;
|
||||
}
|
||||
|
||||
public static void setAlpha(int a)
|
||||
{
|
||||
alpha = a;
|
||||
alphaOverride = true;
|
||||
}
|
||||
|
||||
public static void clearAlphaOverride()
|
||||
{
|
||||
alphaOverride = false;
|
||||
}
|
||||
|
||||
public static void vertexColour(int c)
|
||||
{
|
||||
if(alphaOverride)
|
||||
setColour(c&0xFFFFFF00|alpha);
|
||||
else
|
||||
setColour(c);
|
||||
}
|
||||
|
||||
public static void vertexColour(int r, int g, int b, int a)
|
||||
{
|
||||
if(alphaOverride)
|
||||
Tessellator.instance.setColorRGBA(r, g, b, alpha);
|
||||
else
|
||||
Tessellator.instance.setColorRGBA(r, g, b, a);
|
||||
}
|
||||
|
||||
public static void setBrightness(IBlockAccess world, int x, int y, int z)
|
||||
{
|
||||
Block block = Block.blocksList[world.getBlockId(x, y, z)];
|
||||
setBrightness(block == null ? world.getLightBrightnessForSkyBlocks(x, y, z, 0) : block.getMixedBrightnessForBlock(world, x, y, z));
|
||||
setColour(0xFFFFFFFF);
|
||||
}
|
||||
|
||||
public static void setBrightness(int b)
|
||||
{
|
||||
hasBrightness = true;
|
||||
Tessellator.instance.setBrightness(brightness = b);
|
||||
}
|
||||
|
||||
public static void setColourOpaque(int c)
|
||||
{
|
||||
setColour(c << 8 | 0xFF);
|
||||
}
|
||||
|
||||
//RGBA
|
||||
public static void setColour(int c)
|
||||
{
|
||||
hasColour = true;
|
||||
colour = c;
|
||||
Tessellator.instance.setColorRGBA(colour >> 24 & 0xFF, colour >> 16 & 0xFF, colour >> 8 & 0xFF, colour & 0xFF);
|
||||
}
|
||||
|
||||
public static void changeTexture(String texture)
|
||||
{
|
||||
changeTexture(new ResourceLocation(texture));
|
||||
}
|
||||
|
||||
public static void changeTexture(ResourceLocation texture)
|
||||
{
|
||||
Minecraft.getMinecraft().renderEngine.func_110577_a(texture);
|
||||
}
|
||||
|
||||
public static void apply()
|
||||
{
|
||||
if(hasBrightness)
|
||||
Tessellator.instance.setBrightness(brightness);
|
||||
if(hasColour)
|
||||
Tessellator.instance.setColorRGBA(colour >> 24 & 0xFF, colour >> 16 & 0xFF, colour >> 8 & 0xFF, colour & 0xFF);
|
||||
}
|
||||
|
||||
public static void reset()
|
||||
{
|
||||
hasBrightness = false;
|
||||
useModelColours = false;
|
||||
hasColour = false;
|
||||
alphaOverride = false;
|
||||
}
|
||||
|
||||
public static void startDrawing(int i)
|
||||
{
|
||||
Tessellator.instance.startDrawing(i);
|
||||
apply();
|
||||
}
|
||||
|
||||
public static void draw()
|
||||
{
|
||||
Tessellator.instance.draw();
|
||||
}
|
||||
|
||||
public static void pullLightmap()
|
||||
{
|
||||
setBrightness((int)OpenGlHelper.lastBrightnessY << 16 | (int)OpenGlHelper.lastBrightnessX);
|
||||
}
|
||||
|
||||
public static void applyBrightnessTexCoords()
|
||||
{
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, brightness&0xFFFF, brightness>>>16);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
|
||||
public class ColourModifier implements IVertexModifier
|
||||
{
|
||||
public static final ColourModifier instance = new ColourModifier();
|
||||
|
||||
@Override
|
||||
public void applyModifiers(CCModel m, Tessellator tess, Vector3 vec, UV uv, Vector3 normal, int i)
|
||||
{
|
||||
if(CCRenderState.useModelColours() && m != null && m.colours != null)
|
||||
CCRenderState.vertexColour(m.colours[i]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsNormals()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import codechicken.lib.vec.Cuboid6;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
import net.minecraft.client.particle.EffectRenderer;
|
||||
import net.minecraft.client.particle.EntityFX;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.Icon;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class EntityDigIconFX extends EntityFX
|
||||
{
|
||||
public EntityDigIconFX(World world, double x, double y, double z, double dx, double dy, double dz, Icon icon)
|
||||
{
|
||||
super(world, x, y, z, dx, dy, dz);
|
||||
particleIcon = icon;
|
||||
particleGravity = 1;
|
||||
particleRed = particleGreen = particleBlue = 0.6F;
|
||||
particleScale /= 2.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFXLayer()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* copy pasted from EntityDiggingFX
|
||||
*/
|
||||
@Override
|
||||
public void renderParticle(Tessellator par1Tessellator, float par2, float par3, float par4, float par5, float par6, float par7)
|
||||
{
|
||||
float f6 = (particleTextureIndexX + particleTextureJitterX / 4.0F) / 16.0F;
|
||||
float f7 = f6 + 0.015609375F;
|
||||
float f8 = (particleTextureIndexY + particleTextureJitterY / 4.0F) / 16.0F;
|
||||
float f9 = f8 + 0.015609375F;
|
||||
float f10 = 0.1F * particleScale;
|
||||
|
||||
if (particleIcon != null)
|
||||
{
|
||||
f6 = particleIcon.getInterpolatedU(particleTextureJitterX / 4.0F * 16.0F);
|
||||
f7 = particleIcon.getInterpolatedU((particleTextureJitterX + 1.0F) / 4.0F * 16.0F);
|
||||
f8 = particleIcon.getInterpolatedV(particleTextureJitterY / 4.0F * 16.0F);
|
||||
f9 = particleIcon.getInterpolatedV((particleTextureJitterY + 1.0F) / 4.0F * 16.0F);
|
||||
}
|
||||
|
||||
float f11 = (float)(prevPosX + (posX - prevPosX) * par2 - interpPosX);
|
||||
float f12 = (float)(prevPosY + (posY - prevPosY) * par2 - interpPosY);
|
||||
float f13 = (float)(prevPosZ + (posZ - prevPosZ) * par2 - interpPosZ);
|
||||
float f14 = 1.0F;
|
||||
par1Tessellator.setColorOpaque_F(f14 * particleRed, f14 * particleGreen, f14 * particleBlue);
|
||||
par1Tessellator.addVertexWithUV(f11 - par3 * f10 - par6 * f10, f12 - par4 * f10, f13 - par5 * f10 - par7 * f10, f6, f9);
|
||||
par1Tessellator.addVertexWithUV(f11 - par3 * f10 + par6 * f10, f12 + par4 * f10, f13 - par5 * f10 + par7 * f10, f6, f8);
|
||||
par1Tessellator.addVertexWithUV(f11 + par3 * f10 + par6 * f10, f12 + par4 * f10, f13 + par5 * f10 + par7 * f10, f7, f8);
|
||||
par1Tessellator.addVertexWithUV(f11 + par3 * f10 - par6 * f10, f12 - par4 * f10, f13 + par5 * f10 - par7 * f10, f7, f9);
|
||||
}
|
||||
|
||||
public static void addBlockHitEffects(World world, Cuboid6 bounds, int side, Icon icon, EffectRenderer effectRenderer)
|
||||
{
|
||||
float border = 0.1F;
|
||||
Vector3 diff = bounds.max.copy().subtract(bounds.min).add(-2*border);
|
||||
diff.x*=world.rand.nextDouble();
|
||||
diff.y*=world.rand.nextDouble();
|
||||
diff.z*=world.rand.nextDouble();
|
||||
Vector3 pos = diff.add(bounds.min).add(border);
|
||||
|
||||
if (side == 0)
|
||||
diff.y = bounds.min.y - border;
|
||||
if (side == 1)
|
||||
diff.y = bounds.max.y + border;
|
||||
if (side == 2)
|
||||
diff.z = bounds.min.z - border;
|
||||
if (side == 3)
|
||||
diff.z = bounds.max.z + border;
|
||||
if (side == 4)
|
||||
diff.x = bounds.min.x - border;
|
||||
if (side == 5)
|
||||
diff.x = bounds.max.x + border;
|
||||
|
||||
effectRenderer.addEffect(
|
||||
new EntityDigIconFX(world, pos.x, pos.y, pos.z, 0, 0, 0, icon)
|
||||
.multiplyVelocity(0.2F).multipleParticleScaleBy(0.6F));
|
||||
}
|
||||
|
||||
public static void addBlockDestroyEffects(World world, Cuboid6 bounds, Icon[] icons, EffectRenderer effectRenderer)
|
||||
{
|
||||
Vector3 diff = bounds.max.copy().subtract(bounds.min);
|
||||
Vector3 center = bounds.min.copy().add(bounds.max).multiply(0.5);
|
||||
Vector3 density = diff.copy().multiply(4);
|
||||
density.x = Math.ceil(density.x);
|
||||
density.y = Math.ceil(density.y);
|
||||
density.z = Math.ceil(density.z);
|
||||
|
||||
for (int i = 0; i < density.x; ++i)
|
||||
for (int j = 0; j < density.y; ++j)
|
||||
for (int k = 0; k < density.z; ++k)
|
||||
{
|
||||
double x = bounds.min.x+(i+0.5)*diff.x/density.x;
|
||||
double y = bounds.min.y+(j+0.5)*diff.y/density.y;
|
||||
double z = bounds.min.z+(k+0.5)*diff.z/density.z;
|
||||
effectRenderer.addEffect(
|
||||
new EntityDigIconFX(world, x, y, z, x-center.x, y-center.y, z-center.z, icons[world.rand.nextInt(icons.length)]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
public class FontUtils
|
||||
{
|
||||
public static FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer;
|
||||
|
||||
public static void drawCenteredString(String s, int xCenter, int y, int colour)
|
||||
{
|
||||
fontRenderer.drawString(s, xCenter-fontRenderer.getStringWidth(s)/2, y, colour);
|
||||
}
|
||||
|
||||
public static void drawRightString(String s, int xRight, int y, int colour)
|
||||
{
|
||||
fontRenderer.drawString(s, xRight-fontRenderer.getStringWidth(s), y, colour);
|
||||
}
|
||||
|
||||
public static final String[] prefixes = new String[]{"K", "M", "G"};
|
||||
public static void drawItemQuantity(int x, int y, ItemStack item, String quantity, int mode)
|
||||
{
|
||||
if(item == null || (quantity == null && item.stackSize <= 1))
|
||||
return;
|
||||
|
||||
if(quantity == null)
|
||||
{
|
||||
switch(mode)
|
||||
{
|
||||
case 2:
|
||||
int q = item.stackSize;
|
||||
String postfix = "";
|
||||
for(int p = 0; p < 3 && q > 1000; p++)
|
||||
{
|
||||
q/=1000;
|
||||
postfix = prefixes[p];
|
||||
}
|
||||
quantity = Integer.toString(q)+postfix;
|
||||
case 1:
|
||||
quantity = "";
|
||||
if(item.stackSize/64 > 0)
|
||||
quantity+=item.stackSize/64 + "s";
|
||||
if(item.stackSize%64 > 0)
|
||||
quantity+=item.stackSize%64;
|
||||
break;
|
||||
default:
|
||||
quantity = Integer.toString(item.stackSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
double scale = quantity.length() > 2 ? 0.5 : 1;
|
||||
double sheight = 8*scale;
|
||||
double swidth = fontRenderer.getStringWidth(quantity)*scale;
|
||||
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GL11.glPushMatrix();
|
||||
GL11.glTranslated(x+16-swidth, y+16-sheight, 0);
|
||||
GL11.glScaled(scale, scale, 1);
|
||||
fontRenderer.drawStringWithShadow(quantity, 0, 0, 0xFFFFFF);
|
||||
GL11.glPopMatrix();
|
||||
GL11.glEnable(GL11.GL_LIGHTING);
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
public interface IFaceRenderer
|
||||
{
|
||||
public void renderFace(Vertex5[] face, int side);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
public interface IUVTransformation
|
||||
{
|
||||
public void transform(UV texcoord);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
|
||||
public interface IVertexModifier
|
||||
{
|
||||
public void applyModifiers(CCModel m, Tessellator tess, Vector3 vec, UV uv, Vector3 normal, int i);
|
||||
|
||||
public boolean needsNormals();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import net.minecraft.util.Icon;
|
||||
|
||||
public class IconTransformation implements IUVTransformation
|
||||
{
|
||||
public Icon icon;
|
||||
|
||||
public IconTransformation(Icon icon)
|
||||
{
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(UV texcoord)
|
||||
{
|
||||
texcoord.u = icon.getInterpolatedU(texcoord.u%2*16);
|
||||
texcoord.v = icon.getInterpolatedV(texcoord.v%2*16);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
public class ManagedTextureFX extends TextureFX
|
||||
{
|
||||
public boolean changed;
|
||||
|
||||
public ManagedTextureFX(int size, String name)
|
||||
{
|
||||
super(size, name);
|
||||
imageData = new int[size*size];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup()
|
||||
{
|
||||
}
|
||||
|
||||
public void setData(int[] data)
|
||||
{
|
||||
System.arraycopy(data, 0, imageData, 0, imageData.length);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean changed()
|
||||
{
|
||||
boolean r = changed;
|
||||
changed = false;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import net.minecraft.util.Icon;
|
||||
|
||||
/**
|
||||
* Icon index is specified as (int)u>>1
|
||||
*/
|
||||
public class MultiIconTransformation implements IUVTransformation
|
||||
{
|
||||
public Icon[] icons;
|
||||
|
||||
public MultiIconTransformation(Icon[] icons)
|
||||
{
|
||||
this.icons = icons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(UV texcoord)
|
||||
{
|
||||
int i = (int)texcoord.u>>1;
|
||||
Icon icon = icons[i%icons.length];
|
||||
texcoord.u = icon.getInterpolatedU(texcoord.u%2*16);
|
||||
texcoord.v = icon.getInterpolatedV(texcoord.v%2*16);
|
||||
}
|
||||
|
||||
public static CCModel setIconIndex(CCModel m, int index)
|
||||
{
|
||||
return setIconIndex(m, 0, m.verts.length, index);
|
||||
}
|
||||
|
||||
public static CCModel setIconIndex(CCModel m, int start, int length, int index)
|
||||
{
|
||||
for(int k = start; k < length; k++)
|
||||
{
|
||||
UV uv = m.verts[k].uv;
|
||||
uv.u = uv.u%2+index*2;
|
||||
uv.v %= 2;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.ResourceManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public class PlaceholderTexture extends TextureAtlasSprite
|
||||
{
|
||||
protected PlaceholderTexture(String par1)
|
||||
{
|
||||
super(par1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean load(ResourceManager manager, ResourceLocation location)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import static net.minecraftforge.client.IItemRenderer.ItemRenderType.ENTITY;
|
||||
import static net.minecraftforge.client.IItemRenderer.ItemRendererHelper.BLOCK_3D;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.lib.vec.Cuboid6;
|
||||
import codechicken.lib.vec.Transformation;
|
||||
import codechicken.lib.vec.Rotation;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
import net.minecraft.util.Icon;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.item.ItemBlock;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.entity.RenderItem;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraftforge.client.IItemRenderer;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
|
||||
public class RenderUtils
|
||||
{
|
||||
static Vector3[] vectors = new Vector3[8];
|
||||
static RenderItem uniformRenderItem = new RenderItem()
|
||||
{
|
||||
public boolean shouldBob()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static EntityItem entityItem;
|
||||
|
||||
static
|
||||
{
|
||||
for(int i = 0; i < vectors.length; i++)
|
||||
vectors[i] = new Vector3();
|
||||
|
||||
uniformRenderItem.setRenderManager(RenderManager.instance);
|
||||
|
||||
entityItem = new EntityItem(null);
|
||||
entityItem.hoverStart = 0;
|
||||
}
|
||||
|
||||
public static void renderLiquidQuad(Vector3 point1, Vector3 point2, Vector3 point3, Vector3 point4, Icon icon, double res)
|
||||
{
|
||||
double u1 = icon.getMinU();
|
||||
double du = icon.getMaxU()-icon.getMinU();
|
||||
double v2 = icon.getMaxV();
|
||||
double dv = icon.getMaxV()-icon.getMinV();
|
||||
|
||||
Vector3 wide = vectors[0].set(point4).subtract(point1);
|
||||
Vector3 high = vectors[1].set(point1).subtract(point2);
|
||||
Tessellator t = Tessellator.instance;
|
||||
|
||||
double wlen = wide.mag();
|
||||
double hlen = high.mag();
|
||||
|
||||
double x = 0;
|
||||
while(x < wlen)
|
||||
{
|
||||
double rx = wlen - x;
|
||||
if(rx > res)
|
||||
rx = res;
|
||||
|
||||
double y = 0;
|
||||
while(y < hlen)
|
||||
{
|
||||
double ry = hlen-y;
|
||||
if(ry > res)
|
||||
ry = res;
|
||||
|
||||
Vector3 dx1 = vectors[2].set(wide).multiply(x/wlen);
|
||||
Vector3 dx2 = vectors[3].set(wide).multiply((x+rx)/wlen);
|
||||
Vector3 dy1 = vectors[4].set(high).multiply(y/hlen);
|
||||
Vector3 dy2 = vectors[5].set(high).multiply((y+ry)/hlen);
|
||||
|
||||
t.addVertexWithUV(point2.x+dx1.x+dy2.x, point2.y+dx1.y+dy2.y, point2.z+dx1.z+dy2.z, u1, v2-ry/res*dv);
|
||||
t.addVertexWithUV(point2.x+dx1.x+dy1.x, point2.y+dx1.y+dy1.y, point2.z+dx1.z+dy1.z, u1, v2);
|
||||
t.addVertexWithUV(point2.x+dx2.x+dy1.x, point2.y+dx2.y+dy1.y, point2.z+dx2.z+dy1.z, u1+rx/res*du, v2);
|
||||
t.addVertexWithUV(point2.x+dx2.x+dy2.x, point2.y+dx2.y+dy2.y, point2.z+dx2.z+dy2.z, u1+rx/res*du, v2-ry/res*dv);
|
||||
|
||||
y+=ry;
|
||||
}
|
||||
|
||||
x+=rx;
|
||||
}
|
||||
}
|
||||
|
||||
public static void translateToWorldCoords(Entity entity, float frame)
|
||||
{
|
||||
double interpPosX = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * frame;
|
||||
double interpPosY = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * frame;
|
||||
double interpPosZ = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * frame;
|
||||
|
||||
GL11.glTranslated(-interpPosX, -interpPosY, -interpPosZ);
|
||||
}
|
||||
|
||||
public static void drawOutlinedBoundingBox(AxisAlignedBB par1AxisAlignedBB)
|
||||
{
|
||||
Tessellator var2 = Tessellator.instance;
|
||||
var2.startDrawing(3);
|
||||
var2.addVertex(par1AxisAlignedBB.minX, par1AxisAlignedBB.minY, par1AxisAlignedBB.minZ);
|
||||
var2.addVertex(par1AxisAlignedBB.maxX, par1AxisAlignedBB.minY, par1AxisAlignedBB.minZ);
|
||||
var2.addVertex(par1AxisAlignedBB.maxX, par1AxisAlignedBB.minY, par1AxisAlignedBB.maxZ);
|
||||
var2.addVertex(par1AxisAlignedBB.minX, par1AxisAlignedBB.minY, par1AxisAlignedBB.maxZ);
|
||||
var2.addVertex(par1AxisAlignedBB.minX, par1AxisAlignedBB.minY, par1AxisAlignedBB.minZ);
|
||||
var2.draw();
|
||||
var2.startDrawing(3);
|
||||
var2.addVertex(par1AxisAlignedBB.minX, par1AxisAlignedBB.maxY, par1AxisAlignedBB.minZ);
|
||||
var2.addVertex(par1AxisAlignedBB.maxX, par1AxisAlignedBB.maxY, par1AxisAlignedBB.minZ);
|
||||
var2.addVertex(par1AxisAlignedBB.maxX, par1AxisAlignedBB.maxY, par1AxisAlignedBB.maxZ);
|
||||
var2.addVertex(par1AxisAlignedBB.minX, par1AxisAlignedBB.maxY, par1AxisAlignedBB.maxZ);
|
||||
var2.addVertex(par1AxisAlignedBB.minX, par1AxisAlignedBB.maxY, par1AxisAlignedBB.minZ);
|
||||
var2.draw();
|
||||
var2.startDrawing(1);
|
||||
var2.addVertex(par1AxisAlignedBB.minX, par1AxisAlignedBB.minY, par1AxisAlignedBB.minZ);
|
||||
var2.addVertex(par1AxisAlignedBB.minX, par1AxisAlignedBB.maxY, par1AxisAlignedBB.minZ);
|
||||
var2.addVertex(par1AxisAlignedBB.maxX, par1AxisAlignedBB.minY, par1AxisAlignedBB.minZ);
|
||||
var2.addVertex(par1AxisAlignedBB.maxX, par1AxisAlignedBB.maxY, par1AxisAlignedBB.minZ);
|
||||
var2.addVertex(par1AxisAlignedBB.maxX, par1AxisAlignedBB.minY, par1AxisAlignedBB.maxZ);
|
||||
var2.addVertex(par1AxisAlignedBB.maxX, par1AxisAlignedBB.maxY, par1AxisAlignedBB.maxZ);
|
||||
var2.addVertex(par1AxisAlignedBB.minX, par1AxisAlignedBB.minY, par1AxisAlignedBB.maxZ);
|
||||
var2.addVertex(par1AxisAlignedBB.minX, par1AxisAlignedBB.maxY, par1AxisAlignedBB.maxZ);
|
||||
var2.draw();
|
||||
}
|
||||
|
||||
public static void renderLiquidCuboid(Cuboid6 bound, Icon tex, double res)
|
||||
{
|
||||
renderLiquidQuad(//bottom
|
||||
new Vector3(bound.min.x, bound.min.y, bound.min.z),
|
||||
new Vector3(bound.max.x, bound.min.y, bound.min.z),
|
||||
new Vector3(bound.max.x, bound.min.y, bound.max.z),
|
||||
new Vector3(bound.min.x, bound.min.y, bound.max.z),
|
||||
tex, res);
|
||||
renderLiquidQuad(//top
|
||||
new Vector3(bound.min.x, bound.max.y, bound.min.z),
|
||||
new Vector3(bound.min.x, bound.max.y, bound.max.z),
|
||||
new Vector3(bound.max.x, bound.max.y, bound.max.z),
|
||||
new Vector3(bound.max.x, bound.max.y, bound.min.z),
|
||||
tex, res);
|
||||
renderLiquidQuad(//-x
|
||||
new Vector3(bound.min.x, bound.max.y, bound.min.z),
|
||||
new Vector3(bound.min.x, bound.min.y, bound.min.z),
|
||||
new Vector3(bound.min.x, bound.min.y, bound.max.z),
|
||||
new Vector3(bound.min.x, bound.max.y, bound.max.z),
|
||||
tex, res);
|
||||
renderLiquidQuad(//+x
|
||||
new Vector3(bound.max.x, bound.max.y, bound.max.z),
|
||||
new Vector3(bound.max.x, bound.min.y, bound.max.z),
|
||||
new Vector3(bound.max.x, bound.min.y, bound.min.z),
|
||||
new Vector3(bound.max.x, bound.max.y, bound.min.z),
|
||||
tex, res);
|
||||
renderLiquidQuad(//-z
|
||||
new Vector3(bound.max.x, bound.max.y, bound.min.z),
|
||||
new Vector3(bound.max.x, bound.min.y, bound.min.z),
|
||||
new Vector3(bound.min.x, bound.min.y, bound.min.z),
|
||||
new Vector3(bound.min.x, bound.max.y, bound.min.z),
|
||||
tex, res);
|
||||
renderLiquidQuad(//+z
|
||||
new Vector3(bound.min.x, bound.max.y, bound.max.z),
|
||||
new Vector3(bound.min.x, bound.min.y, bound.max.z),
|
||||
new Vector3(bound.max.x, bound.min.y, bound.max.z),
|
||||
new Vector3(bound.max.x, bound.max.y, bound.max.z),
|
||||
tex, res);
|
||||
}
|
||||
|
||||
public static void renderBlockOverlaySide(int x, int y, int z, int side, double tx1, double tx2, double ty1, double ty2)
|
||||
{
|
||||
double[] points = new double[]{x - 0.009, x + 1.009, y - 0.009, y + 1.009, z - 0.009, z + 1.009};
|
||||
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
switch(side)
|
||||
{
|
||||
case 0:
|
||||
tessellator.addVertexWithUV(points[0], points[2], points[4], tx1, ty1);
|
||||
tessellator.addVertexWithUV(points[1], points[2], points[4], tx2, ty1);
|
||||
tessellator.addVertexWithUV(points[1], points[2], points[5], tx2, ty2);
|
||||
tessellator.addVertexWithUV(points[0], points[2], points[5], tx1, ty2);
|
||||
break;
|
||||
case 1:
|
||||
tessellator.addVertexWithUV(points[1], points[3], points[4], tx2, ty1);
|
||||
tessellator.addVertexWithUV(points[0], points[3], points[4], tx1, ty1);
|
||||
tessellator.addVertexWithUV(points[0], points[3], points[5], tx1, ty2);
|
||||
tessellator.addVertexWithUV(points[1], points[3], points[5], tx2, ty2);
|
||||
break;
|
||||
case 2:
|
||||
tessellator.addVertexWithUV(points[0], points[3], points[4], tx2, ty1);
|
||||
tessellator.addVertexWithUV(points[1], points[3], points[4], tx1, ty1);
|
||||
tessellator.addVertexWithUV(points[1], points[2], points[4], tx1, ty2);
|
||||
tessellator.addVertexWithUV(points[0], points[2], points[4], tx2, ty2);
|
||||
break;
|
||||
case 3:
|
||||
tessellator.addVertexWithUV(points[1], points[3], points[5], tx2, ty1);
|
||||
tessellator.addVertexWithUV(points[0], points[3], points[5], tx1, ty1);
|
||||
tessellator.addVertexWithUV(points[0], points[2], points[5], tx1, ty2);
|
||||
tessellator.addVertexWithUV(points[1], points[2], points[5], tx2, ty2);
|
||||
break;
|
||||
case 4:
|
||||
tessellator.addVertexWithUV(points[0], points[3], points[5], tx2, ty1);
|
||||
tessellator.addVertexWithUV(points[0], points[3], points[4], tx1, ty1);
|
||||
tessellator.addVertexWithUV(points[0], points[2], points[4], tx1, ty2);
|
||||
tessellator.addVertexWithUV(points[0], points[2], points[5], tx2, ty2);
|
||||
break;
|
||||
case 5:
|
||||
tessellator.addVertexWithUV(points[1], points[3], points[4], tx2, ty1);
|
||||
tessellator.addVertexWithUV(points[1], points[3], points[5], tx1, ty1);
|
||||
tessellator.addVertexWithUV(points[1], points[2], points[5], tx1, ty2);
|
||||
tessellator.addVertexWithUV(points[1], points[2], points[4], tx2, ty2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean shouldRenderLiquid(FluidStack stack)
|
||||
{
|
||||
return stack.amount > 0 && stack.getFluid() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stack The fluid stack to render
|
||||
* @return The icon of the fluid
|
||||
*/
|
||||
public static Icon prepareFluidRender(FluidStack stack, int alpha)
|
||||
{
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
Fluid fluid = stack.getFluid();
|
||||
CCRenderState.setColour(fluid.getColor(stack)<<8|alpha);
|
||||
TextureUtils.bindAtlas(fluid.getSpriteNumber());
|
||||
return fluid.getIcon(stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-enables lighting and disables blending.
|
||||
*/
|
||||
public static void postFluidRender()
|
||||
{
|
||||
GL11.glEnable(GL11.GL_LIGHTING);
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a fluid within a bounding box.
|
||||
* If the fluid is a liquid it will render as a normal tank with height equal to density/bound.height.
|
||||
* If the fluid is a gas, it will render the full box with an alpha equal to density.
|
||||
* Warning, bound will be mutated if the fluid is a liquid
|
||||
* @param stack The fluid to render.
|
||||
* @param bound The box within which the fluid is contained.
|
||||
* @param density The volume of fluid / the capacity of the tank. For gases this determines the alpha, for liquids this determines the height.
|
||||
* @param res The resolution to render at.
|
||||
*/
|
||||
public static void renderLiquidCuboid(FluidStack stack, Cuboid6 bound, double density, double res)
|
||||
{
|
||||
if(!shouldRenderLiquid(stack))
|
||||
return;
|
||||
|
||||
int alpha = 255;
|
||||
if(stack.getFluid().isGaseous())
|
||||
alpha = (int) (density*255);
|
||||
else
|
||||
bound.max.y = bound.min.y+(bound.max.y-bound.min.y)*density;
|
||||
|
||||
Icon tex = prepareFluidRender(stack, alpha);
|
||||
CCRenderState.startDrawing(7);
|
||||
renderLiquidCuboid(bound, tex, res);
|
||||
CCRenderState.draw();
|
||||
postFluidRender();
|
||||
}
|
||||
|
||||
public static void renderItemUniform(ItemStack item)
|
||||
{
|
||||
IItemRenderer customRenderer = MinecraftForgeClient.getItemRenderer(item, ENTITY);
|
||||
boolean is3D = customRenderer != null && customRenderer.shouldUseRenderHelper(ENTITY, item, BLOCK_3D);
|
||||
|
||||
boolean larger = false;
|
||||
if (item.getItem() instanceof ItemBlock && RenderBlocks.renderItemIn3d(Block.blocksList[item.itemID].getRenderType()))
|
||||
{
|
||||
int renderType = Block.blocksList[item.itemID].getRenderType();
|
||||
larger = !(renderType == 1 || renderType == 19 || renderType == 12 || renderType == 2);
|
||||
}
|
||||
else if(is3D)
|
||||
{
|
||||
larger = true;
|
||||
}
|
||||
|
||||
double d = 2;
|
||||
double d1 = 1/d;
|
||||
if(larger)
|
||||
GL11.glScaled(d, d, d);
|
||||
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
entityItem.setEntityItemStack(item);
|
||||
uniformRenderItem.doRenderItem(entityItem, 0, larger ? 0.09 : 0.06, 0, 0, 0);
|
||||
|
||||
if(larger)
|
||||
GL11.glScaled(d1, d1, d1);
|
||||
}
|
||||
|
||||
private static Vertex5[] face = new Vertex5[]{new Vertex5(), new Vertex5(), new Vertex5(), new Vertex5()};
|
||||
public static void renderBlock(Cuboid6 c, int sideMask, IFaceRenderer r)
|
||||
{
|
||||
double x1 = c.min.x;
|
||||
double x2 = c.max.x;
|
||||
double y1 = c.min.y;
|
||||
double y2 = c.max.y;
|
||||
double z1 = c.min.z;
|
||||
double z2 = c.max.z;
|
||||
double u1 = 0;
|
||||
double u2 = 0;
|
||||
double v1 = 0;
|
||||
double v2 = 0;
|
||||
|
||||
if((sideMask&1) == 0)
|
||||
{
|
||||
u1 = x1; v1 = z1;
|
||||
u2 = x2; v2 = z2;
|
||||
face[0].set(x1, y1, z2, u1, v2);
|
||||
face[1].set(x1, y1, z1, u1, v1);
|
||||
face[2].set(x2, y1, z1, u2, v1);
|
||||
face[3].set(x2, y1, z2, u2, v2);
|
||||
r.renderFace(face, c.min.y > 0 ? 6 : 0);
|
||||
}
|
||||
|
||||
if((sideMask&2) == 0)
|
||||
{
|
||||
u1 = x1+2; v1 = z1;
|
||||
u2 = x2+2; v2 = z2;
|
||||
face[0].set(x2, y2, z2, u2, v2);
|
||||
face[1].set(x2, y2, z1, u2, v1);
|
||||
face[2].set(x1, y2, z1, u1, v1);
|
||||
face[3].set(x1, y2, z2, u1, v2);
|
||||
r.renderFace(face, c.max.y < 1 ? 7 : 1);
|
||||
}
|
||||
|
||||
if((sideMask&4) == 0)
|
||||
{
|
||||
u1 = 1-x1+4; v1 = 1-y2;
|
||||
u2 = 1-x2+4; v2 = 1-y1;
|
||||
face[0].set(x1, y1, z1, u1, v2);
|
||||
face[1].set(x1, y2, z1, u1, v1);
|
||||
face[2].set(x2, y2, z1, u2, v1);
|
||||
face[3].set(x2, y1, z1, u2, v2);
|
||||
r.renderFace(face, c.min.z > 0 ? 8 : 2);
|
||||
}
|
||||
|
||||
if((sideMask&8) == 0)
|
||||
{
|
||||
u1 = x1+6; v1 = 1-y2;
|
||||
u2 = x2+6; v2 = 1-y1;
|
||||
face[0].set(x2, y1, z2, u2, v2);
|
||||
face[1].set(x2, y2, z2, u2, v1);
|
||||
face[2].set(x1, y2, z2, u1, v1);
|
||||
face[3].set(x1, y1, z2, u1, v2);
|
||||
r.renderFace(face, c.max.z < 1 ? 9 : 3);
|
||||
}
|
||||
|
||||
if((sideMask&0x10) == 0)
|
||||
{
|
||||
u1 = z1+8; v1 = 1-y2;
|
||||
u2 = z2+8; v2 = 1-y1;
|
||||
face[0].set(x1, y1, z2, u2, v2);
|
||||
face[1].set(x1, y2, z2, u2, v1);
|
||||
face[2].set(x1, y2, z1, u1, v1);
|
||||
face[3].set(x1, y1, z1, u1, v2);
|
||||
r.renderFace(face, c.min.x > 0 ? 10 : 4);
|
||||
}
|
||||
|
||||
if((sideMask&0x20) == 0)
|
||||
{
|
||||
u1 = 1-z1+10; v1 = 1-y2;
|
||||
u2 = 1-z2+10; v2 = 1-y1;
|
||||
face[0].set(x2, y1, z1, u1, v2);
|
||||
face[1].set(x2, y2, z1, u1, v1);
|
||||
face[2].set(x2, y2, z2, u2, v1);
|
||||
face[3].set(x2, y1, z2, u2, v2);
|
||||
r.renderFace(face, c.max.x < 1 ? 11 : 5);
|
||||
}
|
||||
}
|
||||
|
||||
public static void renderBlock(Cuboid6 bounds, int sideMask, final Transformation t, final IUVTransformation u, final IVertexModifier m)
|
||||
{
|
||||
renderBlock(bounds, sideMask, new IFaceRenderer(){
|
||||
boolean drawNormal = CCRenderState.useNormals();
|
||||
boolean computeNormal = drawNormal || m != null && m.needsNormals();
|
||||
Vector3 normal = new Vector3();
|
||||
Vertex5 vert;
|
||||
Vector3 vec = new Vector3();
|
||||
UV uv = new UV();
|
||||
|
||||
@Override
|
||||
public void renderFace(Vertex5[] face, int side)
|
||||
{
|
||||
Tessellator tess = Tessellator.instance;
|
||||
for(int i = 0; i < face.length; i++)
|
||||
{
|
||||
if(computeNormal)
|
||||
{
|
||||
if(t != null)
|
||||
t.applyN(normal.set(Rotation.axes[side%6]));
|
||||
else
|
||||
normal = Rotation.axes[side%6];
|
||||
|
||||
if(drawNormal)
|
||||
tess.setNormal((float)normal.x, (float)normal.y, (float)normal.z);
|
||||
}
|
||||
|
||||
vert = face[i];
|
||||
if(t != null)
|
||||
t.apply(vec.set(vert.vec));
|
||||
else
|
||||
vec = vert.vec;
|
||||
|
||||
if(u != null)
|
||||
u.transform(uv.set(vert.uv));
|
||||
else
|
||||
uv = vert.uv;
|
||||
|
||||
if(m != null)
|
||||
m.applyModifiers(null, tess, vec, uv, normal, i);
|
||||
|
||||
tess.addVertexWithUV(vec.x, vec.y, vec.z, uv.u, uv.v);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import static org.lwjgl.opengl.ARBShaderObjects.*;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import org.lwjgl.opengl.ARBShaderObjects;
|
||||
import org.lwjgl.opengl.ARBVertexShader;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.util.vector.Matrix4f;
|
||||
|
||||
public class ShaderProgram
|
||||
{
|
||||
int programID;
|
||||
|
||||
public ShaderProgram()
|
||||
{
|
||||
programID = glCreateProgramObjectARB();
|
||||
if(programID == 0)
|
||||
throw new RuntimeException("Unable to allocate shader program object.");
|
||||
}
|
||||
|
||||
public void attach(int shaderType, String resource)
|
||||
{
|
||||
InputStream stream = ShaderProgram.class.getResourceAsStream(resource);
|
||||
if(stream == null)
|
||||
throw new RuntimeException("Unable to locate resource: "+resource);
|
||||
|
||||
attach(shaderType, stream);
|
||||
}
|
||||
|
||||
public void use()
|
||||
{
|
||||
glUseProgramObjectARB(programID);
|
||||
}
|
||||
|
||||
public static void restore()
|
||||
{
|
||||
glUseProgramObjectARB(0);
|
||||
}
|
||||
|
||||
public void link()
|
||||
{
|
||||
glLinkProgramARB(programID);
|
||||
if(glGetObjectParameteriARB(programID, GL_OBJECT_LINK_STATUS_ARB) == GL11.GL_FALSE)
|
||||
throw new RuntimeException("Error linking program: "+getInfoLog(programID));
|
||||
|
||||
glValidateProgramARB(programID);
|
||||
if(glGetObjectParameteriARB(programID, GL_OBJECT_VALIDATE_STATUS_ARB) == GL11.GL_FALSE)
|
||||
throw new RuntimeException("Error validating program: "+getInfoLog(programID));
|
||||
|
||||
use();
|
||||
onLink();
|
||||
restore();
|
||||
}
|
||||
|
||||
public void attach(int shaderType, InputStream stream)
|
||||
{
|
||||
if(stream == null)
|
||||
throw new RuntimeException("Invalid shader inputstream");
|
||||
|
||||
int shaderID = 0;
|
||||
try
|
||||
{
|
||||
shaderID = glCreateShaderObjectARB(shaderType);
|
||||
if(shaderID == 0)
|
||||
throw new RuntimeException("Unable to allocate shader object.");
|
||||
|
||||
try
|
||||
{
|
||||
glShaderSourceARB(shaderID, asString(stream));
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException("Error reading inputstream.", e);
|
||||
}
|
||||
|
||||
glCompileShaderARB(shaderID);
|
||||
if(glGetObjectParameteriARB(shaderID, GL_OBJECT_COMPILE_STATUS_ARB) == GL11.GL_FALSE)
|
||||
throw new RuntimeException("Error compiling shader: "+getInfoLog(shaderID));
|
||||
|
||||
glAttachObjectARB(programID, shaderID);
|
||||
}
|
||||
catch(RuntimeException e)
|
||||
{
|
||||
glDeleteObjectARB(shaderID);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static String asString(InputStream stream) throws IOException
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
BufferedReader bin = new BufferedReader(new InputStreamReader(stream));
|
||||
String line;
|
||||
while((line = bin.readLine()) != null)
|
||||
sb.append(line).append('\n');
|
||||
stream.close();
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String getInfoLog(int shaderID)
|
||||
{
|
||||
return glGetInfoLogARB(shaderID, glGetObjectParameteriARB(shaderID, GL_OBJECT_INFO_LOG_LENGTH_ARB));
|
||||
}
|
||||
|
||||
public int getUniformLoc(String name)
|
||||
{
|
||||
return ARBShaderObjects.glGetUniformLocationARB(programID, name);
|
||||
}
|
||||
|
||||
public int getAttribLoc(String name)
|
||||
{
|
||||
return ARBVertexShader.glGetAttribLocationARB(programID, name);
|
||||
}
|
||||
|
||||
public void uniformTexture(String name, int textureIndex)
|
||||
{
|
||||
ARBShaderObjects.glUniform1iARB(getUniformLoc(name), textureIndex);
|
||||
}
|
||||
|
||||
public void onLink()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void glVertexAttributeMat4(int loc, Matrix4f matrix)
|
||||
{
|
||||
ARBVertexShader.glVertexAttrib4fARB(loc , matrix.m00, matrix.m01, matrix.m02, matrix.m03);
|
||||
ARBVertexShader.glVertexAttrib4fARB(loc+1, matrix.m10, matrix.m11, matrix.m12, matrix.m13);
|
||||
ARBVertexShader.glVertexAttrib4fARB(loc+2, matrix.m20, matrix.m21, matrix.m22, matrix.m23);
|
||||
ARBVertexShader.glVertexAttrib4fARB(loc+3, matrix.m30, matrix.m31, matrix.m32, matrix.m33);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import codechicken.lib.render.TextureUtils.IIconRegister;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
import net.minecraft.client.renderer.texture.IconRegister;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.util.Icon;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public class SpriteSheetManager
|
||||
{
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static class SpriteSheet implements IIconRegister
|
||||
{
|
||||
private int tilesX;
|
||||
private int tilesY;
|
||||
private ArrayList<Integer> newSprites = new ArrayList<Integer>();
|
||||
private TextureSpecial[] sprites;
|
||||
private ResourceLocation resource;
|
||||
private TextureDataHolder texture;
|
||||
private int spriteWidth;
|
||||
private int spriteHeight;
|
||||
|
||||
public int atlasIndex;
|
||||
|
||||
private SpriteSheet(int tilesX, int tilesY, ResourceLocation textureFile)
|
||||
{
|
||||
this.tilesX = tilesX;
|
||||
this.tilesY = tilesY;
|
||||
this.resource = textureFile;
|
||||
sprites = new TextureSpecial[tilesX*tilesY];
|
||||
}
|
||||
|
||||
public void requestIndicies(int... indicies)
|
||||
{
|
||||
for(int i : indicies)
|
||||
setupSprite(i);
|
||||
}
|
||||
|
||||
public void registerIcons(IconRegister register)
|
||||
{
|
||||
TextureMap textureMap = (TextureMap)register;
|
||||
|
||||
if(TextureUtils.refreshTexture(textureMap, resource.func_110623_a()))
|
||||
{
|
||||
reloadTexture();
|
||||
for(int i = 0; i < sprites.length; i++)
|
||||
if(sprites[i] != null)
|
||||
textureMap.setTextureEntry(sprites[i].getIconName(), sprites[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i : newSprites)
|
||||
textureMap.setTextureEntry(sprites[i].getIconName(), sprites[i]);
|
||||
}
|
||||
newSprites.clear();
|
||||
}
|
||||
|
||||
public TextureSpecial setupSprite(int i)
|
||||
{
|
||||
if(sprites[i] == null)
|
||||
{
|
||||
String name = resource+"_"+i;
|
||||
sprites[i] = new TextureSpecial(name).baseFromSheet(this, i);
|
||||
newSprites.add(i);
|
||||
}
|
||||
return sprites[i];
|
||||
}
|
||||
|
||||
private void reloadTexture()
|
||||
{
|
||||
texture = TextureUtils.loadTexture(resource);
|
||||
spriteWidth = texture.width/tilesX;
|
||||
spriteHeight = texture.height/tilesY;
|
||||
}
|
||||
|
||||
public Icon getSprite(int index)
|
||||
{
|
||||
Icon i = sprites[index];
|
||||
if(i == null)
|
||||
throw new IllegalArgumentException("Sprite at index: "+index+" from texture file "+resource+" was not preloaded.");
|
||||
return i;
|
||||
}
|
||||
|
||||
public TextureDataHolder createSprite(int spriteIndex)
|
||||
{
|
||||
int sx = spriteIndex%tilesX;
|
||||
int sy = spriteIndex/tilesX;
|
||||
TextureDataHolder sprite = new TextureDataHolder(spriteWidth, spriteHeight);
|
||||
TextureUtils.copySubImg(texture.data, texture.width, sx*spriteWidth, sy*spriteHeight,
|
||||
spriteWidth, spriteHeight,
|
||||
sprite.data, spriteWidth, 0, 0);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
public int spriteWidth()
|
||||
{
|
||||
return spriteWidth;
|
||||
}
|
||||
|
||||
public int spriteHeight()
|
||||
{
|
||||
return spriteHeight;
|
||||
}
|
||||
|
||||
public TextureSpecial func_110577_aFX(int i, TextureFX textureFX)
|
||||
{
|
||||
return setupSprite(i).addTextureFX(textureFX);
|
||||
}
|
||||
|
||||
public SpriteSheet selfRegister(int atlas)
|
||||
{
|
||||
TextureUtils.addIconRegistrar(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int atlasIndex()
|
||||
{
|
||||
return atlasIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private static HashMap<String, SpriteSheet> spriteSheets = new HashMap<String, SpriteSheet>();
|
||||
|
||||
public static SpriteSheet getSheet(ResourceLocation resource)
|
||||
{
|
||||
return getSheet(16, 16, resource);
|
||||
}
|
||||
|
||||
public static SpriteSheet getSheet(int tilesX, int tilesY, ResourceLocation resource)
|
||||
{
|
||||
SpriteSheet sheet = spriteSheets.get(resource.toString());
|
||||
if(sheet == null)
|
||||
spriteSheets.put(resource.toString(), sheet = new SpriteSheet(tilesX, tilesY, resource));
|
||||
return sheet;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
public class TextureDataHolder
|
||||
{
|
||||
public int width;
|
||||
public int height;
|
||||
public int[] data;
|
||||
|
||||
public TextureDataHolder(int width, int height)
|
||||
{
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
data = new int[width*height];
|
||||
}
|
||||
|
||||
public TextureDataHolder(int[] data, int width)
|
||||
{
|
||||
this.data = data;
|
||||
this.width = width;
|
||||
height = data.length/width;
|
||||
}
|
||||
|
||||
public TextureDataHolder(BufferedImage img)
|
||||
{
|
||||
this(img.getWidth(), img.getHeight());
|
||||
img.getRGB(0, 0, width, height, data, 0, width);
|
||||
}
|
||||
|
||||
public TextureDataHolder copyData()
|
||||
{
|
||||
int[] copy = new int[data.length];
|
||||
System.arraycopy(data, 0, copy, 0, data.length);
|
||||
data = copy;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import codechicken.lib.render.SpriteSheetManager.SpriteSheet;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class TextureFX
|
||||
{
|
||||
public int[] imageData;
|
||||
public int tileSizeBase = 16;
|
||||
public int tileSizeSquare = 256;
|
||||
public int tileSizeMask = 15;
|
||||
public int tileSizeSquareMask = 255;
|
||||
|
||||
public boolean anaglyphEnabled;
|
||||
public TextureSpecial texture;
|
||||
|
||||
public TextureFX(int spriteIndex, SpriteSheet sheet)
|
||||
{
|
||||
texture = sheet.func_110577_aFX(spriteIndex, this);
|
||||
}
|
||||
|
||||
public TextureFX(int size, String name)
|
||||
{
|
||||
texture = new TextureSpecial(name).blank(size).selfRegister().addTextureFX(this);
|
||||
}
|
||||
|
||||
public TextureFX setAtlas(int index)
|
||||
{
|
||||
texture.atlasIndex = index;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setup()
|
||||
{
|
||||
imageData = new int[tileSizeSquare];
|
||||
}
|
||||
|
||||
public void onTextureDimensionsUpdate(int width, int height)
|
||||
{
|
||||
if(width != height)
|
||||
throw new IllegalArgumentException("Non-Square textureFX not supported ("+width+":"+height+")");
|
||||
|
||||
tileSizeBase = width;
|
||||
tileSizeSquare = tileSizeBase * tileSizeBase;
|
||||
tileSizeMask = tileSizeBase - 1;
|
||||
tileSizeSquareMask = tileSizeSquare - 1;
|
||||
setup();
|
||||
}
|
||||
|
||||
public void update()
|
||||
{
|
||||
anaglyphEnabled = Minecraft.getMinecraft().gameSettings.anaglyph;
|
||||
onTick();
|
||||
}
|
||||
|
||||
public void onTick()
|
||||
{
|
||||
}
|
||||
|
||||
public boolean changed()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import codechicken.lib.render.SpriteSheetManager.SpriteSheet;
|
||||
import codechicken.lib.render.TextureUtils.IIconRegister;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.client.renderer.texture.IconRegister;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import net.minecraft.client.resources.ResourceManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class TextureSpecial extends TextureAtlasSprite implements IIconRegister
|
||||
{
|
||||
//sprite sheet fields
|
||||
private int spriteIndex;
|
||||
private SpriteSheet spriteSheet;
|
||||
|
||||
//textureFX fields
|
||||
private TextureFX textureFX;
|
||||
|
||||
private int blankSize = -1;
|
||||
|
||||
private ArrayList<TextureDataHolder> baseTextures;
|
||||
|
||||
private boolean selfRegister;
|
||||
public int atlasIndex;
|
||||
|
||||
protected TextureSpecial(String par1)
|
||||
{
|
||||
super(par1);
|
||||
}
|
||||
|
||||
public TextureSpecial addTexture(TextureDataHolder t)
|
||||
{
|
||||
if(baseTextures == null)
|
||||
baseTextures = new ArrayList<TextureDataHolder>();
|
||||
baseTextures.add(t);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TextureSpecial baseFromSheet(SpriteSheet spriteSheet, int spriteIndex)
|
||||
{
|
||||
this.spriteSheet = spriteSheet;
|
||||
this.spriteIndex = spriteIndex;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TextureSpecial addTextureFX(TextureFX fx)
|
||||
{
|
||||
textureFX = fx;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void func_110971_a(int sheetWidth, int sheetHeight, int originX, int originY, boolean rotated)
|
||||
{
|
||||
super.func_110971_a(sheetWidth, sheetHeight, originX, originY, rotated);
|
||||
if(textureFX != null)
|
||||
textureFX.onTextureDimensionsUpdate(field_130223_c, field_130224_d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAnimation()
|
||||
{
|
||||
if(textureFX != null)
|
||||
{
|
||||
textureFX.update();
|
||||
if(textureFX.changed())
|
||||
TextureUtil.func_110998_a(textureFX.imageData, field_130223_c, field_130224_d, field_110975_c, field_110974_d, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean load(ResourceManager manager, ResourceLocation location)
|
||||
{
|
||||
if(baseTextures != null)
|
||||
{
|
||||
for(TextureDataHolder tex : baseTextures)
|
||||
{
|
||||
field_110976_a.add(tex.data);
|
||||
field_130223_c = tex.width;
|
||||
field_130224_d = tex.height;
|
||||
}
|
||||
}
|
||||
|
||||
if(spriteSheet != null)
|
||||
{
|
||||
TextureDataHolder tex = spriteSheet.createSprite(spriteIndex);
|
||||
field_130223_c = tex.width;
|
||||
field_130224_d = tex.height;
|
||||
field_110976_a.add(tex.data);
|
||||
}
|
||||
|
||||
if(blankSize > 0)
|
||||
{
|
||||
field_130223_c = field_130224_d = blankSize;
|
||||
field_110976_a.add(new int[blankSize*blankSize]);
|
||||
}
|
||||
|
||||
if(field_110976_a.isEmpty())
|
||||
throw new RuntimeException("No base frame for texture: "+getIconName());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean func_130098_m()
|
||||
{
|
||||
return textureFX != null || super.func_130098_m();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int func_110970_k()
|
||||
{
|
||||
if(textureFX != null)
|
||||
return 1;
|
||||
|
||||
return super.func_110970_k();
|
||||
}
|
||||
|
||||
public TextureSpecial blank(int size)
|
||||
{
|
||||
blankSize = size;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TextureSpecial selfRegister()
|
||||
{
|
||||
selfRegister = true;
|
||||
TextureUtils.addIconRegistrar(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerIcons(IconRegister register)
|
||||
{
|
||||
if(selfRegister)
|
||||
((TextureMap)register).setTextureEntry(getIconName(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int atlasIndex()
|
||||
{
|
||||
return atlasIndex;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
import codechicken.lib.colour.Colour;
|
||||
import codechicken.lib.colour.ColourARGB;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.renderer.texture.IconRegister;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.util.Icon;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.ForgeSubscribe;
|
||||
|
||||
public class TextureUtils
|
||||
{
|
||||
public static interface IIconRegister
|
||||
{
|
||||
public void registerIcons(IconRegister register);
|
||||
public int atlasIndex();
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.register(new TextureUtils());
|
||||
}
|
||||
|
||||
private static ArrayList<IIconRegister> iconRegistrars = new ArrayList<TextureUtils.IIconRegister>();
|
||||
|
||||
public static void addIconRegistrar(IIconRegister registrar)
|
||||
{
|
||||
iconRegistrars.add(registrar);
|
||||
}
|
||||
|
||||
@ForgeSubscribe
|
||||
public void textureLoad(TextureStitchEvent.Pre event)
|
||||
{
|
||||
for(IIconRegister reg : iconRegistrars)
|
||||
if(reg.atlasIndex() == event.map.textureType)
|
||||
reg.registerIcons(event.map);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an array of ARGB pixel data
|
||||
*/
|
||||
public static int[] loadTextureData(ResourceLocation resource)
|
||||
{
|
||||
return loadTexture(resource).data;
|
||||
}
|
||||
|
||||
public static Colour[] loadTextureColours(ResourceLocation resource)
|
||||
{
|
||||
int[] idata = loadTextureData(resource);
|
||||
Colour[] data = new Colour[idata.length];
|
||||
for(int i = 0; i < data.length; i++)
|
||||
data[i] = new ColourARGB(idata[i]);
|
||||
return data;
|
||||
}
|
||||
|
||||
public static InputStream getTextureResource(ResourceLocation textureFile) throws IOException
|
||||
{
|
||||
return Minecraft.getMinecraft().func_110442_L().func_110536_a(textureFile).func_110527_b();
|
||||
}
|
||||
|
||||
public static BufferedImage loadBufferedImage(ResourceLocation textureFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
return loadBufferedImage(getTextureResource(textureFile));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
System.err.println("Failed to load texture file: "+textureFile);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static BufferedImage loadBufferedImage(InputStream in) throws IOException
|
||||
{
|
||||
BufferedImage img = ImageIO.read(in);
|
||||
in.close();
|
||||
return img;
|
||||
}
|
||||
|
||||
public static TextureManager engine()
|
||||
{
|
||||
return Minecraft.getMinecraft().renderEngine;
|
||||
}
|
||||
|
||||
public static void copySubImg(int[] fromTex, int fromWidth, int fromX, int fromY, int width, int height, int[] toTex, int toWidth, int toX, int toY)
|
||||
{
|
||||
for(int y = 0; y < height; y++)
|
||||
for(int x = 0; x < width; x++)
|
||||
{
|
||||
int fp = (y+fromY)*fromWidth+x+fromX;
|
||||
int tp = (y+toX)*toWidth+x+toX;
|
||||
|
||||
toTex[tp] = fromTex[fp];
|
||||
}
|
||||
}
|
||||
|
||||
public static void bindAtlas(int atlasIndex)
|
||||
{
|
||||
engine().func_110577_a(atlasIndex == 0 ? TextureMap.field_110575_b : TextureMap.field_110576_c);
|
||||
}
|
||||
|
||||
public static Icon getBlankIcon(int size, IconRegister iconRegister)
|
||||
{
|
||||
TextureMap textureMap = (TextureMap)iconRegister;
|
||||
String s = "blank_"+size;
|
||||
if(textureMap.getTextureExtry(s) == null)
|
||||
{
|
||||
TextureSpecial icon = new TextureSpecial(s).blank(size);
|
||||
textureMap.setTextureEntry(s, icon);
|
||||
}
|
||||
return iconRegister.registerIcon(s);
|
||||
}
|
||||
|
||||
public static TextureSpecial getTextureSpecial(IconRegister iconRegister, String name)
|
||||
{
|
||||
TextureMap textureMap = (TextureMap) iconRegister;
|
||||
Icon entry = textureMap.getTextureExtry(name);
|
||||
if(entry != null)
|
||||
throw new IllegalStateException("Texture: "+name+" is already registered");
|
||||
|
||||
TextureSpecial icon = new TextureSpecial(name);
|
||||
textureMap.setTextureEntry(name, icon);
|
||||
return icon;
|
||||
}
|
||||
|
||||
public static void prepareTexture(int target, int texture, int min_mag_filter, int wrap)
|
||||
{
|
||||
GL11.glBindTexture(target, texture);
|
||||
GL11.glTexParameteri(target, GL11.GL_TEXTURE_MIN_FILTER, min_mag_filter);
|
||||
GL11.glTexParameteri(target, GL11.GL_TEXTURE_MAG_FILTER, min_mag_filter);
|
||||
switch(target)
|
||||
{
|
||||
case GL12.GL_TEXTURE_3D:
|
||||
GL11.glTexParameteri(target, GL12.GL_TEXTURE_WRAP_R, wrap);
|
||||
case GL11.GL_TEXTURE_2D:
|
||||
GL11.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_T, wrap);
|
||||
case GL11.GL_TEXTURE_1D:
|
||||
GL11.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_S, wrap);
|
||||
}
|
||||
}
|
||||
|
||||
public static TextureDataHolder loadTexture(ResourceLocation resource)
|
||||
{
|
||||
BufferedImage img = loadBufferedImage(resource);
|
||||
if(img == null)
|
||||
throw new RuntimeException("Texture not found: "+resource);
|
||||
return new TextureDataHolder(img);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses an empty placeholder texture to tell if the map has been reloaded since the last call to refresh texture and the texture with name needs to be reacquired to be valid
|
||||
*/
|
||||
public static boolean refreshTexture(TextureMap map, String name)
|
||||
{
|
||||
if(map.getTextureExtry(name) == null)
|
||||
{
|
||||
map.setTextureEntry(name, new PlaceholderTexture(name));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
public class UV
|
||||
{
|
||||
public double u;
|
||||
public double v;
|
||||
|
||||
public UV()
|
||||
{
|
||||
}
|
||||
|
||||
public UV(double u, double v)
|
||||
{
|
||||
this.u = u;
|
||||
this.v = v;
|
||||
}
|
||||
|
||||
public UV(UV uv)
|
||||
{
|
||||
this(uv.u, uv.v);
|
||||
}
|
||||
|
||||
public UV set(double u, double v)
|
||||
{
|
||||
this.u = u;
|
||||
this.v = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UV set(UV uv)
|
||||
{
|
||||
u = uv.u;
|
||||
v = uv.v;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UV copy()
|
||||
{
|
||||
return new UV(this);
|
||||
}
|
||||
|
||||
public UV add(UV uv)
|
||||
{
|
||||
u+=uv.u;
|
||||
v+=uv.v;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UV mul(double d)
|
||||
{
|
||||
u*=d;
|
||||
v*=d;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
MathContext cont = new MathContext(4, RoundingMode.HALF_UP);
|
||||
return "UV("+new BigDecimal(u, cont)+", "+new BigDecimal(v, cont)+")";
|
||||
}
|
||||
|
||||
public UV apply(IUVTransformation transform)
|
||||
{
|
||||
transform.transform(this);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
public class UVScale implements IUVTransformation
|
||||
{
|
||||
double su;
|
||||
double sv;
|
||||
|
||||
public UVScale(double scaleu, double scalev)
|
||||
{
|
||||
su = scaleu;
|
||||
sv = scalev;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(UV uv)
|
||||
{
|
||||
uv.u*=su;
|
||||
uv.v*=sv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
public class UVTranslation implements IUVTransformation
|
||||
{
|
||||
public double du;
|
||||
public double dv;
|
||||
|
||||
public UVTranslation(double u, double v)
|
||||
{
|
||||
du = u;
|
||||
dv = v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(UV texcoord)
|
||||
{
|
||||
texcoord.u+=du;
|
||||
texcoord.v+=dv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package codechicken.lib.render;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
import codechicken.lib.vec.Vector3;
|
||||
|
||||
public class Vertex5
|
||||
{
|
||||
public Vector3 vec;
|
||||
public UV uv;
|
||||
|
||||
public Vertex5()
|
||||
{
|
||||
this(new Vector3(), new UV());
|
||||
}
|
||||
|
||||
public Vertex5(Vector3 vert, UV uv)
|
||||
{
|
||||
this.vec = vert;
|
||||
this.uv = uv;
|
||||
}
|
||||
|
||||
public Vertex5(Vector3 vert, double u, double v)
|
||||
{
|
||||
this(vert, new UV(u, v));
|
||||
}
|
||||
|
||||
public Vertex5(double x, double y, double z, double u, double v)
|
||||
{
|
||||
this(new Vector3(x, y, z), new UV(u, v));
|
||||
}
|
||||
|
||||
public Vertex5 set(double x, double y, double z, double u, double v)
|
||||
{
|
||||
vec.set(x, y, z);
|
||||
uv.set(u, v);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vertex5(Vertex5 vertex5)
|
||||
{
|
||||
this(vertex5.vec.copy(), vertex5.uv.copy());
|
||||
}
|
||||
|
||||
public Vertex5 copy()
|
||||
{
|
||||
return new Vertex5(this);
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
MathContext cont = new MathContext(4, RoundingMode.HALF_UP);
|
||||
return "Vertex: ("+new BigDecimal(vec.x, cont)+", "+new BigDecimal(vec.y, cont)+", "+new BigDecimal(vec.z, cont)+") ("+new BigDecimal(uv.u, cont)+", "+new BigDecimal(uv.v, cont)+")";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
public class AxisCycle
|
||||
{
|
||||
public static Transformation[] cycles = new Transformation[]{
|
||||
new RedundantTransformation(),
|
||||
new VariableTransformation(new Matrix4(0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1)){
|
||||
@Override public void apply(Vector3 vec){
|
||||
double d0 = vec.x; double d1 = vec.y; double d2 = vec.z;
|
||||
vec.x = d2; vec.y = d0; vec.z = d1;
|
||||
} @Override public Transformation inverse(){
|
||||
return cycles[2];
|
||||
}},
|
||||
new VariableTransformation(new Matrix4(0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1)){
|
||||
@Override public void apply(Vector3 vec){
|
||||
double d0 = vec.x; double d1 = vec.y; double d2 = vec.z;
|
||||
vec.x = d1; vec.y = d2; vec.z = d0;
|
||||
} @Override public Transformation inverse(){
|
||||
return cycles[1];
|
||||
}}};
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import codechicken.lib.math.MathHelper;
|
||||
|
||||
public class BlockCoord implements Comparable<BlockCoord>
|
||||
{
|
||||
public int x;
|
||||
public int y;
|
||||
public int z;
|
||||
|
||||
public BlockCoord(int x, int y, int z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public BlockCoord(Vector3 v)
|
||||
{
|
||||
this(MathHelper.floor_double(v.x), MathHelper.floor_double(v.y), MathHelper.floor_double(v.z));
|
||||
}
|
||||
|
||||
public BlockCoord(TileEntity tile)
|
||||
{
|
||||
this(tile.xCoord, tile.yCoord, tile.zCoord);
|
||||
}
|
||||
|
||||
public BlockCoord(int[] ia)
|
||||
{
|
||||
this(ia[0], ia[1], ia[2]);
|
||||
}
|
||||
|
||||
public BlockCoord()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if(!(obj instanceof BlockCoord))
|
||||
return false;
|
||||
BlockCoord o2 = (BlockCoord)obj;
|
||||
return x == o2.x && y == o2.y && z == o2.z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return (x^z)*31 + y;
|
||||
}
|
||||
|
||||
public int compareTo(BlockCoord o)
|
||||
{
|
||||
if(x != o.x)return x < o.x ? 1 : -1;
|
||||
if(y != o.y)return y < o.y ? 1 : -1;
|
||||
if(z != o.z)return z < o.z ? 1 : -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Vector3 toVector3Centered()
|
||||
{
|
||||
return new Vector3(x+0.5, y+0.5, z+0.5);
|
||||
}
|
||||
|
||||
public BlockCoord multiply(int i)
|
||||
{
|
||||
x*=i;
|
||||
y*=i;
|
||||
z*=i;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double mag_()
|
||||
{
|
||||
return Math.sqrt(x*x+y*y+z*z);
|
||||
}
|
||||
|
||||
public int mag2()
|
||||
{
|
||||
return x*x+y*y+z*z;
|
||||
}
|
||||
|
||||
public boolean isZero()
|
||||
{
|
||||
return x == 0 && y == 0 && z == 0;
|
||||
}
|
||||
|
||||
public boolean isAxial()
|
||||
{
|
||||
return x == 0 ? (y == 0 || z == 0) : (y == 0 && z == 0);
|
||||
}
|
||||
|
||||
public BlockCoord add(BlockCoord coord2)
|
||||
{
|
||||
x+=coord2.x;
|
||||
y+=coord2.y;
|
||||
z+=coord2.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockCoord add(int i, int j, int k)
|
||||
{
|
||||
x+=i;
|
||||
y+=j;
|
||||
z+=k;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockCoord sub(BlockCoord coord2)
|
||||
{
|
||||
x-=coord2.x;
|
||||
y-=coord2.y;
|
||||
z-=coord2.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockCoord sub(int i, int j, int k)
|
||||
{
|
||||
x-=i;
|
||||
y-=j;
|
||||
z-=k;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockCoord offset(int side)
|
||||
{
|
||||
return offset(side, 1);
|
||||
}
|
||||
|
||||
public BlockCoord offset(int side, int amount)
|
||||
{
|
||||
BlockCoord offset = sideOffsets[side];
|
||||
x+=offset.x*amount;
|
||||
y+=offset.y*amount;
|
||||
z+=offset.z*amount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockCoord inset(int side)
|
||||
{
|
||||
return inset(side, 1);
|
||||
}
|
||||
|
||||
public BlockCoord inset(int side, int amount)
|
||||
{
|
||||
return offset(side, -amount);
|
||||
}
|
||||
|
||||
public static final BlockCoord[] sideOffsets = new BlockCoord[]{
|
||||
new BlockCoord( 0,-1, 0),
|
||||
new BlockCoord( 0, 1, 0),
|
||||
new BlockCoord( 0, 0,-1),
|
||||
new BlockCoord( 0, 0, 1),
|
||||
new BlockCoord(-1, 0, 0),
|
||||
new BlockCoord( 1, 0, 0)};
|
||||
|
||||
public int[] intArray()
|
||||
{
|
||||
return new int[]{x, y, z};
|
||||
}
|
||||
|
||||
public BlockCoord copy()
|
||||
{
|
||||
return new BlockCoord(x, y, z);
|
||||
}
|
||||
|
||||
public BlockCoord set(int i, int j, int k)
|
||||
{
|
||||
x = i;
|
||||
y = j;
|
||||
z = k;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockCoord set(BlockCoord coord)
|
||||
{
|
||||
return set(coord.x, coord.y, coord.z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
|
||||
public class Cuboid6
|
||||
{
|
||||
public static Cuboid6 full = new Cuboid6(0, 0, 0, 1, 1, 1);
|
||||
|
||||
public Vector3 min;
|
||||
public Vector3 max;
|
||||
|
||||
public Cuboid6(Vector3 min, Vector3 max)
|
||||
{
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public Cuboid6(AxisAlignedBB aabb)
|
||||
{
|
||||
min = new Vector3(aabb.minX, aabb.minY, aabb.minZ);
|
||||
max = new Vector3(aabb.maxX, aabb.maxY, aabb.maxZ);
|
||||
}
|
||||
|
||||
public Cuboid6(Cuboid6 cuboid)
|
||||
{
|
||||
min = cuboid.min.copy();
|
||||
max = cuboid.max.copy();
|
||||
}
|
||||
|
||||
public Cuboid6(double minx, double miny, double minz, double maxx, double maxy, double maxz)
|
||||
{
|
||||
min = new Vector3(minx, miny, minz);
|
||||
max = new Vector3(maxx, maxy, maxz);
|
||||
}
|
||||
|
||||
public AxisAlignedBB toAABB()
|
||||
{
|
||||
return AxisAlignedBB.getBoundingBox(min.x, min.y, min.z, max.x, max.y, max.z);
|
||||
}
|
||||
|
||||
public Cuboid6 copy()
|
||||
{
|
||||
return new Cuboid6(this);
|
||||
}
|
||||
|
||||
public Cuboid6 add(Vector3 vec)
|
||||
{
|
||||
min.add(vec);
|
||||
max.add(vec);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Cuboid6 sub(Vector3 vec)
|
||||
{
|
||||
min.subtract(vec);
|
||||
max.subtract(vec);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setBlockBounds(Block block)
|
||||
{
|
||||
block.setBlockBounds((float)min.x, (float)min.y, (float)min.z, (float)max.x, (float)max.y, (float)max.z);
|
||||
}
|
||||
|
||||
public boolean intersects(Cuboid6 b)
|
||||
{
|
||||
return max.x-1E-5 > b.min.x &&
|
||||
b.max.x-1E-5 > min.x &&
|
||||
max.y-1E-5 > b.min.y &&
|
||||
b.max.y-1E-5 > min.y &&
|
||||
max.z-1E-5 > b.min.z &&
|
||||
b.max.z-1E-5 > min.z;
|
||||
}
|
||||
|
||||
public Cuboid6 offset(Cuboid6 o)
|
||||
{
|
||||
min.add(o.min);
|
||||
max.add(o.max);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 center()
|
||||
{
|
||||
return min.copy().add(max).multiply(0.5);
|
||||
}
|
||||
|
||||
public static boolean intersects(Cuboid6 a, Cuboid6 b)
|
||||
{
|
||||
return a != null && b != null && a.intersects(b);
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
MathContext cont = new MathContext(4, RoundingMode.HALF_UP);
|
||||
return "Cuboid: ("+new BigDecimal(min.x, cont)+", "+new BigDecimal(min.y, cont)+", "+new BigDecimal(min.z, cont)+") -> ("+
|
||||
new BigDecimal(max.x, cont)+", "+new BigDecimal(max.y, cont)+", "+new BigDecimal(max.z, cont)+")";
|
||||
}
|
||||
|
||||
public Cuboid6 enclose(Vector3 vec)
|
||||
{
|
||||
if(min.x > vec.x) min.x = vec.x;
|
||||
if(min.y > vec.y) min.y = vec.y;
|
||||
if(min.z > vec.z) min.z = vec.z;
|
||||
if(max.x < vec.x) max.x = vec.x;
|
||||
if(max.y < vec.y) max.y = vec.y;
|
||||
if(max.z < vec.z) max.z = vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Cuboid6 enclose(Cuboid6 c)
|
||||
{
|
||||
if(min.x > c.min.x) min.x = c.min.x;
|
||||
if(min.y > c.min.y) min.y = c.min.y;
|
||||
if(min.z > c.min.z) min.z = c.min.z;
|
||||
if(max.x < c.max.x) max.x = c.max.x;
|
||||
if(max.y < c.max.y) max.y = c.max.y;
|
||||
if(max.z < c.max.z) max.z = c.max.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Cuboid6 transform(Transformation t)
|
||||
{
|
||||
t.apply(min);
|
||||
t.apply(max);
|
||||
double temp;
|
||||
if(min.x > max.x) {temp = min.x; min.x = max.x; max.x = temp;}
|
||||
if(min.y > max.y) {temp = min.y; min.y = max.y; max.y = temp;}
|
||||
if(min.z > max.z) {temp = min.z; min.z = max.z; max.z = temp;}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
|
||||
public class CuboidCoord
|
||||
{
|
||||
public BlockCoord min;
|
||||
public BlockCoord max;
|
||||
|
||||
public CuboidCoord(BlockCoord min, BlockCoord max)
|
||||
{
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public CuboidCoord(BlockCoord coord)
|
||||
{
|
||||
this(coord, coord.copy());
|
||||
}
|
||||
|
||||
public CuboidCoord(int[] ia)
|
||||
{
|
||||
this(ia[0], ia[1], ia[2], ia[3], ia[4], ia[5]);
|
||||
}
|
||||
|
||||
public CuboidCoord(int x1, int y1, int z1, int x2, int y2, int z2)
|
||||
{
|
||||
this(new BlockCoord(x1, y1, z1), new BlockCoord(x2, y2, z2));
|
||||
}
|
||||
|
||||
public void expand(int side, int amount)
|
||||
{
|
||||
if(side%2 == 0)//negative side
|
||||
min = min.offset(side, amount);
|
||||
else
|
||||
max = max.offset(side, amount);
|
||||
}
|
||||
|
||||
public void shrink(int side, int amount)
|
||||
{
|
||||
if(side%2 == 0)//negative side
|
||||
min = min.inset(side, amount);
|
||||
else
|
||||
max = max.inset(side, amount);
|
||||
}
|
||||
|
||||
public int size(int s)
|
||||
{
|
||||
switch(s)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
return max.y - min.y+1;
|
||||
case 2:
|
||||
case 3:
|
||||
return max.z - min.z+1;
|
||||
case 4:
|
||||
case 5:
|
||||
return max.x - min.x+1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int getVolume()
|
||||
{
|
||||
return (max.x-min.x+1)*(max.y-min.y+1)*(max.z-min.z+1);
|
||||
}
|
||||
|
||||
public Vector3 getCenterVec()
|
||||
{
|
||||
return new Vector3(min.x+(max.x-min.x+1)/2D, min.y+(max.y-min.y+1)/2D, min.z+(max.z-min.z+1)/2D);
|
||||
}
|
||||
|
||||
public BlockCoord getCenter(BlockCoord store)
|
||||
{
|
||||
store.set(min.x+(max.x-min.x)/2, min.y+(max.y-min.y)/2, min.z+(max.z-min.z)/2);
|
||||
return store;
|
||||
}
|
||||
|
||||
public boolean contains(BlockCoord coord)
|
||||
{
|
||||
return contains(coord.x, coord.y, coord.z);
|
||||
}
|
||||
|
||||
public boolean contains(int x, int y, int z)
|
||||
{
|
||||
return x >= min.x && x <= max.x
|
||||
&& y >= min.y && y <= max.y
|
||||
&& z >= min.z && z <= max.z;
|
||||
}
|
||||
|
||||
public int[] intArray()
|
||||
{
|
||||
return new int[]{min.x, min.y, min.z, max.x, max.y, max.z};
|
||||
}
|
||||
|
||||
public CuboidCoord copy()
|
||||
{
|
||||
return new CuboidCoord(min.copy(), max.copy());
|
||||
}
|
||||
|
||||
public AxisAlignedBB toAABB()
|
||||
{
|
||||
return AxisAlignedBB.getBoundingBox(min.x, min.y, min.z, max.x+1, max.y+1, max.z+1);
|
||||
}
|
||||
|
||||
public void set(BlockCoord min, BlockCoord max)
|
||||
{
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class IrreversibleTransformationException extends RuntimeException
|
||||
{
|
||||
public Transformation t;
|
||||
|
||||
public IrreversibleTransformationException(Transformation t)
|
||||
{
|
||||
this.t = t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage()
|
||||
{
|
||||
return "The following transformation is irreversible:\n"+t;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
|
||||
public class Line3
|
||||
{
|
||||
public static final double tol = 0.0001D;
|
||||
|
||||
public Vector3 pt1;
|
||||
public Vector3 pt2;
|
||||
|
||||
public Line3(Vector3 pt1, Vector3 pt2)
|
||||
{
|
||||
this.pt1 = pt1;
|
||||
this.pt2 = pt2;
|
||||
}
|
||||
|
||||
public Line3()
|
||||
{
|
||||
this(new Vector3(), new Vector3());
|
||||
}
|
||||
|
||||
public static boolean intersection2D(Line3 line1, Line3 line2, Vector3 store)
|
||||
{
|
||||
// calculate differences
|
||||
double xD1 = line1.pt2.x - line1.pt1.x;
|
||||
double zD1 = line1.pt2.z - line1.pt1.z;
|
||||
double xD2 = line2.pt2.x - line2.pt1.x;
|
||||
double zD2 = line2.pt2.z - line2.pt1.z;
|
||||
|
||||
double xD3 = line1.pt1.x - line2.pt1.x;
|
||||
double zD3 = line1.pt1.z - line2.pt1.z;
|
||||
|
||||
double div = zD2 * xD1 - xD2 * zD1;
|
||||
if(div == 0)//lines are parallel
|
||||
return false;
|
||||
double ua = (xD2 * zD3 - zD2 * xD3) / div;
|
||||
store.set(line1.pt1.x + ua * xD1, 0, line1.pt1.z + ua * zD1);
|
||||
|
||||
if(store.x >= Math.min(line1.pt1.x, line1.pt2.x)-tol && store.x >= Math.min(line2.pt1.x, line2.pt2.x)-tol
|
||||
&& store.z >= Math.min(line1.pt1.z, line1.pt2.z)-tol && store.z >= Math.min(line2.pt1.z, line2.pt2.z)-tol
|
||||
&& store.x <= Math.max(line1.pt1.x, line1.pt2.x)+tol && store.x <= Math.max(line2.pt1.x, line2.pt2.x)+tol
|
||||
&& store.z <= Math.max(line1.pt1.z, line1.pt2.z)+tol && store.z <= Math.max(line2.pt1.z, line2.pt2.z)+tol)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.DoubleBuffer;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public class Matrix4 extends Transformation
|
||||
{
|
||||
private static DoubleBuffer glBuf = ByteBuffer.allocateDirect(16*8).order(ByteOrder.nativeOrder()).asDoubleBuffer();
|
||||
|
||||
//m<row><column>
|
||||
public double m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33;
|
||||
|
||||
public Matrix4()
|
||||
{
|
||||
m00 = m11 = m22 = m33 = 1;
|
||||
}
|
||||
|
||||
public Matrix4(double d00, double d01, double d02, double d03,
|
||||
double d10, double d11, double d12, double d13,
|
||||
double d20, double d21, double d22, double d23,
|
||||
double d30, double d31, double d32, double d33)
|
||||
{
|
||||
m00 = d00;
|
||||
m01 = d01;
|
||||
m02 = d02;
|
||||
m03 = d03;
|
||||
m10 = d10;
|
||||
m11 = d11;
|
||||
m12 = d12;
|
||||
m13 = d13;
|
||||
m20 = d20;
|
||||
m21 = d21;
|
||||
m22 = d22;
|
||||
m23 = d23;
|
||||
m30 = d30;
|
||||
m31 = d31;
|
||||
m32 = d32;
|
||||
m33 = d33;
|
||||
}
|
||||
|
||||
public Matrix4(Matrix4 mat)
|
||||
{
|
||||
set(mat);
|
||||
}
|
||||
|
||||
public Matrix4 setIdentity()
|
||||
{
|
||||
m00 = m11 = m22 = m33 = 1;
|
||||
m01 = m02 = m03 = m10 = m12 = m13 = m20 = m21 = m23 = m30 = m31 = m32 = 0;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Matrix4 translate(Vector3 vec)
|
||||
{
|
||||
m03 += m00 * vec.x + m01 * vec.y + m02 * vec.z;
|
||||
m13 += m10 * vec.x + m11 * vec.y + m12 * vec.z;
|
||||
m23 += m20 * vec.x + m21 * vec.y + m22 * vec.z;
|
||||
m33 += m30 * vec.x + m31 * vec.y + m32 * vec.z;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Matrix4 scale(Vector3 vec)
|
||||
{
|
||||
m00 *= vec.x;
|
||||
m10 *= vec.x;
|
||||
m20 *= vec.x;
|
||||
m30 *= vec.x;
|
||||
m01 *= vec.y;
|
||||
m11 *= vec.y;
|
||||
m21 *= vec.y;
|
||||
m31 *= vec.y;
|
||||
m02 *= vec.z;
|
||||
m12 *= vec.z;
|
||||
m22 *= vec.z;
|
||||
m32 *= vec.z;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Matrix4 rotate(double angle, Vector3 axis)
|
||||
{
|
||||
double c = Math.cos(angle);
|
||||
double s = Math.sin(angle);
|
||||
double mc = 1.0f - c;
|
||||
double xy = axis.x*axis.y;
|
||||
double yz = axis.y*axis.z;
|
||||
double xz = axis.x*axis.z;
|
||||
double xs = axis.x*s;
|
||||
double ys = axis.y*s;
|
||||
double zs = axis.z*s;
|
||||
|
||||
double f00 = axis.x*axis.x*mc+c;
|
||||
double f10 = xy*mc+zs;
|
||||
double f20 = xz*mc-ys;
|
||||
|
||||
double f01 = xy*mc-zs;
|
||||
double f11 = axis.y*axis.y*mc+c;
|
||||
double f21 = yz*mc+xs;
|
||||
|
||||
double f02 = xz*mc+ys;
|
||||
double f12 = yz*mc-xs;
|
||||
double f22 = axis.z*axis.z*mc+c;
|
||||
|
||||
double t00 = m00 * f00 + m01 * f10 + m02 * f20;
|
||||
double t10 = m10 * f00 + m11 * f10 + m12 * f20;
|
||||
double t20 = m20 * f00 + m21 * f10 + m22 * f20;
|
||||
double t30 = m30 * f00 + m31 * f10 + m32 * f20;
|
||||
double t01 = m00 * f01 + m01 * f11 + m02 * f21;
|
||||
double t11 = m10 * f01 + m11 * f11 + m12 * f21;
|
||||
double t21 = m20 * f01 + m21 * f11 + m22 * f21;
|
||||
double t31 = m30 * f01 + m31 * f11 + m32 * f21;
|
||||
m02 = m00 * f02 + m01 * f12 + m02 * f22;
|
||||
m12 = m10 * f02 + m11 * f12 + m12 * f22;
|
||||
m22 = m20 * f02 + m21 * f12 + m22 * f22;
|
||||
m32 = m30 * f02 + m31 * f12 + m32 * f22;
|
||||
m00 = t00;
|
||||
m10 = t10;
|
||||
m20 = t20;
|
||||
m30 = t30;
|
||||
m01 = t01;
|
||||
m11 = t11;
|
||||
m21 = t21;
|
||||
m31 = t31;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Matrix4 rotate(Rotation rotation)
|
||||
{
|
||||
rotation.apply(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Matrix4 leftMultiply(Matrix4 mat)
|
||||
{
|
||||
double n00 = m00 * mat.m00 + m10 * mat.m01 + m20 * mat.m02 + m30 * mat.m03;
|
||||
double n01 = m01 * mat.m00 + m11 * mat.m01 + m21 * mat.m02 + m31 * mat.m03;
|
||||
double n02 = m02 * mat.m00 + m12 * mat.m01 + m22 * mat.m02 + m32 * mat.m03;
|
||||
double n03 = m03 * mat.m00 + m13 * mat.m01 + m23 * mat.m02 + m33 * mat.m03;
|
||||
double n10 = m00 * mat.m10 + m10 * mat.m11 + m20 * mat.m12 + m30 * mat.m13;
|
||||
double n11 = m01 * mat.m10 + m11 * mat.m11 + m21 * mat.m12 + m31 * mat.m13;
|
||||
double n12 = m02 * mat.m10 + m12 * mat.m11 + m22 * mat.m12 + m32 * mat.m13;
|
||||
double n13 = m03 * mat.m10 + m13 * mat.m11 + m23 * mat.m12 + m33 * mat.m13;
|
||||
double n20 = m00 * mat.m20 + m10 * mat.m21 + m20 * mat.m22 + m30 * mat.m23;
|
||||
double n21 = m01 * mat.m20 + m11 * mat.m21 + m21 * mat.m22 + m31 * mat.m23;
|
||||
double n22 = m02 * mat.m20 + m12 * mat.m21 + m22 * mat.m22 + m32 * mat.m23;
|
||||
double n23 = m03 * mat.m20 + m13 * mat.m21 + m23 * mat.m22 + m33 * mat.m23;
|
||||
double n30 = m00 * mat.m30 + m10 * mat.m31 + m20 * mat.m32 + m30 * mat.m33;
|
||||
double n31 = m01 * mat.m30 + m11 * mat.m31 + m21 * mat.m32 + m31 * mat.m33;
|
||||
double n32 = m02 * mat.m30 + m12 * mat.m31 + m22 * mat.m32 + m32 * mat.m33;
|
||||
double n33 = m03 * mat.m30 + m13 * mat.m31 + m23 * mat.m32 + m33 * mat.m33;
|
||||
|
||||
m00 = n00;
|
||||
m01 = n01;
|
||||
m02 = n02;
|
||||
m03 = n03;
|
||||
m10 = n10;
|
||||
m11 = n11;
|
||||
m12 = n12;
|
||||
m13 = n13;
|
||||
m20 = n20;
|
||||
m21 = n21;
|
||||
m22 = n22;
|
||||
m23 = n23;
|
||||
m30 = n30;
|
||||
m31 = n31;
|
||||
m32 = n32;
|
||||
m33 = n33;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Matrix4 multiply(Matrix4 mat)
|
||||
{
|
||||
double n00 = m00 * mat.m00 + m01 * mat.m10 + m02 * mat.m20 + m03 * mat.m30;
|
||||
double n01 = m00 * mat.m01 + m01 * mat.m11 + m02 * mat.m21 + m03 * mat.m31;
|
||||
double n02 = m00 * mat.m02 + m01 * mat.m12 + m02 * mat.m22 + m03 * mat.m32;
|
||||
double n03 = m00 * mat.m03 + m01 * mat.m13 + m02 * mat.m23 + m03 * mat.m33;
|
||||
double n10 = m10 * mat.m00 + m11 * mat.m10 + m12 * mat.m20 + m13 * mat.m30;
|
||||
double n11 = m10 * mat.m01 + m11 * mat.m11 + m12 * mat.m21 + m13 * mat.m31;
|
||||
double n12 = m10 * mat.m02 + m11 * mat.m12 + m12 * mat.m22 + m13 * mat.m32;
|
||||
double n13 = m10 * mat.m03 + m11 * mat.m13 + m12 * mat.m23 + m13 * mat.m33;
|
||||
double n20 = m20 * mat.m00 + m21 * mat.m10 + m22 * mat.m20 + m23 * mat.m30;
|
||||
double n21 = m20 * mat.m01 + m21 * mat.m11 + m22 * mat.m21 + m23 * mat.m31;
|
||||
double n22 = m20 * mat.m02 + m21 * mat.m12 + m22 * mat.m22 + m23 * mat.m32;
|
||||
double n23 = m20 * mat.m03 + m21 * mat.m13 + m22 * mat.m23 + m23 * mat.m33;
|
||||
double n30 = m30 * mat.m00 + m31 * mat.m10 + m32 * mat.m20 + m33 * mat.m30;
|
||||
double n31 = m30 * mat.m01 + m31 * mat.m11 + m32 * mat.m21 + m33 * mat.m31;
|
||||
double n32 = m30 * mat.m02 + m31 * mat.m12 + m32 * mat.m22 + m33 * mat.m32;
|
||||
double n33 = m30 * mat.m03 + m31 * mat.m13 + m32 * mat.m23 + m33 * mat.m33;
|
||||
|
||||
m00 = n00;
|
||||
m01 = n01;
|
||||
m02 = n02;
|
||||
m03 = n03;
|
||||
m10 = n10;
|
||||
m11 = n11;
|
||||
m12 = n12;
|
||||
m13 = n13;
|
||||
m20 = n20;
|
||||
m21 = n21;
|
||||
m22 = n22;
|
||||
m23 = n23;
|
||||
m30 = n30;
|
||||
m31 = n31;
|
||||
m32 = n32;
|
||||
m33 = n33;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Matrix4 transpose()
|
||||
{
|
||||
double n00 = m00;
|
||||
double n10 = m01;
|
||||
double n20 = m02;
|
||||
double n30 = m03;
|
||||
double n01 = m10;
|
||||
double n11 = m11;
|
||||
double n21 = m12;
|
||||
double n31 = m13;
|
||||
double n02 = m20;
|
||||
double n12 = m21;
|
||||
double n22 = m22;
|
||||
double n32 = m23;
|
||||
double n03 = m30;
|
||||
double n13 = m31;
|
||||
double n23 = m32;
|
||||
double n33 = m33;
|
||||
|
||||
m00 = n00;
|
||||
m01 = n01;
|
||||
m02 = n02;
|
||||
m03 = n03;
|
||||
m10 = n10;
|
||||
m11 = n11;
|
||||
m12 = n12;
|
||||
m13 = n13;
|
||||
m20 = n20;
|
||||
m21 = n21;
|
||||
m22 = n22;
|
||||
m23 = n23;
|
||||
m30 = n30;
|
||||
m31 = n31;
|
||||
m32 = n32;
|
||||
m33 = n33;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Matrix4 copy()
|
||||
{
|
||||
return new Matrix4(this);
|
||||
}
|
||||
|
||||
public Matrix4 set(Matrix4 mat)
|
||||
{
|
||||
m00 = mat.m00;
|
||||
m01 = mat.m01;
|
||||
m02 = mat.m02;
|
||||
m03 = mat.m03;
|
||||
m10 = mat.m10;
|
||||
m11 = mat.m11;
|
||||
m12 = mat.m12;
|
||||
m13 = mat.m13;
|
||||
m20 = mat.m20;
|
||||
m21 = mat.m21;
|
||||
m22 = mat.m22;
|
||||
m23 = mat.m23;
|
||||
m30 = mat.m30;
|
||||
m31 = mat.m31;
|
||||
m32 = mat.m32;
|
||||
m33 = mat.m33;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Matrix4 mat)
|
||||
{
|
||||
mat.multiply(this);
|
||||
}
|
||||
|
||||
private void mult3x3(Vector3 vec)
|
||||
{
|
||||
double x = m00 * vec.x + m01 * vec.y + m02 * vec.z;
|
||||
double y = m10 * vec.x + m11 * vec.y + m12 * vec.z;
|
||||
double z = m20 * vec.x + m21 * vec.y + m22 * vec.z;
|
||||
|
||||
vec.x = x;
|
||||
vec.y = y;
|
||||
vec.z = z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Vector3 vec)
|
||||
{
|
||||
mult3x3(vec);
|
||||
vec.add(m03, m13, m23);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyN(Vector3 vec)
|
||||
{
|
||||
mult3x3(vec);
|
||||
vec.normalize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
MathContext cont = new MathContext(4, RoundingMode.HALF_UP);
|
||||
return "["+new BigDecimal(m00, cont)+","+new BigDecimal(m01, cont)+","+new BigDecimal(m02, cont)+","+new BigDecimal(m03, cont)+"]\n"+
|
||||
"["+new BigDecimal(m10, cont)+","+new BigDecimal(m11, cont)+","+new BigDecimal(m12, cont)+","+new BigDecimal(m13, cont)+"]\n"+
|
||||
"["+new BigDecimal(m20, cont)+","+new BigDecimal(m21, cont)+","+new BigDecimal(m22, cont)+","+new BigDecimal(m23, cont)+"]\n"+
|
||||
"["+new BigDecimal(m30, cont)+","+new BigDecimal(m31, cont)+","+new BigDecimal(m32, cont)+","+new BigDecimal(m33, cont)+"]";
|
||||
}
|
||||
|
||||
public Matrix4 apply(Transformation t)
|
||||
{
|
||||
t.apply(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void glApply()
|
||||
{
|
||||
glBuf.put(m00).put(m10).put(m20).put(m30)
|
||||
.put(m01).put(m11).put(m21).put(m31)
|
||||
.put(m02).put(m12).put(m22).put(m32)
|
||||
.put(m03).put(m13).put(m23).put(m33);
|
||||
glBuf.flip();
|
||||
GL11.glMultMatrix(glBuf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transformation inverse()
|
||||
{
|
||||
throw new IrreversibleTransformationException(this);//Don't waste your cpu with matrix inverses
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import codechicken.lib.math.MathHelper;
|
||||
|
||||
public class Quat
|
||||
{
|
||||
public double x;
|
||||
public double y;
|
||||
public double z;
|
||||
public double s;
|
||||
public static final double SQRT2 = Math.sqrt(2D);
|
||||
|
||||
public Quat()
|
||||
{
|
||||
s = 1;
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
}
|
||||
|
||||
public Quat(Quat quat)
|
||||
{
|
||||
x = quat.x;
|
||||
y = quat.y;
|
||||
z = quat.z;
|
||||
s = quat.s;
|
||||
}
|
||||
|
||||
public Quat(double d, double d1, double d2, double d3)
|
||||
{
|
||||
x = d1;
|
||||
y = d2;
|
||||
z = d3;
|
||||
s = d;
|
||||
}
|
||||
|
||||
public Quat set(Quat quat)
|
||||
{
|
||||
x = quat.x;
|
||||
y = quat.y;
|
||||
z = quat.z;
|
||||
s = quat.s;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Quat set(double d, double d1, double d2, double d3)
|
||||
{
|
||||
x = d1;
|
||||
y = d2;
|
||||
z = d3;
|
||||
s = d;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public static Quat aroundAxis(double ax, double ay, double az, double angle)
|
||||
{
|
||||
return new Quat().setAroundAxis(ax, ay, az, angle);
|
||||
}
|
||||
|
||||
public static Quat aroundAxis(Vector3 axis, double angle)
|
||||
{
|
||||
return aroundAxis(axis.x, axis.y, axis.z, angle);
|
||||
}
|
||||
|
||||
public Quat setAroundAxis(double ax, double ay, double az, double angle)
|
||||
{
|
||||
angle *= 0.5;
|
||||
double d4 = MathHelper.sin(angle);
|
||||
return set(MathHelper.cos(angle), ax * d4, ay * d4, az * d4);
|
||||
}
|
||||
|
||||
public Quat setAroundAxis(Vector3 axis, double angle)
|
||||
{
|
||||
return setAroundAxis(axis.x, axis.y, axis.z, angle);
|
||||
}
|
||||
|
||||
public Quat multiply(Quat quat)
|
||||
{
|
||||
double d = s * quat.s - x * quat.x - y * quat.y - z * quat.z;
|
||||
double d1 = s * quat.x + x * quat.s - y * quat.z + z * quat.y;
|
||||
double d2 = s * quat.y + x * quat.z + y * quat.s - z * quat.x;
|
||||
double d3 = s * quat.z - x * quat.y + y * quat.x + z * quat.s;
|
||||
s = d;
|
||||
x = d1;
|
||||
y = d2;
|
||||
z = d3;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Quat rightMultiply(Quat quat)
|
||||
{
|
||||
double d = s * quat.s - x * quat.x - y * quat.y - z * quat.z;
|
||||
double d1 = s * quat.x + x * quat.s + y * quat.z - z * quat.y;
|
||||
double d2 = s * quat.y - x * quat.z + y * quat.s + z * quat.x;
|
||||
double d3 = s * quat.z + x * quat.y - y * quat.x + z * quat.s;
|
||||
s = d;
|
||||
x = d1;
|
||||
y = d2;
|
||||
z = d3;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public double mag()
|
||||
{
|
||||
return Math.sqrt(x * x + y * y + z * z + s * s);
|
||||
}
|
||||
|
||||
public Quat normalize()
|
||||
{
|
||||
double d = mag();
|
||||
if(d != 0)
|
||||
{
|
||||
d = 1 / d;
|
||||
x *= d;
|
||||
y *= d;
|
||||
z *= d;
|
||||
s *= d;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Quat copy()
|
||||
{
|
||||
return new Quat(this);
|
||||
}
|
||||
|
||||
public void rotate(Vector3 vec)
|
||||
{
|
||||
double d = -x * vec.x - y * vec.y - z * vec.z;
|
||||
double d1 = s * vec.x + y * vec.z - z * vec.y;
|
||||
double d2 = s * vec.y - x * vec.z + z * vec.x;
|
||||
double d3 = s * vec.z + x * vec.y - y * vec.x;
|
||||
vec.x = d1 * s - d * x - d2 * z + d3 * y;
|
||||
vec.y = d2 * s - d * y + d1 * z - d3 * x;
|
||||
vec.z = d3 * s - d * z - d1 * y + d2 * x;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
MathContext cont = new MathContext(4, RoundingMode.HALF_UP);
|
||||
return "Quat("+new BigDecimal(s, cont)+", "+new BigDecimal(x, cont)+", "+new BigDecimal(y, cont)+", "+new BigDecimal(z, cont)+")";
|
||||
}
|
||||
|
||||
public Rotation rotation()
|
||||
{
|
||||
return new Rotation(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
public class Rectangle4i
|
||||
{
|
||||
public int x;
|
||||
public int y;
|
||||
public int w;
|
||||
public int h;
|
||||
|
||||
public Rectangle4i()
|
||||
{
|
||||
}
|
||||
|
||||
public Rectangle4i(int x, int y, int w, int h)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.w = w;
|
||||
this.h = h;
|
||||
}
|
||||
|
||||
public Rectangle4i offset(int dx, int dy)
|
||||
{
|
||||
x+=dx;
|
||||
y+=dy;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Rectangle4i with(int px, int py)
|
||||
{
|
||||
if(x > px) x = px;
|
||||
if(y > py) y = py;
|
||||
if(x + w <= px) w = px-x+1;
|
||||
if(y + h <= py) h = py-y+1;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean contains(int px, int py)
|
||||
{
|
||||
return x <= px && px < x+w && y <= py && py < y+h;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public class RedundantTransformation extends Transformation
|
||||
{
|
||||
@Override
|
||||
public void apply(Vector3 vec){}
|
||||
|
||||
@Override
|
||||
public void apply(Matrix4 mat){}
|
||||
|
||||
@Override
|
||||
public void applyN(Vector3 normal){}
|
||||
|
||||
@Override
|
||||
public Transformation at(Vector3 point)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void glApply(){}
|
||||
|
||||
@Override
|
||||
public Transformation inverse()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Nothing()";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.lib.math.MathHelper;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public class Rotation extends Transformation
|
||||
{
|
||||
/**
|
||||
* Clockwise pi/2 about y looking down
|
||||
*/
|
||||
public static Transformation[] quarterRotations = new Transformation[]{
|
||||
new RedundantTransformation(),
|
||||
new VariableTransformation(new Matrix4(0, 0,-1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1)){
|
||||
@Override public void apply(Vector3 vec){
|
||||
double d1 = vec.x; double d2 = vec.z;
|
||||
vec.x = -d2; vec.z = d1;
|
||||
} @Override public Transformation inverse(){
|
||||
return quarterRotations[3];
|
||||
}},
|
||||
new VariableTransformation(new Matrix4(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1)){
|
||||
@Override public void apply(Vector3 vec){
|
||||
vec.x = -vec.x; vec.z = -vec.z;
|
||||
} @Override public Transformation inverse(){
|
||||
return this;
|
||||
}},
|
||||
new VariableTransformation(new Matrix4(0, 0, 1, 0, 0, 1, 0, 0,-1, 0, 0, 0, 0, 0, 0, 1)){
|
||||
@Override public void apply(Vector3 vec){
|
||||
double d1 = vec.x; double d2 = vec.z;
|
||||
vec.x = d2; vec.z = -d1;
|
||||
} @Override public Transformation inverse(){
|
||||
return quarterRotations[1];
|
||||
}}
|
||||
};
|
||||
|
||||
public static Transformation[] sideRotations = new Transformation[]{
|
||||
new RedundantTransformation(),
|
||||
new VariableTransformation(new Matrix4(1, 0, 0, 0, 0,-1, 0, 0, 0, 0,-1, 0, 0, 0, 0, 1)){
|
||||
@Override public void apply(Vector3 vec){
|
||||
vec.y = -vec.y; vec.z = -vec.z;
|
||||
} @Override public Transformation inverse(){
|
||||
return this;
|
||||
}},
|
||||
new VariableTransformation(new Matrix4(1, 0, 0, 0, 0, 0,-1, 0, 0, 1, 0, 0, 0, 0, 0, 1)){
|
||||
@Override public void apply(Vector3 vec){
|
||||
double d1 = vec.y; double d2 = vec.z;
|
||||
vec.y = -d2; vec.z = d1;
|
||||
} @Override public Transformation inverse(){
|
||||
return sideRotations[3];
|
||||
}},
|
||||
new VariableTransformation(new Matrix4(1, 0, 0, 0, 0, 0, 1, 0, 0,-1, 0, 0, 0, 0, 0, 1)){
|
||||
@Override public void apply(Vector3 vec){
|
||||
double d1 = vec.y; double d2 = vec.z;
|
||||
vec.y = d2; vec.z = -d1;
|
||||
} @Override public Transformation inverse(){
|
||||
return sideRotations[2];
|
||||
}},
|
||||
new VariableTransformation(new Matrix4(0, 1, 0, 0,-1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)){
|
||||
@Override public void apply(Vector3 vec){
|
||||
double d0 = vec.x; double d1 = vec.y;
|
||||
vec.x = d1; vec.y = -d0;
|
||||
} @Override public Transformation inverse(){
|
||||
return sideRotations[5];
|
||||
}},
|
||||
new VariableTransformation(new Matrix4(0,-1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)){
|
||||
@Override public void apply(Vector3 vec){
|
||||
double d0 = vec.x; double d1 = vec.y;
|
||||
vec.x = -d1; vec.y = d0;
|
||||
} @Override public Transformation inverse(){
|
||||
return sideRotations[4];
|
||||
}}
|
||||
};
|
||||
|
||||
public static Vector3[] axes = new Vector3[]{
|
||||
new Vector3( 0,-1, 0),
|
||||
new Vector3( 0, 1, 0),
|
||||
new Vector3( 0, 0,-1),
|
||||
new Vector3( 0, 0, 1),
|
||||
new Vector3(-1, 0, 0),
|
||||
new Vector3( 1, 0, 0)};
|
||||
|
||||
public static int[] sideRotMap = new int[]{
|
||||
3,4,2,5,
|
||||
3,5,2,4,
|
||||
1,5,0,4,
|
||||
1,4,0,5,
|
||||
1,2,0,3,
|
||||
1,3,0,2};
|
||||
|
||||
public static int[] rotSideMap = new int[]{
|
||||
-1,-1, 2, 0, 1, 3,
|
||||
-1,-1, 2, 0, 3, 1,
|
||||
2, 0,-1,-1, 3, 1,
|
||||
2, 0,-1,-1, 1, 3,
|
||||
2, 0, 1, 3,-1,-1,
|
||||
2, 0, 3, 1,-1,-1};
|
||||
|
||||
/**
|
||||
* Rotate pi/2 * this offset for [side] about y axis before rotating to the side for the rotation indicies to line up
|
||||
*/
|
||||
public static int[] sideRotOffsets = new int[]{0, 2, 2, 0, 1, 3};
|
||||
|
||||
public static int rotateSide(int s, int r)
|
||||
{
|
||||
return sideRotMap[s<<2|r];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse of rotateSide
|
||||
*/
|
||||
public static int rotationTo(int s1, int s2)
|
||||
{
|
||||
if((s1&6) == (s2&6))
|
||||
throw new IllegalArgumentException("Faces "+s1+" and "+s2+" are opposites");
|
||||
return rotSideMap[s1*6+s2];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param player The placing player, used for obtaining the look vector
|
||||
* @param side The side of the block being placed on
|
||||
* @return The rotation for the face == side^1
|
||||
*/
|
||||
public static int getSidedRotation(EntityPlayer player, int side)
|
||||
{
|
||||
Vector3 look = new Vector3(player.getLook(1));
|
||||
double max = 0;
|
||||
int maxr = 0;
|
||||
for(int r = 0; r < 4; r++)
|
||||
{
|
||||
Vector3 axis = Rotation.axes[rotateSide(side^1, r)];
|
||||
double d = look.scalarProject(axis);
|
||||
if(max > d)
|
||||
{
|
||||
max = d;
|
||||
maxr = r;
|
||||
}
|
||||
}
|
||||
return maxr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The rotation quat for side 0 and rotation 0 to side s with rotation r
|
||||
*/
|
||||
public static Transformation sideOrientation(int s, int r)
|
||||
{
|
||||
return quarterRotations[(r+sideRotOffsets[s])%4].with(sideRotations[s]);
|
||||
}
|
||||
|
||||
public double angle;
|
||||
public Vector3 axis;
|
||||
|
||||
private Quat quat;
|
||||
|
||||
public Rotation(double angle, Vector3 axis)
|
||||
{
|
||||
this.angle = angle;
|
||||
this.axis = axis;
|
||||
}
|
||||
|
||||
public Rotation(double angle, double x, double y, double z)
|
||||
{
|
||||
this(angle, new Vector3(x, y, z));
|
||||
}
|
||||
|
||||
public Rotation(Quat quat)
|
||||
{
|
||||
this.quat = quat;
|
||||
|
||||
angle = Math.acos(quat.s)*2;
|
||||
if(angle == 0)
|
||||
{
|
||||
axis = new Vector3(0, 1, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
double sa = Math.sin(angle*0.5);
|
||||
axis = new Vector3(quat.x/sa, quat.y/sa, quat.z/sa);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Vector3 vec)
|
||||
{
|
||||
if(quat == null)
|
||||
quat = Quat.aroundAxis(axis, angle);
|
||||
|
||||
vec.rotate(quat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyN(Vector3 normal)
|
||||
{
|
||||
apply(normal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Matrix4 mat)
|
||||
{
|
||||
mat.rotate(angle, axis);
|
||||
}
|
||||
|
||||
public Quat toQuat()
|
||||
{
|
||||
if(quat == null)
|
||||
quat = Quat.aroundAxis(axis, angle);
|
||||
return quat;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void glApply()
|
||||
{
|
||||
GL11.glRotatef((float)(angle*MathHelper.todeg), (float)axis.x, (float)axis.y, (float)axis.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transformation inverse()
|
||||
{
|
||||
return new Rotation(-angle, axis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
MathContext cont = new MathContext(4, RoundingMode.HALF_UP);
|
||||
return "Rotation("+new BigDecimal(angle, cont)+", "+new BigDecimal(axis.x, cont)+", "+
|
||||
new BigDecimal(axis.y, cont)+", "+new BigDecimal(axis.z, cont)+")";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public class Scale extends Transformation
|
||||
{
|
||||
private Vector3 factor;
|
||||
|
||||
public Scale(Vector3 factor)
|
||||
{
|
||||
this.factor = factor;
|
||||
}
|
||||
|
||||
public Scale(double factor)
|
||||
{
|
||||
this(new Vector3(factor, factor, factor));
|
||||
}
|
||||
|
||||
public Scale(double x, double y, double z)
|
||||
{
|
||||
this(new Vector3(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Vector3 vec)
|
||||
{
|
||||
vec.multiply(factor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyN(Vector3 normal)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Matrix4 mat)
|
||||
{
|
||||
mat.scale(factor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void glApply()
|
||||
{
|
||||
GL11.glScaled(factor.x, factor.y, factor.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transformation inverse()
|
||||
{
|
||||
return new Scale(1/factor.x, 1/factor.y, 1/factor.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
MathContext cont = new MathContext(4, RoundingMode.HALF_UP);
|
||||
return "Scale("+new BigDecimal(factor.x, cont)+", "+new BigDecimal(factor.y, cont)+", "+new BigDecimal(factor.z, cont)+")";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
public class SwapYZ extends VariableTransformation
|
||||
{
|
||||
public SwapYZ()
|
||||
{
|
||||
super(new Matrix4(
|
||||
1, 0, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 0, 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Vector3 vec)
|
||||
{
|
||||
double vz = vec.z;
|
||||
vec.z = vec.y;
|
||||
vec.y = vz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transformation inverse()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
/**
|
||||
* Interface for any 3D vector transformation
|
||||
*/
|
||||
public abstract class Transformation
|
||||
{
|
||||
/**
|
||||
* Applies this transformation to a position vector
|
||||
* @param vec The vector to transform
|
||||
*/
|
||||
public abstract void apply(Vector3 vec);
|
||||
|
||||
/**
|
||||
* Applies this transformation to a normal (doesn't translate)
|
||||
* @param normal The normal to transform
|
||||
*/
|
||||
public abstract void applyN(Vector3 normal);
|
||||
|
||||
/**
|
||||
* Applies this transformation to a matrix as a multiplication on the right hand side.
|
||||
* @param mat The matrix to combine this transformation with
|
||||
*/
|
||||
public abstract void apply(Matrix4 mat);
|
||||
|
||||
/**
|
||||
* @param point The point in OBJECT space to apply this transformation around
|
||||
* @return Wraps this transformation in a translation to point and then back from point
|
||||
*/
|
||||
public Transformation at(Vector3 point)
|
||||
{
|
||||
return new TranslatedTransformation(this, point);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a transformation list composed of this transformation followed by t
|
||||
* If this is a TransformationList, the transformation will be appended and this returned
|
||||
*/
|
||||
public TransformationList with(Transformation t)
|
||||
{
|
||||
return new TransformationList(this, t);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public abstract void glApply();
|
||||
|
||||
public abstract Transformation inverse();
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public class TransformationList extends Transformation
|
||||
{
|
||||
private ArrayList<Transformation> transformations = new ArrayList<Transformation>();
|
||||
private Matrix4 mat;
|
||||
|
||||
public TransformationList(Transformation... transforms)
|
||||
{
|
||||
for(Transformation t : transforms)
|
||||
with(t);
|
||||
}
|
||||
|
||||
public Matrix4 compile()
|
||||
{
|
||||
if(mat == null)
|
||||
{
|
||||
mat = new Matrix4();
|
||||
for(int i = transformations.size()-1; i >= 0; i--)
|
||||
transformations.get(i).apply(mat);
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a global space matrix as opposed to an object space matrix (reverse application order)
|
||||
* @return
|
||||
*/
|
||||
public Matrix4 reverseCompile()
|
||||
{
|
||||
Matrix4 mat = new Matrix4();
|
||||
for(Transformation t : transformations)
|
||||
t.apply(mat);
|
||||
return mat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Vector3 vec)
|
||||
{
|
||||
if(mat != null)
|
||||
mat.apply(vec);
|
||||
else
|
||||
for(Transformation t : transformations)
|
||||
t.apply(vec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyN(Vector3 normal)
|
||||
{
|
||||
if(mat != null)
|
||||
mat.applyN(normal);
|
||||
else
|
||||
for(Transformation t : transformations)
|
||||
t.applyN(normal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Matrix4 mat)
|
||||
{
|
||||
mat.multiply(compile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransformationList with(Transformation t)
|
||||
{
|
||||
mat = null;//matrix invalid
|
||||
transformations.add(t);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void glApply()
|
||||
{
|
||||
for(int i = transformations.size()-1; i >= 0; i--)
|
||||
transformations.get(i).glApply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transformation inverse()
|
||||
{
|
||||
TransformationList rev = new TransformationList();
|
||||
for(int i = transformations.size()-1; i >= 0; i--)
|
||||
rev.with(transformations.get(i).inverse());
|
||||
return rev;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
String s = "";
|
||||
for(Transformation t : transformations)
|
||||
s+="\n"+t.toString();
|
||||
return s.trim();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public class TranslatedTransformation extends Transformation
|
||||
{
|
||||
public Vector3 point;
|
||||
public Transformation wrapped;
|
||||
|
||||
public TranslatedTransformation(Transformation t, Vector3 vec)
|
||||
{
|
||||
point = vec;
|
||||
wrapped = t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Vector3 vec)
|
||||
{
|
||||
vec.subtract(point).apply(wrapped).add(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyN(Vector3 normal)
|
||||
{
|
||||
wrapped.applyN(normal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Matrix4 mat)
|
||||
{
|
||||
mat.translate(point).apply(wrapped)
|
||||
.translate(new Vector3(-point.x, -point.y, -point.z));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void glApply()
|
||||
{
|
||||
GL11.glTranslated(point.x, point.y, point.z);
|
||||
wrapped.glApply();
|
||||
GL11.glTranslated(-point.x, -point.y, -point.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transformation inverse()
|
||||
{
|
||||
return new TranslatedTransformation(wrapped.inverse(), point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return point.translation()+"\n"+
|
||||
wrapped.toString()+"\n"+
|
||||
new Translation(-point.x, -point.y, -point.z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public class Translation extends Transformation
|
||||
{
|
||||
private Vector3 vec;
|
||||
|
||||
public Translation(Vector3 vec)
|
||||
{
|
||||
this.vec = vec;
|
||||
}
|
||||
|
||||
public Translation(double x, double y, double z)
|
||||
{
|
||||
this(new Vector3(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Vector3 vec)
|
||||
{
|
||||
vec.add(this.vec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyN(Vector3 normal)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Matrix4 mat)
|
||||
{
|
||||
mat.translate(vec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transformation at(Vector3 point)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void glApply()
|
||||
{
|
||||
GL11.glTranslated(vec.x, vec.y, vec.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transformation inverse()
|
||||
{
|
||||
return new Translation(-vec.x, -vec.y, -vec.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
MathContext cont = new MathContext(4, RoundingMode.HALF_UP);
|
||||
return "Translation("+new BigDecimal(vec.x, cont)+", "+new BigDecimal(vec.y, cont)+", "+new BigDecimal(vec.z, cont)+")";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public abstract class VariableTransformation extends Transformation
|
||||
{
|
||||
public Matrix4 mat;
|
||||
|
||||
public VariableTransformation(Matrix4 mat)
|
||||
{
|
||||
this.mat = mat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyN(Vector3 normal)
|
||||
{
|
||||
apply(normal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Matrix4 mat)
|
||||
{
|
||||
mat.multiply(this.mat);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void glApply()
|
||||
{
|
||||
mat.glApply();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,423 @@
|
||||
package codechicken.lib.vec;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
import org.lwjgl.util.vector.Vector3f;
|
||||
import org.lwjgl.util.vector.Vector4f;
|
||||
|
||||
import codechicken.lib.math.MathHelper;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.Vec3;
|
||||
|
||||
public class Vector3
|
||||
{
|
||||
public static Vector3 zero = new Vector3();
|
||||
public static Vector3 one = new Vector3(1, 1, 1);
|
||||
public static Vector3 center = new Vector3(0.5, 0.5, 0.5);
|
||||
|
||||
public double x;
|
||||
public double y;
|
||||
public double z;
|
||||
|
||||
public Vector3()
|
||||
{
|
||||
}
|
||||
|
||||
public Vector3(double d, double d1, double d2)
|
||||
{
|
||||
x = d;
|
||||
y = d1;
|
||||
z = d2;
|
||||
}
|
||||
|
||||
public Vector3(Vector3 vec)
|
||||
{
|
||||
x = vec.x;
|
||||
y = vec.y;
|
||||
z = vec.z;
|
||||
}
|
||||
|
||||
public Vector3(Vec3 vec)
|
||||
{
|
||||
x = vec.xCoord;
|
||||
y = vec.yCoord;
|
||||
z = vec.zCoord;
|
||||
}
|
||||
|
||||
public Vector3 copy()
|
||||
{
|
||||
return new Vector3(this);
|
||||
}
|
||||
|
||||
public static Vector3 fromEntity(Entity e)
|
||||
{
|
||||
return new Vector3(e.posX, e.posY, e.posZ);
|
||||
}
|
||||
|
||||
public static Vector3 fromEntityCenter(Entity e)
|
||||
{
|
||||
return new Vector3(e.posX, e.posY - e.yOffset + e.height/2, e.posZ);
|
||||
}
|
||||
|
||||
public static Vector3 fromTileEntity(TileEntity e)
|
||||
{
|
||||
return new Vector3(e.xCoord, e.yCoord, e.zCoord);
|
||||
}
|
||||
|
||||
public static Vector3 fromTileEntityCenter(TileEntity e)
|
||||
{
|
||||
return new Vector3(e.xCoord + 0.5, e.yCoord + 0.5, e.zCoord + 0.5);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static Vector3 fromVec3D(Vec3 vec)
|
||||
{
|
||||
return new Vector3(vec);
|
||||
}
|
||||
|
||||
public Vector3 set(double d, double d1, double d2)
|
||||
{
|
||||
x = d;
|
||||
y = d1;
|
||||
z = d2;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 set(Vector3 vec)
|
||||
{
|
||||
x = vec.x;
|
||||
y = vec.y;
|
||||
z = vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double dotProduct(Vector3 vec)
|
||||
{
|
||||
double d = vec.x * x + vec.y * y + vec.z * z;
|
||||
|
||||
if(d > 1 && d < 1.00001)
|
||||
d = 1;
|
||||
else if(d < -1 && d > -1.00001)
|
||||
d = -1;
|
||||
return d;
|
||||
}
|
||||
|
||||
public double dotProduct(double d, double d1, double d2)
|
||||
{
|
||||
return d * x + d1 * y + d2 * z;
|
||||
}
|
||||
|
||||
public Vector3 crossProduct(Vector3 vec)
|
||||
{
|
||||
double d = y * vec.z - z * vec.y;
|
||||
double d1 = z * vec.x - x * vec.z;
|
||||
double d2 = x * vec.y - y * vec.x;
|
||||
x = d;
|
||||
y = d1;
|
||||
z = d2;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 add(double d, double d1, double d2)
|
||||
{
|
||||
x += d;
|
||||
y += d1;
|
||||
z += d2;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 add(Vector3 vec)
|
||||
{
|
||||
x += vec.x;
|
||||
y += vec.y;
|
||||
z += vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 add(double d)
|
||||
{
|
||||
return add(d, d, d);
|
||||
}
|
||||
|
||||
public Vector3 sub(Vector3 vec)
|
||||
{
|
||||
return subtract(vec);
|
||||
}
|
||||
|
||||
public Vector3 subtract(Vector3 vec)
|
||||
{
|
||||
x -= vec.x;
|
||||
y -= vec.y;
|
||||
z -= vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 negate(Vector3 vec)
|
||||
{
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 multiply(double d)
|
||||
{
|
||||
x *= d;
|
||||
y *= d;
|
||||
z *= d;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 multiply(Vector3 f)
|
||||
{
|
||||
x *= f.x;
|
||||
y *= f.y;
|
||||
z *= f.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 multiply(double fx, double fy, double fz)
|
||||
{
|
||||
x *= fx;
|
||||
y *= fy;
|
||||
z *= fz;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double mag()
|
||||
{
|
||||
return Math.sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
public double magSquared()
|
||||
{
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
public Vector3 normalize()
|
||||
{
|
||||
double d = mag();
|
||||
if(d != 0)
|
||||
{
|
||||
multiply(1 / d);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
MathContext cont = new MathContext(4, RoundingMode.HALF_UP);
|
||||
return "Vector3("+new BigDecimal(x, cont)+", "+new BigDecimal(y, cont)+", "+new BigDecimal(z, cont)+")";
|
||||
}
|
||||
|
||||
public Vector3 perpendicular()
|
||||
{
|
||||
if(z == 0)
|
||||
return zCrossProduct();
|
||||
return xCrossProduct();
|
||||
}
|
||||
|
||||
public Vector3 xCrossProduct()
|
||||
{
|
||||
double d = z;
|
||||
double d1 = -y;
|
||||
x = 0;
|
||||
y = d;
|
||||
z = d1;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 zCrossProduct()
|
||||
{
|
||||
double d = y;
|
||||
double d1 = -x;
|
||||
x = d;
|
||||
y = d1;
|
||||
z = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 yCrossProduct()
|
||||
{
|
||||
double d = -z;
|
||||
double d1 = x;
|
||||
x = d;
|
||||
y = 0;
|
||||
z = d1;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 rotate(double angle, Vector3 axis){
|
||||
Quat.aroundAxis(axis.copy().normalize(), angle).rotate(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 rotate(Quat rotator)
|
||||
{
|
||||
rotator.rotate(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vec3 toVec3D()
|
||||
{
|
||||
return Vec3.createVectorHelper(x, y, z);
|
||||
}
|
||||
|
||||
public double angle(Vector3 vec)
|
||||
{
|
||||
return Math.acos(copy().normalize().dotProduct(vec.copy().normalize()));
|
||||
}
|
||||
|
||||
public boolean isZero()
|
||||
{
|
||||
return x == 0 && y == 0 && z == 0;
|
||||
}
|
||||
|
||||
public boolean isAxial()
|
||||
{
|
||||
return x == 0 ? (y == 0 || z == 0) : (y == 0 && z == 0);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public Vector3f vector3f()
|
||||
{
|
||||
return new Vector3f((float)x, (float)y, (float)z);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public Vector4f vector4f()
|
||||
{
|
||||
return new Vector4f((float)x, (float)y, (float)z, 1);
|
||||
}
|
||||
|
||||
public Vector3 YZintercept(Vector3 end, double px)
|
||||
{
|
||||
double dx = end.x - x;
|
||||
double dy = end.y - y;
|
||||
double dz = end.z - z;
|
||||
|
||||
if (dx == 0)
|
||||
return null;
|
||||
|
||||
double d = (px - x) / dx;
|
||||
if(MathHelper.between(-1E-5, d, 1E-5))
|
||||
return this;
|
||||
|
||||
if(!MathHelper.between(0, d, 1))
|
||||
return null;
|
||||
|
||||
x = px;
|
||||
y += d*dy;
|
||||
z += d*dz;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 XZintercept(Vector3 end, double py)
|
||||
{
|
||||
double dx = end.x - x;
|
||||
double dy = end.y - y;
|
||||
double dz = end.z - z;
|
||||
|
||||
if (dy == 0)
|
||||
return null;
|
||||
|
||||
double d = (py - y) / dy;
|
||||
if(MathHelper.between(-1E-5, d, 1E-5))
|
||||
return this;
|
||||
|
||||
if(!MathHelper.between(0, d, 1))
|
||||
return null;
|
||||
|
||||
x += d*dx;
|
||||
y = py;
|
||||
z += d*dz;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 XYintercept(Vector3 end, double pz)
|
||||
{
|
||||
double dx = end.x - x;
|
||||
double dy = end.y - y;
|
||||
double dz = end.z - z;
|
||||
|
||||
if (dz == 0)
|
||||
return null;
|
||||
|
||||
double d = (pz - z) / dz;
|
||||
if(MathHelper.between(-1E-5, d, 1E-5))
|
||||
return this;
|
||||
|
||||
if(!MathHelper.between(0, d, 1))
|
||||
return null;
|
||||
|
||||
x += d*dx;
|
||||
y += d*dy;
|
||||
z = pz;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 negate()
|
||||
{
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Translation translation()
|
||||
{
|
||||
return new Translation(this);
|
||||
}
|
||||
|
||||
public double scalarProject(Vector3 b)
|
||||
{
|
||||
double l = b.mag();
|
||||
return l == 0 ? 0 : dotProduct(b)/l;
|
||||
}
|
||||
|
||||
public Vector3 project(Vector3 b)
|
||||
{
|
||||
double l = b.magSquared();
|
||||
if(l == 0)
|
||||
{
|
||||
set(0, 0, 0);
|
||||
return this;
|
||||
}
|
||||
double m = dotProduct(b)/l;
|
||||
set(b).multiply(m);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if(!(o instanceof Vector3))
|
||||
return false;
|
||||
Vector3 v = (Vector3)o;
|
||||
return x == v.x && y == v.y && z == v.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Equals method with tolerance
|
||||
* @return true if this is equal to v within +-1E-5
|
||||
*/
|
||||
public boolean equalsT(Vector3 v)
|
||||
{
|
||||
return MathHelper.between(x-1E-5, v.x, x+1E-5) &&
|
||||
MathHelper.between(y-1E-5, v.y, y+1E-5) &&
|
||||
MathHelper.between(z-1E-5, v.z, z+1E-5);
|
||||
}
|
||||
|
||||
public Vector3 apply(Transformation t)
|
||||
{
|
||||
t.apply(this);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package codechicken.lib.world;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.ChunkCoordIntPair;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
|
||||
public abstract class ChunkExtension
|
||||
{
|
||||
public final Chunk chunk;
|
||||
public final ChunkCoordIntPair coord;
|
||||
public final WorldExtension world;
|
||||
public HashSet<EntityPlayerMP> watchedPlayers;
|
||||
|
||||
public ChunkExtension(Chunk chunk, WorldExtension world)
|
||||
{
|
||||
this.chunk = chunk;
|
||||
coord = chunk.getChunkCoordIntPair();
|
||||
this.world = world;
|
||||
watchedPlayers = new HashSet<EntityPlayerMP>();
|
||||
}
|
||||
|
||||
public void loadData(NBTTagCompound tag)
|
||||
{
|
||||
}
|
||||
|
||||
public void saveData(NBTTagCompound tag)
|
||||
{
|
||||
}
|
||||
|
||||
public void load()
|
||||
{
|
||||
}
|
||||
|
||||
public void unload()
|
||||
{
|
||||
}
|
||||
|
||||
public final void sendPacketToPlayers(Packet packet)
|
||||
{
|
||||
for(EntityPlayerMP player : watchedPlayers)
|
||||
player.playerNetServerHandler.sendPacketToPlayer(packet);
|
||||
}
|
||||
|
||||
public final void watchPlayer(EntityPlayerMP player)
|
||||
{
|
||||
watchedPlayers.add(player);
|
||||
onWatchPlayer(player);
|
||||
}
|
||||
|
||||
public void onWatchPlayer(EntityPlayerMP player)
|
||||
{
|
||||
}
|
||||
|
||||
public final void unwatchPlayer(EntityPlayerMP player)
|
||||
{
|
||||
watchedPlayers.remove(player);
|
||||
onUnWatchPlayer(player);
|
||||
}
|
||||
|
||||
public void onUnWatchPlayer(EntityPlayerMP player)
|
||||
{
|
||||
}
|
||||
|
||||
public void sendUpdatePackets()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return coord.chunkXPos ^ coord.chunkZPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
return (o instanceof ChunkExtension && ((ChunkExtension)o).coord.equals(coord)) ||
|
||||
(o instanceof ChunkCoordIntPair && coord.equals(o)) ||
|
||||
(o instanceof Long && (Long)o == (((long)coord.chunkXPos)<<32 | coord.chunkZPos));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package codechicken.lib.world;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public abstract class WorldExtension
|
||||
{
|
||||
public final World world;
|
||||
public HashMap<Chunk, ChunkExtension> chunkMap = new HashMap<Chunk, ChunkExtension>();
|
||||
|
||||
public WorldExtension(World world)
|
||||
{
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public void load()
|
||||
{
|
||||
}
|
||||
|
||||
public void unload()
|
||||
{
|
||||
}
|
||||
|
||||
public void save()
|
||||
{
|
||||
}
|
||||
|
||||
public void preTick()
|
||||
{
|
||||
}
|
||||
|
||||
public void postTick()
|
||||
{
|
||||
}
|
||||
|
||||
protected final void addChunk(ChunkExtension extension)
|
||||
{
|
||||
chunkMap.put(extension.chunk, extension);
|
||||
}
|
||||
|
||||
protected final void loadChunk(Chunk chunk)
|
||||
{
|
||||
chunkMap.get(chunk).load();
|
||||
}
|
||||
|
||||
protected final void unloadChunk(Chunk chunk)
|
||||
{
|
||||
chunkMap.get(chunk).unload();
|
||||
}
|
||||
|
||||
protected final void loadChunkData(Chunk chunk, NBTTagCompound tag)
|
||||
{
|
||||
chunkMap.get(chunk).loadData(tag);
|
||||
}
|
||||
|
||||
protected final void saveChunkData(Chunk chunk, NBTTagCompound tag)
|
||||
{
|
||||
chunkMap.get(chunk).saveData(tag);
|
||||
}
|
||||
|
||||
protected final void remChunk(Chunk chunk)
|
||||
{
|
||||
chunkMap.remove(chunk);
|
||||
}
|
||||
|
||||
protected final void watchChunk(Chunk chunk, EntityPlayerMP player)
|
||||
{
|
||||
chunkMap.get(chunk).watchPlayer(player);
|
||||
}
|
||||
|
||||
protected final void unwatchChunk(Chunk chunk, EntityPlayerMP player)
|
||||
{
|
||||
ChunkExtension extension = chunkMap.get(chunk);
|
||||
if(extension != null)
|
||||
extension.unwatchPlayer(player);
|
||||
}
|
||||
|
||||
protected final void sendChunkUpdates(Chunk chunk)
|
||||
{
|
||||
chunkMap.get(chunk).sendUpdatePackets();
|
||||
}
|
||||
|
||||
public boolean containsChunk(Chunk chunk)
|
||||
{
|
||||
return chunkMap.containsKey(chunk);
|
||||
}
|
||||
|
||||
public ChunkExtension getChunkExtension(int chunkXPos, int chunkZPos)
|
||||
{
|
||||
if(!world.blockExists(chunkXPos<<4, 128, chunkZPos<<4))
|
||||
return null;
|
||||
|
||||
return chunkMap.get(world.getChunkFromChunkCoords(chunkXPos, chunkZPos));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package codechicken.lib.world;
|
||||
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public abstract class WorldExtensionInstantiator
|
||||
{
|
||||
public int instantiatorID;
|
||||
|
||||
public abstract WorldExtension createWorldExtension(World world);
|
||||
public abstract ChunkExtension createChunkExtension(Chunk chunk, WorldExtension world);
|
||||
|
||||
public WorldExtension getExtension(World world)
|
||||
{
|
||||
return WorldExtensionManager.getWorldExtension(world, instantiatorID);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package codechicken.lib.world;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.ITickHandler;
|
||||
import cpw.mods.fml.common.TickType;
|
||||
import cpw.mods.fml.common.registry.TickRegistry;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.EmptyChunk;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.ForgeSubscribe;
|
||||
import net.minecraftforge.event.world.ChunkDataEvent;
|
||||
import net.minecraftforge.event.world.ChunkEvent;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import net.minecraftforge.event.world.ChunkWatchEvent.UnWatch;
|
||||
import net.minecraftforge.event.world.ChunkWatchEvent.Watch;
|
||||
|
||||
public class WorldExtensionManager
|
||||
{
|
||||
public static class WorldExtensionEventHandler
|
||||
{
|
||||
@ForgeSubscribe
|
||||
public void onChunkDataLoad(ChunkDataEvent.Load event)
|
||||
{
|
||||
if(!worldMap.containsKey(event.world))
|
||||
WorldExtensionManager.onWorldLoad(event.world);
|
||||
|
||||
createChunkExtension(event.world, event.getChunk());
|
||||
|
||||
for(WorldExtension extension : worldMap.get(event.world))
|
||||
extension.loadChunkData(event.getChunk(), event.getData());
|
||||
}
|
||||
|
||||
@ForgeSubscribe
|
||||
public void onChunkDataSave(ChunkDataEvent.Save event)
|
||||
{
|
||||
for(WorldExtension extension : worldMap.get(event.world))
|
||||
extension.saveChunkData(event.getChunk(), event.getData());
|
||||
|
||||
if(!event.getChunk().isChunkLoaded)
|
||||
removeChunk(event.world, event.getChunk());
|
||||
}
|
||||
|
||||
@ForgeSubscribe
|
||||
public void onChunkLoad(ChunkEvent.Load event)
|
||||
{
|
||||
if(!worldMap.containsKey(event.world))
|
||||
WorldExtensionManager.onWorldLoad(event.world);
|
||||
|
||||
createChunkExtension(event.world, event.getChunk());
|
||||
|
||||
for(WorldExtension extension : worldMap.get(event.world))
|
||||
extension.loadChunk(event.getChunk());
|
||||
}
|
||||
|
||||
@ForgeSubscribe
|
||||
public void onChunkUnLoad(ChunkEvent.Unload event)
|
||||
{
|
||||
if(event.getChunk() instanceof EmptyChunk)
|
||||
return;
|
||||
|
||||
for(WorldExtension extension : worldMap.get(event.world))
|
||||
extension.unloadChunk(event.getChunk());
|
||||
|
||||
if(event.world.isRemote)
|
||||
removeChunk(event.world, event.getChunk());
|
||||
}
|
||||
|
||||
@ForgeSubscribe
|
||||
public void onWorldSave(WorldEvent.Save event)
|
||||
{
|
||||
for(WorldExtension extension : worldMap.get(event.world))
|
||||
extension.save();
|
||||
}
|
||||
|
||||
@ForgeSubscribe
|
||||
public void onWorldLoad(WorldEvent.Load event)
|
||||
{
|
||||
if(!worldMap.containsKey(event.world))
|
||||
WorldExtensionManager.onWorldLoad(event.world);
|
||||
}
|
||||
|
||||
@ForgeSubscribe
|
||||
public void onWorldUnLoad(WorldEvent.Unload event)
|
||||
{
|
||||
WorldExtensionManager.onWorldUnload(event.world);
|
||||
}
|
||||
|
||||
@ForgeSubscribe
|
||||
public void onChunkWatch(Watch event)
|
||||
{
|
||||
Chunk chunk = event.player.worldObj.getChunkFromChunkCoords(event.chunk.chunkXPos, event.chunk.chunkZPos);
|
||||
for(WorldExtension extension : worldMap.get(event.player.worldObj))
|
||||
extension.watchChunk(chunk, event.player);
|
||||
}
|
||||
|
||||
@ForgeSubscribe
|
||||
public void onChunkUnWatch(UnWatch event)
|
||||
{
|
||||
Chunk chunk = event.player.worldObj.getChunkFromChunkCoords(event.chunk.chunkXPos, event.chunk.chunkZPos);
|
||||
for(WorldExtension extension : worldMap.get(event.player.worldObj))
|
||||
extension.unwatchChunk(chunk, event.player);
|
||||
}
|
||||
}
|
||||
|
||||
public static class WorldExtensionClientTickHandler implements ITickHandler
|
||||
{
|
||||
@Override
|
||||
public void tickStart(EnumSet<TickType> type, Object... tickData)
|
||||
{
|
||||
if(type.contains(TickType.CLIENT))
|
||||
{
|
||||
World world = Minecraft.getMinecraft().theWorld;
|
||||
if(worldMap.containsKey(world))
|
||||
preTick(world);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tickEnd(EnumSet<TickType> type, Object... tickData)
|
||||
{
|
||||
if(type.contains(TickType.CLIENT))
|
||||
{
|
||||
World world = Minecraft.getMinecraft().theWorld;
|
||||
if(worldMap.containsKey(world))
|
||||
postTick(world);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<TickType> ticks()
|
||||
{
|
||||
return EnumSet.of(TickType.CLIENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel()
|
||||
{
|
||||
return "WorldExtenstions";
|
||||
}
|
||||
}
|
||||
|
||||
public static class WorldExtensionServerTickHandler implements ITickHandler
|
||||
{
|
||||
@Override
|
||||
public void tickStart(EnumSet<TickType> type, Object... tickData)
|
||||
{
|
||||
if(type.contains(TickType.WORLD))
|
||||
{
|
||||
preTick((World)tickData[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tickEnd(EnumSet<TickType> type, Object... tickData)
|
||||
{
|
||||
if(type.contains(TickType.WORLD))
|
||||
{
|
||||
postTick((World)tickData[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<TickType> ticks()
|
||||
{
|
||||
return EnumSet.of(TickType.WORLD, TickType.CLIENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel()
|
||||
{
|
||||
return "WorldExtenstions";
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean initialised;
|
||||
private static ArrayList<WorldExtensionInstantiator> extensionIntialisers = new ArrayList<WorldExtensionInstantiator>();
|
||||
|
||||
public static void registerWorldExtension(WorldExtensionInstantiator init)
|
||||
{
|
||||
if(!initialised)
|
||||
init();
|
||||
|
||||
init.instantiatorID = extensionIntialisers.size();
|
||||
extensionIntialisers.add(init);
|
||||
}
|
||||
|
||||
private static void init()
|
||||
{
|
||||
initialised = true;
|
||||
MinecraftForge.EVENT_BUS.register(new WorldExtensionEventHandler());
|
||||
TickRegistry.registerTickHandler(new WorldExtensionServerTickHandler(), Side.SERVER);
|
||||
if(FMLCommonHandler.instance().getSide().isClient())
|
||||
{
|
||||
TickRegistry.registerTickHandler(new WorldExtensionClientTickHandler(), Side.CLIENT);
|
||||
}
|
||||
}
|
||||
|
||||
private static HashMap<World, WorldExtension[]> worldMap = new HashMap<World, WorldExtension[]>();
|
||||
|
||||
private static void onWorldLoad(World world)
|
||||
{
|
||||
WorldExtension[] extensions = new WorldExtension[extensionIntialisers.size()];
|
||||
for(int i = 0; i < extensions.length; i++)
|
||||
extensions[i] = extensionIntialisers.get(i).createWorldExtension(world);
|
||||
|
||||
worldMap.put(world, extensions);
|
||||
|
||||
for(WorldExtension extension : extensions)
|
||||
extension.load();
|
||||
}
|
||||
|
||||
public static void onWorldUnload(World world)
|
||||
{
|
||||
for(WorldExtension extension : worldMap.remove(world))
|
||||
extension.unload();
|
||||
}
|
||||
|
||||
private static void createChunkExtension(World world, Chunk chunk)
|
||||
{
|
||||
WorldExtension[] extensions = worldMap.get(world);
|
||||
for(int i = 0; i < extensionIntialisers.size(); i++)
|
||||
if(!extensions[i].containsChunk(chunk))
|
||||
extensions[i].addChunk(extensionIntialisers.get(i).createChunkExtension(chunk, extensions[i]));
|
||||
}
|
||||
|
||||
private static void removeChunk(World world, Chunk chunk)
|
||||
{
|
||||
for(WorldExtension extension : worldMap.get(world))
|
||||
extension.remChunk(chunk);
|
||||
}
|
||||
|
||||
private static void preTick(World world)
|
||||
{
|
||||
for(WorldExtension extension : worldMap.get(world))
|
||||
extension.preTick();
|
||||
}
|
||||
|
||||
private static void postTick(World world)
|
||||
{
|
||||
for(WorldExtension extension : worldMap.get(world))
|
||||
extension.postTick();
|
||||
}
|
||||
|
||||
public static WorldExtension getWorldExtension(World world, int instantiatorID)
|
||||
{
|
||||
return worldMap.get(world)[instantiatorID];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user