-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
24 changed files
with
5,668 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
class Array { | ||
static indexOf<T>(a: T[], what: T): T[] { | ||
let result: T[] = [] | ||
|
||
for (let i = 0; i < a.length; ++i) { | ||
if (a[i] === what) { | ||
result.push(a[i]) | ||
} | ||
} | ||
|
||
return result | ||
} | ||
|
||
static find<T>(a: T[], f: (what: T) => boolean): T | undefined { | ||
for (let i = 0; i < a.length; ++i) { | ||
if (f(a[i])) { | ||
return a[i] | ||
} | ||
} | ||
|
||
return undefined | ||
} | ||
|
||
static filter<T>(a: T[], f: (what: T) => boolean): T[] { | ||
let result: T[] = [] | ||
|
||
for (let i = 0; i < a.length; ++i) { | ||
if (f(a[i])) { | ||
result.push(a[i]) | ||
} | ||
} | ||
|
||
return result | ||
} | ||
} | ||
|
||
export { Array } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
class BitView { | ||
static scratch = new DataView(new ArrayBuffer(8)) | ||
|
||
private view: Uint8Array | ||
|
||
constructor(buffer: ArrayBuffer) { | ||
this.view = new Uint8Array(buffer, 0, buffer.byteLength) | ||
} | ||
|
||
getBits(offset: number, bits: number, signed = false) { | ||
let available = this.view.length * 8 - offset | ||
|
||
if (bits > available) { | ||
throw new Error('Bits out of bounds') | ||
} | ||
|
||
let value = 0 | ||
for (let i = 0; i < bits; ) { | ||
let remaining = bits - i | ||
let bitOffset = offset & 7 | ||
let currentByte = this.view[offset >> 3] | ||
|
||
// the max number of bits we can read from the current byte | ||
let read = Math.min(remaining, 8 - bitOffset) | ||
|
||
// create a mask with the correct bit width | ||
let mask = (1 << read) - 1 | ||
// shift bits we want to the start of the byte and mask of the rest | ||
let readBits = (currentByte >> bitOffset) & mask | ||
value |= readBits << i | ||
|
||
offset += read | ||
i += read | ||
} | ||
|
||
if (signed) { | ||
// If we're not working with a full 32 bits, check the | ||
// imaginary MSB for this bit count and convert to a | ||
// valid 32-bit signed value if set. | ||
if (bits !== 32 && value & (1 << (bits - 1))) { | ||
value |= -1 ^ ((1 << bits) - 1) | ||
} | ||
|
||
return value | ||
} | ||
|
||
return value >>> 0 | ||
} | ||
|
||
getInt8(offset: number) { | ||
return this.getBits(offset, 8, true) | ||
} | ||
|
||
getUint8(offset: number) { | ||
return this.getBits(offset, 8, false) | ||
} | ||
|
||
getInt16(offset: number) { | ||
return this.getBits(offset, 16, true) | ||
} | ||
|
||
getUint16(offset: number) { | ||
return this.getBits(offset, 16, false) | ||
} | ||
|
||
getInt32(offset: number) { | ||
return this.getBits(offset, 32, true) | ||
} | ||
|
||
getUint32(offset: number) { | ||
return this.getBits(offset, 32, false) | ||
} | ||
|
||
getFloat32(offset: number) { | ||
BitView.scratch.setUint32(0, this.getUint32(offset)) | ||
return BitView.scratch.getFloat32(0) | ||
} | ||
|
||
getFloat64(offset: number) { | ||
BitView.scratch.setUint32(0, this.getUint32(offset)) | ||
// DataView offset is in bytes. | ||
BitView.scratch.setUint32(4, this.getUint32(offset + 32)) | ||
return BitView.scratch.getFloat64(0) | ||
} | ||
} | ||
|
||
class BitStream { | ||
private view: BitView | ||
index: number | ||
|
||
constructor(source: ArrayBuffer) { | ||
this.view = new BitView(source) | ||
this.index = 0 | ||
} | ||
|
||
readBits(bits: number, signed = false) { | ||
let val = this.view.getBits(this.index, bits, signed) | ||
this.index += bits | ||
return val | ||
} | ||
|
||
readInt8() { | ||
let val = this.view.getInt8(this.index) | ||
this.index += 8 | ||
return val | ||
} | ||
|
||
readUint8() { | ||
let val = this.view.getUint8(this.index) | ||
this.index += 8 | ||
return val | ||
} | ||
|
||
readInt16() { | ||
let val = this.view.getInt16(this.index) | ||
this.index += 16 | ||
return val | ||
} | ||
|
||
readUint16() { | ||
let val = this.view.getUint16(this.index) | ||
this.index += 16 | ||
return val | ||
} | ||
|
||
readInt32() { | ||
let val = this.view.getInt32(this.index) | ||
this.index += 32 | ||
return val | ||
} | ||
|
||
readUint32() { | ||
let val = this.view.getUint32(this.index) | ||
this.index += 32 | ||
return val | ||
} | ||
|
||
readFloat32() { | ||
let val = this.view.getFloat32(this.index) | ||
this.index += 32 | ||
return val | ||
} | ||
|
||
readFloat64() { | ||
let val = this.view.getFloat64(this.index) | ||
this.index += 64 | ||
return val | ||
} | ||
|
||
readString(bytes = 0, utf8 = false) { | ||
let i = 0 | ||
let chars = [] | ||
let append = true | ||
|
||
// Read while we still have space available, or until we've | ||
// hit the fixed byte length passed in. | ||
while (!bytes || (bytes && i < bytes)) { | ||
let c = this.readUint8() | ||
|
||
// Stop appending chars once we hit 0x00 | ||
if (c === 0x00) { | ||
append = false | ||
|
||
// If we don't have a fixed length to read, break out now. | ||
if (!bytes) { | ||
break | ||
} | ||
} | ||
if (append) { | ||
chars.push(c) | ||
} | ||
|
||
i++ | ||
} | ||
|
||
let string = String.fromCharCode.apply(null, chars) | ||
if (utf8) { | ||
try { | ||
// https://stackoverflow.com/a/17192845 | ||
return decodeURIComponent(string) | ||
} catch (e) { | ||
return string | ||
} | ||
} else { | ||
return string | ||
} | ||
} | ||
} | ||
|
||
export { BitView, BitStream } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
import * as THREE from 'three' | ||
|
||
const isInvisible = (entity: any) => { | ||
const INVISIBLE_ENTITIES = [ | ||
'target_cdaudio', | ||
'trigger_auto', | ||
'trigger_autosave', | ||
'trigger_camera', | ||
'trigger_cdaudio', | ||
'trigger_changelevel', | ||
'trigger_changetarget', | ||
'trigger_counter', | ||
'trigger_endsection', | ||
'trigger_gravity', | ||
'trigger_hurt', | ||
'trigger_monsterjump', | ||
'trigger_multiple', | ||
'trigger_once', | ||
'trigger_push', | ||
'trigger_relay', | ||
'trigger_teleport', | ||
'trigger_transition', | ||
'func_bomb_target', | ||
'func_buyzone', | ||
'func_ladder' | ||
] | ||
|
||
for (let i = 0; i < INVISIBLE_ENTITIES.length; ++i) { | ||
if (INVISIBLE_ENTITIES[i] === entity.classname) { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
class Entities { | ||
list: any[] | ||
|
||
constructor() { | ||
this.list = [] | ||
} | ||
|
||
clear() { | ||
this.list.length = 0 | ||
} | ||
|
||
initialize(entities: any[], resources: any) { | ||
this.clear() | ||
|
||
entities.forEach(e => { | ||
let t = { | ||
meta: e, | ||
model: null | ||
} | ||
|
||
switch (e.classname) { | ||
case 'worldspawn': { | ||
let model = resources.models[0] | ||
let materials = model.material | ||
materials.forEach((m: any, idx: number) => { | ||
if (m.map.name.charAt(0) === '{') { | ||
let data = Uint8Array.from(m.map.image.data) | ||
for (let i = 3; i < data.length; i += 4) { | ||
data[i] = 255 | ||
} | ||
|
||
let w = m.map.image.width | ||
let h = m.map.image.height | ||
|
||
let newTexture = new THREE.DataTexture( | ||
data, | ||
w, | ||
h, | ||
THREE.RGBAFormat, | ||
THREE.UnsignedByteType, | ||
THREE.Texture.DEFAULT_MAPPING, | ||
THREE.RepeatWrapping, | ||
THREE.RepeatWrapping, | ||
THREE.LinearFilter, | ||
THREE.LinearMipMapLinearFilter | ||
) | ||
|
||
newTexture.name = m.map.name | ||
newTexture.premultiplyAlpha = true | ||
newTexture.anisotropy = m.map.anisotropy | ||
newTexture.generateMipmaps = true | ||
newTexture.repeat.y = -1 | ||
newTexture.needsUpdate = true | ||
|
||
let newMaterial = new THREE.MeshLambertMaterial({ | ||
map: newTexture, | ||
transparent: false, | ||
visible: m.visible | ||
}) | ||
|
||
model.material[idx] = newMaterial | ||
} | ||
}) | ||
t.model = model | ||
|
||
break | ||
} | ||
|
||
default: { | ||
if (typeof e.model === 'number' && !isInvisible(e)) { | ||
let model = resources.models[e.model].clone() | ||
model.rotation.order = 'ZXY' | ||
if (e.origin) { | ||
model.position.x = e.origin[0] | ||
model.position.y = e.origin[1] | ||
model.position.z = e.origin[2] | ||
if (e.angles) { | ||
model.rotation.x = (e.angles[0] * Math.PI) / 180 | ||
model.rotation.y = (e.angles[2] * Math.PI) / 180 | ||
model.rotation.z = (e.angles[1] * Math.PI) / 180 | ||
} | ||
} | ||
|
||
if (e.rendermode === 0) { | ||
e.renderamt = 255 | ||
} | ||
|
||
if (e.rendermode !== 4 && e.renderamt < 255) { | ||
let materials = model.material | ||
materials.forEach((m: any) => { | ||
model.renderOrder = 1 | ||
m.depthWrite = false | ||
m.alphaTest = 0.05 | ||
m.opacity = e.renderamt / 255 | ||
}) | ||
} | ||
|
||
if (e.rendermode === 5) { | ||
let materials = model.material | ||
materials.forEach((m: any) => { | ||
model.renderOrder = 1 | ||
m.depthWrite = false | ||
m.blending = THREE.AdditiveBlending | ||
}) | ||
} | ||
|
||
t.model = model | ||
} else if ( | ||
typeof e.model === 'string' && | ||
e.model.indexOf('.spr') > -1 | ||
) { | ||
console.log(e.model) | ||
} | ||
break | ||
} | ||
} | ||
|
||
this.list.push(t) | ||
}) | ||
} | ||
} | ||
|
||
export { Entities } |
Oops, something went wrong.