new sunlight and skybox colouring collected from real world measurements (reports TBA)

Former-commit-id: 6abaa1e16d0fd7a71f95bd0265809aad9e34a425
Former-commit-id: bb871bd87072f27b9cc29594f9eed615351df5f8
This commit is contained in:
Song Minjae
2017-01-17 23:42:25 +09:00
parent e9c7ef4930
commit ea4c4bdb2b
11 changed files with 210 additions and 88 deletions

View File

@@ -49,6 +49,8 @@ class Point2d(var x: Double, var y: Double) : Cloneable {
fun toVector() = Vector2(x, y)
fun copy() = Point2d(x, y)
fun length(other: Point2d) = distSqr(other).sqrt()
fun distSqr(other: Point2d) = ((this.x - other.x).sqr() + (this.y - other.y).sqr())
}

View File

@@ -69,9 +69,6 @@ constructor() : BasicGameState() {
internal val player: ActorHumanoid // currently POSSESSED actor :)
get() = playableActorDelegate!!.actor
//private var GRADIENT_IMAGE: Image? = null
//private var skyBox: Rectangle? = null
var screenZoom = 1.0f
val ZOOM_MAX = 2.0f
val ZOOM_MIN = 0.5f
@@ -99,7 +96,7 @@ constructor() : BasicGameState() {
private val UI_INVENTORY_PLAYER = "uiInventoryPlayer"
private val UI_INVENTORY_ANON = "uiInventoryAnon"
val paused: Boolean
var paused: Boolean = false
get() = consoleHandler.isOpened
@Throws(SlickException::class)
@@ -279,7 +276,11 @@ constructor() : BasicGameState() {
throw IllegalArgumentException("No such actor in actorContainer: $refid")
}
// take care of old delegate
playableActorDelegate!!.actor.collisionType = HumanoidNPC.DEFAULT_COLLISION_TYPE
// accept new delegate
playableActorDelegate = PlayableActorDelegate(getActorByID(refid) as ActorHumanoid)
playableActorDelegate!!.actor.collisionType = ActorWithBody.COLLISION_KINEMATIC
WorldSimulator(world, player, UPDATE_DELTA)
}
@@ -298,7 +299,8 @@ constructor() : BasicGameState() {
blendNormal()
drawSkybox(gwin)
drawSkybox(gwin) // drawing to gwin so that any lights from lamp wont "leak" to the skybox
// e.g. Bright blue light on sunset
// make camara work //
@@ -306,6 +308,8 @@ constructor() : BasicGameState() {
worldG.translate(-MapCamera.x.toFloat(), -MapCamera.y.toFloat())
blendNormal()
/////////////////////////////
// draw map related stuffs //
/////////////////////////////

View File

@@ -40,7 +40,7 @@ class StateNoiseTexGen : BasicGameState() {
return Joise(ridged_autocorrect)
}
private fun noiseSmokyFractal(): Joise {
private fun noiseBrownian(): Joise {
val ridged = ModuleFractal()
ridged.setType(ModuleFractal.FractalType.FBM)
ridged.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
@@ -114,7 +114,7 @@ class StateNoiseTexGen : BasicGameState() {
}
fun generateNoiseImage() {
val noiseModule = noiseBillowFractal() // change noise function here
val noiseModule = noiseBrownian() // change noise function here
for (y in 0..imagesize - 1) {
for (x in 0..imagesize - 1) {

View File

@@ -82,8 +82,8 @@ internal object ExportMap : ConsoleCommand {
try {
RasterWriter.writePNG_RGB(
Terrarum.ingame.world.width, Terrarum.ingame.world.height, mapData, dir + args[1] + ".tga")
Echo("ExportMap: exported to " + args[1] + ".tga")
Terrarum.ingame.world.width, Terrarum.ingame.world.height, mapData, dir + args[1] + ".png")
Echo("ExportMap: exported to " + args[1] + ".png")
}
catch (e: IOException) {

View File

@@ -100,7 +100,9 @@ open class ActorWithBody(renderOrder: ActorOrder) : Actor(renderOrder) {
var scale: Double
get() = (actorValue.getAsDouble(AVKey.SCALE) ?: 1.0) *
(actorValue.getAsDouble(AVKey.SCALEBUFF) ?: 1.0)
set(value) = actorValue.set(AVKey.SCALE, value / (actorValue.getAsDouble(AVKey.SCALEBUFF) ?: 1.0))
set(value) {
actorValue[AVKey.SCALE] = value / (actorValue.getAsDouble(AVKey.SCALEBUFF) ?: 1.0)
}
@Transient val MASS_LOWEST = 0.1 // Kilograms
/** Apparent mass. Use "avBaseMass" for base mass */
var mass: Double
@@ -284,6 +286,10 @@ open class ActorWithBody(renderOrder: ActorOrder) : Actor(renderOrder) {
hitboxTranslateY = ty.toDouble()
}
fun setPosition(pos: Point2d) = setPosition(pos.x, pos.y)
fun setPosition(pos: Vector2) = setPosition(pos.x, pos.y)
/**
* Set hitbox position from bottom-center point
* @param x
@@ -291,14 +297,14 @@ open class ActorWithBody(renderOrder: ActorOrder) : Actor(renderOrder) {
*/
fun setPosition(x: Double, y: Double) {
hitbox.setFromWidthHeight(
x - (baseHitboxW / 2 - hitboxTranslateX) * scale,
y - (baseHitboxH - hitboxTranslateY) * scale,
x - (baseHitboxW / 2 - hitboxTranslateX) * (1 - scale),
y - (baseHitboxH - hitboxTranslateY) * (1 - scale),
baseHitboxW * scale,
baseHitboxH * scale)
nextHitbox.setFromWidthHeight(
x - (baseHitboxW / 2 - hitboxTranslateX) * scale,
y - (baseHitboxH - hitboxTranslateY) * scale,
x - (baseHitboxW / 2 - hitboxTranslateX) * (1 - scale),
y - (baseHitboxH - hitboxTranslateY) * (1 - scale),
baseHitboxW * scale,
baseHitboxH * scale)
}
@@ -1113,9 +1119,12 @@ open class ActorWithBody(renderOrder: ActorOrder) : Actor(renderOrder) {
/**
* Enumerations that exported to JSON
*/
@Transient const val COLLISION_KINEMATIC = 1 // does not displaced by external forces
@Transient const val COLLISION_NOCOLLIDE = 0
@Transient const val COLLISION_KINEMATIC = 1 // does not displaced by external forces when collided, but it still can move (e.g. player, elevator)
@Transient const val COLLISION_DYNAMIC = 2 // displaced by external forces
@Transient const val COLLISION_STATIC = 3 // does not displaced by external forces, target of collision
@Transient const val COLLISION_STATIC = 3 // does not displaced by external forces, target of collision (e.g. nonmoving static obj)
@Transient const val COLLISION_KNOCKBACK_GIVER = 4 // mobs
@Transient const val COLLISION_KNOCKBACK_TAKER = 5 // benevolent NPCs
@Transient const val BLEND_NORMAL = 4
@Transient const val BLEND_SCREEN = 5
@Transient const val BLEND_MULTIPLY = 6

View File

@@ -30,7 +30,13 @@ open class HumanoidNPC(override val scriptPath: String, born: GameDate) : ActorH
private val aiLuaAPI: AILuaAPI
companion object {
val DEFAULT_COLLISION_TYPE = ActorWithBody.COLLISION_DYNAMIC
}
init {
collisionType = DEFAULT_COLLISION_TYPE
luag["io"] = LuaValue.NIL
luag["os"] = LuaValue.NIL
luag["luajava"] = LuaValue.NIL

View File

@@ -58,7 +58,7 @@ object PlayerBuilderSigrid {
p.actorValue[AVKey.INTELLIGENT] = true
p.actorValue[AVKey.LUMINOSITY] = Color(0x434aff).to10bit()
//p.actorValue[AVKey.LUMINOSITY] = Color(0x434aff).to10bit()
p.actorValue[AVKey.BASEDEFENCE] = 141

View File

@@ -5,6 +5,7 @@ import net.torvald.terrarum.gameactors.AIControlled
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.mapdrawer.LightmapRenderer
import net.torvald.terrarum.tileproperties.Tile
import net.torvald.terrarum.tileproperties.TileCodex
import org.luaj.vm2.*
import org.luaj.vm2.lib.OneArgFunction
@@ -39,6 +40,8 @@ internal class AILuaAPI(g: Globals, actor: ActorWithBody) {
g["ai"]["jump"] = Jump(actor)
g["ai"]["getNearbyTiles"] = GetNearbyTiles(actor)
g["ai"]["getFloorsHeight"] = GetFloorsHeight(actor)
g["ai"]["getCeilingsHeight"] = GetCeilingsHeight(actor)
g["game"] = LuaValue.tableOf()
g["game"]["version"] = GameVersion()
@@ -87,6 +90,8 @@ internal class AILuaAPI(g: Globals, actor: ActorWithBody) {
fun Int?.toLua() = if (this == null) LuaValue.NIL else this.toLua()
fun String?.toLua() = if (this == null) LuaValue.NIL else this.toLua()
fun Boolean.toInt() = if (this) 1 else 0
operator fun LuaTable.set(index: Int, value: Int) { this[index] = value.toLua() }
}
class GetSelfActorInfo(val actor: ActorWithBody) : ZeroArgFunction() {
@@ -215,19 +220,18 @@ internal class AILuaAPI(g: Globals, actor: ActorWithBody) {
}
class GetNearbyTiles(val actor: ActorWithBody) : OneArgFunction() {
/** @param radius: lookahead
/** @param radius
*
* 3 will return 7x7 array, 0 will return 1x1, 1 will return 3x3
*
* Index: [-3: y][-3: x] ... [0: y][0: x] ... [3: y][3: x]
* Index: [-3: y][-3: x] ... [0: y][0: x] ... [3: y][3: x] for radius 3
* Return value: bitset (int 0-7)
* 1 -- solidity
* 2 -- liquidity
* 3 -- gravity
*/
override fun call(radius: LuaValue): LuaValue {
val radius = radius.checkint()
override fun call(arg: LuaValue): LuaValue {
val radius = arg.checkint()
if (radius < 0) {
return LuaValue.NONE
@@ -248,8 +252,94 @@ internal class AILuaAPI(g: Globals, actor: ActorWithBody) {
val gravity = tile.isFallable.toInt()
val tileFlag: Int = gravity.shl(2) + liquidity.shl(1) + solidity
luatable[y - feetTilePos[1]][x - feetTilePos[0]] =
LuaInteger.valueOf(tileFlag)
luatable[y - feetTilePos[1]][x - feetTilePos[0]] = tileFlag.toLua()
}
}
return luatable
}
}
}
class GetFloorsHeight(val actor: ActorWithBody) : OneArgFunction() {
/** @param radius
*
* 3 will return len:7 array, 0 will return len:1, 1 will return len:3
*
* Index: [-3] .. [0] .. [3] for radius
* Return value: floor height
* 0: body tile (legs area)
* 1: tile you can stand on
* 2+: tiles down there
*/
override fun call(arg: LuaValue): LuaValue {
val radius = arg.checkint()
val searchDownLimit = 12
if (radius < 0) {
return LuaValue.NONE
}
else if (radius > 8) {
throw IllegalArgumentException("Radius too large -- must be 8 or less")
}
else {
val luatable = LuaTable()
val feetTilePos = actor.feetPosTile
for (x in feetTilePos[0] - radius..feetTilePos[0] + radius) {
// search down
var searchDownCounter = 0
while (true) {
val tile = Terrarum.ingame.world.getTileFromTerrain(x, feetTilePos[1] + searchDownCounter) ?: Tile.STONE
if (TileCodex[tile].isSolid || searchDownCounter >= searchDownLimit) {
luatable[x - feetTilePos[0]] = searchDownCounter
break
}
searchDownCounter++
}
}
return luatable
}
}
}
class GetCeilingsHeight(val actor: ActorWithBody) : OneArgFunction() {
/** @param radius
*
* 3 will return 7x7 array, 0 will return 1x1, 1 will return 3x3
*
* Index: [-3] .. [0] .. [3] for radius
* Return value: floor height
* (-1): tile you can stand on
* 0: body tile (legs area)
* 1: body tile (may be vary depend on the size of the actor)
* 2+: tiles up there
*/
override fun call(arg: LuaValue): LuaValue {
val radius = arg.checkint()
val searchUpLimit = 12
if (radius < 0) {
return LuaValue.NONE
}
else if (radius > 8) {
throw IllegalArgumentException("Radius too large -- must be 8 or less")
}
else {
val luatable = LuaTable()
val feetTilePos = actor.feetPosTile
for (x in feetTilePos[0] - radius..feetTilePos[0] + radius) {
// search up
var searchUpCounter = 0
while (true) {
val tile = Terrarum.ingame.world.getTileFromTerrain(x, feetTilePos[1] - searchUpCounter) ?: Tile.STONE
if (TileCodex[tile].isSolid || searchUpCounter >= searchUpLimit) {
luatable[x - feetTilePos[0]] = searchUpCounter
break
}
searchUpCounter++
}
}

View File

@@ -49,22 +49,26 @@ object CollisionSolver {
collListX.sortBy { it.pos }
// set candidateX
for (it in collListX) {
collListX.forEach {
if (it.kind == STARTPOINT) {
collCandidateStack.push(it)
}
else if (it.kind == ENDPOINT) {
val mark_this = it
val mark_other = collCandidateStack.pop()
val collCandidate: Pair<ActorWithBody, ActorWithBody>
if (mark_this < mark_other) // make sure actor with lower pos comes left
collCandidate = Pair(mark_this.actor, mark_other.actor)
// make sure actor with lower ID comes first
val collCandidate = if (mark_this.actor < mark_other.actor)
Pair(mark_this.actor, mark_other.actor)
else
collCandidate = Pair(mark_other.actor, mark_this.actor)
Pair(mark_other.actor, mark_this.actor)
collCandidateX.add(collCandidate)
// filter out Pair(E, E); Pair(A, B) if Pair(B, A) exists
if (mark_this.actor != mark_other.actor) {
collCandidateX.add(collCandidate)
}
}
}
collCandidateStack.clear()
// mark list y
@@ -78,7 +82,7 @@ object CollisionSolver {
collListY.sortBy { it.pos }
// set candidateY
for (it in collListY) {
collListY.forEach {
if (it.kind == STARTPOINT) {
collCandidateStack.push(it)
}
@@ -86,12 +90,16 @@ object CollisionSolver {
val mark_this = it
val mark_other = collCandidateStack.pop()
val collCandidate: Pair<ActorWithBody, ActorWithBody>
if (mark_this < mark_other) // make sure actor with lower pos comes left
// make sure actor with lower ID comes first
if (mark_this.actor < mark_other.actor)
collCandidate = Pair(mark_this.actor, mark_other.actor)
else
collCandidate = Pair(mark_other.actor, mark_this.actor)
collCandidateY.add(collCandidate)
// filter out Pair(E, E); Pair(A, B) if Pair(B, A) exists
if (mark_this.actor != mark_other.actor) {
collCandidateY.add(collCandidate)
}
}
}
// look for overlaps in candidate X/Y and put them into collCandidates
@@ -99,10 +107,36 @@ object CollisionSolver {
collCandidateY.retainAll(collCandidateX) // list Y will have intersection of X and Y now
collCandidates = collCandidateY // renaming. X and Y won't be used anyway.
//collCandidates.forEach { println(it) }
//println("-----------------------")
// solve collision for actors in collCandidates
collCandidates.forEach { solveCollision(it.first, it.second) }
}
private fun pairEqv(a: Pair<Any?, Any?>, b: Pair<Any?, Any?>) =
(a.first == b.first && a.second == b.second) ||
(a.first == b.second && a.second == b.first)
/** Mimics java's original behaviour, with user-defined equals function */
fun ArrayList<Any?>.containsByFunc(other: Any?, equalsFun: (a: Any?, b: Any?) -> Boolean): Boolean {
fun indexOfEqFn(arrayList: ArrayList<Any?>, o: Any?): Int {
if (o == null) {
for (i in 0..size - 1)
if (arrayList[i] == null)
return i
}
else {
for (i in 0..size - 1)
if (equalsFun(o, arrayList[i]))
return i
}
return -1
}
return indexOfEqFn(this, other) >= 0
}
private fun solveCollision(a: ActorWithBody, b: ActorWithBody) {
// some of the Pair(a, b) are either duplicates or erroneously reported.
// e.g. (A, B), (B, C) and then (A, C);
@@ -110,12 +144,12 @@ object CollisionSolver {
// we are going to filter them
if (a isCollidingWith b) {
// notify collision, but not solve it yet
// (e.g. player vs mob, will pass by but still takes damage)
//println("Collision: $a <-> $b")
// FIXME does work but has duplication
// if they actually makes collision (e.g. player vs ball), solve it
if (a makesCollisionWith b) {
// assuming perfect elastic collision; ignoring 'var elasticity'
val ux_1 = a.veloX
val ux_2 = b.veloX
val uy_1 = a.veloY
@@ -136,10 +170,10 @@ object CollisionSolver {
}
}
private infix fun ActorWithBody.makesCollisionWith(other: ActorWithBody): Boolean {
return true
}
private infix fun ActorWithBody.makesCollisionWith(other: ActorWithBody) =
this.collisionType != ActorWithBody.COLLISION_NOCOLLIDE &&
other.collisionType != ActorWithBody.COLLISION_NOCOLLIDE
private infix fun ActorWithBody.isCollidingWith(other: ActorWithBody): Boolean {
val ax = this.hitbox.centeredX
val ay = this.hitbox.centeredY
@@ -168,16 +202,11 @@ object CollisionSolver {
fun Double.abs() = if (this < 0) -this else this
fun Double.sqr() = this * this
class CollisionMarkings(
data class CollisionMarkings(
val pos: Double,
val kind: Int,
val actor: ActorWithBody
) : Comparable<CollisionMarkings> {
override fun compareTo(other: CollisionMarkings): Int =
if (this.pos > other.pos) 1
else if (this.pos < other.pos) -1
else 0
}
)
/**
* === Some useful physics knowledge ===

View File

@@ -700,45 +700,21 @@ object WorldGenerator {
)
}
ThreadParallel.startAll()
// FIXME game starts prematurely
/* Console:
[mapgenerator] Seed: 85336530
[mapgenerator] Raising and eroding terrain...
[mapgenerator] Shaping world...
[mapgenerator] Carving caves...
[mapgenerator] Carving caves...
[mapgenerator] Carving caves...
[mapgenerator] Flooding bottom lava...
[mapgenerator] Carving caves...
[mapgenerator] Planting grass...
[mapgenerator] Placing floating islands...
[UIHandler] Creating UI 'ConsoleWindow'
Mon Jun 13 00:43:57 KST 2016 INFO:Offscreen Buffers FBO=true PBUFFER=true PBUFFERRT=false
Mon Jun 13 00:43:57 KST 2016 DEBUG:Creating FBO 2048x256
[UIHandler] Creating UI 'BasicDebugInfoWindow'
Mon Jun 13 00:43:57 KST 2016 INFO:Offscreen Buffers FBO=true PBUFFER=true PBUFFERRT=false
Mon Jun 13 00:43:57 KST 2016 DEBUG:Creating FBO 2048x1024
[UIHandler] Creating UI 'Notification'
Mon Jun 13 00:43:57 KST 2016 INFO:Offscreen Buffers FBO=true PBUFFER=true PBUFFERRT=false
Mon Jun 13 00:43:57 KST 2016 DEBUG:Creating FBO 512x64
[mapgenerator] Collapsing caves...
[mapgenerator] Collapsing caves...
[mapgenerator] Collapsing caves...
[mapgenerator] Collapsing caves...
*/
ThreadParallel.startAllWaitForDie()
}
else {
ThreadProcessNoiseLayers(0, HEIGHT - 1, noiseRecords).run()
}
}
private val islandSpacing = 1024
private fun generateFloatingIslands() {
println("[mapgenerator] Placing floating islands...")
val nIslandsMax = Math.round(world.width * 6f / 8192f)
val nIslandsMin = Math.max(2, Math.round(world.width * 4f / 8192f))
val nIslands = random.nextInt(nIslandsMax - nIslandsMin) + nIslandsMin
val nIslands = random.nextInt(Math.max(1, nIslandsMax - nIslandsMin)) + nIslandsMin
val prevIndex = -1
val tiles = intArrayOf(Tile.AIR, Tile.STONE, Tile.DIRT, Tile.GRASS)
@@ -750,7 +726,7 @@ object WorldGenerator {
}
val island = FloatingIslandsPreset.generatePreset(currentIndex, random)
val startingPosX = random.nextInt(world.width - 2048) + 1024
val startingPosX = random.nextInt(islandSpacing) + islandSpacing * i
val startingPosY = minimumFloatingIsleHeight + random.nextInt(minimumFloatingIsleHeight)
for (j in island.indices) {

View File

@@ -7,6 +7,8 @@ import net.torvald.colourutil.CIEXYZUtil
import net.torvald.colourutil.ColourUtil
import net.torvald.random.HQRNG
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.blendMul
import net.torvald.terrarum.blendNormal
import net.torvald.terrarum.gameworld.WorldTime
import net.torvald.terrarum.getPixel
import org.newdawn.slick.Color
@@ -85,26 +87,30 @@ object WeatherMixer {
val skyboxColourMap = currentWeather.skyboxGradColourMap
val lightColourMap = currentWeather.globalLightColourMap
// draw skybox to provided (should be main) graphics instance
val skyColourFill = GradientFill(
0f, 0f,
getGradientColour(skyboxColourMap, 0, timeNow),
0f, Terrarum.HEIGHT.toFloat(),// / Terrarum.ingame.screenZoom,
getGradientColour(skyboxColourMap, 1, timeNow)
)
g.fill(Rectangle(
0f, 0f,
Terrarum.WIDTH.toFloat(),// / Terrarum.ingame.screenZoom,
Terrarum.HEIGHT.toFloat()// / Terrarum.ingame.screenZoom
),skyColourFill)
// calculate global light
val gradCol = getGradientColour(lightColourMap, 0, timeNow)
globalLightNow.r = gradCol.r
globalLightNow.g = gradCol.g
globalLightNow.b = gradCol.b
// draw skybox to provided (should be main) graphics instance
val skyColourFill = GradientFill(
0f, 0f,
getGradientColour(skyboxColourMap, 0, timeNow) * gradCol, // mul with globallight
0f, Terrarum.HEIGHT.toFloat(),// / Terrarum.ingame.screenZoom,
getGradientColour(skyboxColourMap, 1, timeNow) * gradCol // mul with globallight
)
blendNormal()
g.fill(Rectangle(
0f, 0f,
Terrarum.WIDTH.toFloat(),// / Terrarum.ingame.screenZoom,
Terrarum.HEIGHT.toFloat()// / Terrarum.ingame.screenZoom
), skyColourFill)
}
operator fun Color.times(other: Color) = Color(this.r * other.r, this.g * other.g, this.b * other.b, 1f)
/**
* Get a GL of specific time
*/