live2d-widgets
Version:
Live2D widget for web pages
918 lines (917 loc) • 30.5 kB
JavaScript
import logger from '../logger.js';
class L2DBaseModel {
constructor() {
this.live2DModel = null;
this.modelMatrix = null;
this.eyeBlink = null;
this.physics = null;
this.pose = null;
this.initialized = false;
this.updating = false;
this.alpha = 1;
this.accAlpha = 0;
this.lipSync = false;
this.lipSyncValue = 0;
this.accelX = 0;
this.accelY = 0;
this.accelZ = 0;
this.dragX = 0;
this.dragY = 0;
this.startTimeMSec = null;
this.mainMotionManager = new L2DMotionManager();
this.expressionManager = new L2DMotionManager();
this.motions = {};
this.expressions = {};
this.isTexLoaded = false;
}
getModelMatrix() {
return this.modelMatrix;
}
setAlpha(a) {
if (a > 0.999)
a = 1;
if (a < 0.001)
a = 0;
this.alpha = a;
}
getAlpha() {
return this.alpha;
}
isInitialized() {
return this.initialized;
}
setInitialized(v) {
this.initialized = v;
}
isUpdating() {
return this.updating;
}
setUpdating(v) {
this.updating = v;
}
getLive2DModel() {
return this.live2DModel;
}
setLipSync(v) {
this.lipSync = v;
}
setLipSyncValue(v) {
this.lipSyncValue = v;
}
setAccel(x, y, z) {
this.accelX = x;
this.accelY = y;
this.accelZ = z;
}
setDrag(x, y) {
this.dragX = x;
this.dragY = y;
}
getMainMotionManager() {
return this.mainMotionManager;
}
getExpressionManager() {
return this.expressionManager;
}
loadModelData(path, callback) {
const pm = Live2DFramework.getPlatformManager();
logger.info('Load model : ' + path);
pm.loadLive2DModel(path, (l2dModel) => {
this.live2DModel = l2dModel;
this.live2DModel.saveParam();
const _err = Live2D.getError();
if (_err != 0) {
logger.error('Error : Failed to loadModelData().');
return;
}
this.modelMatrix = new L2DModelMatrix(this.live2DModel.getCanvasWidth(), this.live2DModel.getCanvasHeight());
this.modelMatrix.setWidth(2);
this.modelMatrix.setCenterPosition(0, 0);
callback(this.live2DModel);
});
}
loadTexture(no, path, callback) {
texCounter++;
const pm = Live2DFramework.getPlatformManager();
logger.info('Load Texture : ' + path);
pm.loadTexture(this.live2DModel, no, path, () => {
texCounter--;
if (texCounter == 0)
this.isTexLoaded = true;
if (typeof callback == 'function')
callback();
});
}
loadMotion(name, path, callback) {
const pm = Live2DFramework.getPlatformManager();
logger.trace('Load Motion : ' + path);
let motion = null;
pm.loadBytes(path, (buf) => {
motion = Live2DMotion.loadMotion(buf);
if (name != null) {
this.motions[name] = motion;
}
callback(motion);
});
}
loadExpression(name, path, callback) {
const pm = Live2DFramework.getPlatformManager();
logger.trace('Load Expression : ' + path);
pm.loadBytes(path, (buf) => {
if (name != null) {
this.expressions[name] = L2DExpressionMotion.loadJson(buf);
}
if (typeof callback == 'function')
callback();
});
}
loadPose(path, callback) {
const pm = Live2DFramework.getPlatformManager();
logger.trace('Load Pose : ' + path);
try {
pm.loadBytes(path, (buf) => {
this.pose = L2DPose.load(buf);
if (typeof callback == 'function')
callback();
});
}
catch (e) {
logger.warn(e);
}
}
loadPhysics(path) {
const pm = Live2DFramework.getPlatformManager();
logger.trace('Load Physics : ' + path);
try {
pm.loadBytes(path, (buf) => {
this.physics = L2DPhysics.load(buf);
});
}
catch (e) {
logger.warn(e);
}
}
hitTestSimple(drawID, testX, testY) {
const drawIndex = this.live2DModel.getDrawDataIndex(drawID);
if (drawIndex < 0)
return false;
const points = this.live2DModel.getTransformedPoints(drawIndex);
let left = this.live2DModel.getCanvasWidth();
let right = 0;
let top = this.live2DModel.getCanvasHeight();
let bottom = 0;
for (let j = 0; j < points.length; j = j + 2) {
const x = points[j];
const y = points[j + 1];
if (x < left)
left = x;
if (x > right)
right = x;
if (y < top)
top = y;
if (y > bottom)
bottom = y;
}
const tx = this.modelMatrix.invertTransformX(testX);
const ty = this.modelMatrix.invertTransformY(testY);
return left <= tx && tx <= right && top <= ty && ty <= bottom;
}
}
let texCounter = 0;
class L2DExpressionMotion extends AMotion {
constructor() {
super();
this.paramList = [];
}
static loadJson(buf) {
const ret = new L2DExpressionMotion();
const pm = Live2DFramework.getPlatformManager();
const json = pm.jsonParseFromBytes(buf);
ret.setFadeIn(parseInt(json.fade_in) > 0 ? parseInt(json.fade_in) : 1000);
ret.setFadeOut(parseInt(json.fade_out) > 0 ? parseInt(json.fade_out) : 1000);
if (json.params == null) {
return ret;
}
const params = json.params;
const paramNum = params.length;
ret.paramList = [];
for (let i = 0; i < paramNum; i++) {
const param = params[i];
const paramID = param.id.toString();
let value = parseFloat(param.val);
let calcTypeInt = L2DExpressionMotion.TYPE_ADD;
const calc = param.calc != null ? param.calc.toString() : 'add';
if (calc === 'add') {
calcTypeInt = L2DExpressionMotion.TYPE_ADD;
}
else if (calc === 'mult') {
calcTypeInt = L2DExpressionMotion.TYPE_MULT;
}
else if (calc === 'set') {
calcTypeInt = L2DExpressionMotion.TYPE_SET;
}
else {
calcTypeInt = L2DExpressionMotion.TYPE_ADD;
}
if (calcTypeInt == L2DExpressionMotion.TYPE_ADD) {
let defaultValue = param.def == null ? 0 : parseFloat(param.def);
value = value - defaultValue;
}
else if (calcTypeInt == L2DExpressionMotion.TYPE_MULT) {
let defaultValue = param.def == null ? 1 : parseFloat(param.def);
if (defaultValue == 0)
defaultValue = 1;
value = value / defaultValue;
}
const item = new L2DExpressionParam();
item.id = paramID;
item.type = calcTypeInt;
item.value = value;
ret.paramList.push(item);
}
return ret;
}
updateParamExe(model, timeMSec, weight, motionQueueEnt) {
for (let i = this.paramList.length - 1; i >= 0; --i) {
const param = this.paramList[i];
if (param.type == L2DExpressionMotion.TYPE_ADD) {
model.addToParamFloat(param.id, param.value, weight);
}
else if (param.type == L2DExpressionMotion.TYPE_MULT) {
model.multParamFloat(param.id, param.value, weight);
}
else if (param.type == L2DExpressionMotion.TYPE_SET) {
model.setParamFloat(param.id, param.value, weight);
}
}
}
}
L2DExpressionMotion.EXPRESSION_DEFAULT = 'DEFAULT';
L2DExpressionMotion.TYPE_SET = 0;
L2DExpressionMotion.TYPE_ADD = 1;
L2DExpressionMotion.TYPE_MULT = 2;
function L2DExpressionParam() {
this.id = '';
this.type = -1;
this.value = null;
}
class L2DEyeBlink {
constructor() {
this.nextBlinkTime = null;
this.stateStartTime = null;
this.blinkIntervalMsec = null;
this.eyeState = EYE_STATE.STATE_FIRST;
this.blinkIntervalMsec = 4000;
this.closingMotionMsec = 100;
this.closedMotionMsec = 50;
this.openingMotionMsec = 150;
this.closeIfZero = true;
this.eyeID_L = 'PARAM_EYE_L_OPEN';
this.eyeID_R = 'PARAM_EYE_R_OPEN';
}
calcNextBlink() {
const time = UtSystem.getUserTimeMSec();
const r = Math.random();
return time + r * (2 * this.blinkIntervalMsec - 1);
}
setInterval(blinkIntervalMsec) {
this.blinkIntervalMsec = blinkIntervalMsec;
}
setEyeMotion(closingMotionMsec, closedMotionMsec, openingMotionMsec) {
this.closingMotionMsec = closingMotionMsec;
this.closedMotionMsec = closedMotionMsec;
this.openingMotionMsec = openingMotionMsec;
}
updateParam(model) {
const time = UtSystem.getUserTimeMSec();
let eyeParamValue;
let t = 0;
switch (this.eyeState) {
case EYE_STATE.STATE_CLOSING:
t = (time - this.stateStartTime) / this.closingMotionMsec;
if (t >= 1) {
t = 1;
this.eyeState = EYE_STATE.STATE_CLOSED;
this.stateStartTime = time;
}
eyeParamValue = 1 - t;
break;
case EYE_STATE.STATE_CLOSED:
t = (time - this.stateStartTime) / this.closedMotionMsec;
if (t >= 1) {
this.eyeState = EYE_STATE.STATE_OPENING;
this.stateStartTime = time;
}
eyeParamValue = 0;
break;
case EYE_STATE.STATE_OPENING:
t = (time - this.stateStartTime) / this.openingMotionMsec;
if (t >= 1) {
t = 1;
this.eyeState = EYE_STATE.STATE_INTERVAL;
this.nextBlinkTime = this.calcNextBlink();
}
eyeParamValue = t;
break;
case EYE_STATE.STATE_INTERVAL:
if (this.nextBlinkTime < time) {
this.eyeState = EYE_STATE.STATE_CLOSING;
this.stateStartTime = time;
}
eyeParamValue = 1;
break;
case EYE_STATE.STATE_FIRST:
default:
this.eyeState = EYE_STATE.STATE_INTERVAL;
this.nextBlinkTime = this.calcNextBlink();
eyeParamValue = 1;
break;
}
if (!this.closeIfZero)
eyeParamValue = -eyeParamValue;
model.setParamFloat(this.eyeID_L, eyeParamValue);
model.setParamFloat(this.eyeID_R, eyeParamValue);
}
}
const EYE_STATE = () => { };
EYE_STATE.STATE_FIRST = 'STATE_FIRST';
EYE_STATE.STATE_INTERVAL = 'STATE_INTERVAL';
EYE_STATE.STATE_CLOSING = 'STATE_CLOSING';
EYE_STATE.STATE_CLOSED = 'STATE_CLOSED';
EYE_STATE.STATE_OPENING = 'STATE_OPENING';
class L2DMatrix44 {
constructor() {
this.tr = new Float32Array(16);
this.identity();
}
static mul(a, b, dst) {
const c = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
const n = 4;
let i, j, k;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
for (k = 0; k < n; k++) {
c[i + j * 4] += a[i + k * 4] * b[k + j * 4];
}
}
}
for (i = 0; i < 16; i++) {
dst[i] = c[i];
}
}
identity() {
for (let i = 0; i < 16; i++)
this.tr[i] = i % 5 == 0 ? 1 : 0;
}
getArray() {
return this.tr;
}
getCopyMatrix() {
return new Float32Array(this.tr);
}
setMatrix(tr) {
if (this.tr == null || this.tr.length != this.tr.length)
return;
for (let i = 0; i < 16; i++)
this.tr[i] = tr[i];
}
getScaleX() {
return this.tr[0];
}
getScaleY() {
return this.tr[5];
}
transformX(src) {
return this.tr[0] * src + this.tr[12];
}
transformY(src) {
return this.tr[5] * src + this.tr[13];
}
invertTransformX(src) {
return (src - this.tr[12]) / this.tr[0];
}
invertTransformY(src) {
return (src - this.tr[13]) / this.tr[5];
}
multTranslate(shiftX, shiftY) {
const tr1 = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, shiftX, shiftY, 0, 1];
L2DMatrix44.mul(tr1, this.tr, this.tr);
}
translate(x, y) {
this.tr[12] = x;
this.tr[13] = y;
}
translateX(x) {
this.tr[12] = x;
}
translateY(y) {
this.tr[13] = y;
}
multScale(scaleX, scaleY) {
const tr1 = [scaleX, 0, 0, 0, 0, scaleY, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
L2DMatrix44.mul(tr1, this.tr, this.tr);
}
scale(scaleX, scaleY) {
this.tr[0] = scaleX;
this.tr[5] = scaleY;
}
}
class L2DModelMatrix extends L2DMatrix44 {
constructor(w, h) {
super();
this.width = w;
this.height = h;
}
setPosition(x, y) {
this.translate(x, y);
}
setCenterPosition(x, y) {
const w = this.width * this.getScaleX();
const h = this.height * this.getScaleY();
this.translate(x - w / 2, y - h / 2);
}
top(y) {
this.setY(y);
}
bottom(y) {
const h = this.height * this.getScaleY();
this.translateY(y - h);
}
left(x) {
this.setX(x);
}
right(x) {
const w = this.width * this.getScaleX();
this.translateX(x - w);
}
centerX(x) {
const w = this.width * this.getScaleX();
this.translateX(x - w / 2);
}
centerY(y) {
const h = this.height * this.getScaleY();
this.translateY(y - h / 2);
}
setX(x) {
this.translateX(x);
}
setY(y) {
this.translateY(y);
}
setHeight(h) {
const scaleX = h / this.height;
const scaleY = -scaleX;
this.scale(scaleX, scaleY);
}
setWidth(w) {
const scaleX = w / this.width;
const scaleY = -scaleX;
this.scale(scaleX, scaleY);
}
}
class L2DMotionManager extends MotionQueueManager {
constructor() {
super();
this.currentPriority = null;
this.reservePriority = null;
this.super = MotionQueueManager.prototype;
}
getCurrentPriority() {
return this.currentPriority;
}
getReservePriority() {
return this.reservePriority;
}
reserveMotion(priority) {
if (this.reservePriority >= priority) {
return false;
}
if (this.currentPriority >= priority) {
return false;
}
this.reservePriority = priority;
return true;
}
setReservePriority(val) {
this.reservePriority = val;
}
updateParam(model) {
const updated = MotionQueueManager.prototype.updateParam.call(this, model);
if (this.isFinished()) {
this.currentPriority = 0;
}
return updated;
}
startMotionPrio(motion, priority) {
if (priority == this.reservePriority) {
this.reservePriority = 0;
}
this.currentPriority = priority;
return this.startMotion(motion, false);
}
}
class L2DPhysics {
constructor() {
this.physicsList = [];
this.startTimeMSec = UtSystem.getUserTimeMSec();
}
static load(buf) {
const ret = new L2DPhysics();
const pm = Live2DFramework.getPlatformManager();
const json = pm.jsonParseFromBytes(buf);
const params = json.physics_hair;
const paramNum = params.length;
for (let i = 0; i < paramNum; i++) {
const param = params[i];
const physics = new PhysicsHair();
const setup = param.setup;
const length = parseFloat(setup.length);
const resist = parseFloat(setup.regist);
const mass = parseFloat(setup.mass);
physics.setup(length, resist, mass);
const srcList = param.src;
const srcNum = srcList.length;
for (let j = 0; j < srcNum; j++) {
const src = srcList[j];
let id = src.id;
let type = PhysicsHair.Src.SRC_TO_X;
let typeStr = src.ptype;
if (typeStr === 'x') {
type = PhysicsHair.Src.SRC_TO_X;
}
else if (typeStr === 'y') {
type = PhysicsHair.Src.SRC_TO_Y;
}
else if (typeStr === 'angle') {
type = PhysicsHair.Src.SRC_TO_G_ANGLE;
}
else {
UtDebug.error('live2d', 'Invalid parameter:PhysicsHair.Src');
}
let scale = parseFloat(src.scale);
let weight = parseFloat(src.weight);
physics.addSrcParam(type, id, scale, weight);
}
const targetList = param.targets;
const targetNum = targetList.length;
for (let j = 0; j < targetNum; j++) {
const target = targetList[j];
let id = target.id;
let type = PhysicsHair.Target.TARGET_FROM_ANGLE;
let typeStr = target.ptype;
if (typeStr === 'angle') {
type = PhysicsHair.Target.TARGET_FROM_ANGLE;
}
else if (typeStr === 'angle_v') {
type = PhysicsHair.Target.TARGET_FROM_ANGLE_V;
}
else {
UtDebug.error('live2d', 'Invalid parameter:PhysicsHair.Target');
}
let scale = parseFloat(target.scale);
let weight = parseFloat(target.weight);
physics.addTargetParam(type, id, scale, weight);
}
ret.physicsList.push(physics);
}
return ret;
}
updateParam(model) {
const timeMSec = UtSystem.getUserTimeMSec() - this.startTimeMSec;
for (let i = 0; i < this.physicsList.length; i++) {
this.physicsList[i].update(model, timeMSec);
}
}
}
class L2DPose {
constructor() {
this.lastTime = 0;
this.lastModel = null;
this.partsGroups = [];
}
static load(buf) {
const ret = new L2DPose();
const pm = Live2DFramework.getPlatformManager();
const json = pm.jsonParseFromBytes(buf);
const poseListInfo = json.parts_visible;
const poseNum = poseListInfo.length;
for (let i_pose = 0; i_pose < poseNum; i_pose++) {
const poseInfo = poseListInfo[i_pose];
const idListInfo = poseInfo.group;
const idNum = idListInfo.length;
const partsGroup = [];
for (let i_group = 0; i_group < idNum; i_group++) {
const partsInfo = idListInfo[i_group];
const parts = new L2DPartsParam(partsInfo.id);
partsGroup[i_group] = parts;
if (partsInfo.link == null)
continue;
const linkListInfo = partsInfo.link;
const linkNum = linkListInfo.length;
parts.link = [];
for (let i_link = 0; i_link < linkNum; i_link++) {
const linkParts = new L2DPartsParam(linkListInfo[i_link]);
parts.link.push(linkParts);
}
}
ret.partsGroups.push(partsGroup);
}
return ret;
}
updateParam(model) {
if (model == null)
return;
if (!(model == this.lastModel)) {
this.initParam(model);
}
this.lastModel = model;
const curTime = UtSystem.getUserTimeMSec();
let deltaTimeSec = this.lastTime == 0 ? 0 : (curTime - this.lastTime) / 1000.0;
this.lastTime = curTime;
if (deltaTimeSec < 0)
deltaTimeSec = 0;
for (let i = 0; i < this.partsGroups.length; i++) {
this.normalizePartsOpacityGroup(model, this.partsGroups[i], deltaTimeSec);
this.copyOpacityOtherParts(model, this.partsGroups[i]);
}
}
initParam(model) {
if (model == null)
return;
for (let i = 0; i < this.partsGroups.length; i++) {
const partsGroup = this.partsGroups[i];
for (let j = 0; j < partsGroup.length; j++) {
partsGroup[j].initIndex(model);
const partsIndex = partsGroup[j].partsIndex;
const paramIndex = partsGroup[j].paramIndex;
if (partsIndex < 0)
continue;
const v = model.getParamFloat(paramIndex) != 0;
model.setPartsOpacity(partsIndex, v ? 1.0 : 0.0);
model.setParamFloat(paramIndex, v ? 1.0 : 0.0);
if (partsGroup[j].link == null)
continue;
for (let k = 0; k < partsGroup[j].link.length; k++) {
partsGroup[j].link[k].initIndex(model);
}
}
}
}
normalizePartsOpacityGroup(model, partsGroup, deltaTimeSec) {
let visibleParts = -1;
let visibleOpacity = 1.0;
const CLEAR_TIME_SEC = 0.5;
const phi = 0.5;
const maxBackOpacity = 0.15;
for (let i = 0; i < partsGroup.length; i++) {
let partsIndex = partsGroup[i].partsIndex;
const paramIndex = partsGroup[i].paramIndex;
if (partsIndex < 0)
continue;
if (model.getParamFloat(paramIndex) != 0) {
if (visibleParts >= 0) {
break;
}
visibleParts = i;
visibleOpacity = model.getPartsOpacity(partsIndex);
visibleOpacity += deltaTimeSec / CLEAR_TIME_SEC;
if (visibleOpacity > 1) {
visibleOpacity = 1;
}
}
}
if (visibleParts < 0) {
visibleParts = 0;
visibleOpacity = 1;
}
for (let i = 0; i < partsGroup.length; i++) {
let partsIndex = partsGroup[i].partsIndex;
if (partsIndex < 0)
continue;
if (visibleParts == i) {
model.setPartsOpacity(partsIndex, visibleOpacity);
}
else {
let opacity = model.getPartsOpacity(partsIndex);
let a1;
if (visibleOpacity < phi) {
a1 = (visibleOpacity * (phi - 1)) / phi + 1;
}
else {
a1 = ((1 - visibleOpacity) * phi) / (1 - phi);
}
const backOp = (1 - a1) * (1 - visibleOpacity);
if (backOp > maxBackOpacity) {
a1 = 1 - maxBackOpacity / (1 - visibleOpacity);
}
if (opacity > a1) {
opacity = a1;
}
model.setPartsOpacity(partsIndex, opacity);
}
}
}
copyOpacityOtherParts(model, partsGroup) {
for (let i_group = 0; i_group < partsGroup.length; i_group++) {
const partsParam = partsGroup[i_group];
if (partsParam.link == null)
continue;
if (partsParam.partsIndex < 0)
continue;
const opacity = model.getPartsOpacity(partsParam.partsIndex);
for (let i_link = 0; i_link < partsParam.link.length; i_link++) {
const linkParts = partsParam.link[i_link];
if (linkParts.partsIndex < 0)
continue;
model.setPartsOpacity(linkParts.partsIndex, opacity);
}
}
}
}
class L2DPartsParam {
constructor(id) {
this.paramIndex = -1;
this.partsIndex = -1;
this.link = null;
this.id = id;
}
initIndex(model) {
this.paramIndex = model.getParamIndex('VISIBLE:' + this.id);
this.partsIndex = model.getPartsDataIndex(PartsDataID.getID(this.id));
model.setParamFloat(this.paramIndex, 1);
}
}
class L2DTargetPoint {
constructor() {
this.EPSILON = 0.01;
this.faceTargetX = 0;
this.faceTargetY = 0;
this.faceX = 0;
this.faceY = 0;
this.faceVX = 0;
this.faceVY = 0;
this.lastTimeSec = 0;
}
setPoint(x, y) {
this.faceTargetX = x;
this.faceTargetY = y;
}
getX() {
return this.faceX;
}
getY() {
return this.faceY;
}
update() {
const TIME_TO_MAX_SPEED = 0.15;
const FACE_PARAM_MAX_V = 40.0 / 7.5;
const MAX_V = FACE_PARAM_MAX_V / L2DTargetPoint.FRAME_RATE;
if (this.lastTimeSec == 0) {
this.lastTimeSec = UtSystem.getUserTimeMSec();
return;
}
const curTimeSec = UtSystem.getUserTimeMSec();
const deltaTimeWeight = ((curTimeSec - this.lastTimeSec) * L2DTargetPoint.FRAME_RATE) / 1000.0;
this.lastTimeSec = curTimeSec;
const FRAME_TO_MAX_SPEED = TIME_TO_MAX_SPEED * L2DTargetPoint.FRAME_RATE;
const MAX_A = (deltaTimeWeight * MAX_V) / FRAME_TO_MAX_SPEED;
const dx = this.faceTargetX - this.faceX;
const dy = this.faceTargetY - this.faceY;
if (Math.abs(dx) <= this.EPSILON && Math.abs(dy) <= this.EPSILON)
return;
const d = Math.sqrt(dx * dx + dy * dy);
const vx = (MAX_V * dx) / d;
const vy = (MAX_V * dy) / d;
let ax = vx - this.faceVX;
let ay = vy - this.faceVY;
let a = Math.sqrt(ax * ax + ay * ay);
if (a < -MAX_A || a > MAX_A) {
ax *= MAX_A / a;
ay *= MAX_A / a;
a = MAX_A;
}
this.faceVX += ax;
this.faceVY += ay;
{
const max_v = 0.5 *
(Math.sqrt(MAX_A * MAX_A + 16 * MAX_A * d - 8 * MAX_A * d) - MAX_A);
const cur_v = Math.sqrt(this.faceVX * this.faceVX + this.faceVY * this.faceVY);
if (cur_v > max_v) {
this.faceVX *= max_v / cur_v;
this.faceVY *= max_v / cur_v;
}
}
this.faceX += this.faceVX;
this.faceY += this.faceVY;
}
}
L2DTargetPoint.FRAME_RATE = 30;
class L2DViewMatrix extends L2DMatrix44 {
constructor() {
super();
this.screenLeft = null;
this.screenRight = null;
this.screenTop = null;
this.screenBottom = null;
this.maxLeft = null;
this.maxRight = null;
this.maxTop = null;
this.maxBottom = null;
this.max = Number.MAX_VALUE;
this.min = 0;
}
getMaxScale() {
return this.max;
}
getMinScale() {
return this.min;
}
setMaxScale(v) {
this.max = v;
}
setMinScale(v) {
this.min = v;
}
isMaxScale() {
return this.getScaleX() == this.max;
}
isMinScale() {
return this.getScaleX() == this.min;
}
adjustTranslate(shiftX, shiftY) {
if (this.tr[0] * this.maxLeft + (this.tr[12] + shiftX) > this.screenLeft)
shiftX = this.screenLeft - this.tr[0] * this.maxLeft - this.tr[12];
if (this.tr[0] * this.maxRight + (this.tr[12] + shiftX) < this.screenRight)
shiftX = this.screenRight - this.tr[0] * this.maxRight - this.tr[12];
if (this.tr[5] * this.maxTop + (this.tr[13] + shiftY) < this.screenTop)
shiftY = this.screenTop - this.tr[5] * this.maxTop - this.tr[13];
if (this.tr[5] * this.maxBottom + (this.tr[13] + shiftY) >
this.screenBottom)
shiftY = this.screenBottom - this.tr[5] * this.maxBottom - this.tr[13];
const tr1 = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, shiftX, shiftY, 0, 1];
L2DMatrix44.mul(tr1, this.tr, this.tr);
}
adjustScale(cx, cy, scale) {
const targetScale = scale * this.tr[0];
if (targetScale < this.min) {
if (this.tr[0] > 0)
scale = this.min / this.tr[0];
}
else if (targetScale > this.max) {
if (this.tr[0] > 0)
scale = this.max / this.tr[0];
}
const tr1 = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, cx, cy, 0, 1];
const tr2 = [scale, 0, 0, 0, 0, scale, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
const tr3 = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -cx, -cy, 0, 1];
L2DMatrix44.mul(tr3, this.tr, this.tr);
L2DMatrix44.mul(tr2, this.tr, this.tr);
L2DMatrix44.mul(tr1, this.tr, this.tr);
}
setScreenRect(left, right, bottom, top) {
this.screenLeft = left;
this.screenRight = right;
this.screenTop = top;
this.screenBottom = bottom;
}
setMaxScreenRect(left, right, bottom, top) {
this.maxLeft = left;
this.maxRight = right;
this.maxTop = top;
this.maxBottom = bottom;
}
getScreenLeft() {
return this.screenLeft;
}
getScreenRight() {
return this.screenRight;
}
getScreenBottom() {
return this.screenBottom;
}
getScreenTop() {
return this.screenTop;
}
getMaxLeft() {
return this.maxLeft;
}
getMaxRight() {
return this.maxRight;
}
getMaxBottom() {
return this.maxBottom;
}
getMaxTop() {
return this.maxTop;
}
}
class Live2DFramework {
static getPlatformManager() {
return Live2DFramework.platformManager;
}
static setPlatformManager(platformManager) {
Live2DFramework.platformManager = platformManager;
}
}
Live2DFramework.platformManager = null;
export { L2DBaseModel, L2DViewMatrix, L2DEyeBlink, Live2DFramework, L2DMatrix44, L2DTargetPoint };