317 lines
6.9 KiB
JavaScript
317 lines
6.9 KiB
JavaScript
/**
|
|
* Simplified Live2D SDK Wrapper
|
|
* Based on the working implementation from live2d-master
|
|
*/
|
|
|
|
class Live2DModel {
|
|
constructor() {
|
|
this.model = null;
|
|
this.motionManager = null;
|
|
this.eyeBlink = null;
|
|
this.pose = null;
|
|
this.physics = null;
|
|
this.modelMatrix = null;
|
|
this.dragManager = null;
|
|
this.isInitialized = false;
|
|
this.isVisible = true;
|
|
this.alpha = 1.0;
|
|
}
|
|
|
|
// Load model from .moc file
|
|
loadModel(mocPath, callback) {
|
|
const request = new XMLHttpRequest();
|
|
request.open('GET', mocPath, true);
|
|
request.responseType = 'arraybuffer';
|
|
|
|
request.onload = () => {
|
|
if (request.status === 200) {
|
|
try {
|
|
this.model = window.Live2DModelWebGL.loadModel(request.response);
|
|
if (this.model) {
|
|
this.initializeModel();
|
|
callback(this);
|
|
} else {
|
|
console.error('Failed to load Live2D model from:', mocPath);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading Live2D model:', error);
|
|
}
|
|
} else {
|
|
console.error('Failed to fetch model file:', mocPath);
|
|
}
|
|
};
|
|
|
|
request.onerror = () => {
|
|
console.error('Network error loading model:', mocPath);
|
|
};
|
|
|
|
request.send();
|
|
}
|
|
|
|
// Initialize model components
|
|
initializeModel() {
|
|
if (!this.model) return;
|
|
|
|
// Initialize model matrix
|
|
this.modelMatrix = new window.L2DModelMatrix(
|
|
this.model.getCanvasWidth(),
|
|
this.model.getCanvasHeight()
|
|
);
|
|
this.modelMatrix.setWidth(2);
|
|
this.modelMatrix.setCenterPosition(0, 0);
|
|
|
|
// Initialize motion manager
|
|
this.motionManager = new window.L2DMotionManager();
|
|
|
|
// Initialize eye blink
|
|
this.eyeBlink = new window.L2DEyeBlink();
|
|
|
|
// Initialize pose
|
|
this.pose = window.L2DPose.load();
|
|
|
|
// Initialize physics
|
|
this.physics = window.L2DPhysics.load();
|
|
|
|
// Initialize drag manager
|
|
this.dragManager = new window.L2DTargetPoint();
|
|
|
|
this.isInitialized = true;
|
|
}
|
|
|
|
// Load texture
|
|
loadTexture(textureIndex, texturePath, callback) {
|
|
if (!this.model) return;
|
|
|
|
const img = new Image();
|
|
img.crossOrigin = 'Anonymous';
|
|
img.onload = () => {
|
|
const gl = window.Live2D.getGL();
|
|
if (gl) {
|
|
const texture = gl.createTexture();
|
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
|
|
gl.generateMipmap(gl.TEXTURE_2D);
|
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
|
|
this.model.setTexture(textureIndex, texture);
|
|
|
|
if (callback) callback();
|
|
}
|
|
};
|
|
img.onerror = () => {
|
|
console.error('Failed to load texture:', texturePath);
|
|
};
|
|
img.src = texturePath;
|
|
}
|
|
|
|
// Load motion
|
|
loadMotion(motionPath, callback) {
|
|
const request = new XMLHttpRequest();
|
|
request.open('GET', motionPath, true);
|
|
request.responseType = 'arraybuffer';
|
|
|
|
request.onload = () => {
|
|
if (request.status === 200) {
|
|
try {
|
|
const motion = window.Live2DMotion.loadMotion(request.response);
|
|
if (motion && callback) {
|
|
callback(motion);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading motion:', error);
|
|
}
|
|
}
|
|
};
|
|
|
|
request.send();
|
|
}
|
|
|
|
// Start motion
|
|
startMotion(motion, priority = 3) {
|
|
if (!this.motionManager || !motion) return;
|
|
|
|
this.motionManager.startMotionPrio(motion, priority);
|
|
}
|
|
|
|
// Set parameter value
|
|
setParamFloat(paramId, value, weight = 1.0) {
|
|
if (!this.model) return;
|
|
this.model.setParamFloat(paramId, value, weight);
|
|
}
|
|
|
|
// Get parameter value
|
|
getParamFloat(paramId) {
|
|
if (!this.model) return 0;
|
|
return this.model.getParamFloat(paramId);
|
|
}
|
|
|
|
// Update model
|
|
update() {
|
|
if (!this.isInitialized || !this.model) return;
|
|
|
|
// Update drag
|
|
if (this.dragManager) {
|
|
this.dragManager.update();
|
|
this.model.setDrag(this.dragManager.getX(), this.dragManager.getY());
|
|
}
|
|
|
|
// Update eye blink
|
|
if (this.eyeBlink) {
|
|
this.eyeBlink.updateParam(this.model);
|
|
}
|
|
|
|
// Update motion
|
|
if (this.motionManager) {
|
|
this.motionManager.updateParam(this.model);
|
|
}
|
|
|
|
// Update pose
|
|
if (this.pose) {
|
|
this.pose.updateParam(this.model);
|
|
}
|
|
|
|
// Update physics
|
|
if (this.physics) {
|
|
this.physics.updateParam(this.model);
|
|
}
|
|
|
|
// Update model
|
|
this.model.update();
|
|
}
|
|
|
|
// Draw model
|
|
draw() {
|
|
if (!this.isInitialized || !this.model || !this.isVisible) return;
|
|
|
|
const gl = window.Live2D.getGL();
|
|
if (!gl) return;
|
|
|
|
// Apply model matrix
|
|
if (this.modelMatrix) {
|
|
this.model.setMatrix(this.modelMatrix.getArray());
|
|
}
|
|
|
|
// Set alpha
|
|
this.model.setAlpha(this.alpha);
|
|
|
|
// Draw
|
|
this.model.draw();
|
|
}
|
|
|
|
// Set drag position
|
|
setDrag(x, y) {
|
|
if (this.dragManager) {
|
|
this.dragManager.setPoint(x, y);
|
|
}
|
|
}
|
|
|
|
// Hit test
|
|
hitTest(x, y, hitAreaName) {
|
|
if (!this.model) return false;
|
|
return this.model.hitTestSimple(hitAreaName, x, y);
|
|
}
|
|
|
|
// Release resources
|
|
release() {
|
|
if (this.model) {
|
|
this.model.release();
|
|
this.model = null;
|
|
}
|
|
this.isInitialized = false;
|
|
}
|
|
}
|
|
|
|
// Live2D Manager
|
|
class Live2DManager {
|
|
constructor() {
|
|
this.gl = null;
|
|
this.models = [];
|
|
this.currentModelIndex = 0;
|
|
this.isInitialized = false;
|
|
}
|
|
|
|
// Initialize WebGL context
|
|
setGL(gl) {
|
|
this.gl = gl;
|
|
window.Live2D.setGL(gl);
|
|
this.isInitialized = true;
|
|
}
|
|
|
|
// Get WebGL context
|
|
getGL() {
|
|
return this.gl;
|
|
}
|
|
|
|
// Create new model
|
|
createModel() {
|
|
return new Live2DModel();
|
|
}
|
|
|
|
// Add model
|
|
addModel(model) {
|
|
this.models.push(model);
|
|
return this.models.length - 1;
|
|
}
|
|
|
|
// Get model
|
|
getModel(index) {
|
|
if (index >= 0 && index < this.models.length) {
|
|
return this.models[index];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Get current model
|
|
getCurrentModel() {
|
|
return this.getModel(this.currentModelIndex);
|
|
}
|
|
|
|
// Set current model
|
|
setCurrentModel(index) {
|
|
if (index >= 0 && index < this.models.length) {
|
|
this.currentModelIndex = index;
|
|
}
|
|
}
|
|
|
|
// Update all models
|
|
updateModels() {
|
|
this.models.forEach(model => {
|
|
if (model) {
|
|
model.update();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Draw all models
|
|
drawModels() {
|
|
if (!this.gl) return;
|
|
|
|
this.gl.clearColor(0, 0, 0, 0);
|
|
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
|
|
|
|
this.models.forEach(model => {
|
|
if (model) {
|
|
model.draw();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Release all models
|
|
releaseModels() {
|
|
this.models.forEach(model => {
|
|
if (model) {
|
|
model.release();
|
|
}
|
|
});
|
|
this.models = [];
|
|
}
|
|
}
|
|
|
|
// Export to global scope
|
|
window.Live2DManager = Live2DManager;
|
|
window.Live2DModel = Live2DModel;
|
|
|
|
// Global Live2D manager instance
|
|
window.live2dManager = new Live2DManager(); |